Search code examples
foreachforksystem-veriloguvm

How can I use foreach and fork together to do something in parallel?


This question is not UVM specific but the example that I am working on is UVM related. I have an array of agents in my UVM environment and I would like to launch a sequence on all of them in parallel.

If I do the below:

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

, the sequence seq first executes on env.agt[0].sqr. Once that gets over, it then executes on env.agt[1].sqr and so on.

I want to implement a foreach-fork statement so that seq is executed in parallel on all agt[i] sequencers.

No matter how I order the fork-join and foreach, I am not able to achieve that. Can you please help me get that parallel sequence launching behavior?

Thanks.

Update to clarify the problem I am trying to solve: The outcome of the below code constructs is the exact same as above without the 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


Solution

  • Greg's solution below helped me derive the solution to my UVM based problem. Here is my solution:

    The below fork-join block resides in the main_phase task in a test case class. The wait fork; statement waits for all the fork statements in its scope (= the foreach_fork begin-end block) to finish before proceeding further. An important thing to note is that the wrapping fork-join around the begin-end was required for setting the scope of wait fork; to the foreach_fork block.

    
    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
    

    Alternative solution that makes use of the in-sequence objection to delay the sim end.

    
    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  
    

    I realized that I also needed to create a new sequence object for each seq I wanted to run in parallel.

    Thanks to Dave for making the point that the properties in System Verilog classes are automatic by default.

    Note for the alternative solution: As I didn't use wait fork; I use the UVM objections raised in the sequence itself to do the job of holding off the simulation $finish call. To enable raising of the objections in the sequences, I use the seq[j].starting_phase = phase; construct.