Ric*_*ich 5 system-verilog fork-join
请参阅此处的简化示例代码:
process job[num_objs];
// assume also, arr_obj1s (array of type obj1) and
// arr_obj2s (array of type obj2) are arrays of size
// num_objs, and the objects define a run() function
foreach (arr_obj1s[i]) begin
fork
automatic int j = i;
arr_obj1s[j].run(); // these run forever loops
begin
job[j] = process::self();
arr_obj2s[j].run(); // these run finite logic
end
join_none
end
foreach (job[i]) begin
wait (job[i] != null);
job[i].await();
end
// How do we ever reach here?
Run Code Online (Sandbox Code Playgroud)
我的困惑是,调用arr_obj1s[j].run()将永远不会返回(它们永远运行循环)并且我并不完全遵循该调用在开始/结束块之外的位置的含义.永远run()执行哪个进程,await()如果某个进程正在运行run()哪个进程不会返回,那么每个调用将如何返回?
编辑:这是一些更多的信息.发布完整代码将是页面和页面,但我希望这一点额外帮助.
obj1的run()功能如下:
virtual task run;
fork
run_a(); // different logically separated tasks
run_b();
run_c();
join
endtask: run
Run Code Online (Sandbox Code Playgroud)
作为一个例子,run_a看起来基本上像这样(它们都是相似的):
virtual task run_a;
// declare some local variables
forever begin
@(posedge clk)
// ...
end
endtask: run_a
Run Code Online (Sandbox Code Playgroud)
但是obj2的run()功能看起来基本上是这样的:
virtual task run;
fork
run_d(); // different logically separated tasks
run_e();
join
endtask: run
Run Code Online (Sandbox Code Playgroud)
作为一个例子run_d()看起来像这样:
virtual task run_d;
while ((data_que.size() > 0)) begin
// process a pre-loaded queue,
// data will not be pushed on during the simulation
end
endtask:run_d
Run Code Online (Sandbox Code Playgroud)
小智 7
这个代码片段看起来像是在演示过程控制,所以这里是我猜测发生了什么.arr_obj1s和arr_obj2s中有一组进程:
我的困惑是对arr_obj1s [j] .run()的调用永远不会返回(它们永远运行循环)并且我不完全遵循该调用在开始/结束块之外的位置的含义
因此,生成所有进程所需的只是fork..join_none块中的三行代码.
foreach (arr_obj1s[i]) begin
fork
automatic int j = i; // Spawns process
arr_obj1s[j].run(); // Spawns process
arr_obj2s[j].run(); // Spawns process
join_none
end
Run Code Online (Sandbox Code Playgroud)
所述join_none关键字指示所述并联块完成后的执行将继续下去,因而整个foreach循环将执行,然后父进程将继续到下一个foreach循环.此外,join_none还意味着子进程在父进程到达阻塞语句之前不会启动.
但是,这不允许我们检测子进程何时完成,除非他们有一些他们修改的共享变量.为了解决这个问题,SystemVerilog允许处理进程,以便在进程完成时调度事件.但是,它不提供获取单个语句句柄的能力.您必须在过程上下文中使用process :: self()来获取进程句柄.因此,如果直接添加到fork-join块,这将无法正常工作.
foreach (arr_obj1s[i]) begin
fork
automatic int j = i;
arr_obj1s[j].run();
job[j] = process::self(); // Will return parent process
arr_obj2s[j].run();
join_none
end
Run Code Online (Sandbox Code Playgroud)
要解决这个问题,我们需要创建一个新的顺序过程上下文,我们可以从中获取进程句柄,然后从那里运行函数:
foreach (arr_obj1s[i]) begin
fork
automatic int j = i;
arr_obj1s[j].run(); // Spawns a new process for those that don't complete
begin // Spawns a new process for those that complete
job[j] = process::self(); // Saves handle to this begin..end process
arr_obj2s[j].run(); // Process continues though here
end
join_none
end
Run Code Online (Sandbox Code Playgroud)
最后的foreach循环只等待我们处理的进程.永远运行的进程将被忽略.
首先,fork/join在Verilog中的工作方式,fork/join块中的每个语句同时执行.没有开头/结尾,每一行本身就是一个陈述.
因此,您的示例是为循环的每次迭代分配至少两个进程.
fork
automatic int j = i; <= Statement 1 ??
arr_obj1s[j].run(); // these run forever loops <= Statement 2
begin \
job[j] = process::self(); | <= Statement 3
arr_obj2s[j].run(); // these run finite logic |
end /
join_none
Run Code Online (Sandbox Code Playgroud)
我至少说,因为我不完全理解automatic int j在这种情况下如何对待.
所以这就是发生的事情.
对于循环的每次迭代:
arr_obj1s[j].run()开始了.(这将永远循环,永远不会结束.)arr_obj2s[j].run()开始了.(这将在运行一段有限的时间后结束.)存储启动它的进程的进程ID存储在job[j].正在调用的代码await只等待启动调用的进程arr_obj2s[j].run().他们将完成,因为他们正在执行一项有限的任务.
即使在await呼叫全部完成后,永远循环仍将运行.