Tasks and Functions

User Defined Tasks and Functions in SystemVerilog

In SystemVerilog, functions and tasks provide a way to encapsulate a sequence of statements into a reusable block of code. Both allow for code reusability and modular design, but there are significant differences between them.

System Tasks and Function built into the language where as User-Defined Tasks and Functions are defined by the user.

A function is a re-usable block of code that returns a value and does not consume simulation time. It cannot contain time-controlled statements such as delays or waits

A task is a re-usable block of code does not return a value but can contain time-controlled statements, making it suitable for modeling behavior that takes time.

Functions

A function is a named block of code that takes a set of inputs, performs computations, and returns a value. Functions in SystemVerilog are similar to functions in other programming languages.

Here is the syntax for defining a function:

function [return_type] function_name ([input_type] input_name);
    // Statements
endfunction

Here's an example of a function that calculates the square of a number:

function integer square(integer num);
    square = num * num;
endfunction

module MyModule;
    initial begin
        integer a = 5;
        $display("Square of %0d is %0d", a, square(a));
    end
endmodule

This code will display: Square of 5 is 25

Functions in SystemVerilog are called in an expression and they must return a value. They are executed in zero simulation time, meaning they do not consume time steps during the execution of the simulation. Therefore, they cannot contain time-controlled statements like #, @, wait, etc.

Tasks

Tasks, on the other hand, do not return a value, but they allow time-controlled statements. They are useful for modeling activities that consume time such as transactions, bus activities, and test sequences.

Here is the syntax for defining a task:

task task_name ([input/output/inout] variable_name);
    // Statements
endtask

Here's an example of a task that models a simple delay:

task delay;
    #10;  // Wait for 10 time units
endtask

module MyModule;
    initial begin
        $display("Start");
        delay;
        $display("End");
    end
endmodule

This code will print 'Start', wait for 10 time units, and then print 'End'.

Tasks can also have input, output, and inout arguments. They can be called from initial, always, and other task blocks but not from within a function.

User-defined functions and tasks help to create cleaner, more readable code. They can be used in testbenches to model complex behaviors and sequences, making the verification process more efficient and manageable.

Automatic keyword

The automatic keyword in SystemVerilog is used with tasks and functions to make their variables local to each invocation of the task or function. In simple words, when you declare a task or function with the automatic keyword, each call to that task or function will get a fresh set of its variables. This is especially useful in recursive tasks and functions, and in tasks and functions that can be called from multiple threads simultaneously.

Without the automatic keyword, tasks in SystemVerilog default to static storage class, where the variables keep their values between different invocations of the task.

automatic: If a task or function is declared with the automatic keyword, each call to that task or function gets a fresh set of its variables. In other words, the variables are reinitialized each time the task or function is invoked. This is particularly useful for tasks or functions that can be called simultaneously from multiple threads, and you want each thread to have its own copy of the variables, or for recursive tasks and functions.

static: All tasks and functions are static by default in SystemVerilog unless the keyword automatic is used. The variables in a static task or function preserve their values between different invocations of the task or function. So, if you have a variable in a static task or function and you change its value, the next time you call the same task or function, it will start with the last value it was set to. This is useful when you want the task or function to "remember" its state between calls.

Here's a simple example to illustrate the difference:

task automatic auto_task;
    integer i = 0;
    begin
        i = i + 1;
        $display("auto_task: %d", i);
    end
endtask

task static_task;  // static is the default
    integer i = 0;
    begin
        i = i + 1;
        $display("static_task: %d", i);
    end
endtask

initial begin
    auto_task; 
    auto_task; 
    auto_task;  // calls to automatic task 3 times
    static_task; 
    static_task; 
    static_task;  // calls to static task 3 times
end

The output of the provided code will be as follows:

auto_task: 1
auto_task: 1
auto_task: 1
static_task: 1
static_task: 2
static_task: 3

In this example, each call to auto_task will print 1, because i is an automatic variable, and it gets reinitialized to 0 each time the task is called. However, each call to static_task will print an increasing number (1, 2, 3), because i is a static variable, and it retains its value between calls.

Therefore, the use of automatic keyword depends on the specific needs of your design. If you want the task or function to remember its state between calls, you can use static variables. But if you want each call to start with a clean state, you should use automatic variables.

Building on from the last tutorial, here is a structure of a module in SystemVerilog showing tasks and function block. We will expand on this diagram and add more blocks and containers as we move forward in this tutorial.

Have a Question?

Feel free to ask your question in the comments below.

Please Login to ask a question.

Login Now