0% found this document useful (0 votes)
18 views28 pages

6.1dataflow Modeling

The document discusses dataflow modeling in Verilog. It states that dataflow modeling allows a circuit to be designed based on the flow of data between registers rather than instantiating individual gates. Continuous assignments are used to drive values onto nets and replace gates. Delays can be specified for continuous assignments to control when values are assigned. Expressions combine operators and operands to produce results and are used inside continuous assignments.

Uploaded by

snow54685
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views28 pages

6.1dataflow Modeling

The document discusses dataflow modeling in Verilog. It states that dataflow modeling allows a circuit to be designed based on the flow of data between registers rather than instantiating individual gates. Continuous assignments are used to drive values onto nets and replace gates. Delays can be specified for continuous assignments to control when values are assigned. Expressions combine operators and operands to produce results and are used inside continuous assignments.

Uploaded by

snow54685
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 28

Chapter 6.

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

• Describe the continuous assignment (assign) statement, restrictions on the assign


statement, and the implicit continuous assignment statement.

• Explain assignment delay, implicit assignment delay, and net declaration delay for
continuous assignment statements.

• Define expressions, operators, and operands.


• List operator types for all possible operations?arithmetic, logical, relational,


equality, bitwise, reduction, shift, concatenation, and conditional.

• Use dataflow constructs to model practical digital circuits in Verilog.

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.

continuous_assign ::= assign [ drive_strength ] [ delay3 ]


list_of_net_assignments ;
list_of_net_assignments ::= net_assignment { , net_assignment }
net_assignment ::= net_lvalue = expression

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.

2. Continuous assignments are always active. The assignment expression is


evaluated as soon as one of the right-hand-side operands changes and the value is
assigned to the left-hand-side net.

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

// Continuous assign. out is a net. i1 and i2 are nets.


assign out = i1 & i2;

// Continuous assign for vector nets. addr is a 16-bit vector net


// addr1 and addr2 are 16-bit vector registers.
assign addr[15:0] = addr1_bits[15:0] ^ addr2_bits[15:0];

// Concatenation. Left-hand side is a concatenation of a scalar


// net and a vector net.
assign {c_out, sum[3:0]} = a[3:0] + b[3:0] + c_in;

We now discuss a shorthand method of placing a continuous assignment on a net.

6.1.1 Implicit 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.

In the example below, an implicit continuous assignment is contrasted with a regular


continuous assignment.

//Regular continuous assignment


wire out;
assign out = in1 & in2;

//Same effect is achieved by an implicit continuous assignment


wire out = in1 & in2;

6.1.2 Implicit Net Declaration

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.

// Continuous assign. out is a net.


wire i1, i2;
assign out = i1 & i2; //Note that out was not declared as a wire
//but an implicit wire declaration for out
//is done by the simulator

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.

6.2.1 Regular Assignment 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.

assign #10 out = in1 & in2; // Delay in a continuous assign

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).

2. When in1 goes low at 60, out changes to low at 70.

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.

6.2.2 Implicit Continuous Assignment Delay


An equivalent method is to use an implicit continuous assignment to specify both a delay
and an assignment on the net.

//implicit continuous assignment delay


wire #10 out = in1 & in2;

//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.

6.2.3 Net Declaration Delay


A delay can be specified on a net when it is declared without putting a continuous
assignment on the net. If a delay is specified on a net out, then any value change applied
to the net out is delayed accordingly. Net declaration delays can also be used in gate-level
modeling.

//Net Delays
wire # 10 out;
assign out = in1 & in2;

//The above statement has the same effect as the following.


wire out;
assign #10 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

Dataflow modeling describes the design in terms of expressions instead of primitive


gates. Expressions, operators, and operands form the basis of dataflow modeling.

6.3.1 Expressions

Expressions are constructs that combine operators and operands to produce a result.

// Examples of expressions. Combines operands and operators


a ^ b
addr1[20:17] + addr2[20:17]
in1 | in2

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).

integer count, final_count;


final_count = count + 1;//count is an integer operand

real a, b, c;
c = a - b; //a and b are real operands

reg [15:0] reg1, reg2;


reg [3:0] reg_out;
reg_out = reg1[3:0] ^ reg2[3:0];//reg1[3:0] and reg2[3:0] are
//part-select register 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.

d1 && d2 // && is an operator on operands d1 and d2


!a[0] // ! is an operator on operand a[0]
B >> 1 // >> is an operator on operands B and 1
102
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.

Table 6-1. Operator Types and Symbols


Operator Type Operator Symbol Operation Performed Number of Operands
* multiply two

/ divide two

+ add two
Arithmetic
- subtract two

% modulus two

** power (exponent) two


! logical negation one

Logical && logical and two

|| logical or two
> greater than two

< less than two


Relational
>= greater than or equal two

<= less than or equal two


== equality two

!= inequality two
Equality
=== case equality two

!== case inequality two

103
~ bitwise negation one

& bitwise and two

Bitwise | bitwise or two

^ bitwise xor two

^~ or ~^ bitwise xnor two


& reduction and one

~& reduction nand one

| reduction or one
Reduction
~| reduction nor one

^ reduction xor one

^~ or ~^ reduction xnor one


>> Right shift Two

<< Left shift Two


Shift
>>> Arithmetic right shift Two

<<< Arithmetic left shift Two


Concatenation {} Concatenation Any number
Replication {{}} Replication Any number
Conditional ?: Conditional Three

Let us now discuss each operator type in detail.

6.4.1 Arithmetic Operators

There are two types of arithmetic operators: binary and unary.

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

A * B // Multiply A and B. Evaluates to 4'b1100


D / E // Divide D by E. Evaluates to 1. Truncates any fractional part.
A + B // Add A and B. Evaluates to 4'b0111
B - A // Subtract A from B. Evaluates to 4'b0001
F = E ** F; //E to the power F, yields 16

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

Negative numbers are represented as 2's complement internally in Verilog. It is advisable


to use negative numbers only of the type integer or real in expressions. Designers should
avoid negative numbers of the type <sss> '<base> <nnn> in expressions because they are
converted to unsigned 2's complement numbers and hence yield unexpected results.

//Advisable to use integer or real numbers


-10 / 5// Evaluates to -2

//Do not use numbers of type <sss> '<base> <nnn>


-'d10 / 5// Is equivalent (2's complement of 10)/5 = (232 - 10)/5
// where 32 is the default machine word width.
// This evaluates to an incorrect and unexpected result

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:

1. Logical operators always evaluate to a 1-bit value, 0 (false), 1 (true), or x


(ambiguous).

2. If an operand is not equal to zero, it is equivalent to a logical 1 (true condition). If


it is 01equal to zero, it is equivalent to a logical 0 (false condition). If any operand
bit is x or z, it is equivalent to x (ambiguous condition) and is normally treated by
simulators as a false condition.

3. Logical operators take variables or expressions as operands.

Use of parentheses to group logical operations is highly recommended to improve


readability. Also, the user does not have to remember the precedence of operators.

// 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.

6.4.3 Relational Operators

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

A <= B // Evaluates to a logical 0


A > B // Evaluates to a logical 1
Y >= X // Evaluates to a logical 1
Y < Z // Evaluates to an x

6.4.4 Equality Operators

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.

Table 6-2. Equality 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.

Table 6-3. Truth Tables for Bitwise Operators

Examples of bitwise operators are shown below.

// X = 4'b1010, Y = 4'b1101
// Z = 4'b10x1

~X // Negation. Result is 4'b0101


X & Y // Bitwise and. Result is 4'b1000
X | Y // Bitwise or. Result is 4'b1111
X ^ Y // Bitwise xor. Result is 4'b0111
X ^~ Y // Bitwise xnor. Result is 4'b1000
X & Z // Result is 4'b10x0

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

X | Y // bitwise operation. Result is 4'b1010


X || Y // logical operation. Equivalent to 1 || 0. Result is 1.

6.4.6 Reduction Operators

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

&X //Equivalent to 1 & 0 & 1 & 0. Results in 1'b0


|X//Equivalent to 1 | 0 | 1 | 0. Results in 1'b1
^X//Equivalent to 1 ^ 0 ^ 1 ^ 0. Results in 1'b0
//A reduction xor or xnor can be used for even or odd parity
//generation of a vector.

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.

6.4.7 Shift Operators

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

Y = X >> 1; //Y is 4'b0110. Shift right 1 bit. 0 filled in MSB


position.

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.

integer a, b, c; //Signed data types


a = 0;
b = -10; // 00111...10110 binary
c = a + (b >>> 3); //Results in -2 decimal, due to arithmetic shift

Shift operators are useful because they allow the designer to model shift operations, shift-
and-add algorithms for multiplication, and other useful operations.

6.4.8 Concatenation Operator

The concatenation operator ( {, } ) provides a mechanism to append multiple operands.


The operands must be sized. Unsized operands are not allowed because the size of each
operand must be known for computation of the size of the result.

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.

// A = 1'b1, B = 2'b00, C = 2'b10, D = 3'b110

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

6.4.9 Replication Operator

Repetitive concatenation of the same number can be expressed by using a replication


constant. A replication constant specifies how many times to replicate the number inside
the brackets ( { } ).

reg A;
reg [1:0] B, C;
reg [2:0] D;
A = 1'b1; B = 2'b00; C = 2'b10; D = 3'b110;

Y = { 4{A} } // Result Y is 4'b1111


Y = { 4{A} , 2{B} } // Result Y is 8'b11110000
Y = { 4{A} , 2{B} , C } // Result Y is 8'b1111000010

110
6.4.10 Conditional Operator
The conditional operator(?:) takes three operands.

Usage: condition_expr ? true_expr : false_expr ;

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.

The action of a conditional operator is similar to a multiplexer. Alternately, it can be


compared to the if-else expression.

Conditional operators are frequently used in dataflow modeling to model conditional


assignments. The conditional expression acts as a switching control.

//model functionality of a tristate buffer


assign addr_bus = drive_enable ? addr_out : 36'bz;

//model functionality of a 2-to-1 mux


assign out = control ? in1 : in0;

Conditional operations can be nested. Each true_expr or false_expr can itself be a


conditional operation. In the example that follows, convince yourself that (A==3) and
control are the two select signals of 4-to-1 multiplexer with n, m, y, x as the inputs and
out as the output signal.

assign out = (A == 3) ? ( control ? x : y ): ( control ? m : n) ;

111
6.4.11 Operator Precedence

Having discussed the operators, it is now important to discuss operator precedence. If no


parentheses are used to separate parts of expressions, Verilog enforces the following
precedence. Operators listed in Table 6-4 are in order from highest precedence to lowest
precedence. It is recommended that parentheses be used to separate expressions except in
case of unary operators or when there is no ambiguity.

Table 6-4. Operator Precedence

Operators Operator Symbols Precedence


Unary +-!~ Highest precedence
Multiply, Divide, Modulus * / %
Add, Subtract +-
Shift << >>
Relational < <= > >=
Equality == != === !==
&, ~&

Reduction ^ ^~

|, ~|
&&
Logical
||
Conditional ?: Lowest precedence

112
6.5 Examples

A design can be represented in terms of gates, data flow, or a behavioral description. In


this section, we consider the 4-to-1 multiplexer and 4-bit full adder described in Section
5.1.4, Examples. Previously, these designs were directly translated from the logic
diagram into a gate-level Verilog description. Here, we describe the same designs in
terms of data flow. We also discuss two additional examples: a 4-bit full adder using
carry lookahead and a 4-bit counter using negative edge-triggered D-flipflops.

6.5.1 4-to-1 Multiplexer

Gate-level modeling of a 4-to-1 multiplexer is discussed in Section 5.1.4, Examples. The


logic diagram for the multiplexer is given in Figure 5-5 and the gate-level Verilog
description is shown in Example 5-5. We describe the multiplexer, using dataflow
statements. Compare it with the gate-level description. We show two methods to model
the multiplexer by using dataflow statements.

Method 1: logic equation

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.

Example 6-2 4-to-1 Multiplexer, Using Logic Equations

// Module 4-to-1 multiplexer using data flow. logic equation


// Compare to gate-level model
module mux4_to_1 (out, i0, i1, i2, i3, s1, s0);

// Port declarations from the I/O diagram


output out;
input i0, i1, i2, i3;
input s1, s0;

//Logic equation for out


assign out = (~s1 & ~s0 & i0)|
(~s1 & s0 & i1) |
(s1 & ~s0 & i2) |
(s1 & s0 & i3) ;

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.

Example 6-3 4-to-1 Multiplexer, Using Conditional Operators

// Module 4-to-1 multiplexer using data flow. Conditional operator.


// Compare to gate-level model
module multiplexer4_to_1 (out, i0, i1, i2, i3, s1, s0);

// Port declarations from the I/O diagram


output out;
input i0, i1, i2, i3;
input s1, s0;

// Use nested conditional operator


assign out = s1 ? ( s0 ? i3 : i2) : (s0 ? i1 : i0) ;

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.

6.5.2 4-bit Full Adder

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.

Method 1: dataflow operators

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

// Define a 4-bit full adder by using dataflow statements.


module fulladd4(sum, c_out, a, b, c_in);

// I/O port declarations


output [3:0] sum;
output c_out;
input[3:0] a, b;
input c_in;

// Specify the function of a full adder


assign {c_out, sum} = a + b + c_in;

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.

Method 2: full adder with carry lookahead

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.

Example 6-5 4-bit Full Adder with Carry Lookahead

module fulladd4(sum, c_out, a, b, c_in);


// Inputs and outputs
output [3:0] sum;
output c_out;
input [3:0] a,b;
input c_in;

// Internal wires
wire p0,g0, p1,g1, p2,g2, p3,g3;
wire c4, c3, c2, c1;

// compute the p for each stage


assign p0 = a[0] ^ b[0],
p1 = a[1] ^ b[1],
115
p2 = a[2] ^ b[2],
p3 = a[3] ^ b[3];

// compute the g for each stage


assign g0 = a[0] & b[0],
g1 = a[1] & b[1],
g2 = a[2] & b[2],
g3 = a[3] & b[3];

// compute the carry for each stage


// Note that c_in is equivalent c0 in the arithmetic equation for
// carry lookahead computation
assign c1 = g0 | (p0 & c_in),
c2 = g1 | (p1 & g0) | (p1 & p0 & c_in),
c3 = g2 | (p2 & g1) | (p2 & p1 & g0) | (p2 & p1 & p0 & c_in),
c4 = g3 | (p3 & g2) | (p3 & p2 & g1) | (p3 & p2 & p1 & g0) |
(p3 & p2 & p1 & p0 & c_in);
// Compute Sum
assign sum[0] = p0 ^ c_in,
sum[1] = p1 ^ c1,
sum[2] = p2 ^ c2,
sum[3] = p3 ^ c3;

// Assign carry output


assign c_out = c4;

endmodule

6.5.3 Ripple Counter

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.

Figure 6-3. T-flipflop

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.

Example 6-6 Verilog Code for Ripple Counter

// Ripple counter
module counter(Q , clock, clear);

// I/O ports
output [3:0] Q;
input clock, clear;

// Instantiate the T flipflops


T_FF tff0(Q[0], clock, clear);
T_FF tff1(Q[1], Q[0], clear);
T_FF tff2(Q[2], Q[1], clear);
T_FF tff3(Q[3], Q[2], 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.

Example 6-7 Verilog Code for T-flipflop

// Edge-triggered T-flipflop. Toggles every clock


// cycle.
module T_FF(q, clk, clear);

// I/O ports
output q;
input clk, clear;

// Instantiate the edge-triggered DFF


// Complement of output q is fed back.
// Notice qbar not needed. Unconnected port.
edge_dff ff1(q, ,~q, 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.

Example 6-8 Verilog Code for Edge-Triggered D-flipflop

// Edge-triggered D flipflop
module edge_dff(q, qbar, d, clk, clear);

// Inputs and outputs


output q,qbar;
input d, clk, clear;

// Internal variables
wire s, sbar, r, rbar,cbar;

// dataflow statements
//Create a complement of signal clear
119
assign cbar = ~clear;

// Input latches; A latch is level sensitive. An edge-sensitive


// flip-flop is implemented by using 3 SR latches.
assign sbar = ~(rbar & s),
s = ~(sbar & cbar & ~clk),
r = ~(rbar & ~clk & s),
rbar = ~(r & cbar & d);

// 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.

Example 6-9 Stimulus Module for Ripple Counter

// Top level stimulus module


module stimulus;

// Declare variables for stimulating input


reg CLOCK, CLEAR;
wire [3:0] Q;

initial
$monitor($time, " Count Q = %b Clear= %b", Q[3:0],CLEAR);

// Instantiate the design block counter


counter c1(Q, CLOCK, CLEAR);

// Stimulate the Clear Signal


initial
begin
CLEAR = 1'b1;
#34 CLEAR = 1'b0;
#200 CLEAR = 1'b1;
#50 CLEAR = 1'b0;
end
// Set up the clock to toggle every 10 time units
initial
begin
CLOCK = 1'b0;
forever #10 CLOCK = ~CLOCK;
end

// Finish the simulation at time 400


initial
begin
#400 $finish;
end

endmodule
120
The output of the simulation is shown below. Note that the clear signal resets the count to
zero.

0 Count Q = 0000 Clear= 1


34 Count Q = 0000 Clear= 0
40 Count Q = 0001 Clear= 0
60 Count Q = 0010 Clear= 0
80 Count Q = 0011 Clear= 0
100 Count Q = 0100 Clear= 0
120 Count Q = 0101 Clear= 0
140 Count Q = 0110 Clear= 0
160 Count Q = 0111 Clear= 0
180 Count Q = 1000 Clear= 0
200 Count Q = 1001 Clear= 0
220 Count Q = 1010 Clear= 0
234 Count Q = 0000 Clear= 1
284 Count Q = 0000 Clear= 0
300 Count Q = 0001 Clear= 0
320 Count Q = 0010 Clear= 0
340 Count Q = 0011 Clear= 0
360 Count Q = 0100 Clear= 0
380 Count Q = 0101 Clear= 0

121
6.6 Summary

• Continuous assignment is one of the main constructs used in dataflow modeling.


A continuous assignment is always active and the assignment expression is
evaluated as soon as one of the right-hand-side variables changes. The left-hand
side of a continuous assignment must be a net. Any logic function can be realized
with continuous assignments.

• 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.

• Assignment statements contain expressions, operators, and operands.

• 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.

• Dataflow description of a circuit is more concise than a gate-level description.


The 4-to-1 multiplexer and the 4-bit full adder discussed in the gate-level
modeling chapter can also be designed by use of dataflow statements. Two
dataflow implementations for both circuits were discussed. A 4-bit ripple counter
using negative edge-triggered D-flipflops was designed.

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:

D = x'.y'.z + x'.y.z' + x.y'.z' + x.y.z

B = x'.y + x'.z +y.z

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

2: A magnitude comparator checks if one number is greater than or equal to or less


than another number. A 4-bit magnitude comparator takes two 4-bit numbers, A
and B, as input. We write the bits in A and B as follows. The leftmost bit is the
most significant bit.

A = A(3) A(2) A(1) A(0)

B = B(3) B(2) B(1) B(0)

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.

x(i) = A(i).B(i) + A(i)'.B(i)'

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_gt_B = A(3).B(3)' + x(3).A(2).B(2)' + x(3).x(2).A(1).B(1)' +


x(3).x(2).x(1).A(0).B(0)'

A_lt_B = A(3)'.B(3) + x(3).A(2)'.B(2) + x(3).x(2).A(1)'.B(1) +


x(3).x(2).x(1).A(0)'.B(0)

A_eq_B = x(3).x(2).x(1).x(0)

Write the Verilog description of the module magnitude_comparator. Instantiate


the magnitude comparator inside the stimulus module and try out a few
combinations of A and B.

3: A synchronous counter can be designed by using master-slave JK flipflops.


Design a 4-bit synchronous counter. Circuit diagrams for the synchronous
counter and the JK flipflop are given below. The clear signal is active low. Data
gets latched on the positive edge of clock, and the output of the flipflop appears
on the negative edge of clock. Counting is disabled when count_enable signal is
low. Write the dataflow description for the synchronous counter. Write a
stimulus file that exercises clear and count_enable. Display the output count
Q[3:0].
Figure 6-5. Master-Slave JK-flipflop

124

You might also like