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

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中的那些人永远运行,所以他们只需要产生一次并被遗忘.
  • 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循环只等待我们处理的进程.永远运行的进程将被忽略.


dwi*_*kle 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
Run Code Online (Sandbox Code Playgroud)

我至少说,因为我不完全理解automatic int j在这种情况下如何对待.

所以这就是发生的事情.

对于循环的每次迭代:

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

正在调用的代码await只等待启动调用的进程arr_obj2s[j].run().他们将完成,因为他们正在执行一项有限的任务.

即使在await呼叫全部完成后,永远循环仍将运行.