Verilog HDL语法总结
逻辑 0:表示低电平,也就是对应我们电路的GND; 逻辑 1:表示高电平,也就是对应我们电路的VCC; 逻辑 X:表示未知,有可能是高电平,也有可能是低电平; 逻辑 Z:表示高阻态,外部没有激励信号是一个悬空状态。 Verilog的标识符可以是任意一组字母、数字、 $和_(下划线)符号的组合,但标识符的第一个字符必须是字母或者下划线。另外,标识符是区分大小写的。(采用一些前缀或后缀,比如:时钟采用clk前缀: clk_50m, clk_cpu;低电平采用_n后缀: enable_n;统一缩写,如全局复位信号rst;参数统一采用大写,如定义参数使用SIZE) 二进制:4’b0101;十进制:4’d2;十六进制:4’ha。当代码中没有指定数字的位宽与进制时,默认为32位的十进制,比如100,实际上表示的值为32’d100。 在Verilog语法中,主要有三大类数据类型,即寄存器类型、线网类型和参数类型。真正在数字电路中起作用的数据类型应该是寄存器类型和线网类型。 寄存器类型 表示一个抽象的数据存储单元,它只能在always语句和initial语句中被赋值,并且它的值从一个赋值到另一个赋值过程中被保存下来。如果该过程语句描述的是时序逻辑,即always语句带有时钟信号,则该寄存器变量对应为寄存器;如果该过程语句描述的是组合逻辑, 即always语句不带有时钟信号,则该寄存器变量对应为硬件连线;寄存器类型的缺省值是x(未知状态)。 线网 表示Verilog结构化元件间的物理连线。它的值由驱动元件的值决定,例如连续赋值或门的输出。如果没有驱动元件连接到线网,线网的缺省值为z(高阻态) 。 参数 其实就是一个常量,常被用于定义状态机的状态、数据位宽和延迟大小等,由于它可以在编译时修改参数的值,因此它又常被用于一些参数可调的模块中,使用户在实例化模块时,可以根据需要配置参数。在定义参数时,我们可以一次定义多个参数,参数与参数之间需要用逗号隔开。这里我们需要注意的是参数的定义是局部的,只在当前模块中有效。将parameter定义放在紧跟着module的输入输出定义之后。
reg [ 31:0] delay_cnt; //延时计数器
reg key_flag ; //按键标志
wire data_en; //数据使能信号
wire [ 7:0] data ; //数据
parameter DATA_WIDTH = 8; //数据位宽为8位
算术运算符:常用的算术运算符主要包括加减乘除和模除(模除运算也叫取余运算)。Verilog实现乘除比较浪费组合逻辑资源,尤其是除法。一般2的指数次幂的乘除法使用移位运算来完成运算,详情可以看移位运算符章节。非2的指数次幂的乘除法一般是调用现成的IP。 逻辑运算符:!,&&,|| 位运算符:~,&,|,^ 拼接运算符:{},例{a,b}:将a和b拼接起来, 作为一个新信号。 关键字: always 产生reg信号语句的关键字; assign 产生wire信号语句的关键字; 注 :wire信号定义,wire信号就是硬件连线,比如此处的counter_en,代表计数到最大值时产生高电平使能,本质上是一个硬件连线,其实代表的是一些计数器/寄存器做逻辑判断的结果。
parameter WIDTH = 25 ;
parameter COUNT_MAX = 25_000_000;
reg [ WIDTH-1:0] counter ;
wire counter_en ;
assign counter_en = ( counter == ( COUNT_MAX - 1'b1)) ? 1' b1 : 1'b0;
阻塞赋值,顾名思义,即在一个always块中,后面的语句会受到前语句的影响,具体来说,在同一个always中,一条阻塞赋值语句如果没有执行结束,那么该语句后面的语句就不能被执行,即被“阻塞” 。也就是说always块内的语句是一种顺序关系,这里和C语言很类似。符号“=”用于阻塞的赋值(如:b = a) ,阻塞赋值“=”在begin和end之间的语句是顺序执行,属于串行 语句。 非阻塞赋值,符号“<=”用于非阻塞赋值(如:b <= a),非阻塞赋值是由时钟节拍决定,在时钟上升到来时,执行赋值语句右边,然后将begin-end之间的所有赋值语句同时赋值到赋值语句的左边,注意:是begin—end之间的所有语句,一起执行,且一个时钟只执行一次, 属于并行 执行语句。 在描述组合逻辑电路的时候,使用阻塞赋值,比如assign赋值语句和不带时钟的always赋值语句,这种电路结构只与输入电平的变化有关系,代码如下:
assign data = ( data_en == 1'b1) ? 8' d255 : 8'd0;
always @( *) begin
if ( en) begin
a = a0;
b = b0;
end
else begin
a = a1;
b = b1;
end
end
assign语句和always语句是Verilog中的两个基本语句, 这两个都是经常使用的语句。 assign语句使用时不能带时钟。 always语句可以带时钟, 也可以不带时钟。 在always不带时钟时,逻辑功能和assign完全一致,都是只产生组合逻辑。且虽然产生的信号定义还是reg类型,但是该语句产生的还是组合逻辑。 比较简单的组合逻辑推荐使用assign语句,比较复杂的组合逻辑推荐使用always语句。 latch是指锁存器,是一种对脉冲电平敏感的存储单元电路。 锁存器和寄存器都是基本存储单元 ,锁存器 是电平触发的存储器, 寄存器 是边沿触发的存储器。两者的基本功能是一样的,都可以存储数据。锁存器是组合逻辑产生的, 而寄存器是在时序电路中使用,由时钟触发产生的。 latch的主要危害是会产生毛刺(glitch),这种毛刺对下一级电路是很危险的。并且其隐蔽性很强,不易查出。因此,在设计中,应尽量避免latch的使用。代码里面出现latch的两个原因是在组合逻辑中,if或者case语句不完整的描述, 比如if缺少else分支,case缺少default分支,导致代码在综合过程中出现了latch。解决办法就是if必须带else分支,case必须带default分支。