系统Verilog fork混乱,fork和begin之间执行的语句

时间:2013-01-02 17:04:19

标签: 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?

我的困惑是对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

2 个答案:

答案 0 :(得分: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循环只等待我们有句柄的进程。永远运行的进程将被忽略。

答案 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

所以这就是发生的事情。

对于循环的每次迭代:

  1. arr_obj1s[j].run()已启动。 (这将永远循环,永远不会结束。)
  2. arr_obj2s[j].run()已启动。 (这将在运行一段有限时间后结束。)启动此过程的进程的进程ID存储在job[j]中。
  3. 调用await的代码只等待启动arr_obj2s[j].run()调用的进程。他们将完成,因为他们正在执行一项有限的任务。

    即使在await调用全部完成后,forever循环仍将继续运行。