11

I've something similar to following pseudo code:

for (lets say 10 iterations)
begin
// Do some configuration changes
    fork
        begin
        ///apply input to design
        end
        begin
        while (1)
        /// at particular point update expected status / values
        end
        begin
        while (1)
        /// read status and verify with expected values
        end
    join_any
end

From code: only application of input can break fork, since other 2 threads are working under while (1) I want to disable all threads between each iteration of for i.e. once stream of input is applied -- disable all spawned threads until next iteration starts (with new configuration)

so I modified above code to

 ....
 join_any
 disable_fork
 end

However this seems to disable for loop as well / or something similar which I don't understand but effect is test is hung up. Can someone explain me the reason and solution?

wisemonkey
  • 571
  • 3
  • 15
  • 29
  • This can turn into a not useful question: I was not releasing semaphore after disabling fork that would cause no more transactions. I'm currently debugging will update later – wisemonkey Jan 11 '13 at 23:11
  • What are you trying to accomplish? Are you sure you need a parallel block here? –  Jan 14 '13 at 01:04
  • Thanks @Adam12 yes these blocks need to be parallel, the way I need to verify functionality. I'm trying to verify if read while update to particular register in progress gives valid data. And yes the issue was because I was not releasing semaphore. I've resolved it and its working properly now – wisemonkey Jan 14 '13 at 19:47

3 Answers3

32

"disable fork" kills not only the processes launched by your fork...join_any, but also any other processes that are descendants of the same process that executes the disable-fork. If you have launched any other processes (using, for example, fork...join_none) earlier in the life of this process, then those other processes will be killed too.

You can rather easily protect against this by causing your fork...join_any, and its later disable-fork, to run in a new child process of its own. This limits the effect of your disable-fork so that it can affect only the newly launched processes that you care about, and is guaranteed to have no other unwanted effects.

Do this by enclosing the whole mess in "fork begin...end join" like this:

fork begin // isolate the following code as a single child process
  fork  // launch the processes you wish to manage
    apply_input();
    update_status();
    verify_status();
  join_any // kill off the *_status threads when apply_input terminates
  disable fork;
end join // end of child process isolation

This is a well-known issue with fork...join_any and fork...join_none. It's been discussed recently on Verification Guild forum, and is described in sections #79 and #80 of the Sutherland and Mills book "Verilog and SystemVerilog Gotchas".

Putting "fork begin" and "end join" on single lines is unusual, but I like it as a way to make it very obvious that I'm synchronously forking exactly one child process. Normally that would be a useless thing to do, but in this situation it's essential.

This idiom is so common, and so easy to get wrong, that you may prefer to encapsulate it in a pair of macros (I don't like this, but...):

`define BEGIN_FIRST_OF fork begin fork
`define END_FIRST_OF join_any disable fork; end join

Now you can write...

`BEGIN_FIRST_OF
    apply_input();
    update_status();
    verify_status();
`END_FIRST_OF

where the names "...FIRST_OF" are intended to reflect the similarity to the Specman (e) language construct that does the same thing.

  • Thanks, that makes much more sense. As of now there is only one fork in my tests so my approach is working but I'm changing those to what you proposed to isolate them from future changes. And I agree with you, I won't be using defines at least until I get used to proper methodology – wisemonkey Jan 23 '13 at 18:13
2

You should consider using processes instead of disable in SystemVerilog

process process1;
process process2;
fork
  begin
    process1 = process::self();
    # do something in process 1
  end
  begin
    process2 = process::self();
    # do something in process 2
  end
join_any
#1;
if (process1 != null && process1.status != process::FINISHED)
  process1.kill();
if (process2 != null && process2.status != process::FINISHED)
  process2.kill();

It is supposed to be safer than disable.

rubund
  • 7,603
  • 3
  • 15
  • 24
  • Even this would have race condition as the process1 could get complete even before process2 is created. – Vineeth VS Jul 10 '19 at 14:38
  • @VineethVS, not sure what you mean here. Wouldn't the null check cover that? – Diegomanas Jun 23 '20 at 13:11
  • @Diegomanas No, the null check is to make sure that the process that's being called is not null. What happens if process 2 is not even created when process 1 is complete. – Vineeth VS Jul 16 '20 at 08:09
  • I may be missing something here. But if your process isn't even created, the variable holding the process handler (process1/2) wouldn't be initialized as it's done inside the process itself. Hence, null handler – Diegomanas Aug 07 '20 at 20:45
-1

disable fork should work fine. Here is the small example.

 module top;

      initial
      begin
          $display(" BEFORE fork  time = %0t ",$time );

          for(int i = 0 ; i < 3 ; i++)
          begin
            fork
              automatic int j = i;
              begin
                  $display(" Action thread started time = %0t  j: %0d",$time,j);
                  #(10*j);
                  $display(" Action thread ended time = %0t  j: %0d",$time,j);
              end

              begin
                 $display(" Entered timeout_15 = %0t j: %0d",$time,j);
                 #15;
                 $display(" Ended timeout_15 = %0t j: %0d",$time,j);
              end
            join_any

            disable fork; // it kills all processes
            $display("---------------------------------------------------");
        end

       #100; // This is to make sure if there any threads ..

       $display(" time = %0d Outside the main fork ",$time );
       $display(" time = %0d  After wait fork ",$time );
      end
    endmodule

output::

BEFORE fork  time = 0
 Action thread started time = 0  j: 0
 Entered timeout_15 = 0 j: 0
 Action thread ended time = 0  j: 0
---------------------------------------------------
 Action thread started time = 0  j: 1
 Entered timeout_15 = 0 j: 1
 Action thread ended time = 10  j: 1
---------------------------------------------------
 Action thread started time = 10  j: 2
 Entered timeout_15 = 10 j: 2
 Ended timeout_15 = 25 j: 2
---------------------------------------------------
 time = 125 Outside the main fork
 time = 125  After wait fork
Santosh Kumar
  • 511
  • 1
  • 5
  • 5