Logic Design - Control Flow (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 some of the additional Control Flow that it provides. Many are directly derived from Verilog. Therefore, only those that are unique to SystemVerilog will be covered rigorously.

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


Procedural Blocks

Let's start off with the procedural blocks, which are similar to Verilog's.

Procedural statements can only be specified within procedural blocks, which in SystemVerilog are the following:

  • initial : Executes once at the beginning of the simulation
  • final : Executes once at the end of the simulation (unique to SystemVerilog)
  • always blocks : always_comb, always_latch, always_ff (unique to SystemVerilog)
  • task : Executed whenever the task is called
  • function : Executed whenever the function is called, and also returns an optional value

Final Block

A final block is defined using the final keyword. It specifies a procedural block of statements which is executed at the end of the simulation without delays. So, it's common to use it in order to display statistics about the simulation.

The syntax is:

final begin
    // statements to execute at
    // the end of the simulation
end

Always Blocks

The always block in Verilog can be used for both combinational and sequential logic. This may lead to some unintended latches and design mistakes in general. In order to fix this issue SystemVerilog adds specialized always blocks that indicate the exact type of always block that is intented for simulation, synthesis and verification:

  • always_comb : combinational logic
  • always_latch : latch logic
  • always_ff : sequential logic

For all of them keep in mind that:

  • the left hand-side variables can't be written to by other processes
  • the blocks are automatically triggered at time zero after the initial and always blocks, so that the outputs are consistent with the inputs

When specifying combinational logic the sensitivity list can be omitted as it is inferred automatically from the expressions defined.


Loops

In order execute a section of code for more than once, loops are defined. It's also typical to specify a termination condition, so that the simulation doesn't run indefinitely.

SystemVerilog provides the following looping blocks:

  • forever : Executes the statements forever
  • repeat : Executes / repeats the statements for a given number of times
  • while : Executes the statements as long as a specified condition is true
  • for : Similar to a while loop, but it allows specifying the starting value and incremental expression.
  • do while : Executes the statements at least once and then loops as long as a specified condition is true
  • foreach : Iterates through all the elements of an array

The loops that are unique to SystemVerilog are thus the do while and foreach loops. Additionally, the for loop is enhanced allowing the for loop control variable to be declared within the for loop.

For Loop

The enhanced SystemVerilog for loop is exactly like a C for loop:

for (int i = 0; i < 4; i++) begin
    // loop code
end

Do While Loop

A do while loop is defined as:

do begin
    // loop code
end while ([condition]);

Foreach Loop

A foreach loop has the following syntax:

foreach (array[index]) begin
    // what to execute on each element
end

For example, initializing each element of a 1D array arr, to the value of their index can be done as follows:

foreach (arr[i]) begin
    arr[i] = i;
end

Jump Statements

SystemVerilog also adds the C-like jump statements:

  • break : break out of loop
  • continue : skip current and move to next loop iteration
  • return expression : exit from a function with a return value
  • return : exit from a task or a void function

All of these are statements and thus must be followed by an semicolon (;) to be syntactically correct.


Conditional Statements

In addition to the usual if and case conditional statements that Verilog provides, SystemVerilog also adds some additional forms that can report violations. Those are the unique, unique0 and priority versions, which operate as follows:

  • unique : reports an error if none of the conditions match (and there is no explicit else) or more than one of the conditions match.
  • unique0 : reports an error if more than one of the conditions match.
  • priority : reports an error if none of the conditions match (which are evaluated in sequential order). In the case of the if statement there's no else clause to the final if.

The syntax is simple. Add these keywords before the if and case keyword respectively:

unique if ([condition])
unique case ([var])
    // error when no condition matches
    //  OR
    // more than one conditions match

unique0 if ([condition])
unique0 case ([var])
    // error when more than one conditions match

priority if ([condition])
priority case ([var])
    // error when no condition matches

Functions and Tasks

Functions and Tasks in SystemVerilog are the same as in Verilog, but some additional features are added, such as:

  • Declare task and function ports like modules
  • Function output and inout ports
  • Void Functions
  • Declare automatic variables with static tasks and functions, or static variables with automatic tasks and functions
  • Passing arguments by reference, value, name and position
  • Default argument values and optional arguments
  • Multiple statements within a task or function without the need of fork-join logic
  • Return from task or function before the end of the task or function

Argument Passing

In Verilog the only way of passing a variable was by value, which basically meant that the argument's value was copied to the task or function memory space. In SystemVerilog this is done by simply specifying the name of the variable as the argument in both the definition and the call of the function or task:

function func_name;
    input var_name;
    // main body
endfunction

...

func_name (arg_name);

SystemVerilog also adds passing by reference, which is done by adding the keyword ref in the argument definition:

function func_name(ref var_name)
    // main body
endfunction

With this we also showcased the module-like definition of functions.

Next up is, SystemVerilog also allows passing arguments by name or by position. This is the same as connecting module ports using explicit mapping when instantiating them, allowing us to mix up the position and name of the arguments in the task or function call. So, this is basically done using the familiar . notation.

function func_name(ref var1, ref var2)
    // main body
endfunction

func_name(.var2(arg2), .var1(arg1));

Specifying a default value for each argument is as simple as adding an assigment statement to its definition:

function func_name(ref var_name = value)
    // main body
endfunction

When there is such a default value specified then the argument can be omitted, basically becoming optional. It's also possible to have just empty parentheses () when all arguments have defaults specified.

func_name();

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!

Of course, there are more features in SystemVerilog that can be used to control the flow of the program, but these are the most common ones.

Next up are Processes, and maybe even Events...

See Ya!

Keep on drifting!

Posted with STEMGeeks



0
0
0.000
0 comments