Return to previous page Advance to next page
Verilog Reference Guide
Chapter 5: Functional Descriptions

Function Declarations

Using a function declaration is one of three methods for describing combinatorial logic. The other two methods are the always block, described in the “always Blocks” section of this chapter and the continuous assignment, described in the “Continuous Assignment” section of the “Structural Descriptions” chapter. You must declare and use Verilog functions within a module. You can call functions from the structural part of a Verilog description by using them in a continuous assignment statement or as a terminal in a module instantiation. You can also call functions from other functions or from always blocks.

Foundation Express supports the following Verilog function declarations.

Functions begin with the keyword function and end with the keyword endfunction. The width of the function's return value (if any) and the name of the function follow the function keyword, as shown in the syntax below.

function [range] name_of_function;
   [func_declaration]*
   statement_or_null
endfunction

Defining the bit range of the return value is optional. Specify the range inside square brackets ( [ ] ). If you do not define the range, a function returns a 1-bit quantity by default. You set the function's output by assigning it to the function name. A function can contain one or more statements. If you use multiple statements, enclose the statements between a begin...end pair.

The following example shows a simple function declaration.

function [7:0] scramble; 
input [7:0] a;
input [2:0] control;
integer i;
   begin
      for (i = 0;i <= 7;i = i + 1)
         scramble[i] = a[i ^ control];
   end
endfunction

Function statements that Foundation Express supports are discussed under the “Function Statements” section of this chapter.

Input Declarations

Verilog input declarations specify the input signals for a function. You must declare the inputs to a Verilog function immediately after you declare the function name. The syntax of input declarations for a function is the same as the syntax of input declarations for a module.

input [range] list_of_variables;

The optional range specification declares an input as a vector of signals. Specify range inside square brackets ( [ ] ).

Note: The order in which you declare the inputs must match the order of the inputs in the function call.

Function Output

The output from a function is assigned to the function name. A Verilog function has only one output, which can be a vector. For multiple outputs from a function, use the concatenation operation to bundle several values into one return value. This single return value can then be unbundled by the caller. The following example shows how unbundling is done.

function [9:0] signed_add
input [7:0] a, b;
   reg [7:0] sum;
   reg carry, overflow;

   begin 
      ...
      signed_add = carry,overflow,sum};
   end
endfunction
...
assign {C, V, result_bus} = signed_add (busA, busB);

The signed_add function bundles the values of carry, overflow, and sum into one value. This new value is returned in the assign statement following the function. The original values are then unbundled by the function that called the signed_add function.

Register Declarations

A register represents a variable in Verilog. The syntax for a register declaration follows.

reg [range] list_of_register_variables;

A reg can be a single-bit quantity or a vector of bits. The range parameter specifies the most significant bit (msb) and least significant bit (lsb) of the vector enclosed in square brackets ( [ ] ). Both bits must be nonnegative constants, parameters, or constant-valued expressions.

The following example shows some reg declarations.

reg x;               //single bit 
reg a,b,c;    //3 single-bit quantities
reg [7:0] q;  //an 8-bit vector

In Verilog language, you assign a value to a reg variable only within a function or an always block.

In the Verilog simulator, reg variables can hold state information. A reg variable can hold its value across separate calls to a function. In some cases, Foundation Express emulates this behavior by inserting flow-through latches. In other cases, it emulates this behavior without a latch. The concept of holding state is elaborated in the “Inferring Latches” section of the “Register and Three-State Inference” chapter.

Memory Declarations

The memory declaration models a bank of registers or memory. In Verilog, the memory declaration is actually a two-dimensional array of reg variables.

The following example shows memory declarations.

reg [7:0] byte_reg;
reg [7:0] mem_block [255:0];

In the above example, byte_reg is an 8-bit register and mem_block is an array of 256 registers; each is 8 bits wide. You can index the array of registers to access individual registers, but you cannot access individual bits of a register directly. Instead, you must copy the appropriate register into a temporary one-dimensional register. For example, to access the fourth bit of the eighth register in mem_block, enter the following syntax.

byte_reg mem_block [7];
individual_bit = byte_reg [3];

Parameter Declarations

Parameter variables are local or global variables that hold values. The syntax for a parameter declaration follows.

parameter [range] identifier = expression, identifier = expression;

The range specification is optional.

You can declare parameter variables as local to a function. However, you cannot use a local variable outside of that function. Parameter declarations in a function are identical to parameter declarations in a module. The function in the following example contains a parameter declaration.

function gte;
   parameter width = 8;
   input [width - 1 : 0] a, b;
   gte = ( a >= b);
endfunction

Integer Declarations

Integer variables are local or global variables that hold numeric values. The syntax for an integer declaration follows.

integer identifier_list;

You can declare integer variables locally at the function level or globally at the module level. The default size for integer variables is 32 bits. Foundation Express determines bit-widths, except in the case of a don't care condition resulting during a compile.

The following example illustrates integer declarations.

integer a;       //single 32-bit integer 
integer b,c;     //two integers