Verilog系列:always家族
1 always_comb
always_comb相较原有的"always @(sig_lsit)"和"always@*"有以下几点不同:
-
always_comb不用写敏感信号列表,不用担心其中存在函数调用时漏缺了其中的敏感信号;
-
always_comb会在仿真开始的0时刻自动执行一次,可以有效的解决状态机跳转进入死循环的问题,可参见前文《Verilog系列:使用枚举类型表示状态机进入死循环》;
-
综合工具遇到always_comb后会对其模拟的电路结构进行分析,如果不满足组合逻辑要求,会报出warning消息;
-
always_comb中不能对其他进程中已经被赋值的变量也进行赋值操作;
-
always_comb虽然能够将没有写参数的函数中的敏感信号添加到敏感信号列表中,但是对于没有参数的task不能检测到其中的信号并自动添加;
-
always_comb对于其进程中断言实现部分中的信号不会检测和自动添加到敏感信号表中;
`timescale 1 ns / 1 ps module top_tb; logic [2:0] d1,d2; logic [2:0] result_0,result_1; initial begin d1 = 3'b000; d2 = 3'b000; #1 d1 = 3'b001;#1 d2 = 3'b010; #1 d1 = 3'b001;#1 d2 = 3'b011; #1 $stop; end function logic [31:0] func(); func = d1 | d2; endfunction always_comb begin result_0 = func(); $display("@%0t : Result_0 : %h",$time,result_0); end always @* begin result_1 = func(); $display("@%0t : Result_1 : %h",$time,result_1); end endmodule【仿真结果】
示例中always@*并没有对d1和d2的变化进行处理,即并没有对其调用的函数进行处理,而always_comb可以正确的感知到d1和d2的变化做出正确期望的动作.因此在SystemVerilog发布后,推荐在设计验证过程中使用always_comb替换掉用always@*.
`timescale 1 ns / 1 ps module top_tb; logic [2:0] d1,d2; logic [2:0] result_0,result_1; initial begin d1 = 3'b000; d2 = 3'b000; #1 d1 = 3'b001;#1 d2 = 3'b010; #1 d1 = 3'b001;#1 d2 = 3'b011; #1 $stop; end always_comb begin result_0 = d1 | d2; end always @(d1,d2) begin result_0 = d1; // illegal end endmodule
【仿真结果】
编译错误.
always_comb中不能对其他进程中已经被赋值的变量也进行赋值操作.
`timescale 1 ns / 1 ps module top_tb; logic [2:0] d1,d2; logic [2:0] result_0,result_1; initial begin d1 = 3'b000; d2 = 3'b001; #1 d1 = 3'b001;#1 d2 = 3'b010; #1 d1 = 3'b001;#1 d2 = 3'b011; #1 $stop; end function logic [31:0] func(); func = d1 | d2; endfunction always_comb begin result_0 = func(); $display("@%0t : Result_0 : %h",$time,result_0); end always @(d1,d2) begin result_1 = func(); $display("@%0t : Result_1 : %h",$time,result_1); end endmodule【仿真结果】
上例中always_comb会在仿真开始的时候会自动执行一次,而"always @ (d1,d2)"只有在d1或者d2发生变化时其中的进程才会被执行,所以result_0和result_1的结果不一样.always_comb会在仿真开始的时刻自动执行一次.除了这点不同外,综合工具在对always_comb进行综合时还会检查该段逻辑是否符合组合逻辑的要求,如果其结构不符合会给出警告信息,如下例:
`timescale 1 ns / 1 ps module ll(d,q,clr,en); input d; input clr; input en; output q; logic q; always_comb begin if(clr == 1'b1) begin q = 1'b0; end else if(en) begin q = d; end end endmodule【综合结果】
W:CL118 : latch.v(12) | Latch genetated from always block for signal q;possible missing assignment in an if&nbs***bsp;case statement. @W:CL217:latch.v | always_comb does not infer combinatorial logic.
示例中综合工具对always_comb检查时发现其结构不符合always_comb综合为组合逻辑的要求,而实际上为latch结构,所以会给出警告提醒.而如果确实需要使用的结构为latch,那么可以使用下面的always家族成员.
2 always_latch
always_latch与always_comb的用法、结构、注意事项基本一样,只是该结构需要检查电路是否为latch结构,如果其后的代码结构不符合latch结构,一样也会给出警告信息.
【示例】
`timescale 1 ns / 1 ps module ll(d,q,clr,en); input d; input clr; input en; output q; logic q; always_latch begin if(clr == 1'b1) begin q = 1'b0; end else if(en) begin q = d; end end endmodule
【综合结果】
此时电路结构符合latch结构,所以综合不会给出相关警告信息.可见,使用always_comb和always_latch极大的降低了不期望latch结构的出现,对于设计建模是一个极大的提升,极大的减少了设计失误的可能.
3 always_ff
除了always_comb和always_latch外,always家族中还增加了"always_ff",但与其他两者不同的是,always_ff需要敏感信号列表,并且指定关键字posedge和negedge.该结构同样会检查其中代码风格是否符合触发器的编码风格,如果不符合综合工具同样会给出就警告信息.另外,该结构也和其他两个新增的always家族成员一样,不允许其中位于左侧表达式的变量被其他进程赋值.
【示例】
`timescale 1 ns / 1 ps module ll(d,q,clr,en); input d; input clr; input en; output q; logic q; always_ff @(posedge clk,negedge clr) begin if(!clr) begin q <= 1'b0; end else begin q <= d; end end endmodule
【综合结果】
因为该结构符合触发器结构要求,所以综合工具不会报出警告信息.
可见,新增加的always家族成员从可实现(synthesizableRTL)的角度增强了always家族的功能,极大的降低了设计的一些失误,使设计意图更容易被工具理解.
PS:本文使用的综合工具为Synopsys的SynplifyPremier I-2014.03