|
|
|
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.
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: wire, tri, triand/wand, trior/wor
- Registers: reg, integer
- Supply nets: supply0, supply1
- Constants: parameter
- Memories
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).
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 constantLegal Statements
The following are statements that are legal in behavioral Verilog.
Variable and signal assignment:
- for (variable = expression; condition; variable = variable + expression) statement
- while (condition) statement
- forever statement
- functions and tasks
Note All variables are declared as integer or reg. A variable cannot be declared as a wire.
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.
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^bBlocks
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 ..... endwhere 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.
input A, B, C; output D; inout E; wire D, E; ... assign E = oe ? A : 1'bz; assign D = B & E; ... endmoduleThe 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;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:
- Variable and signal assignment
- If... else statement
- Case statement
- For loop statement
- Function and task call
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 endmoduleCase 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 endmoduleThe 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.
always @(sel or a or b or c or d) //synthesis parallel_caseFor 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:
- Constant bounds
- Stop test condition using operators <, <=, > or >=
- Next step computation falling in one of the following specifications:
(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 endmoduleSequential 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 ; endmoduleThe 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; endmoduleThe 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; endmoduleAssign 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 endmoduleMain limitations on support of the assign / deassign statement in XST are as follows:
- For a given signal, there must be only one assign /deassign statement. For example, the following design will be rejected:
module dflop (RST, SET, STATE, CLOCK, DATA_IN); input RST; input SET; input CLOCK; input DATA_IN; output STATE; reg STATE; always @ ( RST ) // block b1 if( RST ) assign STATE = 1'b 0; else deassign STATE; always @ ( SET ) // block b1 if( SET ) assign STATE = 1'b 1; else deassign STATE; always @ ( posedge CLOCK ) // block b2 begin STATE = DATA_IN; end endmodule- The assign / deassign statement must be performed in the same always block through an if /else statement. For example, the following design will be rejected:
module dflop (RST, SET, STATE, CLOCK, DATA_IN); input RST; input SET; input CLOCK; input DATA_IN; output STATE; reg STATE; always @ ( RST or SET ) // block b1 case ({RST,SET}) 2'b00: assign STATE = 1'b 0; 2'b01: assign STATE = 1'b 0; 2'b10: assign STATE = 1'b 1; 2'b11: deassign STATE; endcase always @ ( posedge CLOCK ) // block b2 begin STATE = DATA_IN; end endmodule- You cannot assign a bit/part select of a signal through an assign / deassign statement. For example, the following design will be rejected:
module assig (RST, SELECT, STATE, CLOCK,DATA_IN); input RST; input SELECT; input CLOCK; input [0:7] DATA_IN; output [0:7] STATE; reg [0:7] STATE; always @ ( RST ) // block b1 if( RST ) begin assign STATE[0:7] = 8'b 0; end else begin deassign STATE[0:7]; end always @ ( posedge CLOCK ) // block b2 begin if (SELECT) STATE [0:3]= DATA_IN[0:3]; else STATE [4:7]= DATA_IN[4:7]; endTasks 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]; endmoduleExample 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 endmoduleBlocking 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);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'b1101Later in the design code a reference to the defined macro is made as follows.
if (request == `TESTEQ1)This is shown in the following example.
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 `endifInclude 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"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++.
|
|
|