Easy Tutorial
❮ Npm Slow Use Cnpm Openresty Intro ❯

6.1 Verilog Functions

Category Verilog Tutorial

Keywords: Function, Endian Conversion, Digital Tube Decoding

In Verilog, tasks (keyword: task) or functions (keyword: function) can be used to extract repetitive behavioral designs and call them in multiple places to avoid repeated code writing, making the code more concise and understandable.

Function

Functions can only be defined within a module, in any position, and referenced anywhere within the module. Their scope is limited to this module. Functions have the following characteristics:

The format for declaring a Verilog function is as follows:

function [range-1:0] function_id;
input_declaration;
other_declaration;
procedural_statement;
endfunction

When declaring a function, an implicit register variable with a width of range and named function_id is declared. The function's return value is passed through this variable. If the width of this register variable is not specified, the default width is 1.

The function is called by specifying the function name and input variables. When the function ends, the return value is passed to the calling location.

The format for calling a function is as follows:

function_id(input1, input2, …);

Below is an example of using a function to implement a data endian conversion feature.

When the input is 4'b0011, the output can be 4'b1100. For example:

Example

module endian_rvs
    #(parameter N = 4)
        (
            input               en,     //enable control
            input [N-1:0]       a ,
            output [N-1:0]      b
    );

    reg [N-1:0]            b_temp;
    always @(*) begin
    if (en) begin
            b_temp =  data_rvs(a);
        end
        else begin
            b_temp = 0;
        end
    end
    assign b = b_temp;

    //function entity
    function [N-1:0]     data_rvs;
        input     [N-1:0] data_in;
        parameter         MASK = 32'h3; 
        integer           k;
        begin
            for(k=0; k<N; k=k+1) begin
                data_rvs[N-k-1]  = data_in[k];  
            end
        end
    endfunction

endmodule

Parameters within functions can also be rewritten, for example:

defparam data_rvs.MASK = 32'd7;

However, during simulation, it was found that this writing can compile, but the parameter MASK in the function did not actually rewrite successfully, still being 32'h3. This may be related to the compiler. Interested scholars can experiment with other Verilog compilers.

When declaring a function, a pair of parentheses can also be added after the function name to wrap the input declaration.

For example, the endian declaration function above can be expressed as:

function [N-1:0] data_rvs(
input [N-1:0] data_in
    ......
    );

Constant Function

A constant function refers to a function whose result is a constant calculated during compilation before the simulation starts. Constant functions are not allowed to access global variables or call system functions, but they can call another constant function.

This type of function can be used to reference complex values and thus can be used to replace constants.

For example, the following constant function can be used to calculate the width of the address bus in a module:

Example

parameter    MEM_DEPTH = 256;
reg  [logb2(MEM_DEPTH)-1: 0] addr; //addr width is 8 bits

    function integer logb2;
    input integer depth;
        //256 is 9 bits, our final data should be 8, so we need to stop the loop when depth=2
    for(logb2=0; depth>1; logb2=logb2+1) begin 
        depth = depth >> 1;
    end
endfunction

Automatic Function

In Verilog, the local variables of a general function are static, meaning that each call to the function uses the same storage space. If a function is called concurrently in two different places, the two function calls operating on the same address can lead to an uncertain function result.

Verilog uses the keyword automatic to describe such functions, which can automatically allocate new memory space when called, or can be understood as recursive. Therefore, the local variables declared in an automatic function cannot be accessed by hierarchical naming, but the automatic function itself can be called by hierarchical name.

Below is an example of using an automatic function to implement factorial calculation:

Example

wire [31:0]        results3 = factorial(4);
function automatic integer factorial;
    input integer data;
    integer i;
    begin
        factorial = (data>=2)? data * factorial(data-1) : 1;
    end
endfunction // factorial

Below are the simulation results with and without the automatic keyword.

As shown in the figure, the signal results3 obtained the result we wanted, which is the factorial of 4.

The signal results_noauto value is 1, which is not a predictable normal result, and no further analysis is needed here.

Digital Tube Decoding

The above-mentioned function knowledge does not seem to reflect the superiority of functions. Below is a design of a 4-bit decimal digital tube decoder to illustrate the advantage of functions in simplifying code.

The figure below is a physical diagram of a digital tube, which can display 4-bit decimal numbers. It is widely used in scoring and timekeeping competitions.

Each digital display has 8 light control terminals (shown as a-g in the figure), which can be used to control the display of numbers 0-9.

The digital tube has 4 chip selects (shown as 1-4), which are used to control which digital display should be enabled, i.e., should light up. If in a very short time, the 4 digital displays are sequentially selected for light emission, and different light controls are given under different chip selects (each corresponding to a 4-bit decimal number), then under the condition that the human eye cannot distinguish, the effect of simultaneously displaying 4-bit decimal numbers is achieved.

Below, we use the signal abcdefg to control the light control terminals and the signal csn to control the chip selects. The 4-bit decimal numbers of the ones, tens, hundreds, and thousands places are represented by 4 4-bit signals single_digit, ten_digit, hundred_digit, and kilo_digit, respectively. A digital tube display design can be described as follows:

Example

module digital_tube
     (
      input             clk,
      input             rstn,
      input             en,

      input [3:0]       single_digit,
      input [3:0]       ten_digit,
      input [3:0]       hundred_digit,
      input [3:0]       kilo_digit,

      output reg [3:0]  csn, //chip select, low-available
      output reg [6:0]  abcdefg        //light control
      );

    reg [1:0]           scan_r;  //scan_ctrl
    always @ (posedge clk or negedge rstn) begin
        if(!rstn)begin
            csn            <= 4'b1111;
            abcdefg        <= 'd0;
            scan_r         <= 3'd0;
        end
        else if (en) begin
            case(scan_r)
            2'd0:begin
                scan_r    <= 3'd1;
                csn       <= 4'b0111;     //select single digit
                abcdefg   <= dt_translate(single_digit);
            end
            2'd1:begin
                scan_r    <= 3'd2;
                csn       <= 4'b1011;     //select ten digit
                abcdefg   <= dt_translate(ten_digit);
            end
            2'd2:begin
                scan_r    <= 3'd3;
                csn       <= 4'b1101;     //select hundred digit
                abcdefg   <= dt_translate(hundred_digit);
            end
            2'd3:begin
                scan_r    <= 3'd0;
                csn       <= 4'b1110;     //select kilo digit
                abcdefg   <= dt_translate(kilo_digit);
            end
            endcase
        end
    end

    /*------------ translate function -------*/
    function [6:0] dt_translate;
        input [3:0]   data;
        begin
        case(data)
            4'd0: dt_translate = 7'b1111110;     //number 0 -> 0x7e

4'd1: dt_translate = 7'b0110000; // number 1 -> 0x30 4'd2: dt_translate = 7'b1101101; // number 2 -> 0x6d 4'd3: dt_translate = 7'b1111001; // number 3 -> 0x79 4'd4: dt_translate = 7'b0110011; // number 4 -> 0x33 4'd5: dt_translate = 7'b1011011; // number 5 -> 0x5b 4'd6: dt_translate = 7'b1011111; // number 6 -> 0x5f 4'd7: dt_translate = 7'b1110000; // number 7 -> 0x70 4'd8: dt_translate = 7'b1111111; // number 8 -> 0x7f 4'd9: dt_translate = 7'b1111011; // number 9 -> 0x7b endcase end endfunction

endmodule


Simulation results are as follows.

As shown in the figure, the chip select, decoding, and other signals all meet the design requirements. In practice, the 4-bit number should remain constant over a certain period, while the chip select signal continuously cycles through scanning, allowing the digital tube to present a static display effect to the human eye.

### Summary

If the decoder design does not use the function dt_translate, then when assigning values to the signals abcdefg in each case option, it is also necessary to judge the single_digit, ten_digit, hundred_digit, and kilo_digit. These judgment statements would be repeated four times. Although the actual hardware circuit synthesized in the end might be the same, the code using the function is clearly more concise and readable.

### Source Code Download

[Download](https://www.tutorialpro.org/wp-content/uploads/2020/09/6.1function.zip)

-[1.1 Verilog Tutorial](verilog-tutorial.html)

-[1.2 Verilog Introduction](verilog-intro.html)

-[1.3 Verilog Environment Setup](verilog-install.html)

-[1.4 Verilog Design Method](verilog-design-method.html)

-[2.1 Verilog Basic Syntax](verilog-basic-syntax.html)

-[2.2 Verilog Number Representation](verilog-number.html)

-[2.3 Verilog Data Types](verilog-data-type.html)

-[2.4 Verilog Expressions](verilog-expression.html)

-[2.5 Verilog Compile Instructions](verilog-compile-instruction.html)

-[3.1 Verilog Continuous Assignment](verilog-assign.html)

-[3.2 Verilog Time Delay](verilog-time-delay.html)

-[4.1 Verilog Process Structure](verilog-process-structure.html)

-[4.2 Verilog Process Assignment](verilog-process-assign.html)

-[4.3 Verilog Timing Control](verilog-timing-control.html)

-[4.4 Verilog Statement Blocks](verilog-statements-block.html)

-[4.5 Verilog Conditional Statements](verilog-condition-statement.html)

-[4.6 Verilog Multi-branch Statements](verilog-case.html)

-[4.7 Verilog Loop Statements](verilog-loop.html)

-[4.8 Verilog Procedural Continuous Assignment](verilog-deassign.html)

-[5.1 Verilog Modules and Ports](verilog-module-port.html)

-[5.2 Verilog Module Instantiation](verilog-generate.html)

-[5.3 Verilog Parameterized Instantiation](verilog-defparam.html)

- 6.1 Verilog Functions

-[6.2 Verilog Tasks](verilog-task.html)

-[6.3 Verilog State Machines](verilog-fsm.html)

-[6.4 Verilog Race and Hazard](verilog-competition-hazard.html)

-[6.5 Verilog Avoiding Latch](verilog-latch.html)

-[6.6 Verilog Simulation Stimulus](verilog-testbench.html)

-[6.7 Verilog Pipeline Design](verilog-pipeline-design.html)

-[7.1 Verilog Divider Design](verilog-dividend.html)

-[7.2 Verilog Parallel FIR Filter Design](verilog-fir.html)

-[7.3 Verilog Serial FIR Filter Design](verilog-serial-fir.html)

-[7.4 Verilog CIC Filter Design](verilog-cic.html)

-[7.5 Verilog FFT Design](verilog-fft.html)

-[7.6 Verilog DDS Design](verilog-dds.html)

-[8.1 Verilog Numerical Conversion](verilog-numerical-conversion.html)

-[Verilog Advanced Tutorial](verilog2-tutorial.html)

#### WeChat Subscription
❮ Npm Slow Use Cnpm Openresty Intro ❯