Verilog系列:请不要滥用always@*
在Verilog中,位于always中的过程语句在一定条件下会重复执行,always后一般都会跟着敏感信号列表,当敏感信号列表中的信号发生变化时always中的结构就会被触发执行.always常用来构建组合逻辑和时序逻辑.在实际建模的过程中,很多人往往因为疏忽漏缺了敏感信号列表中的部分信号,导致前后仿真结果异常,为此,Verilog引入了一种隐含敏感信号列表的表示方法,即@*.引入这种方式后极大的简化了设计,一定程度上避免了敏感信号列表中信号缺少带来的问题,为此很多设计人员开始疯狂的无论什么场合都是用@*进行处理,然而即使这样处理了,往往还是发生了其中的部分语句没有执行的问题.本文将示例说明使用@*的注意事项, "东西虽好,切勿滥用!"
因为always构建的时序逻辑敏感信号列表中仅包含时序信号,一般情况下不会存在信号确实的情况,所以本文主要针对always构建的组合逻辑情况进行说明。下面这个示例展示了“@*”和“@(signal_list)”等价的情况。
`timescale 1 ns / 1 ps module top_tb; logic [2:0] d1,d2,d3; logic [2:0] result_0,result_1; // @* always @* begin result_0 = d1 | d2 | d3; end // @list always @(d1&nbs***bsp;d2&nbs***bsp;d3) begin result_1 = d1 | d2 | d3; end initial begin d1 = 3'b000; d2 = 3'b000; d3 = 3'b000; #1 d1 = 3'b001; #1 d2 = 3'b011; #1 d3 = 3'b100; #1 d1 = 3'b100; #1 d2 = 3'b010; #1 d3 = 3'b101; #1 d1 = 3'b111; #1 $stop; end endmodule // top_tb【仿真结果】
从仿真结果可以观测到,6行-9行和11行-14行执行结果一致,即此处“@*”在这里等价于“@(d1 or d2 or d3)”,当d1、d2、d3任意一致信号发生变化时,都会触发进程完成对应的操作。所以“@*”是一种可以将过程语句中需要添加到敏感信号列表中信号省略的方法,但是这种方法是不是对所有的组合逻辑都适用呢?请看下面示例。
`timescale 1 ns / 1 ps module top_tb; logic [2:0] d1,d2,d3; logic [2:0] result_0,result_1; function [2:0] cal; cal = d1 | d2 | d3; endfunction // cal // @* always @* begin result_0 = cal(); end // @list always @(d1&nbs***bsp;d2&nbs***bsp;d3) begin result_1 = cal(); end initial begin d1 = 3'b000; d2 = 3'b000; d3 = 3'b000; #1 d1 = 3'b001; #1 d2 = 3'b011; #1 d3 = 3'b100; #1 d1 = 3'b100; #1 d2 = 3'b010; #1 d3 = 3'b101; #1 d1 = 3'b111; #1 $stop; end endmodule // top_tb【仿真结果】
这个示例中,在两个过程性语句中都调用函数(该函数没有形参),“@*”对应的过程语句中的函数并没有执行,所以result_0一直为不定态,而“@(d1 or d2 or d3)”中的函数被有效执行,即“@*”对应的过程并没有触发。由此可见,“@*”对于没有形参的函数调用并不能有效的进行敏感信号的自动添加感知。那么在什么情况下的函数调用结构中“@*”可以有效的感知函数中的信号呢?请看下例。
`timescale 1 ns / 1 ps module top_tb; logic [2:0] d1,d2,d3; logic [2:0] result_0,result_1; function [2:0] cal(logic [2:0] d1,logic [2:0] d2,logic [2:0] d3); cal = d1 | d2 | d3; endfunction // cal // @* always @* begin result_0 = cal(d1,d2,d3); end // @list always @(d1&nbs***bsp;d2&nbs***bsp;d3) begin result_1 = cal(d1,d2,d3); end initial begin d1 = 3'b000; d2 = 3'b000; d3 = 3'b000; #1 d1 = 3'b001; #1 d2 = 3'b011; #1 d3 = 3'b100; #1 d1 = 3'b100; #1 d2 = 3'b010; #1 d3 = 3'b101; #1 d1 = 3'b111; #1 $stop; end endmodule // top_tb【仿真结果】
上例中可以看到,当调用的函数将其中所有的被读取的信号都列于其参数列表中时,“@*”可以有效的感知到函数中被读取信号的变化,从而触发函数的正确执行。
这里还有一种特殊情况,其中的信号并不会自动添加到“@*”中被感知,如下例。
`timescale 1 ns / 1 ps module top_tb; logic [2:0] d1,d2; logic [2:0] result; logic flag; initial begin d1 = 3'b000; d2 = 3'b010; flag = 1'b0; #1 d1 = 3'b100; #1 flag = 1'b1; #1 d2 = 3'b101; #1 flag = 1'b0; #1 flag = 1'b1; #1 d1 = 3'b001; d2 = 3'b100; #1 $stop; end always @* // equal to (d1&nbs***bsp;d2) begin result = d1 | d2; $display("BEFORE @%0t",$time); @(flag) result = d1 | d2; $display("AFTER @%0t",$time); end endmodule // top_tb【仿真结果】
# BEFORE @1000 # AFTER @2000 # BEFORE @3000 # AFTER @4000 # BEFORE @6000
通过上述示例和仿真结果得到以下结论:
-
过程语句中位于赋值表达式左侧的信号都不会被包含在“@*”隐含的敏感信号列表中;
-
如果当前的过程语句中存在不带参数的函数,那么这些函数中的信号不会被包含在“@*”隐含的敏感信号列表中;
-
如果当前过程性语句中包含一些事件表达式(@(具体型号)或者wait等)时,那么这些事件表达式的敏感信号将不会被包含在“@*”隐含的敏感信号列表中,即当“@*”后还有“@(具体信号)”时,“@(具体信号)”中的敏感信号不会被包含在“@*”中;