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

时间:2013-11-05 15:45:30

标签: foreach fork system-verilog uvm

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

如果我这样做:

foreach (env.agt[i])
  begin
    seq.start(env.agt[i].sqr);
  end

,序列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

4 个答案:

答案 0 :(得分: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

IEEE std 1800-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

输出:

  

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

答案 1 :(得分:2)

我认为更接近“UVM”的方法是使用虚拟序列。假设您已经有一个虚拟序列器实例化一个代理序列发生器数组,那么您的虚拟序列的主体将如下所示:

fork
  begin: isolation_thread
    foreach(p_sequencer.agent_sqr[i])
      automatic int j = i;
      fork
        begin
          `uvm_do_on(seq, p_sequencer.agent_sqr[j]);
        end
      join_none
    end
    wait fork;
  end: isolation_thread
join

过去这对我有用。

答案 2 :(得分:1)

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

下面的fork-join块驻留在测试用例类的main_phase任务中。 wait fork;语句在继续进行之前等待其范围(= foreach_fork开始结束块)中的所有fork语句完成。需要注意的一点是,将wait fork;的范围设置为foreach_fork块需要在开始端周围包装fork-join。


fork 
  foreach_fork : 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].start(env.agt[j].sqr);
          end
        join_none // non-blocking thread
      end
    wait fork;
  end : foreach_fork
join

使用顺序异议来延迟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

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

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

注意替代解决方案: 由于我没有使用wait fork;,我使用序列本身中提出的UVM异议来完成阻止模拟$finish调用的工作。为了提高序列中的异议,我使用seq[j].starting_phase = phase;构造。

答案 3 :(得分:-1)

尝试

int i = 0
foreach (env.agt)
  begin
    seq.start(env.agt[i].sqr);
    i++;
end