6.1dataflow Modeling
6.1dataflow Modeling
Dataflow Modeling
For small circuits, the gate-level modeling approach works very well because the number
of gates is limited and the designer can instantiate and connect every gate individually.
Also, gate-level modeling is very intuitive to a designer with a basic knowledge of digital
logic design. However, in complex designs the number of gates is very large. Thus,
designers can design more effectively if they concentrate on implementing the function at
a level of abstraction higher than gate level. Dataflow modeling provides a powerful way
to implement a design. Verilog allows a circuit to be designed in terms of the data flow
between registers and how a design processes data rather than instantiation of individual
gates. Later in this chapter, the benefits of dataflow modeling will become more apparent.
With gate densities on chips increasing rapidly, dataflow modeling has assumed great
importance. No longer can companies devote engineering resources to handcrafting entire
designs with gates. Currently, automated tools are used to create a gate-level circuit from
a dataflow design description. This process is called logic synthesis. Dataflow modeling
has become a popular design approach as logic synthesis tools have become
sophisticated. This approach allows the designer to concentrate on optimizing the circuit
in terms of data flow. For maximum flexibility in the design process, designers typically
use a Verilog description style that combines the concepts of gate-level, data flow, and
behavioral design. In the digital design community, the term RTL (Register Transfer
Level) design is commonly used for a combination of dataflow modeling and behavioral
modeling.
Learning Objectives
• Explain assignment delay, implicit assignment delay, and net declaration delay for
continuous assignment statements.
97
6.1 Continuous Assignments
A continuous assignment is the most basic statement in dataflow modeling, used to drive
a value onto a net. This assignment replaces gates in the description of the circuit and
describes the circuit at a higher level of abstraction. The assignment statement starts with
the keyword assign. The syntax of an assign statement is as follows.
Notice that drive strength is optional and can be specified in terms of strength levels
discussed in Section 3.2.1, Value Set. We will not discuss drive strength specification in
this chapter. The default value for drive strength is strong1 and strong0. The delay value
is also optional and can be used to specify delay on the assign statement. This is like
specifying delays for gates. Delay specification is discussed in this chapter. Continuous
assignments have the following characteristics:
1. The left hand side of an assignment must always be a scalar or vector net or a
concatenation of scalar and vector nets. It cannot be a scalar or vector register.
Concatenations are discussed in Section 6.4.8, Concatenation Operator.
3. The operands on the right-hand side can be registers or nets or function calls.
Registers or nets can be scalars or vectors.
4. Delay values can be specified for assignments in terms of time units. Delay values
are used to control the time when a net is assigned the evaluated value. This
feature is similar to specifying delays for gates. It is very useful in modeling
timing behavior in real circuits.
Examples of continuous assignments are shown below. Operators such as &, ^, |, {, } and
+ used in the examples are explained in Section 6.4, Operator Types. At this point,
concentrate on how the assign statements are specified.
98
Example 6-1 Examples of Continuous Assignment
Instead of declaring a net and then writing a continuous assignment on the net, Verilog
provides a shortcut by which a continuous assignment can be placed on a net when it is
declared. There can be only one implicit declaration assignment per net because a net is
declared only once.
If a signal name is used to the left of the continuous assignment, an implicit net
declaration will be inferred for that signal name. If the net is connected to a module port,
the width of the inferred net is equal to the width of the module port.
99
6.2 Delays
Delay values control the time between the change in a right-hand-side operand and when
the new value is assigned to the left-hand side. Three ways of specifying delays in
continuous assignment statements are regular assignment delay, implicit continuous
assignment delay, and net declaration delay.
The first method is to assign a delay value in a continuous assignment statement. The
delay value is specified after the keyword assign. Any change in values of in1 or in2 will
result in a delay of 10 time units before recomputation of the expression in1 & in2, and
the result will be assigned to out. If in1 or in2 changes value again before 10 time units
when the result propagates to out, the values of in1 and in2 at the time of recomputation
are considered. This property is called inertial delay. An input pulse that is shorter than
the delay of the assignment statement does not propagate to the output.
The waveform in Figure 6-1 is generated by simulating the above assign statement. It
shows the delay on signal out. Note the following change:
1. When signals in1 and in2 go high at time 20, out goes to a high 10 time units later
(time = 30).
3. However, in1 changes to high at 80, but it goes down to low before 10 time units
have elapsed.
4. Hence, at the time of recomputation, 10 units after time 80, in1 is 0. Thus, out
gets the value 0. A pulse of width less than the specified assignment delay is not
propagated to the output.
100
Figure 6-1. Delays
Inertial delays also apply to gate delays, discussed in Chapter 5, Gate-Level Modeling.
//same as
wire out;
assign #10 out = in1 & in2;
The declaration above has the same effect as defining a wire out and declaring a
continuous assignment on out.
//Net Delays
wire # 10 out;
assign out = in1 & in2;
Having discussed continuous assignments and delays, let us take a closer look at
expressions, operators, and operands that are used inside continuous assignments.
101
6.3 Expressions, Operators, and Operands
6.3.1 Expressions
Expressions are constructs that combine operators and operands to produce a result.
6.3.2 Operands
Operands can be any one of the data types defined in Section 3.2, Data Types. Some
constructs will take only certain types of operands. Operands can be constants, integers,
real numbers, nets, registers, times, bit-select (one bit of vector net or a vector register),
part-select (selected bits of the vector net or register vector), and memories or function
calls (functions are discussed later).
real a, b, c;
c = a - b; //a and b are real operands
reg ret_value;
ret_value = calculate_parity(A, B);//calculate_parity is a
//function type operand
6.3.3 Operators
Operators act on the operands to produce desired results. Verilog provides various types
of operators. Operator types are discussed in detail in Section 6.4, Operator Types.
Verilog provides many different operator types. Operators can be arithmetic, logical,
relational, equality, bitwise, reduction, shift, concatenation, or conditional. Some of these
operators are similar to the operators used in the C programming language. Each operator
type is denoted by a symbol. Table 6-1 shows the complete listing of operator symbols
classified by category.
/ divide two
+ add two
Arithmetic
- subtract two
% modulus two
|| logical or two
> greater than two
!= inequality two
Equality
=== case equality two
103
~ bitwise negation one
| reduction or one
Reduction
~| reduction nor one
Binary operators
Binary arithmetic operators are multiply (*), divide (/), add (+), subtract (-), power (**),
and modulus (%). Binary operators take two operands.
104
A = 4'b0011; B = 4'b0100; // A and B are register vectors
D = 6; E = 4; F=2// D and E are integers
If any operand bit has a value x, then the result of the entire expression is x. This seems
intuitive because if an operand value is not known precisely, the result should be an
unknown.
in1 = 4'b101x;
in2 = 4'b1010;
sum = in1 + in2; // sum will be evaluated to the value 4'bx
Modulus operators produce the remainder from the division of two numbers. They
operate similarly to the modulus operator in the C programming language.
13 % 3 // Evaluates to 1
16 % 4 // Evaluates to 0
-7 % 2 // Evaluates to -1, takes sign of the first operand
7 % -2 // Evaluates to +1, takes sign of the first operand
Unary operators
The operators + and - can also work as unary operators. They are used to specify the
positive or negative sign of the operand. Unary + or ? operators have higher precedence
than the binary + or ? operators.
-4 // Negative 4
+5 // Positive 5
105
6.4.2 Logical Operators
Logical operators are logical-and (&&), logical-or (||) and logical-not (!). Operators &&
and || are binary operators. Operator ! is a unary operator. Logical operators follow these
conditions:
// Logical operations
A = 3; B = 0;
A && B // Evaluates to 0. Equivalent to (logical-1 && logical-0)
A || B // Evaluates to 1. Equivalent to (logical-1 || logical-0)
!A// Evaluates to 0. Equivalent to not(logical-1)
!B// Evaluates to 1. Equivalent to not(logical-0)
// Unknowns
A = 2'b0x; B = 2'b10;
A && B // Evaluates to x. Equivalent to (x && logical 1)
// Expressions
(a == 2) && (b == 3) // Evaluates to 1 if both a == 2 and b == 3 are
true.
// Evaluates to 0 if either is false.
Relational operators are greater-than (>), less-than (<), greater-than-or-equal-to (>=), and
less-than-or-equal-to (<=). If relational operators are used in an expression, the
expression returns a logical value of 1 if the expression is true and 0 if the expression is
false. If there are any unknown or z bits in the operands, the expression takes a value x.
These operators function exactly as the corresponding operators in the C programming
language.
106
// A = 4, B = 3
// X = 4'b1010, Y = 4'b1101, Z = 4'b1xxx
Equality operators are logical equality (==), logical inequality (!=), case equality (===),
and case inequality (!==). When used in an expression, equality operators return logical
value 1 if true, 0 if false. These operators compare the two operands bit by bit, with zero
filling if the operands are of unequal length. Table 6-2 lists the operators.
Possible Logical
Expression Description
Value
a == b a equal to b, result unknown if x or z in a or b 0, 1, x
a not equal to b, result unknown if x or z in a or
a != b 0, 1, x
b
a === b a equal to b, including x and z 0, 1
a !== b a not equal to b, including x and z 0, 1
It is important to note the difference between the logical equality operators (==, !=) and
case equality operators (===, !==). The logical equality operators (==, !=) will yield an x
if either operand has x or z in its bits. However, the case equality operators ( ===, !== )
compare both operands bit by bit and compare all bits, including x and z. The result is 1 if
the operands match exactly, including x and z bits. The result is 0 if the operands do not
match exactly. Case equality operators never result in an x.
// A = 4, B = 3
// X = 4'b1010, Y = 4'b1101
// Z = 4'b1xxz, M = 4'b1xxz, N = 4'b1xxx
A == B // Results in logical 0
X != Y // Results in logical 1
X == Z // Results in x
Z === M // Results in logical 1 (all bits match, including x and z)
Z === N // Results in logical 0 (least significant bit does not match)
M !== N // Results in logical 1
107
6.4.5 Bitwise Operators
Bitwise operators are negation (~), and(&), or (|), xor (^), xnor (^~, ~^). Bitwise operators
perform a bit-by-bit operation on two operands. They take each bit in one operand and
perform the operation with the corresponding bit in the other operand. If one operand is
shorter than the other, it will be bit-extended with zeros to match the length of the longer
operand. Logic tables for the bit-by-bit computation are shown in Table 6-3. A z is
treated as an x in a bitwise operation. The exception is the unary negation operator (~),
which takes only one operand and operates on the bits of the single operand.
// X = 4'b1010, Y = 4'b1101
// Z = 4'b10x1
108
It is important to distinguish bitwise operators ~, &, and | from logical operators !, &&, ||.
Logical operators always yield a logical value 0, 1, x, whereas bitwise operators yield a
bit-by-bit value. Logical operators perform a logical operation, not a bit-by-bit operation.
// X = 4'b1010, Y = 4'b0000
Reduction operators are and (&), nand (~&), or (|), nor (~|), xor (^), and xnor (~^, ^~).
Reduction operators take only one operand. Reduction operators perform a bitwise
operation on a single vector operand and yield a 1-bit result. The logic tables for the
operators are the same as shown in Section 6.4.5, Bitwise Operators. The difference is
that bitwise operations are on bits from two different operands, whereas reduction
operations are on the bits of the same operand. Reduction operators work bit by bit from
right to left. Reduction nand, reduction nor, and reduction xnor are computed by inverting
the result of the reduction and, reduction or, and reduction xor, respectively.
// X = 4'b1010
The use of a similar set of symbols for logical (!, &&, ||), bitwise (~, &, |, ^), and
reduction operators (&, |, ^) is somewhat confusing initially. The difference lies in the
number of operands each operator takes and also the value of results computed.
Shift operators are right shift ( >>), left shift (<<), arithmetic right shift (>>>), and
arithmetic left shift (<<<). Regular shift operators shift a vector operand to the right or
the left by a specified number of bits. The operands are the vector and the number of bits
to shift. When the bits are shifted, the vacant bit positions are filled with zeros. Shift
operations do not wrap around. Arithmetic shift operators use the context of the
expression to determine the value with which to fill the vacated bits.
// X = 4'b1100
109
Y = X << 1; //Y is 4'b1000. Shift left 1 bit. 0 filled in LSB position.
Y = X << 2; //Y is 4'b0000. Shift left 2 bits.
Shift operators are useful because they allow the designer to model shift operations, shift-
and-add algorithms for multiplication, and other useful operations.
Concatenations are expressed as operands within braces, with commas separating the
operands. Operands can be scalar nets or registers, vector nets or registers, bit-select,
part-select, or sized constants.
Y = {B , C} // Result Y is 4'b0010
Y = {A , B , C , D , 3'b001} // Result Y is 11'b10010110001
Y = {A , B[0], C[1]} // Result Y is 3'b101
reg A;
reg [1:0] B, C;
reg [2:0] D;
A = 1'b1; B = 2'b00; C = 2'b10; D = 3'b110;
110
6.4.10 Conditional Operator
The conditional operator(?:) takes three operands.
The condition expression (condition_expr) is first evaluated. If the result is true (logical
1), then the true_expr is evaluated. If the result is false (logical 0), then the false_expr is
evaluated. If the result is x (ambiguous), then both true_expr and false_expr are evaluated
and their results are compared, bit by bit, to return for each bit position an x if the bits are
different and the value of the bits if they are the same.
111
6.4.11 Operator Precedence
Reduction ^ ^~
|, ~|
&&
Logical
||
Conditional ?: Lowest precedence
112
6.5 Examples
We can use assignment statements instead of gates to model the logic equations of the
multiplexer (see Example 6-2). Notice that everything is same as the gate-level Verilog
description except that computation of out is done by specifying one logic equation by
using operators instead of individual gate instantiations. I/O ports remain the same. This
is important so that the interface with the environment does not change. Only the
internals of the module change. Notice how concise the description is compared to the
gate-level description.
endmodule
113
Method 2: conditional operator
There is a more concise way to specify the 4-to-1 multiplexers. In Section 6.4.10,
Conditional Operator, we described how a conditional statement corresponds to a
multiplexer operation. We will use this operator to write a 4-to-1 multiplexer. Convince
yourself that this description (Example 6-3) correctly models a multiplexer.
endmodule
In the simulation of the multiplexer, the gate-level module in Example 5-5 on page 72
can be substituted with the dataflow multiplexer modules described above. The stimulus
module will not change. The simulation results will be identical. By encapsulating
functionality inside a module, we can replace the gate-level module with a dataflow
module without affecting the other modules in the simulation. This is a very powerful
feature of Verilog.
The 4-bit full adder in Section 5.1.4, Examples, was designed by using gates; the logic
diagram is shown in Figure 5-7 and Figure 5-6. In this section, we write the dataflow
description for the 4-bit adder. Compare it with the gate-level description in Figure 5-7.
In gates, we had to first describe a 1-bit full adder. Then we built a 4-bit full ripple carry
adder. We again illustrate two methods to describe a 4-bit full adder by means of
dataflow statements.
A concise description of the adder (Example 6-4) is defined with the + and { } operators.
114
Example 6-4 4-bit Full Adder, Using Dataflow Operators
endmodule
If we substitute the gate-level 4-bit full adder with the dataflow 4-bit full adder, the rest
of the modules will not change. The simulation results will be identical.
In ripple carry adders, the carry must propagate through the gate levels before the sum is
available at the output terminals. An n-bit ripple carry adder will have 2n gate levels. The
propagation time can be a limiting factor on the speed of the circuit. One of the most
popular methods to reduce delay is to use a carry lookahead mechanism. Logic equations
for implementing the carry lookahead mechanism can be found in any logic design book.
The propagation delay is reduced to four gate levels, irrespective of the number of bits in
the adder. The Verilog description for a carry lookahead adder is shown in Example 6-5.
This module can be substituted in place of the full adder modules described before
without changing any other component of the simulation. The simulation results will be
unchanged.
// Internal wires
wire p0,g0, p1,g1, p2,g2, p3,g3;
wire c4, c3, c2, c1;
endmodule
We now discuss an additional example that was not discussed in the gate-level modeling
chapter. We design a 4-bit ripple counter by using negative edge-triggered flipflops. This
example was discussed at a very abstract level in Chapter 2, Hierarchical Modeling
Concepts. We design it using Verilog dataflow statements and test it with a stimulus
module. The diagrams for the 4-bit ripple carry counter modules are shown below.
Figure 6-2 shows the counter being built with four T-flipflops.
116
Figure 6-2. 4-bit Ripple Carry Counter
Figure 6-3 shows that the T-flipflop is built with one D-flipflop and an inverter gate.
Finally, Figure 6-4 shows the D-flipflop constructed from basic logic gates.
117
Figure 6-4. Negative Edge-Triggered D-flipflop with Clear
Given the above diagrams, we write the corresponding Verilog, using dataflow
statements in a top-down fashion. First we design the module counter. The code is shown
in Figure 6-6. The code contains instantiation of four T_FF modules.
// Ripple counter
module counter(Q , clock, clear);
// I/O ports
output [3:0] Q;
input clock, clear;
endmodule
118
Figure 6-6. 4-bit Synchronous Counter with clear and count_enable
Next, we write the Verilog description for T_FF (Example 6-7). Notice that instead of the
not gate, a dataflow operator ~ negates the signal q, which is fed back.
// I/O ports
output q;
input clk, clear;
endmodule
Finally, we define the lowest level module D_FF (edge_dff ), using dataflow statements
(Example 6-8). The dataflow statements correspond to the logic diagram shown in Figure
6-4. The nets in the logic diagram correspond exactly to the declared nets.
// Edge-triggered D flipflop
module edge_dff(q, qbar, d, clk, clear);
// Internal variables
wire s, sbar, r, rbar,cbar;
// dataflow statements
//Create a complement of signal clear
119
assign cbar = ~clear;
// Output latch
assign q = ~(s & qbar),
qbar = ~(q & r & cbar);
endmodule
The design block is now ready. Now we must instantiate the design block inside the
stimulus block to test the design. The stimulus block is shown in Example 6-9. The clock
has a time period of 20 with a 50% duty cycle.
initial
$monitor($time, " Count Q = %b Clear= %b", Q[3:0],CLEAR);
endmodule
120
The output of the simulation is shown below. Note that the clear signal resets the count to
zero.
121
6.6 Summary
• Delay values control the time between the change in a right-hand-side variable
and when the new value is assigned to the left-hand side. Delays on a net can be
defined in the assign statement, implicit continuous assignment, or net
declaration.
• The operator types are arithmetic, logical, relational, equality, bitwise, reduction,
shift, concatenation, replication, and conditional. Unary operators require one
operand, binary operators require two operands, and ternary require three
operands. The concatenation operator can take any number of operands.
• The conditional operator behaves like a multiplexer in hardware or like the if-
then-else statement in programming languages.
122
6.7 Exercises
1: A full subtractor has three 1-bit inputs x, y, and z (previous borrow) and two 1-
bit outputs D (difference) and B (borrow). The logic equations for D and B are
as follows:
Write the full Verilog description for the full subtractor module, including I/O
ports (Remember that + in logic equations corresponds to a logical or operator
(||) in dataflow). Instantiate the subtractor inside a stimulus block and test all
eight possible combinations of x, y, and z given in the following truth table.
x y z B D
0 0 0 0 0
0 0 1 1 1
0 1 0 1 1
0 1 1 1 0
1 0 0 0 1
1 0 1 0 0
1 1 0 0 0
1 1 1 1 1
The magnitude can be compared by comparing the numbers bit by bit, starting
with the most significant bit. If any bit mismatches, the number with bit 0 is the
123
lower number. To realize this functionality in logic equations, let us define an
intermediate variable. Notice that the function below is an xnor function.
The three outputs of the magnitude comparator are A_gt_B, A_lt_B, A_eq_B.
They are defined with the following logic equations:
A_eq_B = x(3).x(2).x(1).x(0)
124