最近总是遇到systemverilog的赋值问题,查看了一下手册发现SV的赋值方式总的还是继承了verilog的赋值方式,而且verilog赋值方面的资料比较多,所以就写了先写一篇关于verilog的赋值总结。
连续赋值
连续赋值就是一旦赋值,输出将随输入改变而变化,一旦修改输入则立刻体现在输出上。
input a,b;
wire out;
assign out = a & b;
- 要求赋值左侧必须为net型
- 关键词assign
- 赋值过程类似于物理场景的导线连接,out将跟随a异或b的电路变化而变化,一旦a或b有修改则立刻体现在输出上。
- 连续赋值属于并发执行,与书写顺序无关,不论在何时赋值都会在一开始执行。
- 对于同一参数多次赋值,视情况和强度而定,但不要进行该操作。
- 常用于组合逻辑
过程赋值
过程赋值只会在赋值时刻进行改变,其余时间保持不变,而且根据使用的赋值符号=
和<=
,分为阻塞赋值和非阻塞赋值。
阻塞赋值 =
//例1
initial begin
ai = 4'd1 ; //(1)
ai = 4'd2 ; //(2)
//ai 最此刻为 2
#20 ; //(3)
ai = 4'd3 ; //(4)
bi = 4'd4 ; //(5)
value_blk = ai + bi ; //(6)
//value_blk 最终为7
#40;
end
- 要求赋值左侧必须为reg、integer, real, time-variable, or memory(等非net型);
- 赋值过程在initial或always块中;
- 阻塞赋值类似于C语言的赋值方式,后面的赋值会覆盖掉前面的赋值,执行顺序为1->2->...->6,类似软件编程的赋值方式,随时赋值随时开始。
- 最终结果与书写顺序相关.
- 常用于testbench测试用例/组合逻辑.
非阻塞赋值 =>
//例1
initial begin
ai = 4'd1 ; //(1)
bi = 4'd2 ; //(2)
//ai和bi的初始值分别为1,2
#20 ; //(3)
//非阻塞赋值
ai <= 4'd3 ; //(4)
bi <= 4'd4 ; //(5)
value_nonblk <= ai + bi ; //(6)
#40;
//value_blk 最终为3
end
- 要求赋值左侧必须为reg型
- 关键词为initial或always
- 非阻塞赋值类似于物理电路中的时序电路,其中可以这样理解代码执行顺序,1->2->3之后其他的非阻塞赋值(4)(5)(6)不着急执行,而是列入到"事件队列"中,一直存到#40需要被执行前,即下一个时刻需要执行前,(4)(5)(6)将会被同时执行,此时对于value_nonblk而言其对应的ai和bi依旧是1和2,因此结果为3,可用于时序电路建模。
- 最终结果与书写顺序相关
- 常用于时序逻辑
下面使用阻塞赋值和非阻塞分别描述构成的组合逻辑和时序逻辑。
module top_module(
input a, output reg out1, output reg out2);
always@(*) begin
out1 = a; //a的值直接到out1
out2 = out1; //当前out1直接赋给out2
end //也就是说a的值直接out2
endmodule
阻塞赋值在always(*)
块下,直接综合时形成了如下的组合逻辑电路。
module top_module(
input a, input clk, output reg out1, output reg out2);
always@(posedge clk) begin
out1 <= a; //a的值赋给out1
out2 <= out1; //此时左侧的out1并不是a的大小,而是上一周期的值,因此这时是将上一周期的out1的值赋给out2
end
endmodule
而非阻塞赋值在always(posedge clk)
块下,在综合时形成了如下时序电路。
⚠️注意
虽然阻塞赋值always@(*) out_block = a & b;
和连续赋值assign out = a & b;
赋值方式不同,而且左侧采用的分别为reg类型和net类型,但最终综合出来却是一样的组合逻辑电路,都不会出现寄存器,这说明声明的类型与合成的硬件无关。
同样赋值过程并完全等同于硬件实现过程always@(posedge clk) a <= 1;
与always@(posedge clk) a = 1;
两者也并没有区别,a作为输出都有寄存器的出现,也就是赋值方式与硬件实现过程并不相同。 这主要是Verilog语法的历史遗留问题, 只采用声明的类型和赋值方式很难区分,而systemverilog干脆直接用logic代替了wire和reg两种类型,对声明的类型不做区分【难办,难办就别办了~~】,因此了解综合后的硬件电路,要重点关注语法本身是组合还是时序。
因此作为一个ICer,不能为了炫技而采用各种花里胡哨的技巧,而是采用一些原则来保证自己的代码可以更便于观察。因此为了更清晰的描述出确定的电路, 在使用非阻塞与阻塞赋值时最好遵循以下原则:
- 时序电路建模时,使用非阻塞赋值 ;
- 锁存器电路建模时,使用非阻塞赋值 ;
- 使用always块写组合逻辑电路时,采用阻塞赋值;
- 在同一个always块中同时建立时序和组合逻辑电路时,用非阻塞赋值 ;
- 在同一个always块中不要同时使用非阻塞赋值和阻塞赋值 ;
- 不要在多个always块中,为一个变量赋值;
- 用$strobe系统任务来显示用非阻塞赋值的变量值;
- 在赋值时不要使用#0延迟【有时间了再去聊原因】;
遵循以上逻辑可以消除90%-100%的在仿真中产生的竞争冒险现象,有助于正确编写可综合硬件。
过程连续赋值
连续赋值通过assign语句驱动net类型变量,而过程赋值通过initial和always块驱动reg类型的变量。这两种驱动方式基本上可以构成常用的电路,但verilog又给出了一种赋值方式,即过程连续赋值,可以通过覆盖现有的赋值,并在一定时期内驱动net、reg数据变量。
主要有两对过程连续赋值:assign-deassign
和force-release
。但这种赋值方式最好不要用于设计,会使得代码难以理解,更多用于testbench,在测试时,为出现对应结果,将设计中某些必要的信号强制赋为期待值。
assign-deassign
assign通过在有一段时间内覆盖掉现有的过程赋值控制的reg变量的值。并在执行deassign之后,就会释放掉过程连续赋值,但可以保持一段时间,直到其他赋值进行修改。
module assign_deassign_ex;
reg [3:0] d1;
initial begin
$monitor("At time T = %0t: d1 = %0d", $time, d1);
d1 = 5;
#20 d1 = 7;
end
initial begin
#5;
assign d1 = 3; //直接覆盖掉d1从5->3
#5 deassign d1; //d1释放之后,d1维持一段时间,在#20时刻从5变为7
$display("At time T = %0t: deassign d1", $time);
end
endmodule
//输出结果
At time T = 0: d1 = 5
At time T = 5: d1 = 3
At time T = 10: deassign d1
At time T = 20: d1 = 7
force-release
force 和 release 语句通过在一段时间内覆盖现有的过程、连续或过程连续赋值来控制 net 和 reg 数据类型变量值,force的权限比assign要高,可以覆盖掉其他的赋值方式。release同deassign一样,可以释放掉赋值,但对于过程赋值和过程连续赋值前可以保持前一个值。而对于net类型,直接恢复到前一个连续赋值的值。
module assign_deassign_ex;
reg [3:0] d1;
wire [3:0] d2;
assign d2 = 2;
initial begin
$monitor("At time T = %0t: d1 = %0d, d2 = %0d", $time, d1, d2);
d1 = 5;
#20 d1 = 7;
end
initial begin
#5;
$display("At time T = %0t: force d1 and d2", $time);
force d1 = 3; //强制将d1赋值为3
force d2 = 4; //d2赋值为4
#5 release d1; //d1可以保持为3,一直到#20恢复为7
release d2; //d2一旦释放则恢复到2
$display("At time T = %0t: release d1 and d2", $time);
end
endmodule
//输出
At time T = 0: d1 = 5, d2 = 2
At time T = 5: force d1 and d2
At time T = 5: d1 = 3, d2 = 4
At time T = 10: release d1 and d2
At time T = 10: d1 = 3, d2 = 2
At time T = 20: d1 = 7, d2 = 2
标签:Verilog,always,阻塞,连续,time,赋值,d2,d1 From: https://www.cnblogs.com/nullbeer/p/18196655参考文献
[1] Procedural continuous assignments - VLSI Verify-教程类
[2] Verilog Assignments-教程类
[3] Verilog 过程连续赋值-教程类
[4] How does SystemVerilog force work? -问答类