Logic Design - Processes (SystemVerilog)

avatar

[Edit of Image1]

Introduction

Hey it's a me again @drifter1!

Today we continue with the Logic Design series on SystemVerilog in order to cover Processes.

So, without further ado, let's dive straight into it!


Processes / Threads

Processes and threads are pieces of code that are executed separately from each other. In Verilog, such threads are created (or spawned) in-parallel at time zero for each initial and always block.


Fork - Join in Verilog

Additionally, Verilog also provides a fork - join mechanic, which is quite limited though. Basically, new threads are spawned at a fork within some existing initial or always block, and then the main thread / process waits for all the spawned threads to finish at the next join.

always | initial begin
    // code before fork-join
    fork
        // thread code
    join
    // code after fork-join
end

Multiple Threads

It's also quite straightforward to specify multiple threads, as any block of code within the begin and end keywords is considered as a separate thread. For example, three separate threads can be spawned as follows:

always | initial begin
    // code before fork-join
    fork
        // thread 1 code

        begin
            // thread 2 code
        end

        // thread 3 code
    join
    // code after fork-join
end

Nested Fork - Join

A fork - join can also be nested within another fork - join, allowing child threads to spawn additional child threads, taking the role of the parent thread.

always | initial begin
    fork
        fork
            // Thread 1_0 code
            begin
                // Thread 1_1 code
            end
        join
        // Thread 2 code
    join
end

Fork - Join in SystemVerilog

SystemVerilog extends on the fork-join mechanic allowing two more join behaviors:

  • join_any : Execute after any of the child threads finish.
  • join_none : Execute directly after the threads have been spawned.

That way SystemVerilog basically introduces dynamic processes, which are built using fork - join_any and fork - join_none, whilst Verilog's initial, always and fork - join could only specify static processes.

Process Control

In order to be able to control these more dynamic processes, SystemVerilog provides us with two more statements:

  • wait fork : Waits for all child processes that have been called / spawned by the calling process to complete.
  • disable fork : Terminates all child processes (and their child processes) of the calling process.

When join_any or join_none has been used, then waiting for all the child processes to finish can be achieved by using the wait fork statement as follows:

always | initial begin
    fork
       // spawn threads
    join_any | join_none

    wait fork;
end

Similarly, the spawned child processes can be killed by using the disable fork statement:

always | initial begin
    fork
       fork
            // Thread 1_0 code
            begin
                // Thread 1_1 code
            end
        join
        // Thread 2 code
    join_any | join_none

    disable fork;
end

Of course, this statement also takes care of the nested child threads 1_0 and 1_1 of thread 1!


RESOURCES:

References

  1. https://www.chipverify.com/systemverilog/systemverilog-tutorial
  2. https://www.asic-world.com/systemverilog/tutorial.html

Images

  1. https://www.flickr.com/photos/creative_stock/5227842611

Block diagrams and other visualizations were made using draw.io


Previous articles of the series

Verilog

SystemVerilog


Final words | Next up

And this is actually it for today's post!

Next time we will start covering interprocess communication, which is achieved using SystemVerilog Events, Semaphores and Mailboxes...

See Ya!

Keep on drifting!

Posted with STEMGeeks



0
0
0.000
0 comments