Verilog Description Styles
Introduction to Description Styles
There are two different styles of description:
-
- Data Flow
- Continuous assignment, using assignment statements.
-
- Behavioral
- Procedural assignment, using procedural statements similar to a program in a high-level language.
- Blocking
- Non-blocking
Data Flow Style: Continuous Assignment (assign
)
Characteristics of assign
- Identified by the keyword
assign
. - Forms a static binding between:
- The
net
being assigned on the left-hand side (LHS). - The expression on the right-hand side (RHS), which may consist of both
net
andregister
type variables.
- The
- The assignment is continuously active.
- Almost exclusively used to model combinational circuits.
- Some points to note:
- A Verilog module can contain any number of
assign
statements. - Typically, the
assign
statements are followed by procedural descriptions. - The
assign
statements are used to model behavioral descriptions.
- A Verilog module can contain any number of
Modeling Combinational Logic with assign
Generating a MUX with a Variable Index
- Point to note:
- Whenever there is an array reference on the RHS with a variable index, a MUX is generated by the synthesis tool.
- If the index is a constant, just a wire will be generated.
- Example:
assign out = data[2];
- Example:
- The use of a non-constant index in an expression on the RHS generates a MUX.
- Example:
module generate_MUX (data, select, out); input [15:0] data; input [3:0] select; output out; assign out = data[select]; endmodule
Generating a MUX with the Conditional Operator
- Point to note:
- Whenever a conditional is encountered in the RHS of an expression, a 2-to-1 MUX is generated.
- In the example below, since the variables
a
,b
, andf
are vectors, an array of 2-to-1 MUX-es are generated.
- Example:
module generate_set_of_MUX (a, b, f, sel); input [0:3] a, b; input sel; output [0:3] f; assign f = sel ? a : b; endmodule
- Question: What hardware will be generated by the following?
assign f = (a==0) ? (c+d) : (c-d);
- Answer: The expression generates an adder and a subtractor whose outputs feed into a 2x1 MUX. The MUX’s select line is controlled by the output of a comparator that checks if
a==0
.
Generating a Decoder with a Variable Index
- A non-constant index in an expression on the LHS generates a decoder.
- Point to note:
- A constant index in the expression on the LHS will not generate a decoder.
- Example:
assign out[5] = in;
- This will simply generate a wire connection.
- Example:
- As a rule of thumb, whenever the synthesis tool detects a variable index in the LHS, a decoder is generated.
- A constant index in the expression on the LHS will not generate a decoder.
- Example:
module generate_decoder (out, in, select); input in; input [0:1] select; output [0:3] out; assign out[select] = in; endmodule
Modeling Sequential Logic with assign
D-Type Latch
- This is an example to describe a sequential logic element using an
assign
statement. - Example:
module level_sensitive_latch (D, Q, En); input D, En; output Q; assign Q = En ? D : Q; endmodule
- Truth Table:
- En | D | Qn
- 0 | x | Qn-1
- 1 | 0 | 0
- 1 | 1 | 1
S-R Latch
- Modeling a simple S-R latch. The circuit consists of two cross-coupled NAND gates.
- Example:
module sr_latch (Q, Qbar, S, R); input S, R; output Q, Qbar; assign Q = ~(R & Qbar); assign Qbar = ~(S & Q); endmodule
- Testbench and Simulation:
- A testbench can be created to apply stimulus to the S and R inputs.
- Simulation Output:
0 S=0, R=1, Q=0, Qbar=1 5 S=1, R=1, Q=0, Qbar=1 10 S=1, R=0, Q=1, Qbar=0 15 S=1, R=1, Q=1, Qbar=0 20 S=0, R=0, Q=1, Qbar=1 and then the simulator hangs
Behavioral Style: Procedural Assignment
Procedural Blocks (initial
and always
)
- Two kinds of procedural blocks are supported in Verilog:
- The
initial
block- Executed once at the beginning of simulation.
- Used only in test benches; cannot be used in synthesis.
- The
always
block- A continuous loop that never terminates.
- The
- The procedural block defines:
- A region of code containing sequential statements.
- The statements execute in the order they are written.
The initial
Block
- All statements inside an
initial
statement constitute aninitial block
. - Statements are grouped inside a
begin...end
structure for multiple statements. - The statements start at time 0, and execute only once.
- If there are multiple
initial
blocks, all the blocks will start to execute concurrently at time 0. - The
initial
block is typically used to write test benches for simulation:- Specifies the stimulus to be applied to the design-under-test (DUT).
- Specifies how the DUT outputs are to be displayed / handled.
- Specifies the file where the waveform information is to be dumped.
The always
Block
- All behavioral statements inside an
always
statement constitute analways block
. - Multiple statements are grouped using
begin...end
. - An
always
statement starts at time 0 and executes the statements inside the block repeatedly, and never stops. - It is used to model a block of activity that is repeated indefinitely in a digital circuit.
- Basic syntax of
always
block:always @(event_expression) begin sequential_statement_1; sequential_statement_2; ... sequential_statement_n; end
- A module can contain any number of
always
blocks, all of which execute concurrently. - The
@(event_expression)
part is required for both combinational and sequential circuit descriptions.
Rules and Statements within Procedural Blocks
Sequential Statements
- In Verilog, one or more sequential statements can be present inside an
initial
oralways
block.- The statements are executed sequentially.
- Multiple assignment statements inside a
begin...end
block may either execute sequentially or concurrently depending upon on the type of assignment.
- Two types of assignment statements: blocking (
a = b + c;
) or non-blocking (a <= b + c;
).
Variable Assignment Rules
- Only
reg
type variable can be assigned within aninitial
oralways
block. - Basic reason:
- The sequential
always
block executes only when the event expression triggers. - At other times the block is doing nothing.
- An object being assigned to must therefore remember the last value assigned (not continuously driven).
- So, only
reg
type variables can be assigned within thealways
block.
- The sequential
- Any kind of variable may appear in the event expression (reg, wire, etc.).
Sequential Control Statements
if ... else
: Conditional branching with optionalelse if
andelse
clauses.case
: Multiway branching (case
,casez
,casex
). Can replace a complexif...else
structure. The expression is compared to alternatives in order. Adefault
statement can be used.while
loop: Executes a statement while an expression is true.for
loop: Consists of an initial condition, a terminating condition check, and a procedural assignment to change the control variable.repeat
loop: Executes a loop a fixed number of times. The expression is evaluated only once at the start.forever
loop: Executes forever until$finish
is encountered. Equivalent to awhile
loop for which the expression is always true.
Timing and Event Control
# (time_value)
: Makes a block suspend fortime_value
units of time.@ (event_expression)
: Makes a block suspend untilevent_expression
triggers.- The event can be any one of the following:
- a) Change of a signal value.
- b) Positive or negative edge occurring on signal (
posedge
ornegedge
). - c) List of above-mentioned events, separated by
or
or comma.
- A
posedge
is any transition from{0, x, z}
to 1, and from0
to{z, x}
. - A
negedge
is any transition from{1, x, z}
to 0, and from1
to{z, x}
. - Examples of event expressions:
@ (in)
:in
changes.@ (a or b or c)
or@ (a, b, c)
: any ofa
,b
,c
changes.@ (posedge clk)
: positive edge ofclk
.@ (posedge clk or negedge reset)
: positive edge ofclk
or negative edge ofreset
.@ (*)
: any variable changes.
Latch Inference in Combinational Logic
- When a
case
statement is incompletely decoded, the synthesis tool will infer the need for a latch to hold the residual output when the select bits take the unspecified values. - This also applies to an
if
statement without a finalelse
clause. - It is up to the designer to code the design in such a way that latch can be avoided where possible.
- To avoid latch inference, either provide a
default
assignment in the branching statement or assign a default value to the variable at the start of the procedural block.
Examples of Procedural Assignments
- Up-down Counter (synchronous clear):
module counter (mode, clr, ld, d_in, clk, count); input mode, clr, ld, clk; input [0:7] d_in; output reg [0:7] count; always @(posedge clk) if (ld) count <= d_in; else if (clr) count <= 0; else if (mode) count <= count + 1; else count <= count - 1; endmodule
- Parameterized Design: An N-bit Counter:
- Use the keyword
parameter
to make a design general for any number of bits. - Parameter values are substituted before simulation or synthesis.
module counter (clear, clock, count); parameter N = 7; input clear, clock; output reg [0:N] count; always @(negedge clock) if (clear) count <= 0; else count <= count + 1; endmodule
- Use the keyword
- Using more than one clock in a module:
module multiple_clk (clk1, clk2, a, b, c, f1, f2); input clk1, clk2, a, b, c; output reg f1, f2; always @(posedge clk1) f1 <= a & b; always @(negedge clk2) f2 <= b ^ c; endmodule
- Using multiple edges of the same clock:
module multi_edge_clk (a, b, c, d, f, clk); input clk; input [7:0] a,b,c,d; output reg [7:0] f; always @(posedge clk) c <= a + b; always @(negedge clk) f <= c - d; endmodule
- Two operations are carried out every clock cycle.
c
is assigned at the rising edge,f
is assigned at the falling edge.
- Two operations are carried out every clock cycle.
Blocking vs. Non-Blocking Assignments
Introduction
- We shall illustrate some examples that show how the modeling style influences the simulator or synthesizer to capture the behavior of the modeled circuit.
- This is a very important concept required to be clearly understood by the designer.
- Even a slight error in modeling can result in a drastically different circuit.
- Highly recommended: For any confusion, write a Verilog code, simulate it and analyze the output(s).
Procedural Assignment Types
- Procedural assignment statements can be used to update variables of types
reg
,integer
,real
, ortime
. - The value assigned to a variable remains unchanged until another procedural assignment statement assigns a new value.
- This is different from continuous assignment (using
assign
) that results in the expression on the RHS to continuously drive thenet
type variable on the left. - Two types of procedural assignment statements:
- a) Blocking (denoted by
=
) - b) Non-blocking (denoted by
<=
)
- a) Blocking (denoted by
- The left-hand side can be a register type variable, a bit select, a part select, or a concatenation.
- The right-hand side can be any expression that evaluates to a value.
- Procedural assignments can only appear within procedural blocks (
initial
oralways
).
Blocking Assignment (=
)
- General syntax:
variable_name = [delay_or_event_control] expression;
- The
=
operator is used to specify blocking assignment. - Blocking assignment statements are executed in the order they are specified in a procedural block.
- The target of an assignment gets updated before the next sequential statement in the block is executed.
- They do not block execution of statements in other procedural blocks.
- This is the recommended style for modeling combinational logic.
Non-Blocking Assignment (<=
)
- General syntax:
variable_name <= [delay_or_event_control] expression;
- The
<=
operator is used to specify non-blocking assignment. - Non-blocking assignment statements allow scheduling of assignments without blocking execution of statements that follow within the procedural block.
- The assignment to the target gets scheduled for the end of the simulation cycle.
- Statements subsequent to the instruction under consideration are not blocked by the assignment.
- Allows concurrent procedural assignment, suitable for sequential logic.
Modeling Guidelines and Rules
- It is recommended that blocking and non-blocking assignments are not mixed in the same
always
block. This is not good design practice. - Verilog synthesizer ignores the delays specified in a procedural assignment statement. This may lead to functional mismatch between the design model and the synthesized netlist.
- A variable cannot appear as the target of both a blocking and a non-blocking assignment.
Comparative Examples
Swapping Values
- Trying to swap using blocking assignment:
always @(posedge clk) begin a=b; b=a; end
- This is incorrect. Both
a
andb
will be getting the value previously stored inb
.
- Swapping values of ‘a’ and ‘b’:
- With Blocking assignments:
always @(posedge clk) a=b; always @(posedge clk) b=a;
- Either
a=b
will execute beforeb=a
, or vice versa, depending on simulator implementation. Both registers will get the same value. This is a race condition.
- Either
- With Non-blocking assignments:
always @(posedge clk) a<=b; always @(posedge clk) b<=a;
- Here the variables are correctly swapped. All RHS variables are read first, and assigned to LHS variables at the positive clock edge.
- With Blocking assignments:
Shift Register Modeling
- Example 3:
- Correct blocking version:
always @(posedge clock) begin q2 = q1; q1 = a; end
- Correct non-blocking version:
always @(posedge clock) begin q1 <= a; q2 <= q1; end
- Correct blocking version:
- Example 4 (Correct): Using separate non-blocking
always
blocks correctly models a shift register.always @(posedge clock) q2 <= q1; always @(posedge clock) q1 <= a;
- Example 5 (Incorrect): Using separate blocking
always
blocks creates a race condition, and the outputq2
will be indeterminate.always @(posedge clock) q1 = a; always @(posedge clock) q2 = q1;
- Example 6 (Correct): Modeling a 4-bit shift register with blocking assignments requires careful ordering.
// Correct order for blocking always @(posedge clock or negedge clear) begin if (!clear) ... else begin E = D; D = C; C = B; B = A; end end
- Example 6a (Incorrect): Reversing the order of blocking assignments is incorrect.
// Incorrect order for blocking else begin B = A; C = B; D = C; E = D; end
- The effect of the assignment
B=A
is immediate. The updated value is used inC=B
, and so on. The four statements are equivalent to a single statement that assignsA
toE
.
- The effect of the assignment
- Example 6b (Correct): This is the recommended style for modeling sequential circuits, using non-blocking assignments.
- The statements can appear in any order. The chances of errors are less.
- The right-hand side expressions are evaluated in parallel, so that order of the statements is not important.
always @(posedge clock or negedge clear) begin if (!clear) ... else begin E <= D; D <= C; C <= B; B <= A; end end
Ring Counter Modeling
- A ring counter is a circular shift register.
- Incorrect with Blocking assignments:
// Incorrect code else begin count = count << 1; count[0] = count[7]; end
- This solution is wrong.
count[7]
will get overwritten in the first statement. Rotation of the bits will not happen.
- This solution is wrong.
- Correct with Non-blocking assignments (Modified version 1):
// Correct code else begin count <= count << 1; count[0] <= count[7]; end
- Since non-blocking assignments are used, rotation will take place correctly.
- Correct with Blocking assignment (Modified version 2):
// Correct code using concatenation else count = {count[6:0], count[7]};
- This is a correct way of modeling using blocking assignment.
Generate Blocks
Introduction to generate
generate
statements allow Verilog code to be generated dynamically before the simulation or synthesis begins.- Very convenient to create parameterized module descriptions. Example: N-bit ripple carry adder for arbitrary value of N.
- Requires the keywords
generate
andendgenerate
. - Generate instantiations can be carried out for various Verilog blocks: Modules, user-defined primitives, gates, continuous assignments,
initial
andalways
blocks, etc. - Generated instances have unique identifier names and can be referenced hierarchically.
genvar
Variables
- Special
genvar
variables:- The keyword
genvar
can be used to declare variables that are used only in the evaluation of a generate block. - These variables do not exist during simulation or synthesis.
- The value of a
genvar
can be defined only in a generate loop. - Every generate loop is assigned a name, so that variables inside the generate loop can be referenced hierarchically.
- The keyword
Examples of Generate Blocks
Example 1: Design of a Parameterized Bitwise XOR
module xor_bitwise (f, a, b);
parameter N = 16;
output [N-1:0] f;
input [N-1:0] a, b;
genvar p;
generate
for (p=0; p<N; p=p+1)
begin: xorlp
xor XG (f[p], a[p], b[p]);
end
endgenerate
endmodule
- In the bitwise xor example, the name
xorlp
was given to the generate loop. - The relative hierarchical names of the xor gates will be:
xorlp[0].XG, xorlp[1].XG, ..., xorlp[15].XG
.
Example 2: Design of N-bit Ripple Carry Adder
- We can use
generate
to dynamically create N copies of a full adder and connect them to make an N-bit ripple-carry adder. - Full Adder Module:
module full_adder (a, b, c, sum, cout); input a, b, c; output, sum, cout; wire t1, t2, t3; xor G1 (t1, a, b); xor G2 (sum, t1, c); and G3 (t2, a, b); and G4 (t3, t1, c); or G5 (cout, t2, t3); endmodule
- RCA Module using
generate
:module RCA (carry_out, sum, a, b, carry_in); parameter N = 8; input [N-1:0] a, b; input carry_in; output [N-1:0] sum; output carry_out; wire [N:0] carry; assign carry[0] = carry_in; assign carry_out = carry[N]; genvar i; generate for (i=0; i<N; i++) begin: fa_loop // This implicitly instantiates a full adder wire t1, t2, t3; xor G1 (t1, a[i], b[i]); xor G2 (sum[i], t1, carry[i]); and G3 (t2, a[i], b[i]); and G4 (t3, t1, carry[i]); or G5 (carry[i+1], t2, t3); end endgenerate endmodule
- Some of the relative hierarchical instance names that are generated are:
fa_loop[0].G1
,fa_loop[1].G1
,fa_loop[7].G1
, etc. - Some of the nets (
wires
) that are generated are:fa_loop[0].t1
,fa_loop[1].t2
,fa_loop[0].t3
, etc.