请参阅此处的简化示例代码:
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?
我的困惑是对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_a
基本上看起来像这样(它们都很相似):
virtual task run_a;
// declare some local variables
forever begin
@(posedge clk)
// ...
end
endtask: run_a
但是obj2的run()
函数基本上看起来像这样:
virtual task run;
fork
run_d(); // different logically separated tasks
run_e();
join
endtask: run
例如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
答案 0 :(得分: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
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
要解决这个问题,我们需要创建一个新的顺序过程上下文,我们可以从中获取进程句柄,然后从那里运行函数:
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
最后的foreach循环只等待我们有句柄的进程。永远运行的进程将被忽略。
答案 1 :(得分:6)
首先,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
我至少说,因为我不完全理解在这种情况下如何处理automatic int j
。
所以这就是发生的事情。
对于循环的每次迭代:
arr_obj1s[j].run()
已启动。 (这将永远循环,永远不会结束。)arr_obj2s[j].run()
已启动。 (这将在运行一段有限时间后结束。)启动此过程的进程的进程ID存储在job[j]
中。 调用await
的代码只等待启动arr_obj2s[j].run()
调用的进程。他们将完成,因为他们正在执行一项有限的任务。
即使在await
调用全部完成后,forever循环仍将继续运行。