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)”,当d1d2d3任意一致信号发生变化时,都会触发进程完成对应的操作。所以“@*”是一种可以将过程语句中需要添加到敏感信号列表中信号省略的方法,但是这种方法是不是对所有的组合逻辑都适用呢?请看下面示例。


【示例】
`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

1ns时,d1发生变化,此时“@*”被触发,23行被执行,25行因为flag没有发生变化所以处于等待状态。在2nsflag发生变化,“@(flag)”被触发,其后语句被执行,但是此时“@*”并没有被触发,因为“@*”并没有包括flag,虽然“@flag”位于“@*”开始的进程中。可见当“@*”后还有“@(具体信号)”时,“@(具体信号)”中的具体的敏感信号不会被包含在“@*”中,后续分析类似,不再赘述 。

通过上述示例和仿真结果得到以下结论:

  • 过程语句中位于赋值表达式左侧的信号都不会被包含在“@*”隐含的敏感信号列表中;

  • 如果当前的过程语句中存在不带参数的函数,那么这些函数中的信号不会被包含在“@*”隐含的敏感信号列表中;

  • 如果当前过程性语句中包含一些事件表达式(@(具体型号)或者wait等)时,那么这些事件表达式的敏感信号将不会被包含在“@*”隐含的敏感信号列表中,即当“@*”后还有“@(具体信号)”时,“@(具体信号)”中的敏感信号不会被包含在“@*”中;






全部评论

相关推荐

头像
11-21 11:39
四川大学 Java
是红鸢啊:忘了还没结束,还有字节的5k 违约金
点赞 评论 收藏
分享
爱看电影的杨桃allin春招:我感觉你在炫耀
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务