In Verilog, you often encounter repetitive code that is used multiple times in your RTL (Register Transfer Level) design. This code may involve complex calculations that need to be performed with different data values. Instead of writing the same code over and over, you can create a Verilog functions to handle the calculations and return the result. This reduces the amount of code in your RTL and makes it easier to manage.
A Verilog function is very similar to functions in C. It allows you to wrap repetitive code in a reusable block, making your design cleaner and more efficient.
What Is a Function in Verilog?
A function in Verilog is used to return a value, which can then be used in expressions within the design. When you define a function, it will always start with the keyword function
, followed by the return type, function name, and a list of input ports. The function ends with the keyword endfunction
.
If the function doesn’t return anything, you can use void
as the return type.
Function Syntax
function [automatic] [return_type] name ([port_list]);
[statements]
endfunction
- automatic: Makes the function reentrant, allowing multiple invocations.
- return_type: The type of value the function will return.
- name: The function’s name.
- port_list: The inputs (and possibly outputs) for the function.
- statements: The code that executes inside the function.
The Role of automatic
Using the keyword automatic
makes the function reentrant. This means each time the function is called, the variables are dynamically allocated rather than shared. This is especially useful for recursive functions and concurrent executions when multiple processes call the same function at the same time.
Function Declaration in Verilog
There are two ways to declare inputs to a function in Verilog. Let’s look at both methods.
Example 1: Declaration with input
function [7:0] sum;
input [7:0] a, b;
begin
sum = a + b;
end
endfunction
Example 2: Declaration with Inline Input List
function [7:0] sum(input [7:0] a, b);
begin
sum = a + b;
end
endfunction
In both examples, the function sum calculates the sum of two 8-bit inputs, a
and b
, and returns the result.
Returning a Value
When you define a function in Verilog, the function automatically creates an internal variable with the same name as the function. You cannot declare a new variable with the same name inside the function. To return a value, assign the result to this internal variable.
For example:
sum = a + b;
Calling a Function in Verilog
You can call a function by including it in an expression. Here’s the syntax for calling a function:
Example of Function Call
reg [7:0] result;
reg [7:0] a, b;
initial begin
a = 4;
b = 5;
#10 result = sum(a, b); // Function call
end
In this example, we define two variables a
and b
, and then call the function sum(a, b)
to calculate their sum. The result is stored in the result
variable.
Function Rules
Verilog functions have specific rules that must be followed:
- No time-controlled statements: You cannot use
#
,@
,posedge
,negedge
, orwait
inside a function. - No tasks: Functions cannot start tasks since tasks can consume simulation time.
- At least one input: A function must have at least one input.
- No non-blocking assignments: Functions cannot use non-blocking assignments (
<=
) or force/release statements. - No triggers: Functions cannot have event triggers (e.g.,
@
). - No output or inout: Functions cannot have outputs or inouts.
Recursive Functions
A recursive function is one that calls itself. These are useful for problems like calculating factorials, Fibonacci numbers, or other mathematical problems that require repeated calculations.
Example: Recursive Factorial Function
In this example, we create a recursive function to compute the factorial of a given number.
module tb;
initial begin
integer result = factorial(4);
$display("factorial(4) = %0d", result);
end
function automatic integer factorial(integer i);
integer result = i;
if (i > 0) begin
result = i * factorial(i - 1); // Recursive call
$display("i=%0d result=%0d", i, result);
end else
result = 1; // Base case for recursion
return result;
endfunction
endmodule
Simulation Log Output
i=1 result=1
i=2 result=2
i=3 result=6
i=4 result=24
factorial(4) = 24
In this example, the factorial
function calls itself with a decremented value until it reaches the base case (when i
is 0), at which point it returns 1. Each recursive call multiplies the current value of i
by the result of the next call, eventually calculating the factorial of the given number.
Key Takeaways
Concept | Details |
---|---|
Function Syntax | function [return_type] name([port_list]); |
Automatic Keyword | Makes the function reentrant (useful for recursion) |
Return Type | Can be any type (e.g., integer, reg, etc.) |
No Time-Control | Functions cannot include time-sensitive statements like # , @ , or posedge |
Recursive Functions | Functions can call themselves to solve problems like factorials or Fibonacci series |
By using functions in Verilog, you can make your code more modular, reduce redundancy, and improve readability. Whether you’re performing simple calculations or solving complex recursive problems, Verilog functions offer a powerful tool to manage your design efficiently.