目录
1. always, always_comb, alwasy_ff, always_latch
1. always, always_comb, alwasy_ff, always_latch
在旧版本的Verilog中只有一个通用的always过程块(procedural block),SV中追加了三个具有更明确目的专用always块:always_ff, always_comb, always_latch.
与原始的always块一样,这三个新的东西也是无限循环过程块—即每一个仿真周期都执行,与之相对的是initial块,在一次仿真中只执行一次。
但是与原始的always块不一样的是,三个新的always块是专门针对可综合性RTL逻辑建模而定义的,而原始always块则是万金油。事实上,always_ff, always_comb, always_latch能实现的,always都能够实现。
2. 为什么要追加三个新的always块呢?
正因为原始的always是万能的,相应地在信息表述的精确度方面就比较欠缺。也就是说真正的建模意图要根据内部具体语句才能确定,这样就留出了犯错误的空间。
新追加的三个专用块的目的就是要消除这样的模糊性,从名字上就可以一目了然地知道always_ff, always_comb, always_latch分别是用于寄存器(flip-flop,代表时序逻辑sequential logic),组合逻辑以及锁存器的建模。这样从名字就可以清楚地知道设计者的设计意图。人和各种EDA工具都可以更准确地理解代码并作出正确而相互一致的判断。
比如说,设计者本来是想设计一块组合逻辑,但是利用always进行建模时在敏感列表中遗漏了一些信号,或者内部存在不完整的if..else…或者case语句,这样综合工具就可能把它综合成锁存器。但是如果使用always_comb的话,综合工具从名字上就可以确定设计者的意图是要综合一块组合逻辑,因此针对latch式的不完整的if..else…或者case语句就会给出warning信息。
[always_comb]
always_comb用于可综合组合逻辑的建模,不需要明确地定义敏感列表。缺省地,所有出现在always_comb中所有input信号(从always_comb的外部获得赋值,出现在块内赋值语句的右边,或者条件判断语句中出现的信号,等等)都被推断属于它的敏感列表。但是内部定义的用阻塞方式进行赋值的临时变量不属于敏感列表。
always_comb与always @(*)在表达组合逻辑时几乎是等价的,但是仍然存在细微的区别。有兴趣可以阅读[S.Sutherland].6.2.1的说明。在日常的设计中记住坚决地使用always_comb就可以了。
[always_ff]
always_ff用于可综合时序逻辑的建模。
必须带由posedge或者negedge所定义的敏感列表。通常就是”@(posedge clock, negedge resetN”.
在always_ff块中只能使用非阻塞赋值。
[always_latch]
由于在一般的同步设计中是不允许(或至少不推荐)使用latch式设计,所以这里不作说明。在一般的设计中不用它就是了。
【寄存器 vs 锁存器】
简而言之,寄存器(通常仅指flip-flop)就是时钟沿触发的存储单元;而锁存器则指电平敏感的存储单元。前者是(因为是时钟沿触发)同步时序电路,后者因为不是由时钟沿触发而是由其它信号的电平决定其赋值,因此是异步电路。在同步时序电路设计中严禁使用latch设计。
3.always相关的可综合设计规则
【可综合设计规则】
- 使用always_comb进行组合逻辑建模,always_comb中使用阻塞赋值;
- 使用always_ff进行时序逻辑建模,always_ff中只能使用非阻塞赋值。只有非阻塞赋值才能正确描述时钟沿触发的同步逻辑行为。
- 同步设计中一般不允许使用latch,因此不要使用always_latch;
- 通用的always只用于testbench的设计;
- 在always_comb/always_ff/always_latch不允许使用时间和事件控制语句;
- 绝对不允许在同一个过程块内混合使用阻塞(=)和非阻塞(<=)两种赋值。
- 组合逻辑和时序逻辑尽量不要在一个过程块中描述。
4. 几个简单的设计例
Example1: 组合逻辑(阻塞赋值)和时序逻辑(非阻塞赋值)混用
always_ff @(posedge clock)
begin
P = A & B;
Z <= P | C;
end
这个例子同时违反了上面两条规则(6)、(7)。当然这两规则本身是有一定相关性的:时序逻辑用非阻塞赋值;组合逻辑用阻塞赋值。理论上来说,以上语句可能能够正确并且可以综合,但是为了避免混乱以及潜在的问题,不应该用这种组合逻辑和时序逻辑混合的描述方式。有以下两种可能的改正方案:
//--solution#1
always_ff @(posedge clock)
begin
Z <= (A & B) | C;
end
//--solution#2
assign P = A&B;
always_ff @(posedge clock)
begin
Z <= P | C;
end
Example2:
//--solution#1
always_comb
case(cur_state)
s0: begin
out1 = 1’b1;
end
s1: begin
out1 = in1;
out2 = 1’b1;
end
s2: begin
out2 = in2;
end
endcase
以上这段代码存在的问题是,对于out1/out2的赋值而言case语句是不完全的。比如说在cur_state=s0时没有对out2的明确赋值。如果是用旧版本的verilog中的always的话,综合工具就会认为这里要综合成一个锁存器(latch)。但是这里使用的是always_comb,所以综合工具能辨别出内部语句与always_comb是不一致的,因此会给出warning,但是如果设计者不理会这个warning,综合工具仍然可能会将其综合成latch。
解决这里的问题的显然办法就是补齐所有分支对输出变量的赋值。一个简单的方法是在块的开始处先对输出变量给出缺省赋值,这样接下来的分支赋值就不必完全了。如下所示:
//--solution#1
always_comb begin
out1 = 1’b0; out2 = 1’b0;
case(cur_state)
s0: begin
out1 = 1’b1;
end
s1: begin
out1 = in1; out2 = 1’b1;
end
s2: begin
out2 = in2;
end
endcase
end
【参考文献】
[S.Sutherland] SystemVerilog For Design(Second Edition).