How an ALU Works: Arithmetic Logic Unit from Gates
Build a small ALU from an adder, bitwise logic, a shifter, and a result multiplexer—then derive its carry, zero, negative, and overflow outputs.
TL;DR: A small ALU is a combinational datapath: candidate arithmetic, logic, and shift functions operate on the same input words, and a multiplexer selects the visible result. The design is not complete until operation encodings, carry/borrow semantics, overflow behavior, and flag-write policy are explicit.
An arithmetic logic unit is where a processor transforms data. ADD and SUB need an adder; AND, OR, and XOR need parallel gate lanes; shifts need routing or a shifter network; and a result multiplexer chooses which candidate reaches the destination.
That block diagram is simple enough to build at four or eight bits, yet rich enough to expose several real engineering decisions:
- Does subtraction report borrow or not-borrow?
- Does right shift fill with zero or copy the sign bit?
- Which operations define the overflow output?
- Does the ALU itself store flags, or does a separate register capture them?
- Is one large function selected after parallel computation, or is logic shared to reduce area?
This guide develops one coherent 4-bit teaching ALU and marks the places where another architecture may choose differently.
Start with an Interface Contract
Assume unsigned bit vectors unless an operation explicitly requests signed interpretation:
| Port | Width | Direction | Meaning |
|---|---|---|---|
A | 4 | input | first operand |
B | 4 | input | second operand |
OP | 3 | input | operation selection |
Y | 4 | output | selected result |
C | 1 | output | carry for ADD; borrow for SUB |
Z | 1 | output | 1 when Y == 0000 |
N | 1 | output | Y[3] |
V | 1 | output | signed overflow for ADD or SUB |
One possible operation map is:
OP | Function | Definition |
|---|---|---|
000 | ADD | |
001 | SUB | |
010 | AND | bit by bit |
011 | OR | bit by bit |
100 | XOR | bit by bit |
101 | NOT A | |
110 | logical shift left | zero enters bit 0 |
111 | logical shift right | zero enters bit 3 |
This is a teaching specification, not a standard encoding. Real instruction sets may separate shifts, multiplication, address generation, and comparison into other execution units. They may also have several ALUs with different capabilities.
The ALU described here has no internal state. Its outputs settle after input changes and propagation through the combinational network. Operand registers, destination registers, and the flags register are separate sequential elements.
Block 1: One Adder for Addition and Subtraction
A ripple-carry adder connects four full-adder stages. For bit :
Use a control bit SUB to transform B and the initial carry:
When SUB=0, the network evaluates . When SUB=1, it evaluates:
The final adder carry-out has different interpretations:
- after ADD, means the unsigned sum exceeded 15;
- after SUB, means no unsigned borrow was required.
DigiSim’s ALU exposes the raw carry-out as C after ADD but its complement after SUB:
Thus C=1 after subtraction means an unsigned borrow occurred (). The result bits still come from the same modulo- sum; only the flag polarity changes.
Overflow is not carry
Signed overflow concerns the two’s-complement range through , while carry concerns the unsigned range 0 through 15.
For addition:
For subtraction:
Equivalently, for this adder/subtractor, . Keeping C and V separate lets the same four result bits support unsigned and signed interpretations correctly.
Block 2: Parallel Bitwise Logic
Each bit lane computes its logic independently:
In a conceptual implementation, all four candidate buses exist in parallel. A synthesis tool is free to share terms, map functions into lookup tables, or choose compound standard cells. “One gate per bit” describes Boolean structure, not a guaranteed transistor count or physical delay.
Bitwise functions avoid a word-wide carry dependency, but their selected result still traverses gates, routing, and the output multiplexer. They are not instantaneous.
Block 3: Shifts
A fixed one-position logical shift can be expressed as wiring:
SHL(A): Y3=A2, Y2=A1, Y1=A0, Y0=0
SHR(A): Y3=0, Y2=A3, Y1=A2, Y0=A1
The bit shifted out can be preserved as a carry candidate if the ISA specifies it:
SHL carry candidate = A3
SHR carry candidate = A0
An arithmetic right shift differs only at the top: it copies A3 into Y3 so the sign extends. A variable-distance shift needs a selectable routing network such as a barrel shifter; a fixed shift by one does not.
Block 4: The Result Multiplexer
The output multiplexer gives OP its meaning. Conceptually:
case OP
000: Y = arithmetic_sum
001: Y = arithmetic_difference
010: Y = bitwise_and
011: Y = bitwise_or
100: Y = bitwise_xor
101: Y = bitwise_not_a
110: Y = shift_left
111: Y = shift_right
At four bits, this can be drawn as one 8-to-1 multiplexer for each result bit. In programmable logic it may map to lookup tables and routing resources. In an ASIC it may become mux cells, AOI/OAI structures, or a different factored network chosen by synthesis.
The result select is part of the critical timing path:
That expression is a design estimate, not a signoff equation. Physical timing depends on cells, fan-out, wiring, voltage, temperature, and implementation.
Result Flags
The selected Y value produces two operation-independent candidates:
C and V are operation-specific. A coherent policy for this teaching ALU is:
| Operation | C | V |
|---|---|---|
| ADD | final carry-out | addition overflow |
| SUB | borrow (), the inverse of final carry-out | subtraction overflow |
| SHL | bit shifted out of MSB | 0 |
| SHR | bit shifted out of LSB | 0 |
| AND/OR/XOR/NOT | 0 | 0 |
Another design could preserve C and V for logical operations instead of clearing them. That is an ISA and control-policy choice. The combinational ALU can present candidate signals, while a clocked flags register decides whether to load, preserve, or selectively update stored bits.
The sign flag N is the result MSB. It is not by itself a correct signed less-than test after subtraction: use to compensate for signed overflow.
Worked 4-Bit Cases
6 - 3
A = 0110
B = 0011
~B = 1100
A + ~B+1 = 1 0011
The stored result is 0011 (3). The raw carry-out is 1, so no borrow occurred and DigiSim’s borrow-style C output is 0:
Y=0011, Z=0, C=0, N=0, V=0
3 - 6
A = 0011
B = 0110
A + ~B+1 = 0 1101
1101 is 13 unsigned or -3 signed. The raw carry-out is 0, so an unsigned borrow occurred and C is 1:
Y=1101, Z=0, C=1, N=1, V=0
7 + 1
0111 + 0001 = 1000
There is no unsigned carry, but +8 cannot fit in signed 4-bit two’s complement:
Y=1000, Z=0, C=0, N=1, V=1
15 + 1
1111 + 0001 = 1 0000
The unsigned result wraps and generates a carry. As signed arithmetic, -1 + 1 = 0 is valid:
Y=0000, Z=1, C=1, N=0, V=0
Those last two vectors prove that C and V are not aliases.
Where the ALU Sits in a CPU
A small single-cycle datapath might perform this sequence:
- source registers present A and B;
- the control unit selects
OP; - the ALU and flag-generation logic settle;
- a destination register captures Y on the active clock edge;
- if enabled, the flags register captures the selected status bits.
The maximum clock frequency depends on the longest complete register-to-register path, not on the ALU alone. That path can include register clock-to-Q delay, operand selection, ALU logic, result routing, destination setup time, clock uncertainty, and skew. The ALU may be critical, but cache access, bypass networks, decode, or other logic can dominate in another microarchitecture.
Optimizing an adder does not automatically improve system frequency one-for-one. It only helps if that path is limiting after placement and routing, and the recovered margin is not consumed elsewhere. See propagation delay and clock skew for the full timing context.
The 74181 as a Historical Reference
The 4-bit 74181 family is a useful example because its interface exposes function selection, mode selection, carry input, carry output, and group carry signals. It supports a catalog of arithmetic and logic functions and can be combined into wider words with carry-lookahead support.
It should not be treated as a transistor-level blueprint for a modern CPU. Contemporary processors may use several specialized integer execution units, separate vector and floating-point datapaths, distributed branch-comparison logic, and implementation-specific adder structures. What persists is the architectural pattern: transform operand words, select a result, and report precisely defined status information.
Build and Verify the 4-Bit Version
Use DigiSim’s 8-bit ALU template as a larger reference, or construct the four-bit interface directly:
- Build a four-stage adder/subtractor with
B XOR SUBandc0=SUB. - Add parallel AND, OR, XOR, and NOT lanes.
- Wire logical shifts and retain the shifted-out bits if C uses them.
- Select the result with an 8-way, four-bit multiplexer.
- Generate Z from the selected result and N from its MSB.
- Select C and V according to the operation table.
- Probe A, B, OP, Y, C, Z, N, and V.
Verify every opcode with ordinary values, then target boundaries:
- zero result;
- all-ones plus one;
- largest signed positive plus one;
- smallest signed negative minus one;
- subtraction both with and without borrow;
- left and right shifts with the outgoing bit set;
- flag behavior while changing between operation classes.
A functional simulator can establish the truth table and control sequencing. It does not prove a physical chip’s maximum frequency, analog metastability behavior, or timing across process and environmental corners.
Once the datapath works, connect it to a clocked CPU flags register and a branch decoder. That is the point where a combinational calculation becomes persistent architectural state and can redirect the program counter.



