Process control

In the run phase of UVM (no this is no a UVM question but SV related) 3 functions are called sequentially in a forever loop, as follows:

task run_phase(uvm_phase phase);
    ... 
    forever begin
      seq_item_port.get_next_item(req);
      send_transaction(req);
      seq_item_port.item_done();
    end
  endtask

in send_transaction method, I have 2 forever threads spawned by a fork process, as follows:

 task send_transaction (rx_function_st_txn_seq_item req);
    fork
      begin
      end
        forever begin
          ...
        end

        forever begin
          ...
        end
      end
    join

In the called method (send_transcation), I would like to terminate the send_transaction method and return back to calling method (run_phase). Would it work if I modify the code as follows:

 task send_transaction (rx_function_st_txn_seq_item req);
    fork
      begin
      end
        forever begin
          ...
        end

        forever begin
         if (condition) disable_fork
      end
      end
    join
endtask

In reply to verif_learner:

Sending an individual transaction should not take ‘forever’, so why would you fork two ‘forever’ threads? I would recommend having a discrete send_transaction() task that follows a linear process from start to finish without needing any forks.

Perhaps if you can describe what you are trying to accomplish in greater detail, we can provide an easier solution.

In reply to cgales:

In reply to verif_learner:
Sending an individual transaction should not take ‘forever’, so why would you fork two ‘forever’ threads? I would recommend having a discrete send_transaction() task that follows a linear process from start to finish without needing any forks.
Perhaps if you can describe what you are trying to accomplish in greater detail, we can provide an easier solution.

The driver code is implemented as a state machine. One thread deals with the states of the state machine and the other one takes care of output logic.

The issue with straight linear process is that the interface can back pressure randomly and for random intervals. So, imagine checking the status of back pressure after every clock cycle after driving every word of data.

Also, in my experience, state machine is highly readable and hence easy to maintain and easier to extend later to add additional function.

In reply to verif_learner:

Nesting forever loops seems very suspicious. Also a forever loop without any blocking statement gets you into a deadlock situation where time cannot advance. What you probably want is

task send_transaction (rx_function_st_txn_seq_item req);
        while (!condition) begin
          ...
        end
endtask

or maybe

task send_transaction (rx_function_st_txn_seq_item req);
    fork
      begin
        fork 
        forever begin
          ...
        end
        join_none
        wait(condition) disable fork;
      end
    join
endtask

You can also have a break statement inside a forever loop to exit the loop.

In reply to dave_59:

In reply to verif_learner:
Nesting forever loops seems very suspicious. Also a forever loop without any blocking statement gets you into a deadlock situation where time cannot advance. What you probably want is

task send_transaction (rx_function_st_txn_seq_item req);
while (!condition) begin
...
end
endtask

or maybe

As the logic has state machine that runs from start to finish and then returns control back to caller thread, I tried to use forever as a loop. I think I will modify it to while as this looks like a clean way to do the same thing.

task send_transaction (rx_function_st_txn_seq_item req);
fork
begin
fork 
forever begin
...
end
join_none
wait(condition) disable fork;
end
join
endtask

You can also have a break statement inside a forever loop to exit the loop.

This is what my original intention of question was.
Thanks for the inputs.