You are on page 1of 20

Chapter 11 Combinational Arithmetic Devices

Combinational logic circuits can also perform arithmetic functions. In chapter 8, for example, we
saw how logic circuits could be used to perform addition and subtraction, but logic circuits can
perform other functions as well. Again, many of these have proved to be useful design building
blocks, and many are available commercially as standard logic chips. In this chapter, we will see
how these devices are designed, write VHDL models for them, and list some standard logic chips
that implement them.
11.1 Magnitude Comparators
There are several combinational building blocks that perform arithmetic operations. Probably the
simplest of these is the magnitude comparator. A magnitude comparator takes two binary numbers
and reports whether or not they are the same. More sophisticated comparators also report which
of the two is greater in the event they are different. Some can be cascaded, which means multiple
comparators can be combined to compare numbers with more bits.
When comparators are cascaded, they take as input, the comparison results from less significant
bits. Then, if its two input signals are equal, these earlier comparison results are forwarded on to
the outputs.
Schematic Symbol
A comparator is drawn as a rectangle, with its inputs drawn on the left and its outputs on the right.
The comparison inputs from less significant bits are usually drawn halfway down, splitting the
numeric inputs above and below (Figure 11-1).

Figure 11-1 Schematic Symbol for 4-bit cascade comparator.
A3
A2
A1
A0
A=Bin A=Bout
B3
B2
B1
B0
Gate Implementation
The workhorse of the magnitude comparator is the exclusive-or gate (or the exclusive-nor gate)
shown in Figure 11-2.

(a) Exclusive OR (b) exclusive NOR
Function Table Function Table
A B Y A B Y
L L L L L H
L H H L H L
H L H H L L
H H L H H H
Figure 11-2 Exclusive OR and Exclusive NOR gates.
We have neglected exclusive-or until now because it can be derived from the other Boolean
operations we have studied. (Recall from Chapter 4 that AB = AB+AB.) The important thing to
note about the exclusive-or gate is that its complement (AB

) is true only when A and B are the


same. For this reason, the exclusive-nor gate is sometimes called an equivalence gate.
But if we wish to think of these as equivalence gates whose output is true when A=B, the bubble on
the output of the gate is exactly backwards; the output of the exclusive-or (which has no bubble)
would be active low and the output of the exclusive-nor (which has a bubble) would be active high.
Sadly, there is nothing we can do about it unless we are willing to define a new schematic symbol,
which we are not.
We compare two multi-bit signals by comparing each pair of corresponding bits independently, and
if all are equal, the multi-bit signals are equal. For a 4-bit comparator, the logic expression for
A=Bout is:
A=B
out
= (A
0
B
0
)(A
1
B
1
)(A
2
B
2
)(A
3
B
3
)(A=B
n
) (11.1)
The A=Bin term is the cascaded input that tells whether less significant bits are equal. Clearly if they
are not, we should not assert A=Bout. The implementation of this comparator in gates is
straightforward and is shown in Figure 11-3.
Y
A
B
Y
A
B

Figure 11-3 A 4-bit comparator with a cascade A=B input.
Now, suppose we wish to also generate an A>B output. Clearly, if A3 is 1 and B3 is 0 (i.e. A3B3), A is
greater than B. Then, if A3=B3 and A2 is 1 and B2 is 0 (i.e. (A
3

B
3

)A2B2), A is greater than B as well.


Continuing this pattern, we have:
A>B
out
= A
3
B
3

+(A
3
B
3
)A
2
B
2

+(A
3
B
3
)(A
2
B
2
)A
1
B
1

+
(A
3
B
3
)(A
2
B
2
)(A
1
B
1
)A
0
B
0

+(A
3
B
3
)(A
2
B
2
)(A
1
B
1
)(A
0
B
0
)(A>B
n
) (11.2)
This expression is not a complicated as it looks because the exclusive-nor terms already exist in
Figure 11-3. Modifying Figure 11-3 to include A>B is left as an exercise for the student.
Standard Logic Chips
The most popular comparator available in the 7400 series of standard logic chips is the 85. This is a
4-bit cascadable comparator with A>B, A<B and A=B inputs and outputs. The 682, and the 688 are
8-bit comparators available primarily in the LS and HC logic families. The 682 is a non-cascadable
comparator with A=B and A>B outputs. The 688 can be cascaded, but it has only the A=B output.
VHDL Model
The VHDL model for a comparator is so simple that it rarely justifies a separate entity. In VHDL,
any two signals of the same type can be compared using the equals operator (=) or the not-equals
operator (/ =). The result of that operation is of type Boolean, but we can use that in a conditional
statement to get any output we wish. Figure 11-4 shows a simple comparator that only generates
an A=B output and cannot be cascaded. (Library and entity declarations are omitted for brevity.)
architecture behavioral of comparitor1 is
begin
AeqB_H <=1 when A=B else 0; -- A and B are vectors of the same length.
end behavioral;
Figure 11-4 A comparator model using a conditional assignment statement.
A comparator that generates an A>B output is not much more difficult, that is if the package
ieee.std_logic_unsigned (or one of the other numeric packages) is used. IEEE.std_logic_unsigned
A3
B3
A2
B2
A1
B1
A0
B0
A=Bin
A=Bout
defines the relational operators <. >, <=and >=for signals of type std_logic_vector. To illustrate how
these operators can be used to model a comparator, consider Figure 11-5, the model of a
comparator with both an A=B and an A>B output.
architecture behavioral of comparitor2 is
begin
AeqB_H <=1 when A=B else 0; -- A and B are vectors of the same length.
AgtB_H <=1 when A>B else 0; -- Assumes A and B are unsigned binary numbers.
end behavioral;
Figure 11-5 A comparator that provides an A=B and A>B output.
The ease with which VHDL handles comparisons like A<B or A>B has a downside. It is easy to forget
that these comparisons require substantially more hardware to implement than A=B and AB. A
good engineer will favor the equality comparisons and use greater-than and less-than comparisons
only when necessary.
In VHDL, there is not much need for comparators that can be cascaded because it is easy to design
them to be the right size in the first place. We will therefore forgo any discussion of cascading in
VHDL.
11.2 Adders
The half adders and full adders we discussed in Chapter 8 are fundamental building blocks that are
used in all manner of arithmetic devices such as fast carry adders and multipliers. From Figure 8-6,
the logic equations for a full adder is:
=A

C
n
+A

B C
in

+A B C
n
+A B

C
in

, and (11.3)
C
out
=AB +AC
n
+BC
n
. (11.4)
It is worthwhile to note that an equivalent expression for S using exclusive-or is:
=A B C
n
(11.5)
Verification that Equation 11.3 is equivalent to 11.5 is left as an exercise. A half adder behaves the
same as a full adder when Cin is false, so, substituting Cin =0 into Equations 11.4 and 11.5 we have:
C
out
=AB, =A B. (11.6)
Schematic Symbol
The schematic symbol for a 1-bit half or full adder is a rectangle (often a square) with inputs and
outputs placed wherever it is convenient. (When these devices are drawn in larger circuits such as
array multipliers or fast-carry adders, circuit topology dictates the best place to make the
connections.) A multi-bit adder is drawn as a rectangle with inputs on the left and outputs on the
right. (Figure 11-6). Half adders and Full adders are sometimes denoted HA and FA respectively.

Figure 11-6 Schematic symbols for (a) 1-bit half adder, (b) 1-bit full adder and (c) 4-bit full adder.
Gate Implementation/ Fast Carry Adder
Gate implementations for the 1-bit half adder, the 1-bit full adder and the ripple carry adder were
addressed in Chapter 8, and they need not be repeated here. But there is another adder called the
fast carry adder that we should discuss. The ripple carry adder suffers from a large propagation
delay because the critical path from carry-in to carry-out carry passes through every single 1-bit
adder; if the number of bits is large, this delay can become substantial. The fast carry adder. On the
other hand, is able to predict the carry input to each adder simultaneously. To understand how this
works, we will need to introduce some new concepts.
We first define Gi, a Boolean variable that is true if adder i generates a carry, and Pi, a Boolean
variable that is true if adder i propagates a carry (i.e. if adder i generates a carry when it receives
one). These two variables can be determined based on the A and B inputs alone, without regard to
the carry input, Cin. Specifically,
0

=A

, and P

=A

. (11.7)
The fast carry adder doesnt generate a carry-out for each bit, but each 1-bit adder needs a carry-in
and we will denote that Ci. The carry-in for the least significant adder, C0, is simply Cin. Carry C1 is 1
if either (a) adder 0 generates a carry, or (b) Cin is 1 and adder 0 propagates it. Carry C2 is 1 if (a)
adder 1 generates a carry, (b) adder 0 generates a carry and adder 1 propagates it, or (c) Cin is 1 and
both adder 0 and adder 1 propagate it. This pattern is best illustrated using the logic expressions in
Equation 11.8, below:
C
0
=C
n

C
1
=0
0
+P
0
C
n

C
2
=0
1
+P
1
0
0
+P
1
P
0
C
n

C
3
=0
2
+P
2
0
1
+P
2
P
1
0
0
+P
2
P
1
P
0
C
n
(11.8)
If a carry-out is needed for the entire n-bit adder, it can be obtained by generating Cn, where n is
the number of bits in the adder. Once the carry-in is known for each 1-bit adder, each bit of the
sum can be obtained using Equation 11.9.

=A

(11.9)
A B
HA
Co
A B Ci
FA
Co
A3
A2
A1
A0
Ci
B3
B2
B1
B0
Co
3
2
1
0
(a) (b) (c)
Suppose we wish to build a fast carry adder using gates. What Equations 11.7-11.9 teach us is that
the fast carry adder computes the sum in three stages. First, it produces the generate and propagate
signals, Gi and Pi. From these, it produces carry-in signals, Ci, for each bit of the adder. Finally, it
generates the sum bits, i. These stages are illustrated in the 4-bit fast carry adder shown in Figure
11-7. For simplicity, positive logic is assumed throughout.
Note that Pi is implemented with Ai+Bi instead of Ai
Bi. This implementation is simpler and faster
than the exclusive-or, and it turns out to work just as well.

Figure 11-7 Four-bit fast carry adder.
The adder in Figure 11-7 cannot be combined with others to make an 8- or 16-bit adder. There are
two ways to modify it so that it can be. The first is to have the 4-bit adder produce Group Generate
(GG) and Group Propagate (GP) signals:
00 =0
3
+P
3
0
2
+P
3
P
2
0
1
+P
3
P
2
P
1
0
0
(11.10)
0P =P
3
P
2
P
1
P
0
(11.11)
These signals can then be used in a manner similar to Equation 11.8 to produce the carry inputs for
all the 4-bit adders in the system. This approach is very fast, especially for large adders, but it does
require extra carry lookahead logic circuitry. For more information about how to cascade fast carry
adders, please refer to [ROTH].
Another approach is to have the adder generate a carry-out signal:
C
out
=C
4
=0
3
+P
3
0
2
+P
3
P
2
0
1
+P
3
P
2
P
1
0
0
+P
3
P
2
P
1
P
0
C
n
(11.12)
This carry-out can be connected to the carry-in of the next 4-bit adder. It works very much like a
ripple carry adder, except it is faster because the carry propagates 4-bits at a time. This type of
adder is called a carry lookahead adder.
A3
B3
P3
G3
A2
B2
P2
G2
A1
B1
P1
G1
A0
B0
P0
G0
G1
P2
G2
G0
P1
P2
Cin
P0
P1
P2
C3
G1
G0
P1
Cin
P0
P1
C2
G0
Cin
P0
C1
3
A3
B3
C3
2
A2
B2
C2
1
A1
B1
C1
0
A0
B0
Cin
Standard Logic Chips
The 7400 series of standard logic chips defines two adders that are still in common use today. The
83 and the 283 are 4-bit full, carry look-ahead adders that differ only in the order the pins are
connected. The 83 and the 283 may be used either as a positive logic device or a negative logic
device. In other words, the inputs and outputs of the 83 and the 283 can all be active low or high
and the device will still perform addition.
The 7400 series does not define a combinational subtractor. If subtraction is necessary, as in
Section 8.4, it can be performed by the by inverting the 4-bit subtrahend (B) input, the carry-in and
the carry-out. Alternatively (or in addition) you may think of the 283 as a subtractor with active
low borrow-in, borrow-out, and subtrahend (Figure 11-8).

Figure 11-8 A 4-bit full subtractor using a 74HC283.
The port labels on the 283 are a little confusing because the bits are numbered 1-4 rather than 0-3,
the carry-in is labeled C0 (zero, not O) and the carry-out is labeled C4.
Note that if you cascade multiple subtractors, only two inverters are needed for borrow: one at the
beginning of the borrow-in/ borrow-out chain and one at the end. One last point that should be
made about subtraction is the 83 and 283 will also perform subtraction if the A and inputs are
inverted. This approach is less popular because it uses more inverters for positive logic, but if you
find yourself working on a design where either A or are active low, it is an option worth
considering.
VHDL Model
Half and full adders only need to be modeled individually when they are part of a larger structural
model. Models for the half and full adders are shown in Figure 11-9. These models introduce a new
A3
A3
Bin
A3
A3
B3
D0
Bout
B2
B1
B0
A4
A3
A2
A1
C0
B4
B3
B2
B1
C4

4
3
2
1
D1
D2
D3
U2
7
5
14
12
3
6
2
15
9
4
11
4
13
10
1
3
2 1
6 5
8 9
10 11
12 13
U1A
U1B
U1C
U1D
U1E
U1F
Designator Part
U1 74HC04
U2 74HC283

VHDL operator, xor, which simply evaluates the exclusive-or of its operands. Again, the library and
entity declarations are omitted for brevity.
architecture structural of half_adder is
begin
Sum <=A xor B;
Cout <=A and B;
end structural;
architecture structural of full_adder is
begin
Sum <=A xor B xor Cin;
Cout <=(A and B) or (A and Cin) or (B and Cin);
end structural;
(a) Half Adder (b) Full Adder
Figure 11-9 VHDL models for a half and full adder.
Multi-bit half adders (ones with no carry-in) can be modeled about as easily as comparators in
VHDL so long as an arithmetic package like IEEE.std_logic_unsigned is used. The package defines
addition (and subtraction) for std_logic_vector, so that two signals can be added together using
the + symbol. For example, an 8-bit half adder can be modeled in VHDL as shown in Figure 11-10.
Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
entity half_adder8 is
port ( A, B: in std_logic_vector(7 downto 0); -- input addends
Sum: out std_logic_vector(7 downto 0) ); -- output sum
end half_adder8;
architecture behavioral of half_adder8 is
begin
Sum <=A+B; -- Calculate A+B, ignoring carry
end behavioral;
Figure 11-10 A model for an 8-bit half adder with no carry-out.
This model can be synthesized, but there are so many ways to make an adder, you cant tell exactly
what you are going to get. You may get a ripple carry adder; you may get a fast carry adder, or you
may get something else entirely. Usually, youll be satisfied with the adder that the synthesizer
gives you, but if it is important to model the adder in a particular way, youll probably have to use a
structural model.
Suppose now that we wish to generate a carry-out from the adder in Figure 11-10. One way to do
this is to extend the length of one of the operands (e.g. signal A) by one bit. (Operands of different
sizes may be added, and the sum is the size of the longest operand.) We can lengthen one of the
operands by concatenating a leading 0 as shown in Figure 11-11.
architecture behavioral of half_adder8 is
signal T: std_logic_vector (8 downto 0); -- 9-bit temporary signal to receive sum
begin
T <=(0 & A)+B; -- Extend A to 9-bits so the carry-out wont be lost
Cout <=T(8); -- Calculate A+B, putting the sum in T(7 downto 0)
Sum <=T(7 downto 0); -- and the carry in T(8).
end behavioral;
Figure 11-11 A model for an 8-bit half adder with carry-out.
Note that a temporary 9-bit std_logic_vector signal had to be declared to receive the 9-bit sum.
Suppose now that we wish to include a carry input in the computation. Fortunately the package
std_logic_unsigned also defines addition for signals of type std_logic (e.g. carry-in), so all thats
needed is to include the carry in the addition (Figure 11-12).
architecture behavioral of full_adder8 is
signal T: std_logic_vector (8 downto 0); -- 9-bit temporary signal to receive sum
begin
T <=(0 & A)+B+Cin; -- Extend A to 9-bits so the carry-out wont be lost
Cout <=T(8); -- Calculate A+B, putting the sum in T(7 downto 0)
Sum <=T(7 downto 0); -- and the carry in T(8).
end behavioral;
Figure 11-12 A model for an 8-bit full adder with carry-in and carry-out.
Suppose we need to design an adder for signed 2s complement numbers that also generates an
overflow output. (Recall that the overflow condition occurs when the carry-out from the last two
1-bit adders are different.) Here, the library packages are of little value because they do not give us
access to the intermediate carries. Instead, we need to write a loop (as we did in Section 10.3) that
computes both the sum and the carries, then we will use the last 2 carries to generate the overflow.
(See Figure 11-13, the library declaration is omitted for brevity).
entity signed_adder8 is
port ( A, B: in std_logic_vector(7 downto 0); -- input addends
Overflow: out std_logic; -- carry output
Sum: out std_logic_vector(7 downto 0) ); -- output sum
end signed_adder8;
architecture behavioral of signed_add8 is
begin
process (A, B) -- wait for any signal to change
variable C: std_logic_vector (0 to 8);
begin
C(0) :=0; -- this could be the carry-in if we had one
for i in 0 to 7 loop -- loop once for each adder
Sum(i) <=A(i) xor B(i) xor C(i);
C(i+1) :=(A(i) and B(i)) or (A(i) and C(i)) or (B(i) and C(i));
end loop;
Overflow <=C(8) xor C(7); -- equals 1 if C(8) C(7)
end process;
end behavioral;
Figure 11-13 An 8-bit signed adder with overflow output.
There are a couple things we should say about Figure 11-13. The first is to point out that this loop
is synthesizable. It can be unrolled, even though the intermediate carries are variables instead of
signals. Second, the range of the carry vector, C, is declared to be (0 to 8) but could just as easily
have been (8 downto 0). Since we never use it as a data signal, either ordering is okay, and
(0 to 8) seems more natural in this case.
On a related subject, subtraction can also be modeled with VHDL. All that is needed is to change the
signs on B and Cin (and, of course, most of the signal names and comments) in Figures 11-10
through 11-12. Figure 11-13 can also be modified to perform signed subtraction by changing the
expression for C(i+1); of course, C would then be a vector of borrows instead of carries. This
modification is left as an exercise.
One final note on modeling addition and subtraction in VHDL, many engineers use the package
IEEE.numeric_std because it allows both signed and unsigned arithmetic in the same module.
Rather than defining arithmetic operations for std_logic_vector (as IEEE.std_logic_unsigned does), it
defines different types for signed and unsigned numbers, each with its own operators. The upshot
is that if you want to, say, compare two unsigned values, they must be converted to the unsigned
type first. Fortunately, the package also defines functions to perform the conversions (Table 11-1).
Table 11-1 Conversion Functions for IEEE.numeric_std
Convert From Convert To Conversion Function
std_logic_vector unsigned u <=unsigned(v)
std_logic_vector signed s <=signed(v)
unsigned integer i <=to_integer(u)
signed integer i <=to_integer(s)
integer unsigned u <=to_unsigned(i,bits)
integer signed s <=to_signed(i,bits)
unsigned std_logic_vector v <=std_logic_vector(u)
signed std_logic_vector v <=std_logic_vector(s)

The other pitfall to watch out for when using IEEE.numeric_std is that none of the arithmetic
operators work on std_logic. If you have a single bit (e.g. carry-in) that needs to be added or
subtracted, it is necessary to turn it into a vector first. This can be done using the same syntax we
used to initialize constant arrays in Section 10.2. To make a 1-bit vector out of Cin (which would be
needed in Figure 11-12), we simply write (0=>Cin). This expression defines a vector and initializes
entry 0 to Cin. The type of the vector is somewhat ambiguous; it can be used wherever signed,
unsigned or std_logic_vector signals are expected. (If you need it to be unambiguous, you may use a
qualified expression to force the vector to be whichever type you wish. This is done by preceding the
vector with a type name and a single quote, e.g. unsigned'(0=>Cin).)
Figure 11-14 illustrates how to model the 8-bit full adder in Figure 11-12 using the package
IEEE.numeric_std. Note that the type of the intermediate signal, T, was changed to unsigned. This
change was simply for convenience. Otherwise we would have had to convert A and B to unsigned,
add them, and convert the sum back to std_logic_vector, all before assigning the result to T.
Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity full_adder8 is
port ( A, B: in std_logic_vector(7 downto 0); -- input addends
Cin: in std_logic; -- carry input
Cout: out std_logic; -- carry output
Sum: out std_logic_vector(7 downto 0) ); -- output sum
end full _adder8;
architecture behavioral of full_adder8 is
signal T: unsigned (8 downto 0); -- 9-bit temporary signal to receive sum
begin
T <=unsigned(0 & A) +unsigned(B) +(0=>Cin);
Sum <=std_logic_vector(T(7 downto 0)); -- The sum is in T(7 downto 0)
Cout <=T(8); -- and the carry is in T(8).
end behavioral;
Figure 11-14 A model for an 8-bit full adder using IEEE.numeric_std.

11.3 Shifters
In binary, multiplication and division by 2
k
can be accomplished by shifting a value k bits left or k
bits right respectively. The shifter provides a means to do just that. It takes an input value and a
shift count and produces a result that is the original value shifted (left or right) by the shift count.
Usually, if the shift count is n-bits, the number of bits in the input and output signals is 2
n
. Shifters
are characterized by the number of bits they shift, so for example, a 32-bit shifter has 32 input lines,
32 output lines and a 5-bit shift count input. (Recall 2
5
=32).
Shifters come in several flavors. A zero-backfill shifter fills in the vacated bits with zeros. A sign
extend shifter only shifts right and fills the vacated bits with copies of the sign bit (this results in
signed, 2s complement division). An end-around or barrel shifter fills the vacated bits with the bits
shifted out of the other end. For simplicity, this text will only address zero-backfill shifters. Once
they are understood, other shifters are not difficult to grasp.
Schematic Symbol
While there is no universally accepted way to draw a shifter, it can be drawn as a rectangle, with the
multi-bit input signal drawn on the left and the (same sized) multi-bit output signal drawn on the
right. The shift count may be drawn on the top, bottom, or on the left next to the other input bus
(Figure 11-15).

Figure 11-15 Schematic Symbols for a 2
n
-bit shifter.

Implementation
A shifter can be implemented with n stages of 2-to-1 multiplexers. If we use S to denote the shift
count, then stage connected to S0 shifts the value one bit if S0 is one, or leaves it alone if S0 is zero.
Likewise the stage connected to S1 either shifts the value 2-bits or 0-bits depending on S1. It doesnt
matter which stage is first or, for that matter, what order the stages are in. As an example, Figure
11-16 shows a 4-bit zero-backfill right-shifter. Depending on the value of S, this shifter can divide a
4-bit binary number by 1, 2, 4, or 8.)
Notice that the first stage optionally shifts right (upward) 1 bit. Stage 2 optionally shifts right 2 bits.
If there were a third stage, it would optionally shift 4 bits, depending on S. The order of the stages
doesnt matter because the shifts can occur in any order with the same result.
It is also possible to use larger multiplexers (such as 4-to-1 line MUXs) to implement a shifter. This
reduces the number of stages and therefore the propagation delay, but if the multiplexers get too
large, there is an adverse effect on the fan-out. As an exercise, the student is encouraged to
implement the shifter in Figure 11-16 using only 4-to-1 line multiplexers.
I
2
n

n
2
n

O
S
I
2
n

n
2
n

O
S

Figure 11-16 Implementation of a 4-bit, zero backfill shifter.
Standard Logic Chips
The only shifter defined in the 7400 series of standard logic chips is the 350, but this chip is
practically unavailable. If it is necessary to build a shifter using standard logic chips, the best
alternative is to use standard multiplexers (151s, 153s and 157s) configured similar to those in
Figure 11-16.
VHDL Model
Technically, VHDL has operators that model the shift and rotate functions performed by a shifter,
but in practice, designers rarely use them. The reason is that VHDL provides enough flexibility in
the way it defines index ranges that a shift can be performed in a simple assignment. (That, and
those operators are not defined for std_logic_vector.) The general approach is to concatenate some
of the bits from the input with the backfill bits to form the correct output. As an example, the VHDL
fragment in Figure 11-17 shows how to model the shifter in Figure 11-16. Again, the library and
entity declarations are omitted for brevity.
architecture behavioral of shifter4 is
signal k: integer range 0 to 3;
begin
k <=conv_integer(S); -- get number of bits to shift, as an integer
O <=(k downto 1=>'0') & I(3 downto k); -- shift I right by k, pad left side with zeros
end behavioral;
Figure 11-17 A model for of a 4-bit, zero backfill shifter.
Y
I0
I1 S
S0
Y
I0
I1 S
Y
I0
I1 S
Y
I0
I1 S
I0
I1
I2
I3
Y
I0
I1 S
S1
Y
I0
I1 S
Y
I0
I1 S
Y
I0
I1 S
O0
O1
O2
O3
The first thing we do in Figure 11-17 is represent S, the number of bits to shift, as an integer called
k. Then, we use it to create two variable-length vectors that are concatenated to form the output, O.
Even though the vectors have variable lengths, their sum is always 4 (the size of the shifter in bits).
In this case, we are shifting right by k, so the first vector is a pad of k zeros and the second consists
of the leftmost 4-k bits of the input. The student is encouraged to take a moment to verify that this
output is the same as the output from Figure 11-16.
Lets list some other statements that we could have used in Figure 11-17 to model different shifters.
These statements are shown in Figure 11-18, along with comments that explain the direction and
type of the shifter.
-- shift right with sign extension, back fill with the sign bit, I(3)
O <=(k downto 1 =>I(3)) & I(3 downto k);
-- shift left, back fill with zeros
O <=I(3-k downto 0) & (k downto 1 =>0);
-- shift right, end-around or barrel shift (also called rotate right)
O <=I(k-1 downto 0) & I(3 downto k);
-- shift left, end-around or barrel shift (also called rotate left)
O <=I(3-k downto 0) & I(3 downto 4-k);
Figure 11-18 Models for various 4-bit shifters.
Of course, for shifters with more than four bits, it is a trivial matter to change the constants (3s and
4s) in Figures 11-17 and 11-18 to reflect the larger number of bits.
11.4 Arithmetic Logic Units
An arithmetic logic unit (or ALU) is a device that performs an arbitrary operation on its input
signal(s) and outputs the result. The ALU has two main data inputs (that, like the adder are
traditionally labeled A and B). These serve as operands for an arithmetic or logical operation
performed by the ALU based on the function code input. The function set of the ALU is a list of all
valid function codes and the operations they perform. For example, one function code may cause
the ALU to compute A-B while another may cause the ALU to compute AB. Each ALU design has
its own function set, but some common ALU functions are given in Figure 11-19.
Function Result Function Result
Add A+B AND NOT A and B (bitwise)
Add w/ carry A+B+Cin OR A or B (bitwise)
Subtract A-B XOR A B (bitwise)
Subtract w/ borrow A-B-Cin Shift Left A, Shifted left by B bits
Negate -A Shift Right A, Shifted right by B bits
Complement A Rotate Left A, Rotated left by B bits
AND A and B (bitwise) Rotate Right A, Rotated right by B bits
Figure 11-19 Common ALU functions.
An ALU is characterized both by its function set and its word size (the number of bits in its inputs
and result). For example, an ALU with 16 bit word size is referred to as a 16-bit ALU. Note that
the ALU may also have some 1-bit ancillary inputs and outputs such as carry-in and carry-out.
These inputs and outputs may be used for some functions but not others, depending on the function
set.
Schematic Symbol
Like the multiplexer, the ALU has its own schematic symbol, which is a trapezoid with a notch on
the long base. The inputs are drawn coming into the large base (with A on one side of the notch and
B on the other). The output is drawn coming out of the smaller base. The function code connects to
one side. Ancillary signals, if they exist, are placed on one of the bases, depending on whether they
are inputs or outputs (Figure 11-20).

Figure 11-20 Schematic Symbol for an n-bit ALU with carry-in and carry-out.
One common ancillary signal not shown in Figure 11-20 that should be mentioned is the zero signal,
which is true if the result of the ALU operation is zero.
Implementation
An ALU is too large a device to break down into gates (at least all at once), so we will take a lesson
from our design steps and start at a higher level of abstraction (i.e. considering objects in terms of
their characteristics rather than concrete realities). The simplest way to design an ALU (and the
only one we will cover in this text) is to simultaneously compute every possible result and use a
multiplexer to select between them (based on the function code). Figure 11-21 illustrates such an
ALU that is 8-bits wide and has four function codes: add, subtract, shift left and shift right.
R
n
F
A
B
Cin
n
n
Cout
m

Figure 11-21 Eight-bit ALU with 4 functions.
Note that each signal shown in Figure 11-21 is a bus (multi-bit signal). Some have 8 bits; some have
fewer. Busses are usually drawn with thicker lines and often have markings to indicate the number
of bits (lines or wires) that comprise them. When data are placed on the A and B inputs, all four
functional units (the adder, subtractor, left shifter and right shifter) start computing their result,
but only the one selected by the function code, F, will be output to R. (Note, the S inputs on the
shifters have only 3 bits, so only 3 bits of B, probably the least significant, are connected to it.)
We can easily derive the function set for this ALU from the schematic diagram in Figure 11-21.
When F is 002, the sum A+B is output. When F is 012, the difference A-B is output. If we continue this
process, we get the function set shown in Figure 11-22.
Function Code (F) ALU Output
00 A+B, Carry is ignored
01 A-B, Borrow is ignored
10 A shifted left by B2..B0
11 A shifted right by B2..B0
Figure 11-22 Function Set for the ALU in Figure 11-21.

Y
I0
I1
I2
I3
S
A

B

ADDER
A

B

SUBTRACTOR
I

S
O
LEFT SHIFT
I

S
O
RIGHT SHIFT
8
8
3
8
8
8
8
8
A
B
F
R
2
Standard Logic Chips
There are several ALUs defined in the 7400 series, but only the 74LS181 is still available today.
This 4-bit ALU uses a 5-bit function code and can perform 32 different operations, including AND,
OR, NOT, XOR, add with carry, subtract with borrow and shift left (by one bit). The ALU can be
cascaded up to 64 bits using a 74LS182 to perform fast carry addition and subtraction, or it can be
cascaded to any number of bits using the look-ahead/ ripple carry approach (similar to the 183
adder discussed in Section 11.2).
VHDL Model
Modeling the behavior of an ALU using VHDL is very similar to modeling a multiplexer, except that
arithmetic or logical expressions take the place of the inputs. To illustrate, we will model the ALU
in Figure 11-21 using VHDL (Figure 11-23).
Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
entity ALU8 is
port ( A, B: in std_logic_vector(7 downto 0); -- input operands
F: in std_logic_vector(1 downto 0); -- input function
R: out std_logic_vector(7 downto 0) ); -- output result
end ALU8;
architecture behavioral of ALU8 is
signal k: integer range 0 to 7;
signal R0, R1, R2, R3: std_logic_vector(7 downto 0);
begin
k <=conv_integer(B(2 downto 0)); -- number of bits for shift operators
-- compute the result of each function unit
R0 <=A+B; -- add
R1 <=A-B; -- subtract
R2 <=A(7-k downto 0) & (k downto 1 =>0); -- shift left
R3 <= (k downto 1=>'0') & A(7 downto k); -- shift right

R <=R0 when F =00 else -- add
R1 when F =01 else -- subtract
R2 when F =10 else -- shift left
R3 when F =11 else -- shift right
--------; -- handle meta-values
end behavioral;
Figure 11-23 A model for an 8-bit ALU in Figure 10-61.
There should be nothing in Figure 11-23 that is new to us, however you may wonder why we
declared intermediate signals R0-R3 instead of simply writing those expressions in the conditional
assignment statement. The reason is practical, not technical. When we simulate a module, it is
possible to look at any signal that has a name. When a circuit does not perform the way we expect,
it is handy to have intermediate signals to look at so we can isolate the cause of the problem. The
intermediate signals are also more convenient when functional units are replaced with structural
models.
Example 11.1. Write a VHDL behavioral model for a 16-bit ALU. The ALU has two 16-bit inputs (A
and B), a 16-bit result (R), and a 3-bit function code (F), all active high. It also has an active-high
carry input (Cin) and output (Cout) as well as an active-low zero output (Z_L). The ALU should
support the following function set:
Function code (F) Function ALU Output (R) Carry Output (Cout)
000 Add A+B 1 if A+B generates carry, else 0
001 Subtract A-B 1 if A-B generates a borrow, else 0
010 Add w/ carry A+B+Cin 1 if A+B+Cin generates carry, else 0
011 Subtract w/ borrow A-B-Cin 1 if A-B-Cin generates borrow, else 0
100 Bitwise AND A and B Cin
101 Bitwise OR A or B Cin
110 Bitwise XOR AB Cin
111 Complement A Cin
Solution: We will use Figure 10-63 as a template, but this ALU has two additional outputs: carry-out
(Cout) and zero (Z_L). The carry-out is a little tricky. Recall that in VHDL we get the carry-out by
enlarging the result one bit to accommodate it. We will want to do this for the ALU as well, so we
will have a 17-bit internal result that can be split into R and Cout for output. Generation of the zero
signal (Z_L) is straightforward, given that we have access to the internal ALU result. The VHDL
module below should do the trick:
Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
entity ALU16 is
port ( A, B: in std_logic_vector (15 downto 0); -- input binary operands
F: in std_logic_vector (2 downto 0); -- input function code
Cin: in std_logic; -- carry input
Z_L, Cout: out std_logic; -- ancillary results
R: out std_logic_vector(15 downto 0) -- main result of the ALU
);
end ALU16;
architecture behavioral of ALU16 is
signal Result, R0, R1, R2, R3, R4, R5, R6, R7: std_logic_vector(16 downto 0);
begin
R0 <=(0 & A)+B; -- add without carry
R1 <=(0 & A)-B; -- subtract without borrow
R2 <=(0 & A)+B+Cin; -- add with carry
R3 <=(0 & A)-B-Cin; -- subtract with borrow
R4 <=Cin & (A and B); -- bitwise and, retaining carry-out =carry-in
R5 <=Cin & (A or B); -- bitwise or, retaining carry-out =carry-in
R6 <=Cin & (A xor B); -- bitwise or, retaining carry-out =carry-in
R7 <=Cin & (not A); -- complement input A

Result <=R0 when F=000 else -- add without carry
R1 when F=001 else -- subtract without borrow
R2 when F=010 else -- add with carry
R3 when F=011 else -- subtract with borrow
R4 when F=100 else -- bitwise and, retaining carry-out =carry-in
R5 when F=101 else -- bitwise or, retaining carry-out =carry-in
R6 when F=110 else -- bitwise or, retaining carry-out =carry-in
R7 when F=111 else -- complement input A
(others =>-); -- handles meta-values
Cout <=Result(16); -- strip out carry flag
R <=Result(15 downto 0); -- strip out the ALU result
Z_L <=0 when Result(15 downto 0) =0 else 1;
end Behavioral;
It is worthwhile at this point to stop and consider the size and complexity of the ALU in Example
11-1. This circuit requires well over 500 gates to implement, yet we could have drawn its schematic
on one page using abstract building blocks like those in Figure 11-21, and we did write its VHDL
model in about 35 lines of text. For complicated designs, we need the ability to use abstractions like
these to think of objects in terms of their characteristics rather than their gates. Without them, we
would never be able to design the complicated circuits that give life to computers, portable phones
or tablets, and that would mean that we would never get to play angry birds.
Exercises
1. Modify Figure 11-3 to include an output for A>B.
2. Verify Equation 11.3 and Equation 11.5 are equivalent.
3. Modify the VHDL code in Figure 11-13 so that it performs a signed subtraction.
4. Write a test fixture and simulate your VHDL code from problem 3, above.
5. Implement the 4-bit, zero backfill shifter in Figure 11-18 using only 4-to-1 line multiplexers.
6. Modify your shifter in problem 5 to get a 4-bit barrel shifter. Do not add or remove any
components (just rewire it).