Disable fork join when one of the tasks complete

I have the following code in the run_phase of the monitor:

virtual task run_phase (uvm_phase phase);
      forever begin
                fork 
                    monitor_cycle_count();
                    forever begin
                       mon_trx = axi_transaction::type_id::create("mon_trx");
                       fork
                                  monitor_write();
                                  monitor_read();                 
                       join_any
                    end
                join
      end
  endtask: run_phase

I use fork_any because I want that when one of the tasks completes (monitor_write/monitor_read), a new loop will start with new transaction.
But I can see that even when for example tasks which the monitor_write()contains continue to execute even when monitor_write() is done, with the same transaction.

In reply to saritr:

Hi,
You can give a name to the fork…join_any and disable it after join_any.

In reply to R.B. Vijey Shankar:

  1. How can I give name?
    2.Is there any option to disable from a task which is contained in one of the task of the fork?

In reply to saritr:
Having nested
forever
loops does not make much sense. Normally you put
disable fork
after a
join_any
. Doing that with your example would not disable the process calling the monitor_cycle_count() because it is a sibling to the thread doing the disable, not a child of it.

Maybe you intended to write

virtual task run_phase (uvm_phase phase);
forever begin
          mon_trx = axi_transaction::type_id::create("mon_trx");
          fork 
            monitor_cycle_count();
          join_none
          fork
            monitor_write();
            monitor_read();                 
          join_any;
          disable fork;
      end
  endtask: run_phase

The
disable fork
above will wait for one of the monitor_read/write tasks to finish, then kill the other one as well as killing the monitor)cycle_count task if it is still running. It was not very clear what your intention was.

In reply to saritr:
you should use process in your case or labeled , code is not tested .


virtual task run_phase (uvm_phase phase);
     process thread ;
      forever begin
                fork 
                    monitor_cycle_count();
                    forever begin
                       mon_trx = axi_transaction::type_id::create("mon_trx");
                       fork
                                  thread = process::self();
                                  monitor_write();
                                  monitor_read();                 
                       join_any
                       thread.kill();
                    end
                join
      end
  endtask: run_phase

In reply to kddholak:
Your code does not work. The thread that you kill will already be completed.

In reply to dave_59:

Thanks Dave . I have updated the solution.


virtual task run_phase (uvm_phase phase);
     process thread[$] ;
      forever begin
                fork 
                    monitor_cycle_count();
                    forever begin
                       mon_trx = axi_transaction::type_id::create("mon_trx");
                       fork
                                 begin
                                  thread.push_back(process::self());
                                  monitor_write();
                                  end begin
                                  thread.push_back(process::self());
                                  monitor_read();                 
                                  end 
                       join_any
                       foreach(thread[i]) thread[i].kill();
                       thread = {}; // delete the Q
                  end
                join
      end
  endtask: run_phase

In reply to kddholak:
What you wrote is exactly what a disable fork does. Also does not address the problem with the nested forever loops.

In reply to dave_59:

The disable fork shouldn’t kill the monitor_cycle_count() (this task should always run, that’s why I put it in forever loop).
The monitor_write()and monitor_read()tasks should run parallel on each new transaction (because I don’t know if it will be read or write transactiobn… this code is part of uvm_monitor). That’s why I put them also in forever loop.

In reply to saritr:
This code below using
disable fork
is identical to kddholak’s most recent post above using the
kill()
method

virtual task run_phase (uvm_phase phase);
      forever begin
                fork 
                    monitor_cycle_count();
                    forever begin
                       mon_trx = axi_transaction::type_id::create("mon_trx");
                       fork
                                 begin
                                  monitor_write();
                                  end begin
                                  monitor_read();                 
                                  end 
                       join_any
                       disable fork;
                  end
                join
      end
  endtask: run_phase

The problem with either of these, is that the inner
forever
loop is persistent. It never terminates, so the
fork/join
never joins, so the outer
forever
loop never repeats.

In reply to dave_59:

What for the begin and end inside the fork?
As I said this code is a part of the monitor, that’s why the forever loop.
Shouldn’t I use forever loop?

In reply to saritr:

*In reply to dave_59:*What for the begin and end inside the fork?

I was just trying to keep the two examples as similar as possible. A begin/end around a single statement does not do anything meaningful. The fork/join_any still creates two processes.

Look at this simpler example

module top;
int A,B,C;
initial forever fork : outer
            #1 A = A + 1;
            forever begin : inner
                fork
                  #2 B = B + 1;
                  #4 C = C + 1;
                join_any
                disable fork;
             end : inner
         join : outer
endmodule

The outer forever loop only executes once; it behaves the same as if there was no outer forever keyword.

The outer fork creates two processes, one for the
#1 A = A + 1;
statement, and one for the inner forever loop. The inner forever loop executes a inner fork/join_any that creates two more processes; the assignments to B and C.
The process that makes the assignment to A completes at time 1, then the process that makes the assignment to B completes at time 2. The join_any happens at time 2, and the process that would make the assignment to C gets disabled. Now we have gotten to the end of the inner forever loop, and we repeat the inner fork. So the assignment to B happens at times 2,4,6,8, etc. The assignment to C never occurs because the statement gets disabled before ever having a chance to execute.
The outer fork never joins because the second process it spawns never finishes. It is an infinite forever loop. Since the outer fork never joins, the outer forever loop never has a chance to repeat.

In reply to dave_59:

Yes but what if the process is timing process how can I ensure that if one start (the first condition in the task existed) the other one won’t start?