
Discrete math with computers_3
.pdf
368CHAPTER 13. DISCRETE MATHEMATICS IN CIRCUIT DESIGN
x y
c’ |
Add |
c |
|
4 |
|||
|
|
||
|
s |
|
Figure 13.12: 4-Bit Ripple Adder Black Box
|
x 0 y0 |
|
x1 y1 |
|
x 2 y2 |
|
x 3 y3 |
|
|
|
full |
|
full |
|
full |
|
full |
c |
|
c0 |
Add |
c1 |
Add |
c2 |
Add |
c3 |
Add |
||
|
|||||||||
|
s0 |
|
s1 |
|
s2 |
|
s3 |
|
Figure 13.13: 4-Bit Ripple Carry Adder
where (c0,s0) = fullAdd (x0,y0) c1 (c1,s1) = fullAdd (x1,y1) c2 (c2,s2) = fullAdd (x2,y2) c3 (c3,s3) = fullAdd (x3,y3) c
To use the adder, we must convert the input numbers into 4-bit binary representations. For example, here is the addition of 3 + 8.
Example: addition of |
3 |
+ 8 |
||||
3 |
+ |
8 |
|
|
|
|
= |
|
0011 |
( |
2+1 |
= |
3) |
|
+ |
1000 |
( |
8 |
= |
8) |
= |
|
1011 |
(8+2+1 |
= 11) |
Calculate this by evaluating
add4 False [(False,True),(False,False), (True,False),(True,False)]
The expected result is
(False, [True,False,True,True])
Exercise 7. Use Haskell to evaluate the example above, and check that the result matches the expected result.

13.3. RIPPLE CARRY ADDITION |
|
|
|
369 |
||||
|
|
|
|
|
|
|
||
|
|
|
|
:: b |
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:: a |
|
|
f |
|
:: a |
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:: c |
|
|
||
|
|
|
|
|
|
|
|
|
Figure 13.14: Building Block Circuit for mscanr
13.3.1Circuit Patterns
The add4 specification in the previous section is not too complicated, but it would be awfully tedious to extend it to handle words containing 32 or 64 bits (which are the sizes most commonly used with current generation processors). The analogous specifications would contain 32 or 64 local equations, and there would be a correspondingly large number of indexed names. Besides the sheer size, such specifications would be highly error-prone. Furthermore, it would be better to define the family of all ripple carry adders, rather than to keep on defining new ones at various di erent word sizes.
A much better approach is to define the general k-bit ripple carry adder once and for all, so that it works for arbitrary k. To do this, we can’t name the individual bits explicitly, like x0, x1 and so on. Instead, we need to use a method that works for any word size without referring explicitly to the individual bits. The most intuitive description of the adder would say ‘each full adder has its carry input connected to the carry output of its right neighbour,’ and this is exactly the idea that needs to be formalised with a function.
A higher order function can be used to express the abstract structure of circuits like add4. The idea is to write a function whose argument is a circuit specification; the higher order function connects up as many copies as required of the circuit it is given. Figure 13.14 shows the sort of building block needed for the ripple carry adder; it matches the black box structure of the full adder.
The mscanr function takes a building-block circuit with an appropriate type. It creates as many copies of the building block as are required and makes all the internal connections that are needed. Figure 13.15 depicts the structure of the resulting circuit, and Figure 13.16 shows the circuit defined by mscanr as a black box.
mscanr :: (b->a->(a,c)) -> a -> [b] -> (a,[c]) mscanr f a [] = (a,[])
mscanr f a (x:xs) =
let (a’,ys) = mscanr f a xs (a’’,y) = f x a’
in (a’’, y:ys)

370 CHAPTER 13. DISCRETE MATHEMATICS IN CIRCUIT DESIGN
|
x |
0 |
x1 |
|
x n-1 |
|
a’ |
f |
|
f |
... |
f |
a |
|
y0 |
y1 |
|
yn-1 |
|
Figure 13.15: Structure of mscanr Pattern
:: [b]
:: a |
|
|
mscanr f |
|
|
:: a |
|
|
|||||
|
|
|
|
|
|
|
:: [c]
Figure 13.16: Black Box for mscanr Pattern
Exercise 8. Let f :: b → a → (a, c) be a black box circuit. Draw a diagram showing the structure of the circuit specified by
mscanr f a [(x0, y0), (x1, y1), (x2, y2)].
13.3.2The n-Bit Ripple Carry Adder
Now we can use the higher order function mscanr to define a general ripple carry adder that works for any word size. The definition is much more intuitive than brute-force definitions like add4, once you understand the idea of using higher order functions to express regular circuit patterns.
The mscanr function expresses the pattern of the ripple carry adder. It simply says that a ripple carry adder consists of a row of full adders, one for every bit position. The carry input to each full adder is connected to the carry output from the full adder to the right, and the carry input to the rightmost (least significant) bit position is the carry input to the entire addition.
The first argument is a circuit specification with type b → a → (a, c). Recall that a full adder has type
Signal a (a, a) → a → (a, a).
This fits the mscanr pattern, and a ripple carry adder consists of a row of full
13.3. RIPPLE CARRY ADDITION |
371 |
adders with the carry input of each connected to the carry output of its right neighbour.
rippleAdd :: Signal a => a -> [(a,a)] -> (a, [a]) rippleAdd c zs = mscanr fullAdd c zs
This definition works for arbitrary word size. The size of a particular circuit is determined by the size of the input data word. The definition itself doesn’t get longer if the words become longer! It’s now easy to specify a 6-bit adder, as the following test case demonstrates.
Example: addition of 23+11 23 + 11
=010111 (16+4+2+1 = 23)
+ 001011 |
( |
8+2+1 |
= |
11) |
with |
carry |
input = 0 |
= 100010 |
( |
32+2 |
= |
34) |
with |
carry |
output = 0 |
Calculate with the circuit by evaluating
rippleAdd False [(False,False),(True,False),(False,True), (True,False),(True,True),(True,True)]
The expected result is
(False, [True,False,False,False,True,False])
Exercise 9. Work out a test case using the ripple carry adder to calculate 13+41=54, using 6-bit words. Test it using the computer.
13.3.3Correctness of the Ripple Carry Adder
Theorem 108. Let xs and ys be k-bit words, so xs, ys :: Signal a [a]. Define (c, sum) = rippleAdd zero (zip xs ys); thus c :: a is the carry output and ss :: [a] is the sum word. Then
bitValue c × 2k + wordValue ss = wordValue xs + wordValue ys.
The left-hand side of the equation is the numeric value of the output of the ripple carry adder circuit, and the right-hand side is the numeric value of its inputs. Thus the equation says that the circuit produces the correct answer.
Proof. Induction over k. For the base case, k = 0, and xs = ys = []. First we simplify:
(c, ss) = rippleAdd zero [ ]
=mscanr fullAdd zero [ ]
=(zero, [ ])
c = zero ss = []
wordValue [ ] + wordValue [ ] = 0 + 0

372 CHAPTER 13. DISCRETE MATHEMATICS IN CIRCUIT DESIGN
= 0
bitValue c × 2k + wordValue ss
=0 × 20 + 0
=0 × 20 + wordValue [ ]
For the inductive case, let k = length xs = length ys, and assume
bitValue c × 2k + wordValue ss = wordValue xs + wordValue ys,
where (c, ss) = rippleAdd zero (zip xs ys). The aim is to prove that
bitValue c × 2k+1 + wordValue ss =
wordValue (x : xs) + wordValue (y : ys),
where (c, ss) = rippleAdd zero (zip (x : xs) (y : ys)). First we simplify:
(c, ss) = mscanr fullAdd zero (zip (x : xs) (y : ys))
=mscanr fullAdd zero ((x, y) : zip xs ys)
=let (c , ss) = mscanr fullAdd zero (zip xs ys)
=rippleAdd zero (zip xs ys)
(c , s) = fullAdd (x, y) c in (c , s : ss)
Now the left-hand side of the equation can be transformed into the right-hand side, using equational reasoning:
lhs (numeric value of output from the adder)
=bitValue c × 2k+1 + wordValue ss
=bitValue c × 2k+1 + wordValue (s : ss)
=bitValue c × 2k+1 + bitValue s × 2k + wordValue ss
=(bitValue c × 2 + bitValue s) × 2k + wordValue ss
=(bitValue x + bitValue y + bitValue c ) × 2k + wordValue ss
=(bitValue x + bitValue y) × 2k + (bitValue c ) × 2k + wordValue ss)
=(bitValue x + bitValue y) × 2k + wordValue xs + wordValue ys
=(bbitValue x × 2k + wordValue xs) + (bitValue y × 2k + wordValue ys)
=wordValue (x : xs) + wordValue (y : ys)
=rhs (numeric value of inputs to the adder)
13.3.4Binary Comparison
Comparison of binary numbers is just as important as adding them. It is particularly interesting to consider how to implement a comparison circuit, since this problem has some strong similarities and also some strong di erences to the ripple carry adder.
13.3. RIPPLE CARRY ADDITION |
373 |
First, let’s consider the comparison of two bits. This is analogous to starting out with a half adder. The problem is to design a circuit halfCmp that compares two one-bit numbers x and y. The result of the comparison should be a triple of bits of the form (lt, eq, gt), where lt is true if x < y, eq is true if x = y, and gt is true if x > y. The problem is pretty simple, as x and y are both just one bit! The type should be
halfCmp :: Signal a (a, a) → (a, a, a),
where the input bits x and y are provided as a pair (x, y), and the result
triple consists of the result |
bits (lt, eq, gt) :: (a, a, a). Finding a solution is |
straightforward: |
|
halfCmp :: Signal a => (a,a) -> (a,a,a) |
|
halfCmp (x,y) = |
|
(and2 (inv x) y, |
-- x<y when x=0,y=1 |
inv (xor x y), |
-- x=y when x=0,y=0 or x=1,y=1 |
and2 x (inv y)) |
-- x>y when x=1,y=0 |
The next problem to consider is that of designing a ripple comparator that takes two words representing binary numbers (the words must have the same size), and returns a triple of three bits (lt, eq, gt) that indicate the result of comparing x and y. The meanings of the output bits are just the same as in the previous problem; the only di erence is that now the inputs to the comparator are words rather than bits.
Just as you compare two numbers by looking first at the most significant digits, a binary comparison is performed by moving from left to right through the word. Initially we assume the two words are equal; represent this by (lt, eq, gt) = (0, 1, 0). If the next bit position has x = 1 and y = 0, then we know that the final result must be (0, 0, 1) regardless of any bits to the right; conversely if x = 0 and y = 1, then the final result must be (1, 0, 0) regardless of the bit values to the right. However, if x and y have the same value in this bit position, then as far as we know the result is still (0, 1, 0) but that result might be changed later.
The calculation in each bit position requires the two local bits (that is, for position i we need the ith bit of both of the input words). It also requires the result of the comparison for all the bits to the left. The task is performed by a full comparison circuit, which is analogous to the full adder.
fullCmp |
:: Signal a => (a,a,a) -> (a,a) -> (a,a,a) |
|||
fullCmp |
(lt,eq,gt) (x,y) = |
|
|
|
(or2 lt (and3 eq (inv x) y), |
-- < |
|||
and2 |
eq (inv (xor |
x y)), |
-- |
= |
or2 gt (and3 eq x |
(inv y))) |
-- |
> |
Now we can define the ripple comparison circuit, which compares two binary numbers. Its definition is similar to the ripple carry adder, but there are several
13.5. REVIEW EXERCISES |
375 |
Exercise 13. Work out a complete set of test cases for the full adder, and calculate the expected results. Then simulate the test cases and compare with the predicted results.
Exercise 14. Suppose that a computer has 8 memory locations, with addresses 0, 1, 2, . . . 7. Notice that we can represent an address with 3 bits, and the size of the memory is 23 locations. We name the address bits a0a1a2, where a0 is the most significant bit and a2 is the least significant. When a memory location is accessed, the hardware needs to send a signal to each location, telling it whether it is the one selected by the address a0a1a2. Thus there are 8 select signals, one for each location, named s0, s1, . . . , s7. Design a circuit that takes as inputs the three address bits a0, a1, a2, and outputs the select signals s0, s1, . . . , s7. Hint: use demultiplexors, arranged in a tree-like structure. (Note: modern computers have an address size from 32 to 64 bits, allowing for a large number of locations, but a 3-bit address makes this exercise more tractable!)
Exercise 15. Does the definition of rippleAdd allow the word size to be 0? If not, what prevents it? If so, what does it mean?
Exercise 16. Does the definition of rippleAdd allow the word size to be negative? If not, what prevents it? If so, what does it mean?
Exercise 17. Note that for the half adder and full adder, we did thorough testing—we checked the output of the circuit for every possible input. Note also that we did not do this for the ripple carry adder, where we just tried out a few particular examples. The task: Explain why it is infeasible to do thorough testing of a ripple carry adder circuit, and estimate how long it would take to test all possible input values for the binary adder in a modern processor where the words are 64 bits wide.
Exercise 18. Computer programs sometimes need to perform arithmetic, including additions and comparisons, on big integers consisting of many words. Most computer processor architectures provide hardware support for this, and part of that hardware support consists of the ability to perform an addition where the carry input is supplied externally, and is not assumed to be 0. Explain why the carry input to the rippleAdd circuit helps to implement multiword addition, but we don’t need an analogous horizontal input to rippleCmp for multiword comparisons.
