文章目录
前言
这一篇文章主要比较Verilog always块与Systemverilog 中的always_comb、always_ff、always_latch块的区别。一、Verilog always与Systemverilog always_comb、always_latch、always_ff对比
1、Verilog中使用always过程块表示锁存逻辑和组合逻辑的区别:always表示锁存逻辑的情况:
过程块中赋值的变量至少有一个不能被某些输入条件更新。
always表示组合逻辑的情况:
所有在过程块中赋值的变量必须随所有可能的输入组合变量而更新。(if else 要完整) 因为Verilog的always过程块是通用的,因此不能强制其他软件工具也采用这些综合原则,比如,仿真工具必须允许always 过程块能够灵活地使用,不必遵循综合编译器的使用方式,因为always过程块的仿真和综合不依照同样的语法规则,所以如果设计者没有遵循严格的、自我约束的建模规则的情况下,自然会发生仿真和综合结果不一致的情况,形式验证工具也可能会要求设计遵循自我约束的建模规则来避免仿真结果和形式验证结果的不一致。
Systemverilog 新加入了3个特有的过程块,用来减少通用always过程块建模硬件时的不确定性,它们是always_comb、always_ff、always_latch。
Verilog和Systemverilog 关于锁存器建模的区别
Verilog
always @(a,en)
if(en) y=a;
Systemverilog
always_comb
if(en) y=a;
分析:
从always_comb这个关键字出发,软件工具就能够明确设计者想要对组合逻辑建模,并且给出一个警告信息,指出在硬件实现该功能块时会产生一个锁存器。
在上面的例子中,如果想得到组合逻辑,正确的描述应该包括一个else分支,这样输出y就能够在en的条件下更新。
二、零时刻自动求值
1、 always_comb 与 always不同
always_comb过程块与always过程块的另一个不同之处在于,在所有initial和always过程块启动后,always_comb块会在仿真的零时刻自动触发,不管推断出的敏感表中的信号是否发生了变化,这样的自动求值都会发生。always_comb的这种特殊的语义确保了组合逻辑在零时刻产生与输入相对应的输出结果,特别是在使用缺省值为逻辑0的两态变量建模时,这种零时刻的自由自动求值尤为重要,复位信号很可能不会引起组合逻辑的敏感表中的信号发生变化,而如果没有变化,通用always过程块不会被触发,从而使输出变量也不会变化。代码如下:
module controller(output logic read,write,
input instr_t instruction,
input logic clk,resetN);
enum {WAITE,LOAD,STORE} State,NextState;
always @(posedge clk or negedge resetN)
if(!resetN)
State <= WAITE;
else
State <= NextState;
//always_comb
always @(State) begin
case (State)
WAITE: NextState=LOAD;
LOAD: NextState=STORE;
STORE: NextState=WAITE;
endcase
end
always @(State,instruction) begin
read=0;write=0; //初始值 防止生成锁存器
if(State==LOAD && instruction ==FETCH)
read=1;
else if(State==STORE && instruction == WRITE)
write=1;
end
endmodule
由于read=0,write=0,所以即时if没有else的分支,
也不会有锁存器生成。
这个仿真会有一个微秒的结果,在仿真的零时刻,枚举类型变量默认为枚举类型基类的缺省值。除非显式声明,否则基类为两态int类型,仿真开始时int的初始值为0,这也是枚举值列表中WAITE的值。因此,变量State和NextState的缺省值都为WAITE,在clk的上升沿,状态时序逻辑会将NextState的值赋给State,由于两个变量的值是一样的,所以赋值后State的值实际上并没有变化,而正因为State没有发生变化,也就不会触发always@(State)过程块,导致变量NextState的值不断更新,这样变量State和NextState的值始终相同,导致此模型的仿真结果锁定。
使用always_comb
module controller(output logic read,write,
input instr_t instruction,
input logic clk,resetN);
enum {WAITE,LOAD,STORE} State,NextState;
always @(posedge clk or negedge resetN)
if(!resetN)
State <= WAITE;
else
State <= NextState;
//always_comb
always_comb begin
case (State)
WAITE: NextState=LOAD;
LOAD: NextState=STORE;
STORE: NextState=WAITE;
endcase
end
always @(State,instruction) begin
read=0;write=0; //初始值 防止生成锁存器
if(State==LOAD && instruction ==FETCH)
read=1;
else if(State==STORE && instruction == WRITE)
write=1;
end
endmodule
当所有过程块启动之后,always_comb过程块会在仿真零时刻自动执行一次,也就是说在仿真零时刻,这个例子中的变量NextState会更新为与变量State值相对应的新值。当第一个时钟上升沿来临之后,State的值变为NextState的值,这个值与原来的State值不同的,这种变化会触发always_comb过程块,然后更新NextState值对应新的State的值。
2.always_comb 和 always@*的比较
与always相比always_comb敏感列表包含函数的读取信号。always_comb过程块会对data、sel、c、d和e敏感
always @* begin
a1 = data << 1;
b1 = decode();
end
//推断出@(data)
always_comb begin
a2 = data << 1;
b2 = decode();
end
//推断出@(data,sel,c,d,e)
function decode; //不带输入的函数
begin
case(sel)
2'b01: decode = d| e;
2'b10: decode = d & e;
default: decode = c;
endcase
end
endfunction