Building An ALU L
Building An ALU L
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:
o Carry: was there a carry in the most-significant bit which we could not output
in the given number of bits (32 bits, above).
o 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
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
Internally, here is what a full adder looks like, just 5 logic gates.
The truth table for the full adder is:
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
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.
o Set the initial carry-in to 1 instead of 0, thus adding an extra 1 to the sum.
o 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
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:
o if any result bit is on, the output is 0, i.e. not zero.
o if all result bits are off, the output is 1, i.e. zero.
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.
1.10 Finally
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.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:
o an ALU is just a collection of logic components;
o the logic components can be made from ordinary logic gates;
o data can flow in parallel to multiple units;
o everything operates in parallel: several units can produce output internally;
o 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.