Logo

Multiple process in system verilog

17 Jul 2022
4 mins

In Verilog, fork-join construct is used to create a new thread which can run in parallel to code defined in another block. In System Verilog, a few additions were made to give more flexibility in the way we can create a new process. Also, a new process class is introduced in System Verilog which can be used to fine-grain control the process.

Fork-join

Fork-join construct was available in Verilog. In this construct different blocks defined inside fork will be executed in a new thread and thus run parallel. But in fork-join we must wait for all the process that was started inside the fork-join to end before moving to the next statement.

Syntax

fork
    ...
join

Example

module fork_join;
    initial begin
        $display("Starting 2 processes");
        fork
            begin
                repeat(5) begin
                    $display("[%0t] Printing from 1st process", $time);
                    #10;
                end
            end
            begin
                repeat(2) begin
                    $display("[%0t] Printing from 2nd process", $time);
                    #20;
                end
            end
        join
        $display("Both the process ended");
    end
endmodule
Output
# Starting 2 processes
# [0] Printing from 1st process
# [0] Printing from 2nd process
# [10] Printing from 1st process
# [20] Printing from 2nd process
# [20] Printing from 1st process
# [30] Printing from 1st process
# [40] Printing from 1st process
# Both the process ended

Fork-join_any

This is a new construct added to System Verilog. In fork-join we have seen that the program waits for all the process to end and then it will move ahead. Sometimes we may need more flexibility in this behavior. This is the reason System Verilog introduces a few more constructs to make new thread.

In fork-join_any, the simulator does not wait for all the process started within the fork block to end, instead even if any one of the process is completed, the simulator will execute the consecutive statements defined after fork-join_any block.

Syntax

fork
    ...
join_any

Example

module fork_joinany;
    initial begin
        $display("Starting 2 processes");
        fork
            begin
                repeat(5) begin
                    $display("[%0t] Printing from 1st process", $time);
                    #10;
                end
            end
            begin
                repeat(2) begin
                    $display("[%0t] Printing from 2nd process", $time);
                    #20;
                end
            end
        join_any
        $display("One of the process ended");
    end
endmodule
Output
# Starting 2 processes
# [0] Printing from 1st process
# [0] Printing from 2nd process
# [10] Printing from 1st process
# [20] Printing from 2nd process
# [20] Printing from 1st process
# [30] Printing from 1st process
# One of the process ended
# [40] Printing from 1st process

Fork-join_none

This is also new to System Verilog. In this construct, the simulator does not wait for any of the process to get completed. Once the process is started the simulator will proceed to execute statements defined after the fork-join_none block.

This construct is a non-blocking one and it won’t block the execution even if a time-consuming task is started within the fork-join_none.

Syntax

fork
    ...
join_none

Example

module fork_joinnone;
    initial begin
        $display("Starting 2 processes");
        fork
            begin
                repeat(5) begin
                    $display("[%0t] Printing from 1st process", $time);
                    #10;
                end
            end
            begin
                repeat(2) begin
                    $display("[%0t] Printing from 2nd process", $time);
                    #20;
                end
            end
        join_none
        $display("Started the process and came out of fork_join block");
        wait fork;
        $display("All process finished");
    end
endmodule
Output
# Starting 2 processes
# Started the process and came out of fork_join block
# [0] Printing from 1st process
# [0] Printing from 2nd process
# [10] Printing from 1st process
# [20] Printing from 2nd process
# [20] Printing from 1st process
# [30] Printing from 1st process
# [40] Printing from 1st process
# All process finished

Wait and disable forked processes

SV provides some inbuilt functions that can be used to wait for all the processes to complete before moving ahead or disable all the processes that are running.

Wait

wait fork can be used to wait for all the processes to complete. We have seen that in SV there are fork-join_any and fork-join_none which will not wait for all the processes to be complete and start executing the consecutive statements. Now if we need to wait for all the process to be complete, we can use wait fork.

Disable

disable fork disables or terminates all the process that has been started by the fork block.

Process class

Process is an in-built class in System Verilog which provides fine-grain control over the execution of the process. This class can also be used to control a process from another process.

Functions of process class

System Verilog provides some in-built functions in the process class which helps in having fine control over the process.

  • self() – This is a static function and returns the handle of the current process. This handle can be stored and used to perform different tasks.

  • status() – This function returns the status of the process. The returned value is an Enum and can take following values.

    • FINISHED – Process executed and terminated normally.
    • RUNNING – Process is currently running
    • WAIITNG – Process is in a wait state. Generally due to blocking statement.
    • SUSPENED – Process is currently stopped and waiting for a resume.
    • KILLED – Process has been stopped forcibly by user.
  • kill() – This task can be used to terminate a process and its subprocess.

  • suspend() – This task can be used to stop the current process or any other process temporarily. If there is some - blocking statement, the process would first come out of the blocking statement and then stop.

  • resume() – This tasks resume the suspended process.

  • await() – This tasks can be used to make the current process wait for any other process to end. It is

Example

module process_demo;
    process proc[4];
    initial begin
        for (int i=0; i<4; ++i) begin
            fork
                automatic int k = i;
                begin
                    proc[k] = process::self();
                    repeat($urandom_range(2,8)) begin
                        $display("[%0t] From process [%0d]", $time, k);
                        #10;
                    end
                end
            join_none
            #2;
        end

        //  wait for all process to start
        for (int i=0; i<4; ++i) begin
            wait(proc[i] != null);
        end
        $display("[%0t]All process started", $time);

        //  suspend proc[1] until proc[2] is finished
        proc[1].suspend();
        #1;
        $display("[%0t] Status of proc 1 = %s", $time, proc[1].status().name());

        //  waits for the proc[2] to finish
        proc[2].await();

        //  resumes proc[1]
        proc[1].resume();
        $display("[%0t] Status of proc 1 = %s", $time, proc[1].status().name());

        //  kill all process
        #20;
        $display("[%0t] Killing all process", $time);
        for (int i=0; i<4; ++i) begin
            if(proc[i].status() != process::FINISHED)
                proc[i].kill();
        end
        $display("[%0t] Killed all process", $time);

        //  prints status of all the processes after killing them
        //  while killing a process we first check whether process
        //  is finished or not
        #2;
        $display("Status of all processes");
        $display("[%0t] Status of proc 0 = %s", $time, proc[0].status().name());
        $display("[%0t] Status of proc 1 = %s", $time, proc[1].status().name());
        $display("[%0t] Status of proc 2 = %s", $time, proc[2].status().name());
        $display("[%0t] Status of proc 3 = %s", $time, proc[3].status().name());
        // #120;
        $finish();
    end
endmodule
Output
# [0] From process [0]
# [2] From process [1]
# [4] From process [2]
# [6] From process [3]
# [8]All process started
# [9] Status of proc 1 = SUSPENDED
# [10] From process [0]
# [14] From process [2]
# [16] From process [3]
# [20] From process [0]
# [24] From process [2]
# [26] From process [3]
# [30] From process [0]
# [34] From process [2]
# [36] From process [3]
# [40] From process [0]
# [44] From process [2]
# [50] From process [0]
# [54] From process [2]
# [60] From process [0]
# [64] Status of proc 1 = RUNNING
# [64] From process [1]
# [70] From process [0]
# [74] From process [1]
# [84] Killing all process
# [84] Killed all process
# Status of all processes
# [86] Status of proc 0 = FINISHED
# [86] Status of proc 1 = KILLED
# [86] Status of proc 2 = FINISHED
# [86] Status of proc 3 = FINISHED
Try this code in EDA Playground