Register Transfer Level Hardware Description with Verilog:Combinational Circuits
Combinational Circuits
A combinational circuit can be represented by its gates, its Boolean functionality, description of its behavior, or a mix of all these forms. At the gate level, interconnection of its gates are shown; at the functional level, Boolean expressions representing its outputs are written; and at the behavioral level, a software-like procedural description represents its functionality. This section shows these levels of abstraction for describing combinational circuits.
Gate-Level Combinational Circuits
Verilog provides primitive gates and transistors. Some of the more commonly used Verilog primitives and their logical representations are shown in Figure 88.9. In this figure, w is used for gate outputs, i for inputs, and c for control inputs.
Basic logic gates are and, nand, or, nor, xor, xnor. These gates can be used with one output and any number of inputs. The other two structures shown are not and buf. These gates can be used with one input and any number of outputs.
Another group of primitives shown in this figure are three-state (tri-state is also used to refer to these structures) gates. Gates shown have w for their outputs, i for data inputs, and c for their control inputs. These primitives are bufif1, notif1, bufif0, and notif0. When control c for such gates is active (1 for first
and third, and 0 for the others), the data input, i, or its complement appears on the output of the gate. When control input of a gate is not active, its output becomes high impedance, or Z.
Also shown in Figure 88.9 are NMOS, PMOS, and CMOS structures. These are switches that are used in switch-level description of gates, complex gates, and buses. The nmos (pmos) primitive is a simple switch with an active high (low) control input. The cmos switch is usually used with two complementary control inputs. These switches behave like the three-state gates. They are different in their output voltage levels and drive strengths. These parameters are modeled by wire strengths and are not discussed in this book.
Majority Example
We use the majority circuit of Figure 88.10 to illustrate how primitive gates are used in a design. The description shown in Figure 88.11 corresponds to this circuit. The module description has inputs and outputs according to the schematic of Figure 88.10.
Line 1 of the code shown is the timescale directive. This defines all time units in the description and their precision. For our example, 1ns/100ps means that all numbers in the code that represent a time value are in nanoseconds and they can have up to one fractional digit (100 ps).
The statement that begins in Line 6 and ends in Line 9 instantiates three and primitives. The construct that follows the primitive name specifies rise and fall delays for the instantiated primitive (tplh = 2, tphl = 4). This part is optional and if eliminated, 0 values are assumed for rise and fall delays. Line 7 shows inputs and outputs of one of the three instances of the and primitive. The output is im1 and inputs are module input ports a and b. The port list in Line 7 must be followed by a comma if other instances of the same primitive are to follow, otherwise a semicolon should be used, like the end of Line 9. Lines 8 and 9 specify input and output ports of the other two instances of the and primitive. Line 10 is for instantiation of the or primitive at the output of the majority gate. The output of this gate is y that comes first in the port list, and is followed by inputs of the gate. In this example, intermediate signals for interconnection of gates are im1, im2, and im3. Scalar interconnecting wires need not be explicitly declared in Verilog.
The three and instances could be written as three separate statements, such as instantiation of the or primitive. If we were to specify different delay values for the three instances of the and primitive, we had to have three separate primitive instantiation statements.
Three-state gates are instantiated in the same way as the regular logic gates. Outputs of three-state gates can be wired to form wired-and, wired-or, or wiring logic. For various wiring functions, Verilog uses wire, wand, wor, tri, tri0, and tri1 net types. When two wires (nets) are connected, the resulting value depends on the two net values as well as the type of the interconnecting net. Figure 88.12 shows net values for net types wire, wand, and wor.
The table shown in Figure 88.12 is called a net resolution table. Several examples of net resolutions are shown in Figure 88.13. The tri net type mentioned above is the same as the wire type. tri0 and tri1 types resolve to 0 and 1, respectively, when driven by all Z values.
Multiplexer Example
Figure 88.14 shows a 2-to-1 multiplexer using three-state gates. The Verilog code of this multiplexer is shown in Figure 88.15.
Lines 6 and 7 in Figure 88.15 instantiate two three-state gates. Their output is y, and since it is driven by both gates a wired-net is formed. Since y is not declared, its net type defaults to wire. When s is 1, bufif 1 conducts and the value of b propagates to its output. At the same time, because s is 1,
bufif 0 does not conduct and its output becomes Z. Resolution of these values driving net y is determined by the wire net resolution as shown in Figure 88.12.
CMOS NAND Example
As another example of instantiation of primitives, consider the two-input CMOS NAND gate shown in Figure 88.16.
The Verilog code of Figure 88.17 describes this CMOS NAND gate. Logically, NMOS transistors in a CMOS structure push 0 into the output of the gate. Therefore, in the Verilog code of the CMOS NAND, input to output direction of NMOS transistors are from Gnd towards w. Likewise, PMOS transistors push a 1 value into w, and therefore, their inputs are considered the Vdd node and their outputs are connected to the w node. The im1 signal is an intermediate net and is explicitly declared.
In the Verilog code of CMOS NAND gate, primitive gate instance names are used. This naming (T1, T2, T3, T4) is optional for primitives and mandatory when modules are instantiated. Examples of module instantiations are shown in the next section.
Descriptions by Use of Equations
At a higher level than gates and transistors, a combinational circuit may be described by use of Boolean, logical, and arithmetic expressions. For this purpose the Verilog concurrent assign statement is used. Figure 88.18 shows Verilog operators that can be used with assign statements.
As our first example for using an assign statement consider the description of an XOR gate as shown in Figure 88.19. The assign statement uses y on the left-hand side and equates it to Exclusive-OR of a, b, and c inputs.
Effectively, this assign statement is like driving y with the output of a three-input xor primitive gate. The difference is that, the use of an assign statement gives us more flexibility and allows the use of more complex functions than what is available as primitive gates. Instead of being limited to the gates shown in Figure 88.9, we can write our own expressions using operators of Figure 88.18.
Full-Adder Example
Figure 88.20 shows another example of using assign statements. This code corresponds to a full-adder circuit. The s output is the XOR result of a, b, and ci inputs, and the co output is an AND–OR expression involving these inputs.
A delay value of 10 ns is used for the s output and 8 ns for the co output. As with the gate outputs, rise and fall delay values can be specified for a net that is used on the left-hand side of an assign statement. This construct allows the use of two delay values. If only one value is specified, it applies to both rise and fall transitions.
Another property of assign statements that also corresponds to gate instantiations is their concurrency. The statements in the Verilog module of Figure 88.20 are concurrent. This means that the order in which they appear in this module is not important. These statements are sensitive to events on their right-hand
sides. When a change of value occurs on any of the right-hand side net or variables, the statement is evaluated and the resulting value is scheduled for the left-hand side net.
Multiplexer Example
Figure 88.21 shows a 2-to-1 multiplexer using a conditional operator. The expression shown reads as follows: if s is 1, then y is i1 else it becomes i0. The right-hand side of the assign statement used in this example has a condition expression that uses the (? :) conditional operator. This operator is like if-then- else. In reading expressions that involve a conditional operator, ? and : take places of then and else, respectively. The if-condition appears to the left of ?.
Decoder Example
Figure 88.22 shows another example using the condition operator. In this example, a nesting of several ?:
operators are used to describe a decoder.
The decoder description also uses the concatenation operator { } to form vectors from its scalar inputs and outputs. The decoder has four outputs, d3, d2, d1, and d0 and two inputs a and b. Input values 00, 01, 10, and 11 produce 0001, 0010, 0100, and 1000 outputs. To be able to compare a and b with their possible values, a 2-bit vector is formed by concatenating a and b. The {a, b} vector is then compared with the four possible values it can take using a nesting of ?: operators.
Similarly, to be able to place vector values on the outputs, the four outputs are concatenated using the { } operator and used on the left-hand side of the assign statement shown in Figure 88.22.
This example also shows the use of sized numbers. Constants for the inputs and outputs have the general format of n`bm. In this format, n is the number of bits, b the base specification, and m the number in base b. For calculation of the corresponding constant, number m in base b is translated to n bit binary. For example, 4`hA becomes 1010 in binary.
Adder Example
For another example using assign statements, consider an 8-bit adder circuit with a carry-in and a carry- out output. The Verilog code of this adder, shown in Figure 88.23, uses an assign statement to set concatenation of co on the left-hand side of s to the sum of a, b and ci. This sum results in nine bits with the left- most bit being the resulting carry. The sum is captured in the 9-bit left-hand side of the assign statement in {co, s}.
So far in this section we have shown the use of operators of Figure 88.18 in assign statements. A Verilog description may contain any number of assign statements and can use any mix of the operators discussed. The next example shows multiple assign statements.
ALU Example
As our final example of assign statements, consider an ALU that performs add and subtract operations and has two flag outputs gt and zero. The gt output becomes 1 when input a is greater than input b, and the zero output becomes 1 when the result of the operation performed by the ALU is 0.
Figure 88.24 shows the Verilog code of this ALU. Used in this description are arithmetic, concatenation, condition, compare, and relational operations.
Descriptions with Procedural Statements
At a higher level of abstraction, Verilog provides constructs for procedural description of hardware instead of describing hardware with gates and expressions. Unlike gate instantiations and assign statements which correspond to concurrent substructures of a hardware component, procedural state- ments describe the hardware by its behavior. Also, unlike concurrent statements that appear directly in a module body, procedural statements must be enclosed in procedural blocks before they can be put inside a module.
The main procedural block in Verilog is the always block. This is considered a concurrent statement that runs concurrent with all other statements in a module. Within this statement, procedural statements like if-else and case statements are used and are executed sequentially. If there are more than one procedural statement inside a procedural block, they must be bracketed by begin and end keywords.
Unlike assignments in concurrent bodies that model driving logic for left-hand side wires, assignments in procedural blocks are assignments of values to variables that hold their assigned values until a different value is assigned to them. A variable used on the left-hand side of a procedural assignment must be declared as reg. An event-control statement is considered a procedural statement, and is used inside an always block. This statement begins with an at-sign, and in its simplest form, includes a list of variables in the set of parenthesis
that follow the at-sign, e.g., @ (v1 or v2 …).
When the flow of the program execution within an always block reaches an event-control statement, the execution halts (suspends) until an event occurs on one of the variables in the enclosed list of variables. If an event-control statement appears at the beginning of an always block, the variable list it contains is referred to as the sensitivity list of the always block. For combinational circuit modeling all variables that are read inside a procedural block must appear on its sensitivity list.
Examples that follow show various ways combinational component may be modeled by procedural blocks.
Majority Example
Figure 88.25 shows a majority circuit described by the use of an always block. In the declarative part of the module shown, the y output is declared as reg since this variable is to be assigned a value inside a procedural block.
The always block describing the behavior of this circuit uses an event-control statement that encloses a list of variables that is considered as the sensitivity list of the always block. The always block is said to be sensitive to a, b, and c variables. When an event occurs on any of these variables, the flow into the always block begins and as a result, the result of the Boolean expression shown will be assigned to variable y. This variable holds its value until the next time an event occurs on a, b, or c inputs.
In this example, since the begin and end bracketing only includes one statement, its use is not necessary. Furthermore, the syntax of Verilog allows elimination of semicolon after an event-control statement. This effectively collapses the event control and the statement that follows it into one statement.
Majority Example with Delay
The Verilog code shown in Figure 88.26 is a majority circuit with a 5 ns delay. Following the always keyword, the statements in this procedural block are an event-control, a delay-control, and a procedural assignment. The delay-control statement begins with a sharp-sign and is followed by a delay value. This statement causes the flow into this procedural block to be suspended for 5 ns. This means that after an event on one of the circuit inputs, evaluation and assignment of the output value to y takes place after 5 ns. Note in the description of Figure 88.26 that begin and end bracketing is not used. As with the event- control statement, a delay-control statement can collapse into its next statement by removing their separating semicolon. The event-control, delay-control, and assignment to y become a single procedural statement in the always block of maj3 code.
Full-Adder Example
Another example of using procedural assignments in a procedural block is shown in Figure 88.27. This example describes a full-adder with sum and carry-out outputs.
The always block shown is sensitive to a, b, and ci inputs. This means that when an event occurs on any of these inputs, the always block wakes up and executes all its statements in the order that they appear. Since assignments to s and co outputs are procedural, both these outputs are declared as reg.
The delay mechanism used in the full-adder of Figure 88.27 is called an intrastatement delay that is different than that of the majority circuit of Figure 88.26.
In the majority circuit, the delay simply delays execution of its next statement. However, the intra- statement delay of Figure 88.27 only delays the assignment of the calculated value of the right-hand side to the left-hand side variable. This means that in Figure 88.27, as soon as an event occurs on an input, the expression a^b^c is evaluated. But, the assignment of the evaluated value to s and proceeding to the next statement takes 5 ns.
Because assignment to co follows that to s, the timing of the former depends on that of the latter, and evaluation of the right-hand side of co begins 5 ns after an input change. Therefore, co receives its value 8 ns after an input change occurs. To remove this timing dependency and be able to define the timing of each statement independent of its previous one, a different kind of assignment must be used.
Assignments in Figure 88.27 are of the blocking type. Such statements block the flow of the program until they are completed. A different assignment is of the nonblocking type. A different version of the full-adder that uses this construct is shown in Figure 88.28. This assignment schedules its right-hand side value into its left-hand side to take place after the specified delay. Program flow continues into the next statement while propagation of values into the first left-hand side is still going on.
In the example of Figure 88.28, evaluation of the right-hand side of s is done immediately after an input changes. Evaluation of the right-hand side of co occurs 8 ns after that. To make s and co delays match those of Figure 88.27, an 8 ns delay is used for assignment to co.
Since our focus is on synthesizable coding and gate delay timing issues are not of importance, we will mostly use blocking assignments for combinational circuit descriptions.
Procedural Multiplexer Example
For another example of a procedural block, consider the 2-to-1 multiplexer of Figure 88.29. This example uses an if-else construct to set y to i0 or i1 depending on the value of s.
As in the previous examples, all circuit variables that participate in determining the value of y appear on the sensitivity list of the always block. Also since y appears on the left-hand side of a procedural assignment, it is declared as reg.
The if-else statement shown in Figure 88.29 has a condition part that uses an equality operator. If the condition is false (or equal to 0), the block of statements that follow it will be taken, otherwise block of statements after the else are taken. In both cases, the block of statements must be bracketed by begin and end keywords if there is more than one statement in a block.
Procedural ALU Example
The if-else statement, used in the previous example, is easy to use, descriptive, and expandable. However, when many choices exist, a case-statement which is more structured may be a better choice. The ALU description of Figure 88.30 uses a case statement to describe an ALU with add, subtract, AND, and XOR functions.
The ALU has a and b data inputs and a 2-bit f input that selects its function. The Verilog code shown in Figure 88.30 uses a, b, and f on its sensitivity list. The case-statement shown in the always block uses f to select one of the case alternatives. The last alternative is the default alternative, which is taken when f does not match any of the alternatives that appear before it. This is necessary to make sure that unspecified input values (here, those that contain X and/or Z) cause the assignment of the default value to the output and not leave it unspecified.
Combinational Rules
Completion of case alternatives or if-else conditions is an important issue in combinational circuit coding. In an always block, if there are conditions under which the output of a combinational circuit is not assigned a value, because of the property of reg variables the output retains its old value. The retaining of old value infers a latch on the output. Although, in some designs this latching is intentional, obviously it is unwanted when describing combinational circuits. With this, we have set two rules for coding combinational circuits with always blocks.
1. List all inputs of the combinational circuit in the sensitivity list of the always block describing it.
2. Make sure all combinational circuit outputs receive some value regardless of how the program flows in the conditions of if-else and/or case statements. If there are too many conditions to check, set all outputs to their inactive values at the beginning of the always block.
Busing
Bus structures can be implemented by use of multiplexers or three-state logic. In Verilog, various methods of describing combinational circuits can be used for the description of a bus.
Figure 88.31 shows Verilog coding of busout that is a three-state bus and has three sources, busin1, busin2, and busin3. Sources of busout are put on this bus by active high-enabling control signals, en1, en2, and en3. Using the value of an enabling signal, a condition statement either selects a bus driver or a 4-bit Z value to drive the busout output.
Verilog allows multiple concurrent drivers for nets. However, a variable declared as a reg and used on a left-hand side in a procedural block (always block), can only be driven by one source. This makes the use of nets more appropriate for representing buses.
Comments
Post a Comment