我正在尝试理解静态任务。在这里我理解线程 1、2 和 3 同时发生,并且_time
&t_name
是任务内部的静态变量。当我模拟代码时,我得到:
[0] Main Thread: Fork join going to start
[0] Main Thread: Fork join has finished
[0] Nested fork has finished
[10] Thread2
[20] Thread2
[30] Thread2
但是,后来我想,既然“Thread2”与其他两个线程共享t_name
,那么_time
三个线程的共享内存量是不是应该都是 20?我期望的答案是:
[0] Main Thread: Fork join going to start
[0] Main Thread: Fork join has finished
[0] Nested fork has finished
[20] Thread2
[20] Thread2
[20] Thread2
有人能帮助我理解吗?
module tb;
initial begin
$display("[%0t] Main Thread: Fork join going to start", $time);
fork
begin
fork
print(10, "Thread1");
print(20, "Thread2");
join_none
$display("[%0t] Nested fork has finished", $time);
end
print(30, "Thread3");
join_none
$display("[%0t] Main Thread: Fork join has finished", $time);
end
task print (int _time, string t_name);
#(_time) $display ("[%0t] %s", $time, t_name);
endtask
endmodule
首先要意识到您的示例中有五个线程。有一个由
initial
块创建的父线程,即所谓的“主”线程。主线程分叉出 2 个线程,即begin/end
包含嵌套语句fork
和调用print(30, "Thread3");
语句的块。这 2 个分叉线程直到主线程终止(或暂停)后才会启动。两个分叉线程中哪个先启动是一个竞争条件。嵌套分叉线程会创建另外 2 个线程,它们也会在嵌套分叉线程终止后开始竞争。
最终,对的三个调用
print()
争相在时间 0 进入。尽管 Verilog 规则允许这些线程交错,但 SystemVerilog 模拟器的所有单核实现都不会切换到另一个线程,直到它阻塞或终止。因此,无论任务具有静态还是自动生命周期,对的每次调用都会print()
进入任务,并且其第一个参数_time
将用于在指定的时间内阻塞线程,然后静态参数才有机会被覆盖。请注意,一旦过程遇到#(delay_expression)
,该表达式只会被评估一次。随后更改变量不会改变计划的等待时间。这就是打印 $times 为 10、20 和 30 的原因。但是由于该任务声明为具有静态生存期,因此只有一组参数变量,并且每次调用时它们都会被覆盖
print()
。如果您使用 $display 两个任务参数,您将在所有三个 $display 语句中看到上次调用的值print()
。