FPGA的Veilog HDL语法、框架总结

摘要:Verilog HDL硬件描述语言是在用途最广泛的C语言的基础上发展起来的一种硬件描述语言,具有灵活性高、易学易用等特点。Verilog HDL可以在较短的时间内学习和掌握,FPGA的Veilog HDL基础语法总结,看完这些,FPGA的基本语法应该就没啥问题了!

一、基础知识

1、逻辑值

逻辑0:表示低电平,也就对应我们电路GND

逻辑1:表示高电平,也就是对应我们电路的VCC

逻辑X:表示未知,有可能是高电平,也有可能是低电平;

逻辑Z:表示高阻态,外部没有激励信号,是一个悬空状态

2、进制格式

Verilog数字进制格式包括二进制、八进制、十进制和十六进制。

一般常用的为二进制、十进制和十六进制。

二进制表示如下:4b0101表示4位二进制数字0101

十进制表示如下:4'd2表示4位十进制数字2(二进制0010)

十六进制表示如下:4ha表示4位十六进制数字a(二进制1010)

16'b1001 1010 1010 1001=16'h9AA9

3、标识符

标识符(identifier)用于定义模块名、端口名、信号名等。

标识符可以是任意一组字母、数字、$符号和(下划线)符号的组合;

但标识符的第一个字符必须是字母或者下划线;

标识符是区分大小写的;

4、标识符推荐写法

不建议大小写混合使用;

普通内部信号建议全部小写;

信号命名最好体现信号的含义,简洁、清晰、易懂;

以下是一些推荐的写法:

  • 1、用有意义的有效的名字如sumcpu_addr等。
  • 2、用下划线区分词,如cpu addr
  • 3、采用一些前缀或后缀,比如时钟采用clk前缀:clk_50clk_cpu

二、数据类型

在Verilog 语言中,主要有三大类数据类型。

寄存器数据类型、线网数据类型和参数数据类型

从名称中,我们可以看出,真正在数字电路中起作用的数据类型应该是寄存器数据类型线网数据类型

1、寄存器类型

寄存器表示一个抽象的数据存储单元,通过赋值语句可以改变寄存器储存的值寄存器数据类型的关键字是reg,reg类型数据的默认初始值为不定值x。

reg类型的数据只能在always语句initial语句中被赋值。

如果该过程语句描述的是时序逻辑,即always语句带有时钟信号,则该寄存器变量对应为触发器;

如果该过程语句描述的是组合逻辑,即always语句不带有时钟信号,则该寄存器变量对应为硬件连线;

//计数器对系统时钟计数,计时0.2秒
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        counter <= 24'd0;
    else if (counter < 24'd999_9999)
        counter <= counter + 1'b1;
    else
        counter <= 24'd0;
end
//通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        led <= 4'b0001;
    else if(counter == 24'd999_9999) 
        led[3:0] <= {led[2:0],led[3]};
    else
        led <= led;
end

2、线网类型

线网数据类型表示结构实体(例如门)之间的物理连线

线网类型的变量不能储存值,它的值是由驱动它的元件所决定的。驱动线网类型变量的元件有连续赋值语句assign等。

如果没有驱动元件连接到线网类型的变量上,则该变量就是高阻的,即其值为z。

线网数据类型包括wire型tri型,其中最常用的就是wire类型。

3、参数类型

参数其实就是一个常量,在Verilog HDL中用parameter定义常量。

我们可以一次定义多个参数,参数与参数之间需要用逗号隔开

每个参数定义的右边必须是一个常数表达式

参数型数据常用于定义状态机的状态数据位宽延迟大小等。

采用标识符来代表一个常量可以提高程序的可读性和可维护性

在模块调用时,可通过参数传递来改变被调用模块中已定义的参数

三、运算符

1、算数运算符

2、关系运算符

3、逻辑运算符

4、条件操作符

result=(a>=b)?a:b;

5、位运算符

6、移位运算符

两种移位运算都用0来填补移出的空位。

左移时,位宽增加;右移时,位宽不变。

4b1001 <<2 = 6'b100100;

4b1001 >>1 = 4b0100;

7、拼接运算符

c={a,b[3:0];

8、优先级运算符

四、模块结构

Verilog的基本设计单元是“模块'(block)。

一个模块是由两部分组成的,一部分描述接口,另一部分描述逻辑功能。

使用quartusii软件编写出上图左边的硬件描述代码,通过软件编译,就能生成最右边组合逻辑电路图来。每个Verilog程序包括4个主要的部分:端口定义、I0说明、内部信号声明、功能定义

流水灯代码

上图时流水灯的代码,第一个always块代码的意思:

如果时钟信号的上升沿或者复位信号的下降沿到来,就执行beginend之间的代码。

如果产生了复位信号(低电平),计数器清0,如果计数器的值小于10000000,计数器的值就+1,如果没有产生复位信号和计数值不小于10000000,计数器的值就为0。在这个always块中,逻辑是顺序执行的。

第二个always块代码的意思:

如果时钟信号的上升沿或者复位信号的下降沿到来,就执行beginend之间的代码。如果产生了复位信号(低电平),led0点亮,如果计数器的值小于10000000,led0-3顺序点亮,如果没有产生复位信号和计数值不小于10000000,led灯状态保持不变。在这个always块中,逻辑是顺序执行的。

但是这个always块代码是并行执行的,也就是说时钟信号一直在产生。

功能定义部分有三种方法:

  • 1、assign语句描述组合逻辑
  • 2、always语句描述组合/时序逻辑
  • 3、例化实例元件

上述三种逻辑功能是并行执行的。

五、结构语句

1、initial和always语句

initial语句它在模块中只执行一次。

它常用于测试文件的编写,用来产生仿真测试信号(激励信号),或者用于对存储器变量赋初值。

always 语句一直在不断地重复活动。但是只有和一定的时间控制结合在一起才有作用。

一般initial语句常用于测试文件,在测试文件中初始化使用。比如上面的代码首先始终信号初始化为0,之后在always语句中让其10个时钟周期翻转一次,就达到了时钟的要求。

复位信号最开始为低电平,然后延时20个时钟周期就拉高。触摸按键信号最开始为低电平,延时10和时钟周期后拉高,再延时30个时钟周期再拉低,延时110个时钟周期再拉高,再延时30个时钟周期再拉低。

always的时间控制可以是沿触发,也可以是电平触发;可以是单个信号,也可以是多个信号多个信号中间要用关键字or连接。always 语句后紧跟的过程块是否运行,要看它的触发条件是否满足。

沿触发的always块常常描述时序逻辑行为。由关键词or连接的多个事件名或信号名组成的列表称为“敏感列表”。

电平触发的always块常常描述组合逻辑行为。

2、组合逻辑和时序逻辑电路

根据逻辑功能的不同特点,可以将数字电路分成两大类:

组合逻辑电路和时序逻辑电路。

  • 组合逻辑电路中,任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。

  • 时序逻辑电路中,任时刻的输出不仅取决于当时的输入信号,而且还取决于电路原来的状态。或者说还与以前的输入有关,因此时序逻辑必须具备记忆功能。

3、赋值语句

Verilog HDL 语言中,信号有两种赋值方式

1、阻塞赋值(blocking),如b=a

2、非阻塞赋值(Non_Blocking),如b<=a

3.1、阻塞赋值

阻塞赋值可以认为只有一个步骤的操作:即计算RHS(左侧)并更新LHS(右侧)。

所谓阻塞的概念是指,在同一个always块中,后面的赋值语句是在前一句赋值语句结束后才开始赋值的。

module block_nonblock(Clk,Rst_n,a,b,c,out)  input Clk;  input Rst_n;  input a;  input b;  input c;  output reg [1:0] out;// out a + b + c;最大值为3,所以应该定义为2位的位宽// d = a+b;// out = d+c;  reg [1:0]d;//定义一个中间变量always @(posedge Clk or negedge Rst_n)  if (!Rst_n)    out = 2'b0;  else  begin     d   = a+b;    out = d+c;   end

endmodule

现在我们改变一下d= a+b;out = d+c;的顺序,就会发现综合出来的电路是完全不同的。

module block_nonblock(Clk,Rst_n,a,b,c,out)
  input Clk;
  input Rst_n;
  input a;
  input b;
  input c;
  output reg [1:0] out;
  reg [1:0]d;//定义一个中间变量
always @(posedge Clk or negedge Rst_n)
  if (!Rst_n)
    out = 2'b0;
  else  begin     
    out = d+c;
    d   = a+b;
   end
   
endmodule

3.2、非阻塞赋值

非阻塞赋值的操作过程可以看作两个步骤

(1)赋值开始的时候,计算RHS(左侧);

(2)赋值结束的时候,更新LHS(右侧)。

所谓非阻塞的概念是指,在计算非阻塞赋值的RHS以及更新LHS期间,允许其他的非阻塞赋值语句同时计算RHS和更新LHS。

非阻塞赋值只能用于对寄存器类型的变量进行赋值,因此只能用在initial块和always块等过程块中

还是用上面的例子

module block_nonblock(Clk,Rst_n,a,b,c,out)  input Clk;  input Rst_n;  input a;  input b;  input c;  output reg [1:0] out;  reg [1:0]d;//定义一个中间变量always @(posedge Clk or negedge Rst_n)  if (!Rst_n)    out = 2'b0;  else  begin     d   <= a+b;      out <= d+c;     end   endmodule

生成效果如下:

现在我们改变一下d= a+b;out = d+c;的顺序,就会发现综合出来的电路是完全相同的。这里由于采用的非阻塞赋值,因此交换语句的前后顺序并不会对最终生成的逻辑电路有实际影响。

module block_nonblock(Clk,Rst_n,a,b,c,out)
  input Clk;
  input Rst_n;
  input a;
  input b;
  input c;
  output reg [1:0] out;
  reg [1:0]d;//定义一个中间变量
always @(posedge Clk or negedge Rst_n)
  if (!Rst_n)
    out = 2'b0;
  else  begin 
    out <= d+c;
    d   <= a+b;    
   end   
endmodule

1、在描述组合逻辑(电平触发)的always 块中用阻塞赋值=,综合成组合逻辑的电路结构;这种电路结构只与输入电平的变化有关系。

2、在描述时序逻辑(沿触发)的always 块中用非阻塞赋值=,综合成时序逻辑的电路结构;这种电路结构往往与触发沿有关系,只有在触发沿时才可能发生赋值的变化。

注意:在同一个always块中不要既用非阻塞赋值又用阻塞赋值 不充许在多个always块中对同一个变量进行赋值!因为在多个always块中代码时并行执行的。

一般在设计中掌握以下六个原则,可解决在综合后仿真中出现绝大多数的冒险竞争问题。

1)时序电路(沿触发的always块)建模时,用非阻塞赋值;

2)锁存器电路建模时,用非阻塞赋值;

3)用always块建立组合逻辑(电平触发的always块)模型时,用阻塞赋值;

4)在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值:

5)在同一个always块中不要既用非阻塞赋值又用阻塞赋值;

6)不要在一个以上的always块中为同一个变量赋值。

4、条件语句

条件语句必须在过程块中使用。过程块语句是指由initial语句和always语句引导的块语句。

4.1 if_else语句

1、允许一定形式的简写,如:

if(a) 等同于if(a==1)if(la)等同于if(a!=1)

2、if语句对表达式的值进行判断,若为0,x,z,则按假处理;若为1,按真处理。

3、if和else后面的操作语句可以用begin和end包含多个语句。

4、允许if语句的嵌套

4.1 case语句

case语句(多分支选择语句)

1、分支表达式的值互不相同;

2、所有表达式的位宽必须相等;不能用’bx来代替n'bx

3、casez比较时,不考虑表达式中的高阻值

4、casex不考虑高阻值z不定值x

注意

if_else需要配对,一个if语句就应该必须有一个else语句。好处是避免latch产生。latch是一个锁存器,在数字电路中latch是一个电平触发的存储器,触发器是一个边沿触发的存储器。在编写veilog语句中应避免产生无畏锁存器,锁存器只在组合逻辑电路中产成,而锁存器会导致电路生成的毛刺比较多,还会影响我们对整个电路的时序分析。

什么样的情况下会产生这个锁存器呢?

首先在组合逻辑电路中,如果我们有if语句但是没有相应的else语句,他就有可能产生锁存器。第二点,比如case语句,如果我们的case语句没有给完全,没有列举完所有应该的产生的case语句,就应该写一个default,否则也会生成一个锁存器。

End

(0)

相关推荐

  • Verilog非阻塞赋值添加#1延迟设计讨论

    导言: 在进入文章前,先看一段代码如下,这段代码是上次发的IIC的IP核中的一段,具体内容大家可以参阅IIC(一)以及 IIC(二) . // generate clk enable signal   ...

  • Gowin(高云)IDE探索

    快速导航页面 我觉得学习一个软件,自我探索这个环节是必不可少的,软件又用不坏,戳戳点点认识基本功能是极好的.闲话不多说,我们开始~ 首先必须建立一个新项目 可以拉长这个对话框 然后就是写项目名字以及项 ...

  • 【学术论文】多时钟系统下跨时钟域同步电路的设计

    随着时间的推移,集成电路行业发展至今,已有类似如Intel这样先进的foundry,提出迈入10 nm制程的规划.高集成度的推行,也使芯片规模越来越大,功能越来越复杂,设计难度越来越高[1].SOC的 ...

  • 一天一个设计实例-门控时钟和使能时钟

    门控时钟的简介: 组合逻辑中多用门控时钟,一般驱动门控时钟的逻辑都是只包含一个门,如果有其他的附加逻辑,就容易因竞争而产生不希望的毛刺.门控时钟通过一个时能信号控制时钟的开关.当系统不工作时可以关闭时 ...

  • 奇数分频的Verilog实现

    来源:EETOP BLOG从功能上来说,时钟分频电路主要分为整数分频和小数分频,而整数分频又分为奇数分频和偶数分频.下面主要讲整数分频(这里讲的整数分频的占空比都是50%):1.偶数分频偶数分频是分频 ...

  • 【Verilog】阻塞和非阻塞赋值引申出的原则分析

    参考来源:<Verilog数字系统设计(夏宇闻)> 在介绍<[Verilog]深入理解阻塞和非阻塞赋值的不同>时得到下面几个原则: 原则1:时序电路建模时,用非阻塞赋值. 原则 ...

  • 面试中经常会遇到的FPGA基本概念,你会几个?

    面试中经常会遇到的FPGA基本概念,你会几个?

  • 凔海笔记之FPGA(四):Verilog HDL语法简单述

    在百度百科中,是这样介绍Verilog HDL的,它是一种硬件描述语言(HDL:Hardware Description Language),以文本形式来描述数字系统硬件的结构和行为的语言,用它可以表 ...

  • 了解Verilog HDL语法规则吗,看完这篇就知道了

    本节介绍Verilog HDL语法规则,包括文字规则.数据对象及运算符的使用等. Verilog HDL文字规则 1.关键词与标识符 关键词是Verilog HDL中预先定义的单词,它们在程序中有特别 ...

  • 一个视频说清整个英语语法体系(重塑你的语法认知框架)(学浪计划)

    一个视频说清整个英语语法体系(重塑你的语法认知框架)(学浪计划)

  • 语法基本框架,还有时态基本结构,之后精讲...

    语法基本框架,还有时态基本结构,之后精讲语法需要用到,可以先保存

  • FPGA开发流程中的HDL代码生成和验证

    一个汽车工程师团队开始为一辆乘用车建立一个控制系统,比如ADAS控制器(ADAS-ECU).使用的是基于模型的设计,所以他们首先从系统需求中建立一个结构模型:在这种情况下,系统包含环境模型,感知模型, ...

  • 几十张PPT带你了解国产FPGA研究框架

    FPGA(现场可编程逻辑器件)产品的应用领域已经从原来的通信扩展到消费电子.汽车电子.工业控制.测试测量等广泛的领域.而应用的变化也使FPGA产品近几年的演进趋势越来越明显:一方面,FPGA供应商致力 ...

  • 使用 Verilog HDL 在 FPGA 上进行图像处理

    来源:Hack电子该FPGA项目旨在详细展示如何使用Verilog处理图像,从Verilog中读取输入位图图像(.bmp),处理并将处理结果写入Verilog中的输出位图图像.提供了用于读取图像.图像 ...

  • 英语语法认知框架、体系

    在学习英语的过程中,复杂的语法可谓让许多学生倍感难受的部分.时态.虚拟语气.被动语态.词类和句子成分,还有纷繁不清的从句类型,每一样都让人渐渐对学习英语缺乏耐心.对于那些本就被种种语法知识的细枝末节所 ...

  • 正则表达式 – 语法 | 菜鸟教程

    正则表达式 - 语法 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串.将匹配的子串替换或者从某个串中取出符合某个条件 ...