Verilog functions and tasks
When we write a code, we may need to use some line of code repeatedly in various places. Writing the same line of code leads to errors in code and decreases the readability of the code. Thus to execute the same code several times, there are functions in a programming language (also known as a subroutine). In Verilog, there are two types of subroutines, functions and tasks. A function is the same as functions in any programming language, but tasks are slightly different. Let us see them in detail.
Before learning more about functions and tasks, let us see what a subroutine is. A routine defines the execution flow of a code. A subroutine is some code that is executed only when called by the main programme. Once the execution of the subroutine is complete, the pointer moves back to the main programme.
A function, as discussed earlier, is the same as that found in other programming languages. To write a function, first, we need to declare it. This step is known as a functional declaration. After declaration, we write the function's body, which is nothing but the function's functionality and is known as a function definition. Please remember, we cannot define a function before declaring it.
Function declaration consists of 3 parts:
- Function name – This is the function's name that will be used to call the function. The name can be any valid identifier.
- Automatic - This keyword is used to make the function automatic. Detailed explanation about automatic function is discussed later in this lesson.
- Return type – This is the data type of the value that is returned by the function. If no value needs to be returned, then void is used.
- Arguments – This is the input to the functions. There can be no or even more than one argument in a function. Arguments are similar to the port list in modules.
A function is declared using a
function keyword, and the function definition ends with the
endfunction keyword. The function definition goes inside
begin end block.
function [automatic] [return_type] name ([arguments]); // Function Declaration begin // function definition goes here end endfunction
module function_demo; reg b; function reg print(input reg [3:0] data); begin $display("data is %0b", data); print = 1; end endfunction // This function declaration is not valid as // there is no argument. /** function reg print1(); begin $display("data is"); print1 = 1; end endfunction **/ initial begin /** `if` is used here because function will return a value Thus, the returned value needs to be assigned or used in conditional statement. If it is not assigned then Verilog will treat it as a task and throw an error saying task print is not declared **/ if(print(4'b1100)) begin $display("printed successfully"); end // This is also valid as returned value is assigned to variable b = print(4'b1010); // This is not valid // print(4'b1010); end endmodule
# data is 1100 # printed successfully # data is 1010
In Verilog, while declaring a function, some rules need to be remembered. These rules are:
- A function should have at least one argument.
- Function argument can only have
inoutcannot be used in function arguments.
- Function definition should have any delay statement or
@statements, as functions cannot consume time.
- Function definition should not have a non-blocking assignment or procedural-continuous assignment.
In Verilog, a task is a particular subroutine type, which can consume time. As we know, Verilog is a time-dependent simulator. Thus, in some cases, we need to provide a delay in subroutines. However, delays cannot be used in functions as they should not consume time; otherwise, a compile-time error is shown. A task helps to tackle this problem.
Similar to functions, a task is also declared first and then defined. However, a task declaration does not consist of a return type. Instead, we use a port with output direction in the arguments.
In Verilog, global functions or tasks are invalid. It is only valid in System Verilog. Declaring a global function or task will give a compile-time error. The concept of global declaration will be discussed in System Verilog. This fact is mentioned here as people tend to confuse whether they can use global declaration in Verilog.
task [automatic] name ([arguments]); begin // task definition end endtask
module task_demo; reg [3:0] op; // Task without any arguments. task print(); begin $display("At [%0t] printing from task", $time); $display("At [%0t], output from task = %b", $time, op); end endtask // Task with 2 ip arguments and 1 output argument. As task calculates // sum thus it needs to return a value. Function cannot // be used as we want to give some delay also. task sum(input reg[3:0] a, b, output reg [3:0] sum); begin $display("At [%0t], Calculating sum...", $time); #10; sum = a + b; end endtask initial begin // As there is no return type, thus tasks are not // assigned to any variable. Rather, the variable in which // output needs to be assigned is passed as an argument. sum(4'b1010, 4'b0101, op); print(); end endmodule
# At , Calculating sum... # At  printing from task # At , output from task = 1111
|Can have zero or more than one argument.||It needs to have at least one argument.|
|A task can have delay statements inside it.||A function cannot have a delay statement. It should return a value at the same time step.|
|A Task Does not have a return type. However, output arguments help return value.||A function does have a return type.|
|A task can return more than one values as there can be any number of output arguments.||A function can return only one value as output arguments cannot be used in functions.|
|A task can call another function or a task from its body.||A function can only call another function from its body. A task cannot be called as it can consume time, and function is not allowed to consume time.|
Both functions and tasks are of 2 types, static and automatic.
By default, all functions and tasks are static in Verilog. Static tasks mean that the variable or nets declared inside a task will retain their previous value whenever the task is called. Therefore, it can be said that the memory allocated for the function or task remains the same during each call, making it static.
Whereas in Automatic function or task, a new memory location is allocated each time they are called. Thus any internal variable present inside the task would not retain its previous value.