本文参考绿皮书第七章,线程及其线程间的通信,Verilog HDL A guide to digital design and synthesis 2nd第七章。主要对于begin…end,fork…join等进行简单总结。然后给出相对应的实例进行理解。有问题欢迎同学指出。
System Verilog线程——fork join的理解使用
概述
在Verilog中,存在组合逻辑与时序逻辑,需要仿真器对于两者进行模拟。因此测试平台需要并发执行某些操作,称之为并发的线程。在verilog中存在begin…end,以及fork…join两中分组方式,用于顺序或者是并发执行。在system vrilog中,引入了fork…join_any以及fork…join_none新的方法用于创建线程。最后解释停止进程。
顺序块与并发块(Sequential and Parallel Blocks)
begin…end块
关键字begin和end用于将语句分组为顺序块。
顺序块具有以下特征:
- 顺序块中的语句按指定顺序处理。只有在前一条语句完成执行后,才会执行该语句。
- 如果指定了延迟或事件控制,则该延时或事件与块中的前一条语句完成执行时的模拟时间有关。
分别举例如下
//案例 1: Sequential block without delay
module
reg x, y;
reg [1:0] z, w;
initial begin
x = 1'b0;
y = 1'b1;
z = {x, y};
w = {y, x};
end
endmodule
对应案例 1: Sequential block without delay
//案例2: Sequential blocks with delay.
reg x, y;
reg [1:0] z, w;
initial begin
x = 1'b0; //completes at simulation time 0
#5 y = 1'b1; //completes at simulation time 5
#10 z = {x, y}; //completes at simulation time 15
#20 w = {y, x}; //completes at simulation time 35
end
对应案例2: Sequential blocks with delay.
注意这里的仿真时间,对w的赋值时间再35ns处
fork…join块
由关键字fork和join指定的并行块提供了有趣的模拟特征。平行块具有以下特征:
•并行块中的语句并行执行。
•语句的顺序由分配给每个语句的延迟或事件控制来控制陈述
•如果指定了延迟或事件控制,则它与执行块的时间有关进入。
案例1:
module tb();
reg x, y;
reg [1:0] z, w;
initial fork
x = 1'b0; //completes at simulation time 0
#5 y = 1'b1; //completes at simulation time 5
#10 z = {x, y}; //completes at simulation time 10
#20 w = {y, x}; //completes at simulation time 20
join
endmodule
注意这里的仿真时间,在20ns已经结束了对w的赋值
案例2:
module tb();
reg x, y;
reg [1:0] z, w;
initial fork
x = 1'b0;
y = 1'b1;
z = {x, y};
w = {y, x};
join
endmodule
产生同样波形的原因是存在竞争问题。本例中特意引入了竞争条件。所有语句都在模拟时间0开始。语句的执行顺序未知。如果先执行x=1’b0和y=1’b1,变量z和w将得到值1和2。如果x=1’b0和y=1’b1最后执行,变量z和w将得到值2’bxx和2’bxx。因此,z和w的结果是不确定的,并且取决于模拟器的实现。在模拟时,fork-join块中的所有语句都会立即执行。然而,在现实中,运行模拟的CPU一次只能执行一条语句。不同的模拟器以不同的顺序执行语句。因此,竞争条件是模拟器的限制,而不是fork-join块的限制。
fork…join_any与fork…join_none
前面对于fork的引入是为了更好的理解如下点。
join 父进程会阻塞直到这个分支产生的所有进程结束。
join_any 父进程会阻塞直到这个分支产生的任意一个进程结束。 然后继续执行
join_none 父进程会继续与这个分支产生的所有进程并发执行。在父线程执行一条阻塞语句之前,产生的进程不会启动执行。
简单的测试如下
停止进程disable
关键字disable提供了一种终止命名块执行的方法。disable可用于根据控制信号跳出循环、处理错误条件或控制代码段的执行。禁用块会将执行控制传递给紧随该块之后的语句。对于C程序员来说,这与用于退出循环的break语句非常相似。区别在于break语句只能中断当前循环,而关键字disable允许禁用设计中的任何命名块。
在下面的这个例子中,等待时钟或者是等待到特定信号的下降沿。就会推出这个fork线程。两种写法都是可以的。
task get_payload();
pkt2cmp_payload.delete();//清除之前缓存的数据
fork begin : wd_timer_fork
fork : frameo_wd_timer
@(negedge rtr_io.cb.frameo_n[da]); //这里是第一个线程(thread)
begin //begin...end这里是第二个线程start
repeat(100) @(rtr_io.cb);
$display("\n%m\n[ERROR]%t Frame signal timed out!\n", $realtime);
$finish;
end //begin...end这里是第二个线程end
join_any : frameo_wd_timer
//disable fork; //等待失败,或者是等到下降沿,推出fork join。执行下面的内容
disable frameo_wd_timer; //同disable fork;
end : wd_timer_fork
join
后需要在进行补充吧~
附录
线程与进程的关系
摘自绿皮书第七章绪论注释