Parallel tasks

I want to develop these requirements:

  • n generator task in which there is a for loop with 1000 repetitions.
  • n model task in which there is a forever loop.
  • n driver task in which there is a forever loop.
    I knew how to develop 1 generator, 1 driver, and 1 model but I don’t know how can develop n of these.
    How should do this job whit fork-join?

In reply to Moein75:
It would help if you were more specific about your usage of the tasks.
If your n==2, show us the code.
Tasks are called and automatic tasks are dynamically created and destroyed when done.
Objects can be created at elaboration time with the generate endgenerate statement.
That is why I am not clear about your question. What is your usage?
Ben Cohen
Ben@systemverilog.us
Link to the list of papers and books that I wrote, many are now donated.

or Cohen_Links_to_papers_books - Google Docs

Getting started with verification with SystemVerilog

In reply to ben@SystemVerilog.us:
for n = 2, this code gives correct answer. Where n is a variable, I don’t know how do it with for.


                fork          
                     gen[0].run();
                     drv[0].run();
                     model[0].run();
                     gen[1].run();
                     drv[1].run();
                     model[1].run();
                
                join_any

In reply to Moein75:
The issue of using a fork join_any for each group of the loop variable i is taht it joins just that group (e.g., group 0, but not group1)

Can you do something like this where done is a common variable to the t_master task
and any forked task return a 1 to done.
The return end the t_master task


automatic task run(inout bit done); // example of one task
  ....
  done=1; 
endtask 

automatic task t_master(int v);
  bit done;
  for (int i = 1; i<=v; i=i+1) begin
    fork
    // NEED THE FOLLOWING as explained below 
      automatic int j = i; // Updated 6/27/
      gen[j].run(done); // 
      drv[j].run(done);
      model[j].run(done);
    join_any
  end
  wait(done); 
  return;
endtask
  
// For v==2, the for loop will create 
fork          
    gen[0].run(done);
    drv[0].run(done);
    model[0].run(done);
join_any 
fork  
    gen[1].run(done);
    drv[1].run(done);
    model[1].run(done);
join_any
wait(done); return;
// since any task sets the done to 1, the t_master will end. 

I used this technique to kill multiple threads in my modeling of SVA.
See my paper Understanding the SVA Engine Using the Fork-Join Model Verification Horizons Using a model, the paper addresses important concepts about attempts and threads. Emphasizes the total independence of attempts.
Ben Cohen
Ben@systemverilog.us
Link to the list of papers and books that I wrote, many are now donated.
http://systemverilog.us/vf/Cohen_Links_to_papers_books.pdf
or Cohen_Links_to_papers_books - Google Docs

Getting started with verification with SystemVerilog

In reply to Moein75:

foreach (gen[i])
   fork
     int j = i;
     gen[j].run();
     drv[j].run();
     model[j].run();
   join_none

This assumes the size of the gen/model/drv are all the same, and that int j is delcared with an automatic lifetime (It would be if this code is inside a class method or a task with an explicit automatic lifetime. You also need to come up with a mechanism for when the test ends.

The most basic fundamentals of the UVM takes care of these issues trivially.

In reply to dave_59:
Dave,
I like your approach.
Q: Why the need to declare and use the “j”?
COuld you have use the “i” instead?
Thanks,
Ben

In reply to ben@SystemVerilog.us:

https://verificationacademy.com/forums/systemverilog/fork-joinnone-inside-loop#reply-46114

In reply to ben@SystemVerilog.us:

Thank you, if use an automatic variable and replace join_any with join_none, your solution gives my desire output.
If use join_any, gen[1] runs after gen[0].

In reply to Moein75:

Dave,
Why is the following code NOT producing an error?
1800 9.3.2 Parallel blocks states

Variables declared in the block_item_declaration of a fork-join block shall be initialized to their initialization value expression whenever execution enters their scope and before any processes are spawned. Within a fork-join_any or fork-join_none block, it shall be illegal to refer to formal arguments passed by reference other than in the initialization value expressions of variables declared in a block_item_declaration of the fork.


module test;
  initial begin
    for(int i = 0; i < 16; i++)
      begin
        fork 
	    //automatic int index =i  ;	    
          send(i); // am within a fork-join-any 
                   // "i" is referring to formal argument "j" of function send
                   // "j" is a formal where actual is passed by reference 
                   // All simulators give the same results 
                   // Am I misunderstanding 1800?  
        join_any 
      end
      wait fork;
  end
        function automatic void send( ref int j);
    $display("driving  %0d %t" , j, $realtime);
  endfunction  
endmodule
// Sim 
# driving  0                    0
# driving  1                    0
# driving  2                    0
...
# driving  15                    0
# exit
        

Ben

In reply to ben@SystemVerilog.us:

Ben,

You may be confusing the terms “refers to” with “a reference argument”.

The term “refers to” is a mention of an identifier in the source text, referring to something declared in another place. In the code
send(i)
, The variable i is referred to inside the fork/join_any, and was declared outside the fork. ‘i’ is not a formal argument.

A “formal argument passed by reference” is an indirection. The formal argument identifier becomes a symbolic reference to the actual identifier. And the reference is unidirectional and only active for the duration of the task/function call. Formal j is a reference to actual i, but actual i is not a reference to j. That’s because there is no variable j.

In reply to dave_59:

Dave,
Thanks for the clarification.
I understand the need for the **automatic int index =i ; **
But I fail to understand what the 1800 statement (copied below) has to do as a rational
for the need of automatic int k = j;

1800: Within a fork-join_any or fork-join_none block, it shall be illegal to refer to formal arguments passed by reference other than in the initialization value expressions of variables declared in a block_item_declaration of the fork. These variables are useful in processes spawned by looping constructs to store unique, per-iteration data. For example:


initial 
          for( int j = 1; j <= 3; ++j ) 
            fork 
              automatic int k = j; // local copy, k, for each value of j 
              #k $write( "%0d", k ); 
              begin automatic int m = j; // the value of m is undetermined
                ... 
            end join_none

Also, can you give an example where a compiler would provide a violation of th2 1800 statement?

I also tried the 1800 example, but I saw no violation of the value of “m”


module test;
  initial begin
    for(int i = 0; i < 16; i++)
      begin
        fork 
	      automatic int index =i  ;	    
          send(index);
          begin 
             automatic int m = i; // the value of m is undetermined
            send(m);
          end
        join_any 
      end
      wait fork;
  end
        function automatic void send( ref int j);
          j=j+2;
          
    $display("driving  %0d %t" , j, $realtime);
  endfunction  
endmodule
// sim 
driving  2                    0
driving  3                    0
driving  3                    0
driving  4                    0
driving  4                    0
driving  5                    0
driving  5                    0
..,
driving  16                    0
driving  17                    0
driving  17                    0
driving  18                    0


In reply to ben@SystemVerilog.us:

The example from the LRM you show is about the sentence before the two sentences you quoted, and last sentence. It is not trying to explain the restriction in the first sentence.

The issue with arguments passed by reference is dealing with the end of life of a variable with an automatic lifetime. The life of an automatic variable begins when entering the procedural block (begin/fork, task/function) containing its declaration. Normally its life ends when exiting that block, but gets extended by any fork block enclosed within its scope. Calling a task or function procedure does not make that procedure an enclosing scope.

An contrived illegal example would be:

module top;
  task automatic t1;
    int i1=4;
    t2(i1);
    #1 return; // i1's lifetime ends when this task returns at time  1
  endtask
  task t2(ref int arg); 
    fork
      while(arg>0) #1 $display($time, arg--);
    join_none
  endtask
  initial fork t1; t1; join
endmodule

The problem with this code when calling t2, the lifetime of arg needs to exist for the duration of the while loop., but it disappears when t2 exits.

In reply to dave_59:

Excellent explanation!
I got it now, many thanks.
Ben