5

在此处查看简化的示例代码:

    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
4

2 回答 2

7

这个代码片段看起来像是在演示过程控制,所以这是我对正在发生的事情的猜测。arr_obj1s和arr_obj2s中有一组进程:

  • 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 循环只等待我们有句柄的进程。永远运行的进程将被忽略。

于 2013-01-02T19:11:56.260 回答
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在这种情况下是如何处理的。

这就是发生的事情。

对于循环的每次迭代:

  1. arr_obj1s[j].run()已启动。(这会永远循环,永远不会结束。)
  2. arr_obj2s[j].run()已启动。(这将在运行一段时间后结束。)启动它的进程的进程 ID 存储在job[j].

正在调用的代码await只等待开始调用arr_obj2s[j].run(). 他们将完成,因为他们正在运行一个有限的任务。

await即使在调用全部完成之后,永远循环仍将运行。

于 2013-01-02T19:08:57.080 回答