Week 2 Tutorial - Building an ALU
1 Introduction
- This week, we are going to build an Arithmetic Logic Unit from scratch,
using a handful of simple logic gates and other components.
- The ALU will take in two 32-bit values, and 2 control lines. Depending
on the value of the control lines, the output will be the addition,
subtraction, bitwise AND or bitwise OR of the inputs.
- Schematically, here is what we want to build:
- Note! This is an interface for the ALU: what goes in, what
comes out. It also shows the ALU as an abstraction: you can't
see how it works, but you do know what it does.
- Also note that there are three status outputs as well as the main
result: is the result zero, was there a carry, and did the operation
result in an overflow?
- Note: just a reminder on the difference between a carry and an overflow:
- Carry: was there a carry in the most-significant bit which
we could not output in the given number of bits (32 bits, above).
- Overflow: does the sign of the output differ from the inputs,
indicating (for example) that a sum of two positive numbers has overflowed
and is now a negative result!
- Some of the data and control lines are shown with a slash and a number
like 32.
- This indicates that the line is actually 32 lines in parallel, e.g.
the result is 32-bits wide. If you don't see a slash in the diagrams
below, you can assume that the line is only 1-bit wide.
1.1 Basic Components
- We are going to use four logic gates: AND, OR, NOT and XOR. You should
have seen these already in other subjects. Below are the icons for
each, and their truth table.
- We are going to use another component, the multiplexor. The
job of the multiplexor is to choose one of several inputs, based on
a control line, and send the chosen input to the output.
- This multiplexor above is a 1-bit wide 2-way multiplexor: 2 inputs,
1 bit wide each. If you add extra control lines, you can choose more
inputs: 2 control lines is used in a 4-way multiplexor, 3 control
lines in an 8-way multiplexor etc.
- And by using multiple multiplexors in parallel, you can make N-bit
wide multiplexors.
- I'm not going to show you the internals of the multiplexor. You should,
however, know that it can be easily built with the 4 logic gates above.
1.2 Bitwise AND
- Bitwise AND is very useful, for example to calculate an IP network's
identity:
- 131.245.7.18 AND 255.255.255.0 => 131.245.7.0
- Building the logic to do 32-bit AND on two inputs is easy: as each
bit is independent, we just need 32 AND gates in parallel.
- Note the interface diagram on the right. Each time we build a larger
component, we are going to hide it behind an interface.
1.3 Bitwise OR
- 32-bit bitwise OR is just as easy as bitwise AND, we just need 32
OR gates in parallel.
1.4 Addition
- Addition isn't going to be so easy.
- We saw previously that we have to add bits, and this may produce a
carry. Columns further up need to accept a carry as input, along with
two inputs, and produce the 1-bit output and another carry for the
next column up.
- The component which will perform a 1-bit ADD, receiving a carry in
and producing a 1-bit output and a carry out is called a full
adder. Its interface looks like the following:
- Internally, here is what a full adder looks like, just 5 logic gates.
- The truth table for the full adder is:
Cin | A | B | Result | Cout |
|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 1 |
1 | 1 | 0 | 0 | 1 |
1 | 1 | 1 | 1 | 1 |
- At some point sit down, try some inputs, and convince yourself that
the logic works as advertised!
- To make a 32-bit full adder, we simply have to string 32 of these
1-bit full adders together.
- Except for the least-significant adder, each one is going to receive
its carry from the one below and pass up its own carry to the one
above.
- For the most significant bit, if the carry is a 1, then we ran out
of bits to store the result.
- When the final carry output is 1, this indicates that the result was
too big to fit into 32 bits.
- I'm going to delay showing you the interface diagram for the 32-bit
full adder, because we can modify it to also do subtraction.
1.5 Subtraction
- We could build a completely separate component, a 32-bit subtractor,
once we work out how to build a 1-bit subtractor.
- Fortunately, we can simplify things a bit.
- According to the rules of maths: 3-2=3+(-2).
- If we could negate one of the inputs, we could use the existing 32-bit
full adder.
- We have already seen two algorithms to negate a twos complement binary
integer.
- One of them works as follows: invert every bit in the number, then
add 1.
- Putting all of the above together, we can say:
A - B = A + (-B) = A + ~B + 1
- Inverting every bit is easy: we can use a NOT gate for each bit in
B.
- But now we need to do A + ~B + 1. How can we do this?
- We are going to use a very clever trick.
- Set the initial carry-in to 1 instead of 0, thus adding an extra 1
to the sum.
- And instead of using NOT gates, we will use XOR gates.
- If we are doing addition (Control=0), then one arm of the XOR gates
is zero, and the B bits go into the adders unchanged, and the carry-in
is zero.
- If we are doing subtraction (Control=1), then one arm of the XOR gates
is one. This inverts all of the B bits before they get to the adders.
As well, the carry-in is now 1, so we achieve the result of doing
A - B = A + ~B + 1!
- Overall, we end up with this unit which can do addition and subtraction:
- If the operation bit is 0, we pass in B and perform A+B. If the operation
bit is 1, we invert B and do A + ~B + 1.
- We can now distinguish between data lines (which pass data
around) and control lines (which control the actions of the
components).
- The data lines are also known as datapaths.
- The Operation line above is a control line, as it controls
the action being performed. But note, it is also used as a 1-value
for the carry-in, so it is also a piece of data!
1.6 Overflow Output
- We've seen that a carry occurs when the final addition or subtraction
is too big to fit into 32 bits.
- A related mathematical output is overflow. This indicates that
the sign of the maths result differs from the sign of the two inputs.
- Imagine we were using 4-bit numbers: you do 7 + 7 and get the result
-2!
- Why? Because 7 (0111) + 7 (0111) = 1110, which in 4-bit twos-complement
is -2.
- Overflow occurs when the size of the inputs is such that there is
a carry which changes the most-significant sign bit.
- The ALU will always output both carry and overflow, but both only
makes sense when the operation is add or subtract.
- When we are doing the logical operations (AND and OR), the values
on the carry and overflow outputs have no meaning.
- As overflow is only maths related, this should be implemented in the
ADD/SUBTRACT unit.
- The logic is follows: when adding, if the sign of the two inputs is
the same, but the result sign is different, then we have an overflow.
- The boolean expression is [`a31]·[`b31]·result31+a31·b31·[`result31].
- We have to do this only for addition, so we take the b31 value after
the XOR gate, i.e. just as it goes into the most-significant adder.
1.7 Negative Output
- When is the result negative? When its most-significant bit is 1.
- We can wire this bit directly out of the ALU, so that it indicates
if the result is negative.
1.8 Zero Output
- One thing left to do is to provide the zero output: is the result
zero?
- This is only true if all of the bits of the result are zero.
We can do this for the logical and the maths units.
- To do this, we can use a 32-bit OR gate followed by a 1-bit NOT gate:
- The OR gate outputs a 0 only if all input bits are 0. If any input
bit is a 1, the OR's output is a 1.
- The NOT gate simply inverts this, giving the result:
- if any result bit is on, the output is 0, i.e. not zero.
- if all result bits are off, the output is 1, i.e. zero.
1.9 Putting It All Together
- We are getting close to being able to build the full ALU.
- We now have three main units:
- a 32-bit bitwise AND unit,
- a 32-bit bitwise OR unit, and
- a 32-bit ADD/SUBTRACT unit with a control line.
- the logic to output carry, overflow, zero and negative.
- We can pass the inputs A and B to all three units, and perform all
three operations in parallel:
- But, we need to choose which of the three results we want, based on
the two control lines coming into the ALU. We would like:
c1 | c0 | Result |
|
0 | 0 | A AND B |
0 | 1 | A OR B |
1 | 0 | A + B |
1 | 1 | A - B |
- How do we control which of the four operations actually becomes the
result? With multiplexors again.
- When c0=0, the ANDORresult is A AND B, and the ADDSUBresult
is A + B.
- When c0=1, the ANDORresult is A OR B, and the ADDSUBresult is
A - B.
- Now we just have to choose between these two results, and that is
done with the second multiplexor, which chooses either the bitwise
logic result or the maths result.
- To finish off, we can draw the three components, the multiplexors
and the zero logic, to reveal the final 32-bit ALU.
- The dotted line shows the interface to the ALU.
1.11 Implementing the ALU
- Here is the above ALU implemented in Logisim:
ALUweek2.circ
- This is only an 8-bit version, but extending it to be a 32-bit CPU
is simple but tedious.
- Download the file, run Logisim, and open the above circuit.
- Select the Hand icon in the top-left of the Logisim window, then click
on the data inputs and the two control inputs to change their values.
- Try out some additions, a subtraction, some ANDs and ORs, and satisfy
yourself that the ALU works as advertised.
- See if you can put in some input values which cause an oveflow.
1.12 Conclusion
- That's about all we can cover in terms of ALU design in this subject.
- Obviously, real ALUs perform many more operations, and use many performance
optimisations.
- This is just a taste of ALU design, but you should now understand
that:
- an ALU is just a collection of logic components;
- the logic components can be made from ordinary logic gates;
- data can flow in parallel to multiple units;
- everything operates in parallel: several units can produce output
internally;
- control logic, via multiplexors, chooses which output to use as the
result.
- For more interested students, the ALU chapter in Patterson & Hennessy
goes into much more detail and covers more operations such as multiplication
and division.
File translated from
TEX
by
TTH,
version 3.85.
On 2 May 2011, 12:26.