源自:微信公众号 “数字芯片实验室”
1、连续赋值和过程赋值之间有什么区别?
2、initial和always中的赋值有什么区别?
initial和always中的赋值都是过程赋值。
3、阻塞和非阻塞赋值之间有什么区别?
阻塞和非阻塞赋值都是过程赋值。
4、如何使用连续赋值建模双向nets?
assign语句构成一个连续赋值。 RHS变化立即影响LHS。 然而,LHS的任何变化都不会影响RHS。
例如,在以下声明,rhs net的更改将更新到lhs net,但反之不会。
wire rhs,1hs ;
assign lhs = rhs ;
System Verilog引入了一个关键字alias,能定义双向nets。
例如,在以下代码中,rhs的任何变化都会更新到lhs,反之亦然。
module test_alias ;
wire [3:0] lhs ,rhs ;
alias lhs = rhs ;
initial begin
force rhs = 4’h2 ;
$display(“lhs = %0h , rhs = %0h”,lhs,rhs) ;
release rhs ;
force lhs = 4’hc ;
$display(“lhs = %0h , rhs = %0h”,lhs,rhs) ;
release lhs ;
end
endmodule //test_alias
如果上述alias换成assign,则输出如下:
lhs = 2 , rhs = 2
lhs = c , rhs = z
然而,由于定义了alias,输出如下:
lhs = 2 , rhs = 2
lhs = c, rhs = c
在上面的示例中,对net任何一侧的更改都会更新到另一侧。
5、task和function之间有什么区别?
Verilog中的task和function都可以实现常用功能,有助于代码的清晰和可维护,避免在不同位置复制大量代码。 本质上,task和function都提供了在模块中不同位置重用相同代码段的“子程序”机制。
但是,task和function在以下方面有所不同:
6、静态task和动态task有何不同?
动态task在关键字task和名称之间有automatic关键字。 动态task在每个task调用期间,自动分配变量内存空间,即每次调用都不会覆盖这些值。没有automatic关键字,变量是静态分配的,这意味着这些变量在不同的task调用之间共享,因此可以被覆盖。
以下示例说明了关键字automatic的效果。 这是一个不可综合的代码。
module modify_taskval;
integer out_val ;
task automatic modify_value;
input [1:0] in_value ;
output [3:0] out_value ;
reg [1:0] my_value ;
begin
//syntax error to use nonblocking assignment with automatic variables
my_value = in_value ; //blocking assignment
#5
$display(“my_value = \t%0d, t = %0d”,my_value,$time) ;
out_value = my_value + 2 ;
end
endtask
initial begin
fork
begin //First parallel call
#1
$display(“in1 = \t\t %0d,t = %0d”,2,$time) ;
modify_value(2,out_val) ;
end
begin //Second parallel call
#2
$display(“in2 = \t\t%0d,t = %0d”,3,$time) ;
modify_value(3,out_val) ;
end
join
end
endmodule
在上面的示例中,my_value是task中的局部变量。 每当调用此task时,输入in_value在5个仿真时间单位之后赋值给局部变量。 在initial begin中,有一个fork-join,它启动两个并行进程,分别在仿真时间单位#1和#2之后开始。 第1个进程赋值2给my_value,第2个进程赋值3给my_value。 假设没有automatic关键字,使用上面的代码运行仿真,会输出以下内容:
in1 = 2 , t = 1 //passed value is 2
in2 = 3 , t = 2
my_value = 3 , t = 6 //retained value is 3
my_calue = 3 , t =7
没有automatic关键字的事件序列如下:
1. 从仿真时间0开始启动fork-join两个进程。
2.第一个进程在#1之后调用modify_value,并赋值局部变量my_value为2.此时 t = 1。
3.第二个进程在#2之后调用modify_value,并赋值局部变量my_value为3.此时t = 2。请注意现在赋值给局部变量my_value的值被3覆盖。
4.再经过4个时间单位,即在t = 1 + 5 = 6时,第1个task调用$display。由于最新值现在是“3”, my_value显示“3”,而不是 “2”。
类似地,对于第二个过程,即t =2 + 5 = 7,第2个task调用$display。由于最新值仍为“3”,因此此处my_value显示“3”。
关键在上面的步骤3中,其中第2个进程的my_value在第1个进程$display之前覆盖了第1个进程的my_value。这是因为没有automatic关键字,task中的变量是静态的,并且对task的所有调用共享。
现在,在task和task名称之间使用关键字automatic,仿真输出一下内容:
in1 = 2 . t = 1 //passed value is 2
in2 = 3 ,t = 2
my_value = 2,t = 6 //passed value 2 preserved
my_value = 3 , t = 7
按照上述相同的步骤,这次,由于存在关键字automatic,变量不会被其他进程覆盖。
下表总结了动态task和静态task之间的差异:
持续更新~