Return to previous page Advance to next page

Behavioral Verilog Features


This section contains descriptions of the behavioral features of Verilog.

Variable Declaration

Variables in Verilog may be declared as integers or real. These declarations are intended only for use in test code. Verilog provides data types such as reg and wire for actual hardware description.

The difference between reg and wire is whether the variable is given its value by behavioral (reg) or structural (wire) Verilog code. Both reg and wire have a default width being one bit wide (scalar). To specify an N-bit width (vectors) for a declared reg or wire, the left and right bit positions are defined in square brackets separated by a colon.

Example:

reg [3:0] arb_priority; 
wire [31:0] arb_request; 

where arb_request[31] is the MSB and arb_request[0] is the LSB.

Verilog allows arrays of reg and wires to be defined as following two examples:

reg [3:0] mem_array [31:0];  

The above describes an array of 32 Elements each, 4 bits wide which can be assigned via behavioral verilog code.

wire [7:0] mem_array [63:0];  

The above describes an array of 64 elements each 8 bits wide which can only be assigned via structural Verilog code.

Data Types

The Verilog representation of the bit data type contains the following four values:

XST includes support for the following Verilog data types:

Net and registers can be either single bit (scalar) or multiple bit (vectors).

The following example gives some examples of Verilog data types (as found in the declaration section of a Verilog module).

Example 7-1 Basic Data Types

wire net1;                  // single bit net 
reg r1;                  // single bit register 
tri [7:0] bus1;                  // 8 bit tristate bus 
reg [15:0] bus1;                  // 15 bit register 
reg [7:0] mem[0:127];                        // 8x128 memory register 
parameter state1 = 3'b001;                            // 3 bit constant 

Legal Statements

The following are statements that are legal in behavioral Verilog.

Variable and signal assignment:

constant: statement

...

default: statement

endcase

Expressions

An expression involves constants and variables with arithmetic (+, -, *, /, %), logical (&, &&, |, ||, ^, ~, <<, >>), relational (<, ==, ===, <=, >=, !=, !==, >), and conditional (?) operators. The logical operators are further divided as bit-wise versus logical depending on whether it is applied to an expression involving several bits or a single bit. The following table lists the expressions supported by XST.
Table 7-1 Expressions
Concatenation
{}
Supported
Replication
{{}}
Supported

Arithmetic

+, -, *
Supported
/
Supported only if second operand is a power of 2
Modulus
%
Supported only if second operand is a power of 2
Addition
+
Supported
Subtraction
-
Supported
Multiplication
*
Supported
Division
/
Supported
XST generates incorrect logic for the division operator between signed and unsigned constants. Example: -1235/3'b111
Remainder
%
Supported
Relational
>, <, >=, <=
Supported
Logical Negation
!
Supported
Logical AND
&&
Supported
Logical OR
||
Supported
Logical Equality
==
Supported
Logical Inequality
!=
Supported
Case Equality
===
Unsupported
Case Inequality
!==
Unsupported
Bitwise Negation
~
Supported
Bitwise AND
&
Supported
Bitwise Inclusive OR
|
Supported
Bitwise Exclusive OR
^
Supported
Bitwise Equivalence
~^, ^~
Supported
Reduction AND
&
Supported
Reduction NAND
~&
Supported
Reduction OR
|
Supported
Reduction NOR
~|
Supported
Reduction XOR
^
Supported
Reduction XNOR
~^, ^~
Supported
Left Shift
<<
Supported
Right Shift
>>
Supported
Conditional
?:
Supported
Event OR
or
Supported

The following table lists the results of evaluating expressions using the more frequently used operators supported by XST.

Note The (===) and (!==) are special comparison operators useful in simulations to check if a variable is assigned a value of (x) or (z). They have no meaning in hardware.
Table 7-2 Results of Evaluating Expressions
a b
a==b
a===b
a!=b
a!==b
a&b
a&&b
a|b
a||b
a^b
0 0
1
1
0
0
0
0
0
0
0
0 1
0
0
1
1
0
0
1
1
1
0 x
x
0
x
1
0
0
x
x
x
0 z
x
0
x
1
0
0
x
x
x
1 0
0
0
1
1
0
0
1
1
1
1 1
1
1
0
0
1
1
1
1
0
1 x
x
0
x
1
x
x
1
1
x
1 z
x
0
x
1
x
x
1
1
x
x 0
x
0
x
1
0
0
x
x
x
x 1
x
0
x
1
x
x
1
1
x
x x
x
1
x
0
x
x
x
x
x
x z
x
0
x
1
x
x
x
x
x
z 0
x
0
x
1
0
0
x
x
x
z 1
x
0
x
1
x
x
1
1
x
z x
x
0
x
1
x
x
x
x
x
z z
x
1
x
0
x
x
x
x
x

Blocks

Block statements are used to group statements together. XST only supports sequential blocks. Within these blocks, the statements are executed in the order listed. Parallel blocks are not supported by XST. Block statements are designated by begin and end keywords, and are discussed within examples later in this chapter.

Modules

In Verilog a design component is represented by a module. The connections between components are specified within module instantiation statements. Such a statement specifies an instance of a module. Each module instantiation statement must be given a name (instance name). In addition to the name, a module instantiation statement contains an association list that specifies which actual nets or ports are associated with which local ports (formals) of the module declaration.

All procedural statements occur in blocks that are defined inside modules. There are two kinds of procedural blocks: the initial block and the always block. Within each block, Verilog uses a begin and end to enclose the statements. Since initial blocks are ignored during synthesis, only always blocks are discussed. Always blocks usually take the following format:

always 
  begin 
  statement 
  ..... 
end 

where each statement is a procedural assignment line terminated by a semicolon.

Module Declaration

In the module declaration, the I/O ports of the circuit are declared. Each port has a name and a mode (in, out, and inout) as shown in the example below.

module EXAMPLE (A, B, C, D, E);
 input A, B, C; 
 output D; 
 inout E; 
 wire D, E; 
  ... 
  assign E = oe ? A : 1'bz; 
  assign D = B & E; 
  ... 
endmodule 

The input and output ports defined in the module declaration called EXAMPLE are the basic input and output I/O signals for the design. The inout port in Verilog is analogous to a bi-directional I/O pin on the device with the data flow for output versus input being controlled by the enable signal to the tristate buffer. The preceding example describes E as a tristate buffer with a high-true output enable signal. If oe = 1, the value of signal A will be output on the pin represented by E. If oe = 0, then the buffer is in high impedance (Z) and any input value driven on the pin E (from the external logic) will be brought into the device and fed to the signal represented by D.

Verilog Assignments

There are two forms of assignment statements in the Verilog language:

Continuous Assignments

Continuous assignments are used to model combinatorial logic in a concise way. Both explicit and implicit continuous assignments are supported. Explicit continuous assignments are introduced by the assign keyword after the net has been separately declared. Implicit continuous assignments combine declaration and assignment.

Note Delays and strengths given to a continuous assignment are ignored by XST.

Example of an explicit continuous assignment:

wire par_eq_1; 
..... 
assign par_eq_1 = select ? b : a; 

Example of an implicit continuous assignment:

wire temp_hold = a | b; 
Note Continuous assignments are only allowed on wire and tri data types.

Procedural Assignments

Procedural assignments are used to assign values to variables declared as regs and are introduced by always blocks, tasks, and functions. Procedural assignments are usually used to model registers and FSMs.

XST includes support for combinatorial functions, combinatorial and sequential tasks, and combinatorial and sequential always blocks.

Combinatorial always blocks

Combinatorial logic can be modeled efficiently using two forms of time control, namely the # and @ Verilog time control statements. The # time control is ignored for synthesis and hence this section describes modeling combinatorial logic with the @ statement.

A combinatorial always block has a sensitivity list appearing within parenthesis after the word "always @". An always block is activated if an event (value change or edge) appears on one of the sensitivity list signals. This sensitivity list contains all signals that appear in conditions (If, Case, for example), and any signal appearing on the right hand side of an assignment.

Note In combinatorial processes, if a signal is not explicitly assigned in all branches of "If" or "Case" statements, XST will generate a latch to hold the last value. To avoid latch creation, assure that all assigned signals in a combinatorial process are always explicitly assigned in all paths of the process statements.

Different statements can be used in a process:

The following sections provide examples of each of these statements.

if...else statement

If... else statements use true/false conditions to execute statements. If the expression evaluates to true, the first statement is executed. If the expression evaluates to false (or x or z), the else statement is executed. A block of multiple statements may be executed using begin and end keywords. If...else statements may be nested. The following example shows how a MUX can be described using an If...else statement.

Example 7-2 MUX Description Using If... Else Statement

module mux4 (sel, a, b, c, d, outmux); 
input [1:0] sel; 
input [1:0] a, b, c, d; 
output [1:0] outmux; 
reg [1:0] outmux; 
 
always @(sel or a or b or c or d) 
  begin 
    if (sel[1]) 
      if (sel[0]) 
        outmux = d; 
      else 
        outmux = c; 
    else 
      if (sel[0]) 
        outmux = b; 
      else 
        outmux = a; 
    end 
endmodule 

Case statement

Case statements perform a comparison to an expression to evaluate one of a number of parallel branches. The Case statement evaluates the branches in the order they are written. The first branch that evaluates to true is executed. If none of the branches match, the default branch is executed.

Note Do no use unbounded integers in case statements. Always bound integers to a specific number of bits, or results will be unpredictable.

Casez treats all z values in any bit position of the branch alternative as a don't care.

Casex treats all x and z values in any bit position of the branch alternative as a don't care.

The question mark (?) can be used as a "don't care" in any of the preceding case statements. The following example shows how a MUX can be described using a Case statement.

Example 7-3 MUX Description Using Case Statement

module mux4 (sel, a, b, c, d, outmux); 
input [1:0] sel; 
input [1:0] a, b, c, d; 
output [1:0] outmux; 
reg [1:0] outmux; 
 
always @(sel or a or b or c or d) 
  begin 
    case (sel) 
      2'b00:  outmux = a; 
      2'b01:  outmux = b; 
      2'b10:  outmux = c; 
      default: outmux = d; 
    endcase 
  end 
endmodule 

The preceding Case statement will evaluate the values of the input sel in priority order. To avoid priority processing, it is recommended that you use a parallel-case Verilog meta comment which will ensure parallel evaluation of the sel inputs as in the following.

Example:

always @(sel or a or b or c or d) //synthesis parallel_case 

For and Repeat loops

When using always blocks, repetitive or bit slice structures can also be described using the "for" statement or the "repeat" statement.

The "for" statement is supported for:

(where var is the loop variable and step is a constant value).

The repeat statement is only supported for constant values. The following example shows the use of a For Loop.

Example 7-4 For Loop Description

module countzeros (a, Count); 
input [7:0] a; 
output [2:0] Count; 
reg [2:0] Count; 
reg [2:0] Count_Aux; 
integer i; 
 
always @(a) 
  begin 
    Count_Aux = 3'b0; 
  for (i = 0; i < 8; i = i+1) 
    begin 
      if (!a[i]) 
        Count_Aux = Count_Aux+1; 
    end 
  Count = Count_Aux; 
  end 
 
endmodule 

Sequential Always Blocks

Sequential circuit description is based on always blocks with a sensitivity list.

The sensitivity list contains a maximum of three edge-triggered events: the clock signal event (which is mandatory), possibly a reset signal event, and a set signal event. One, and only one "If...else" statement is accepted in such an always block.

An asynchronous part may appear before the synchronous part in the first and the second branch of the "If...else" statement. Signals assigned in the asynchronous part must be assigned to the constant values '0', '1', 'X' or 'Z' or any vector composed of these values.

These same signals must also be assigned in the synchronous part (that is, the last branch of the "if-else" statement). The clock signal condition is the condition of the last branch of the "if-else" statement. The following example gives the description of an 8-bit register.

Example 7-5 8 Bit Register Using an Always Block

module seq1 ( DI, CLK, DO); 
  input [7:0] DI; 
  input CLK; 
  output [7:0] DO; 
  reg [7:0] DO; 
 
  always @( posedge CLK ) 
    DO = DI ; 
 
endmodule 

The following example gives the description of an 8-bit register with a clock signal and an asynchronous reset signal.

Example 7-6 8 Bit Register with Asynchronous Reset (high-true) Using an Always Block

module EXAMPLE ( DI, CLK, RST, DO); 
  input [7:0] DI; 
  input CLK, RST; 
  output [7:0] DO; 
  reg [7:0] DO; 
 
  always @( posedge CLK or posedge RST) 
    if (RST == 1'b1) 
      DO = 8'b00000000; 
    else 
      DO = DI; 
endmodule 
 

The following example describes an 8-bit counter.

Example 7-7 8 Bit Counter with Asynchronous Reset (low-true) Using an Always Block

module seq2 ( CLK, RST, DO); 
  input CLK, RST; 
  output [7:0] DO; 
  reg [7:0] DO; 
 
  always @( posedge CLK or posedge RST ) 
    if (RST == 1'b1) 
      DO = 8'b00000000; 
    else 
      DO = DO + 8'b00000001; 
endmodule 

Assign and Deassign Statements

Assign and deassign statements are supported within simple templates.

The following is an example of the general template for assign / deassign statements:

module assig (RST, SELECT, STATE, CLOCK, DATA_IN); 
   input RST; 
  input SELECT; 
   input CLOCK; 
   input [0:3] DATA_IN; 
   output [0:3] STATE; 
 
  reg [0:3] STATE; 
 
always @ ( RST ) 
   if( RST ) 
   begin 
    assign STATE = 4'b 0; 
   end else 
   begin 
     deassign STATE; 
   end 
  
always @ ( posedge CLOCK ) 
   begin 
     STATE = DATA_IN; 
  end 
 
endmodule 

Main limitations on support of the assign / deassign statement in XST are as follows:

Tasks and Functions

The declaration of a function or task is intended for handling blocks used multiple times in a design. They must be declared and used in a module. The heading part contains the parameters: input parameters (only) for functions and input/output/inout parameters for tasks. The content is similar to the combinatorial always block content. Recursive function and task calls are not supported.

Example 7-8 shows a function declared within a module. The ADD function declared is a single-bit adder. This function is called 4 times with the proper parameters in the architecture to create a 4-bit adder. The same example, described with a task, is shown in Example 7-9.

Example 7-8 Function Declaration and Function Call

module comb15 ( A, B, CIN, S, COUT); 
  input [3:0] A, B; 
  input CIN; 
  output [3:0] S; 
  output COUT; 
  wire [1:0] S0, S1, S2, S3; 
  function [1:0] ADD; 
    input A, B, CIN; 
    reg S, COUT; 
    begin 
      S = A ^ B ^ CIN; 
      COUT = (A&B) | (A&CIN) | (B&CIN); 
      ADD = {COUT, S}; 
    end 
  endfunction 
 
  assign S0 = ADD ( A[0], B[0], CIN), 
    S1 = ADD ( A[1], B[1], S0[1]), 
    S2 = ADD ( A[2], B[2], S1[1]), 
    S3 = ADD ( A[3], B[3], S2[1]), 
    S = {S3[0], S2[0], S1[0], S0[0]}, 
 
    COUT = S3[1]; 
endmodule 

Example 7-9 Task Declaration and Task Enable

module EXAMPLE ( A, B, CIN, S, COUT); 
  input [3:0] A, B; 
  input CIN; 
  output [3:0] S; 
  output COUT; 
  reg [3:0] S; 
  reg COUT; 
  reg [1:0] S0, S1, S2, S3; 
 
  task ADD; 
    input A, B, CIN; 
    output [1:0] C; 
    reg [1:0] C; 
    reg S, COUT; 
 
    begin 
      S = A ^ B ^ CIN; 
      COUT = (A&B) | (A&CIN) | (B&CIN); 
      C = {COUT, S}; 
    end 
  endtask 
 
  always @( A or B or CIN) 
  begin 
    ADD ( A[0], B[0], CIN, S0); 
    ADD ( A[1], B[1], S0[1], S1); 
    ADD ( A[2], B[2], S1[1], S2); 
    ADD ( A[3], B[3], S2[1], S3); 
    S = {S3[0], S2[0], S1[0], S0[0]}; 
    COUT = S3[1]; 
  end 
 
endmodule 

Blocking Versus Non-Blocking Procedural Assignments

The # and @ time control statements delay execution of the statement following them until the specified event is evaluated as true. Use of blocking and non-blocking procedural assignments have time control built into their respective assignment statement.

The # delay is ignored for synthesis.

The syntax for a blocking procedural assignment is shown in the following example:

reg a; 
a = #10 (b | c); 

or

if (in1) out = 1'b0; 
else out = in2; 

As the name implies, these types of assignments block the current process from continuing to execute additional statements at the same time. These should mainly be used in simulation.

Non-blocking assignments, on the other hand, evaluate the expression when the statement executes, but allow other statements in the same process to execute as well at the same time. The variable change only occurs after the specified delay.

The syntax for a non-blocking procedural assignment is as follows:

variable <= @(posedge or negedge bit) expression; 

The following shows an example of how to use a non-blocking procedural assignment.

if (in1) out <= 1'b1; 
else out <= in2; 

Constants, Macros, Include Files and Comments

This section discusses constants, macros, include files, and comments.

Constants

By default, constants in Verilog are assumed to be decimal integers. They can be specified explicitly in binary, octal, decimal, or hexadecimal by prefacing them with the appropriate syntax. For example, 4'b1010, 4'o12, 4'd10 and 4'ha all represent the same value.

Macros

Verilog provides a way to define macros as shown in the following example.

`define TESTEQ1 4'b1101 

Later in the design code a reference to the defined macro is made as follows.

 if (request == `TESTEQ1) 

This is shown in the following example.

`define myzero 0
assign mysig = `myzero; 

Verilog provides the `ifdef and `endif constructs to determine whether a macro is defined or not. These constructs are used to define conditional compilation. If the macro called out by the `ifdef command has been defined, that code will be compiled. If not, the code following the `else command is compiled. The `else is not required, but the `endif must complete the conditional statement. The `ifdef and `endif constructs are shown in the following example.

  `ifdef MYVAR 
  module if_MYVAR_is_declared; 
  ... 
  endmodule 
  `else 
  module if_MYVAR_is_not_declared; 
  ... 
  endmodule 
   `endif 

Include Files

Verilog allows separating source code into more than one file. To use the code contained in another file, the current file has the following syntax:

 `include "path/file-name-to-be-included" 

Note The path can be relative or absolute.

Multiple `include statements are allowed in a single Verilog file. This is a great feature to make code modular and manageable in a team design environment where different files describe different modules of the design.

If files are referenced by an `include statement, they must not be manually added to the project. For example, at the top of a Verilog file you might see this:

`timescale 1ns/1ps 
`include "modules.v" 
... 

If the specified file (in this case, modules.v) has been added to an ISE project and is specified with an `include, conflicts will occur and an error message displays:

ERROR:Xst:1068 - fifo.v, line 2. Duplicate declarations of module'RAMB4_S8_S8' 

Comments

There are two forms of comments in Verilog similar to the two forms found in a language like C++.


Return to previous page Advance to next page