Wednesday, August 11, 2010

SystemVerilog disable fork

The short of it is that 'disable fork' is a pain. The long of it is that you have no choice, and perhaps an example might make your lives easier:

fork
begin
fork
begin
#3000;
$display("timeout");
end
begin
sem.get(1);
end
join_any
disable fork;
end
join

Why wrap it in another fork-join pair? This is due to how disable fork works. disable fork disables ALL forks at its level and below. If the above code is inside of a task which is part of a module, then calling disable fork without it being wrapped will disable all forks running in the module. I KNOW! It's ludicrous! Yes, but this is how it works.
What about named forks? Also problematic. This is because when you use a named fork and then call disable , it will disable all instances of the same named fork. If you have multiple threads running the same task and both are in the named fork, then one disable will kill both of them. I KNOW! It's ludicrous! ...
Any other options? I don't know. This seems to be the only way to do the job in SystemVerilog. Perhaps avoiding semaphores and doing everything with wait statements or events, but for now I prefer to use semaphores as they are an item I can 'new', and therefore they allow for much more flexibilty.

6 comments:

  1. Thanks a lot. This writeup was really helpful.
    I spent last two days debugging what's going wrong. And finally it turns out that the disable fork disables ALL forks at its level and below.

    It needs to be wrapped in another fork-join.
    Thanks again.

    ReplyDelete
  2. Thanks a ton for this info. Its a SV Gotcha!!!

    ReplyDelete
  3. A better alternative is to use the process::kill() method.

    ReplyDelete
  4. program a();
    initial
    begin
    fork
    thread_1();
    thread_2();
    join_none
    #20;
    $display("Out Of Fork");
    end


    task thread_1();
    $display("thread_1");
    fork
    begin
    $display("thread_1.1");
    end
    begin
    $display("thread_1.2");
    end
    join_any
    disable fork;


    $display("thread_3");
    fork
    begin
    $display("thread_3.1");
    end
    begin
    $display("thread_3.2");
    end
    join_any
    disable fork;
    endtask


    task thread_2();
    $display("thread_2");
    fork
    begin
    $display("thread_2.1");
    #5;
    $display("thread_2.1.1");
    end
    begin
    $display("thread_2.2");
    #5;
    $display("thread_2.2.1");
    end
    join_any


    $display("thread_4");
    fork
    begin
    $display("thread_4.1");
    #5;
    $display("thread_4.1.1");
    end
    begin
    $display("thread_4.2");
    #5;
    $display("thread_4.2.1");
    end
    join_any
    endtask
    endprogram


    I tried this code to check , i didn't get that disable fork disabled other fork , other that its fork..

    ReplyDelete
  5. thread_1
    thread_2
    thread_1.1
    thread_3
    thread_2.1
    thread_2.2
    thread_3.1
    thread_2.1.1
    thread_4
    thread_2.2.1
    thread_4.1
    thread_4.2
    thread_4.1.1
    thread_4.2.1
    Out Of Fork
    $finish at simulation time 20

    ReplyDelete
  6. Hi,

    I know this is old thread, but I want to answer the above question so new learners can have some help.

    tasks, and functions have implicit begin/end statements. Hence its usage is optional in task/function as compared to Verilog.

    Now consider the thread_1.
    you are spawning two threads thread_1.1 and thread_1.2 using fork..join_any and then disabling fork. entire this code is inside of task i.e., inside the implicit begin/end block of task. when disable fork is called, all threads spawned in that block are killed. Here t_1.2 got killed.

    ReplyDelete