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, or wait 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

ConceptDetails
Function Syntaxfunction [return_type] name([port_list]);
Automatic KeywordMakes the function reentrant (useful for recursion)
Return TypeCan be any type (e.g., integer, reg, etc.)
No Time-ControlFunctions cannot include time-sensitive statements like #, @, or posedge
Recursive FunctionsFunctions 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.

Scroll to Top