如何将foreach和fork一起使用来并行执行某些操作?

Kau*_*odi 9 foreach fork system-verilog uvm

这个问题不是UVM特定的,但我正在研究的例子是UVM相关的.我在我的UVM环境中有一系列代理,我想在所有这些代理上并行启动一个序列.

如果我这样做:

foreach (env.agt[i])
  begin
    seq.start(env.agt[i].sqr);
  end
Run Code Online (Sandbox Code Playgroud)

,序列seq首先执行env.agt[0].sqr.一旦结束,它就会执行env.agt[1].sqr,依此类推.

我想实现一个foreach-fork语句,以便seq在所有agt[i]顺控程序上并行执行.

无论我如何订购fork-join和foreach,我都无法实现这一目标.能帮我解决并行序列启动行为吗?

谢谢.

更新以澄清我试图解决的问题:下面的代码结构的结果与上面没有fork-join完全相同.


foreach (env.agt[i])
  fork
    seq.start(env.agt[i].sqr);
  join


fork
  foreach (env.agt[i])
    seq.start(env.agt[i].sqr);
  join


// As per example in § 9.3.2 of IEEE SystemVerilog 2012 standard
for (int i=0; i<`CONST; ++i)
  begin
    fork
      automatic int var_i = i;
      seq.start(env.agt[var_i].sqr);
    join
  end

Gre*_*reg 10

问题是fork的每个线程都指向同一个静态变量i.每个线程都需要自己唯一的副本,这可以通过automatic关键字实现.

foreach (env.agt[i])
  begin
    automatic int var_i = i;
    fork
      seq.start(env.agt[var_i].sqr);
    join_none // non_blocking, allow next operation to start
  end
wait fork;// wait for all forked threads in current scope to end
Run Code Online (Sandbox Code Playgroud)

IEEE std1800-2012§6.21 "范围和寿命"给出了静态和自动使用的示例.另请参阅§9.3.2"并行块",最后一个示例演示了for循环中的并行线程.

使用join_none创建新线程; §9.3.2"并行块",表9-1-"fork-join控制选项".

使用fork wait语句等待当前作用域中的所有线程完成; §9.6.1"等待叉语句"


例:

byte a[4];
initial begin
    foreach(a[i]) begin
        automatic int j =i;
        fork
            begin
                a[j] = j;
                #($urandom_range(3,1));
                $display("%t :: a[i:%0d]:%h a[j:%0d]:%h",
                        $time, i,a[i], j,a[j]);
            end
        join_none // non-blocking thread
    end
    wait fork; // wait for all forked threads in current scope to end
    $finish;
end
Run Code Online (Sandbox Code Playgroud)

输出:

2 :: a [i:4]:00 a [j:3]:03
2 :: a [i:4]:00 a [j:0]:00
3 :: a [i:4]:00 a [j:2]:02
3 :: a [i:4]:00 a [j:1]:01

  • 顺便说一句,如果此代码在类方法中,则不需要`automatic`关键字 - 默认情况下,类方法中的所有变量声明都是自动的.反向是类外的默认静态,与Verilog向后兼容. (3认同)

Kau*_*odi 1

下面 Greg 的解决方案帮助我找到了基于 UVM 的问题的解决方案。这是我的解决方案:

下面的 fork-join 块位于测试用例类的 main_phase 任务中。该wait fork;语句等待其范围内的所有 fork 语句(= foreach_fork begin-end 块)完成,然后再继续进行。wait fork;需要注意的重要一点是,为了设置foreach_fork 块的范围,需要在 begin-end 周围包装 fork-join 。


fork 
  begin : foreach_fork
    seq_class seq **[`CONST]**;
    foreach(env.agt[i])
      begin
        int j = i;
        **seq[j] = seq_class::type_id::create
                  (.name($sformatf("seq_%0d", j)), .contxt(get_full_name()));**
        fork
          begin
            seq[j].start(env.agt[j].sqr);
          end
        join_none // non-blocking thread
      end
    **wait fork;**
  end : foreach_fork  
join
Run Code Online (Sandbox Code Playgroud)

替代解决方案利用顺序反对来延迟 sim 结束。


begin
  seq_class seq **[`CONST]**;
  foreach(env.agt[i])
    begin
      int j = i;
      **seq[j] = seq_class::type_id::create
                (.name($sformatf("seq_%0d", j)), .contxt(get_full_name()));**
      fork
        begin
          **seq[j].starting_phase = phase;**
          seq[j].start(env.agt[j].sqr);
        end
      join_none // non-blocking thread
    end
end  
Run Code Online (Sandbox Code Playgroud)

我意识到我还需要为我想要并行运行的每个 seq 创建一个新的序列对象。

感谢 Dave 指出 System Verilog 类中的属性默认情况下是自动的。

替代解决方案的注意事项:由于我没有使用,所以wait fork;我使用序列本身中提出的 UVM 反对来完成推迟模拟调用的工作$finish。为了能够在序列中提出反对意见,我使用了该seq[j].starting_phase = phase;构造。