|
|
|
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 in a procedural block (reg) or in a continuous assignment (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. In Verilog-2001, both reg and wire data types can be signed or unsigned.
Example:
reg [3:0] arb_priority;wire [31:0] arb_request;wire signed [8:0] arb_signed;where arb_request[31] is the MSB and arb_request[0] is the LSB.
Initial Values
In Verilog-2001, you can initialize registers when you declare them.
The value:
When you give a register an initial value in a declaration, XST sets this value on the output of the register at global reset, or at power up. A value assigned this way is carried in the NGC file as an INIT attribute on the register, and is independent of any local reset.
Example:
reg arb_onebit = 1'b0;reg [3:0] arb_priority = 4'b1011;You can also assign a set/reset (initial) value to a register via your behavioral Verilog code. Do this by assigning a value to a register when the register’s reset line goes to the appropriate value as in the following example.
Example:
always @(posedge clk)beginif (rst)arb_onebit <= 1'b0;endendWhen you set the initial value of a variable in the behavioral code, it is implemented in the design as a flip-flop whose output can be controlled by a local reset; as such it is carried in the NGC file as an FDP or FDC flip-flop.
Local Reset ¼ Global Reset
Note that local reset is independent of global reset. Registers controlled by a local reset may be set to a different value than ones whose value is only reset at global reset (power up). In the following example, the register, arb_onebit, is set to '0' at global reset, but a pulse on the local reset (rst) can change its value to '1'.
Example:
module mult(clk, rst, A_IN, B_OUT);input clk,rst,A_IN;output B_OUT;reg arb_onebit = 1'b0;always @(posedge clk or posedge rst)beginif (rst)arb_onebit <= 1'b1;elsearb_onebit <= A_IN;endendB_OUT <= arb_onebit;endmoduleThis sets the set/reset value on the register’s output at initial power up, but since this is dependent upon a local reset, the value changes whenever the local set/reset is activated.
Arrays
Verilog allows arrays of reg and wires to be defined as in the 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.
Multi-dimensional Arrays
XST supports multi-dimensional array types of up to two dimensions. Multi-dimensional arrays can be any net or any variable data type. You can code assignments and arithmetic operations with arrays, but you cannot select more than one element of an array at one time. You cannot pass multi-dimensional arrays to system tasks or functions, or regular tasks or functions.
Examples
The following describes an array of 256 x 16 wire elements each 8 bits wide, which can only be assigned via structural Verilog code.
wire [7:0] array2 [0:255][0:15];
The following describes an array of 256 x 8 register elements, each 64 bits wide, which can be assigned via behavioral Verilog code.
reg [63:0] regarray2 [255:0][7:0];
The following is a three dimensional array. It can be described as an array of 15 arrays of 256 x 16 wire elements, each 8 bits wide, which can be assigned via structural Verilog code.
wire [7:0] array3 [0:15][0:255][0:15];
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 netreg r1; // single bit registertri [7:0] bus1; // 8 bit tristate busreg [15:0] bus1; // 15 bit registerreg [7:0] mem[0:127]; // 8x128 memory registerparameter state1 = 3’b001; // 3 bit constantparameter component = "TMS380C16"; // stringLegal Statements
The following are statements that are legal in behavioral Verilog.
Variable and signal assignment:
- Variable = expression
- if (condition) statement
- if (condition) statement else statement
- case (expression)
expression: statement
…
default: statement
endcase
- 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 are treated as (==) or (!=) in synthesis.
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 andend 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:
alwaysbeginstatement…...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.
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;...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 is 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) is 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, 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 parentheses 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 can contain any signal that appears in conditions (If, Case, for example), and any signal appearing on the right hand side of an assignment. By substituting a * without parentheses, for a list of signals, the always block is activated for an event in any of the always block’s signals as described above.
Note: In combinatorial processes, if a signal is not explicitly assigned in all branches of "If" or "Case" statements, XST generates a latch to hold the last value. To avoid latch creation, be sure 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)beginif (sel[1])if (sel[0])outmux = d;elseoutmux = c;elseif (sel[0])outmux = b;elseoutmux = a;endendmoduleCase 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 not use unsized integers in case statements. Always size integers to a specific number of bits, or results can 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 either the casez or casex 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)begincase (sel)2'b00: outmux = a;2'b01: outmux = b;2'b10: outmux = c;default: outmux = d;endcaseendendmoduleThe preceding Case statement evaluates 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 ensures parallel evaluation of the sel inputs as in the following.
Example:
case(sel) //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:
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)beginCount_Aux = 3'b0;for (i = 0; i < 8; i = i+1)beginif (!a[i])Count_Aux = Count_Aux+1;endCount = Count_Aux;endendmoduleWhile Loops
When using always blocks, use the "while" statement to execute repetitive procedures. A "while" loop executes other statements until its test expression becomes false. It is not executed if the test expression is initially false.
The following example shows the use of a While Loop.
Example 7-5 While Loop Description
parameter P = 4;always @(ID_complete)begin : UNIDENTIFIEDinteger i;reg found;unidentified = 0;i = 0;found = 0;while (!found && (i < P))beginfound = !ID_complete[i];unidentified[i] = !ID_complete[i];i = i + 1;endendSequential 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-6 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-7 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;elseDO <= DI;endmoduleThe following example describes an 8-bit counter.
Example 7-8 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;elseDO <= 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)beginassign STATE = 4'b0;endelsebegindeassign STATE;endalways @ (posedge CLOCK)beginSTATE <= DATA_IN;endendmoduleThe main limitations on support of the assign/deassign statement in XST are as follows:
module dflop (RST, SET, STATE, CLOCK, DATA_IN);input RST;input SET;input CLOCK;input DATA_IN;output STATE;reg STATE;always @ (RST) // block b1if(RST)assign STATE = 1'b0;elsedeassign STATE;always @ (SET) // block b1if(SET)assign STATE = 1'b1;elsedeassign STATE;always @ (posedge CLOCK) // block b2beginSTATE <= DATA_IN;endendmodulemodule 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 b1case ({RST,SET})2'b00: assign STATE = 1'b0;2'b01: assign STATE = 1'b0;2'b10: assign STATE = 1'b1;2'b11: deassign STATE;endcasealways @ (posedge CLOCK) // block b2beginSTATE <= DATA_IN;endendmodulemodule 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 b1if(RST)beginassign STATE[0:7] = 8'b0;endelsebegindeassign STATE[0:7];endalways @ (posedge CLOCK) // block b2beginif (SELECT)STATE [0:3] <= DATA_IN[0:3];elseSTATE [4:7] <= DATA_IN[4:7];endAssignment Extension Past 32 Bits
If the expression on the left-hand side of an assignment is wider than the expression on the right-hand side, the left-hand side is padded to the left according to the following rules.
- If the right-hand expression is signed, the left-hand expression is padded with the sign bit (0 for positive, 1 for negative, z for high impedance or x for unknown).
- If the right-hand expression is unsigned, the left-hand expression is padded with '0's.
- For unsized x or z constants only the following rule applies. If the value of the right-hand expression’s left-most bit is z (high impedance) or x (unknown), regardless of whether the right-hand expression is signed or unsigned, the left-hand expression is padded with that value (z or x, respectively).
Note: The above rules follow the Verilog-2001 standard, and are not backward compatible with Verilog-1995.
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 return value of a function can be declared either signed or unsigned. The content is similar to the combinatorial always block content.
Example 7-9 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-10.
Example 7-9 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 signed [1:0] ADD;input A, B, CIN;reg S, COUT;beginS = A ^ B ^ CIN;COUT = (A&B) | (A&CIN) | (B&CIN);ADD = {COUT, S};endendfunctionassign 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-10 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;beginS = A ^ B ^ CIN;COUT = (A&B) | (A&CIN) | (B&CIN);C = {COUT, S};endendtaskalways @(A or B or CIN)beginADD (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];endendmoduleRecursive Tasks and Functions
Verilog-2001 adds support for recursive tasks and functions. You can only use recursion with the automatic keyword.
The syntax using recursion is shown in the following example:
function automatic [31:0] fac;input [15:0] n;if (n == 1)fac = 1;elsefac = n * fac(n-1); //recursive function callendfunctionBlocking 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 and4 '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.
`define myzero 0assign 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 is 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 MYVARmodule if_MYVAR_is_declared;...endmodule`elsemodule 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-to-be-included"Note: The path can be relative or absolute.
Multiple `include statements are allowed in a single Verilog file. This feature makes your code modular and more manageable in a team design environment where different files describe different modules of the design.
To have the file in your `include statement recognized, you must identify the directory where it resides either to ISE™ or to XST.
- By default, ISE searches the ISE project directory, so adding the file to your project directory will identify the file to ISE.
- You can direct ISE to a different directory by including a path (relative or absolute) in the `include statement in your source code.
- You can point XST directly to your include file directory by using the Verilog Include Directories option. See "Verilog Include Directories (Verilog Only)" in Chapter 5.
- If the include file is required for ISE to construct the design hierarchy, this file must either reside in the project directory, or be referenced by a relative or absolute path. The file need not be added to the project.
Be aware that conflicts can occur. For example, at the top of a Verilog file you might see the following:
`timescale 1 ns/1 ps`include "modules.v"...If the specified file (in this case, modules.v) has been added to an ISE project directory and is specified with an `include, conflicts may 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++.
Generate Statement
Generate is a construct that allows you to dynamically create Verilog code from conditional statements. This allows you to create repetitive structures or structures that are only appropriate under certain conditions. Structures that are likely to be created via a generate statement are:
XST supports the following types of generate statements:
Generate For
Use a generate for loop to create one or more instances that can be placed inside a module. Use the generate for loop the same way you would a normal Verilog for loop with the following limitations.
The following is an example of an 8-bit adder using a generate for loop.
generategenvar i;for (i=0; i<=7; i=i+1)begin : for_nameadder add (a[8*i+7 : 8*i], b[8*i+7 : 8*i],
ci[i], sum_for[8*i+7 : 8*i], c0_or[i+1]);
endendgenerateGenerate If... else
A generate if statement can be used inside a generate block to conditionally control what objects get generated.
The following is an example of a generate If... else statement. The generate controls what type of multiplier is instantiated. Please note that the contents of each branch of the if... else statement must be enclosed by
begin andend statements, and thebegin statement must be named with a unique qualifier.generateif (IF_WIDTH < 10)begin : if_nameadder # (IF_WIDTH) u1 (a, b, sum_if);endelsebegin : else_namesubtractor # (IF_WIDTH) u2 (a, b, sum_if);endendgenerateGenerate Case
A generate case statement can be used inside a generate block to conditionally control what objects get generated. Use a generate case statement when there are several conditions to be tested to determine what the generated code would be. Please note that each test statement in a generate case statement must be enclosed by
begin andend statements, and thebegin statement must be named with a unique qualifier.The following is an example of a generate case statement. The generate controls what type of adder is instantiated.
generatecase (WIDTH)1:begin : case1_nameadder #(WIDTH*8) x1 (a, b, ci, sum_case, c0_case);end2:begin : case2_nameadder #(WIDTH*4) x2 (a, b, ci, sum_case, c0_case);enddefault:begin : d_case_nameadder x3 (a, b, ci, sum_case, c0_case);endendcaseendgenerate
|
|
|