初学verilog的同学们,可能潜意识就认为一个reg对应一个触发器,其实不然。
我们先看一个例子:
1 `timescale 1ns/100ps 2 3 module prj_1( 4 input b1,b2, 5 output reg a1,a2 6 ); 7 8 always@(*) begin 9 #5 a1 = b1; 10 end 11 12 always@(*) begin 13 a2 = #5 b2; 14 end 15 endmodule
由于always模块要求赋值左端都是reg类型,所以定义a1、a2均为reg类型。
注意,第8行,敏感列表是"*",*号代替了本always模块里面所有的触发信号。
查看其RTL电路图,会发现是这样的
可以看到,设计的电路是一个纯粹的组合逻辑电路,不会综合出DFF。为什么会这样呢?
以下是verilog-2001的标准中对wire和reg的定义如下:
wire: A wire net can be used for nets that are driven by a single gate or continuous assignment. reg: Assignments to a reg are made by procedural assignments (see 6.2 and 9.2). Since the reg holds a value between assignments, it can be used to model hardware registers. Edge-sensitive (i.e., flip-flops) and level sensitive (i.e., RS and transparent latches) storage elements can be modeled. A reg needs not represent a hardware storage element since it can also be used to represent combinatorial logic.
这段话的意思是:在过程赋值语句中,表达式右侧的计算结果在某种条件的触发下放到一个变量当中,而这个变量可以声明成reg类型的。根据触发条件的不同,过程赋值语句可以建模不同的硬件结构:如果这个条件是时钟的上升沿或下降沿,那么这个硬件模型就是一个触发器;如果这个条件是某一信号的高电平或低电平,那么这个硬件模型就是一个锁存器;如果这个条件是赋值语句右侧任意操作数的变化,那么这个硬件模型就是一个组合逻辑。
也就是说,综合出触发器的条件是,需要有时钟的沿触发。
同样,我们看一段使用function的代码
1 module prj_1( 2 input [7:0]a,b,c,d, 3 input [15:0]e,f, 4 output [15:0] x,y 5 ); 6 7 8 function reg [15:0] muladd; 9 input [7:0] a; 10 input [7:0] b; 11 input [15:0] c; 12 begin 13 muladd = a*b + c; 14 end 15 endfunction 16 17 assign x = muladd(a,b,e); 18 assign y = muladd(c,d,f); 19 20 21 endmodule
function默认返还类型为reg,所以第8句,reg类型可以不声明。muladd实现的功能就是一个乘加运算,是一个纯粹的组合电路。这个module的RTL电路图是这样的
因为没有时钟触发,所以仍是一个纯粹的组合逻辑。
总结:
在一些语句中,需要定义变量为reg类型(always、fuction等)。根据触发条件的不同,过程赋值语句可以建模不同的硬件结构。如果有时钟沿触发,则会生成DFF。
======================================================
ps:
其实
always@(*)
是设计组合逻辑最常的使用方式。