← Back to blogArithmetic logic unit (ALU)

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.

DennyDenny
8 min read
Chalkboard feature illustration for “How an ALU Works: Arithmetic Logic Unit from Gates”

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:

PortWidthDirectionMeaning
A4inputfirst operand
B4inputsecond operand
OP3inputoperation selection
Y4outputselected result
C1outputcarry for ADD; borrow for SUB
Z1output1 when Y == 0000
N1outputY[3]
V1outputsigned overflow for ADD or SUB

One possible operation map is:

OPFunctionDefinition
000ADDA+BA+B
001SUBA−BA-B
010ANDA∧BA\land B bit by bit
011ORA∨BA\lor B bit by bit
100XORA⊕BA\oplus B bit by bit
101NOT AA‾\overline A
110logical shift leftzero enters bit 0
111logical shift rightzero 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 ii:

Si=Ai⊕Bi′⊕ciS_i=A_i\oplus B'_i\oplus c_i ci+1=AiBi′+Aici+Bi′cic_{i+1}=A_iB'_i+A_ic_i+B'_ic_i

Use a control bit SUB to transform B and the initial carry:

Bi′=Bi⊕SUBB'_i=B_i\oplus SUB c0=SUBc_0=SUB

When SUB=0, the network evaluates A+BA+B. When SUB=1, it evaluates:

A+B‾+1=A−B(mod24)A+\overline B+1=A-B\pmod {2^4}

The final adder carry-out has different interpretations:

  • after ADD, c4=1c_4=1 means the unsigned sum exceeded 15;
  • after SUB, c4=1c_4=1 means no unsigned borrow was required.

DigiSim’s ALU exposes the raw carry-out as C after ADD but its complement after SUB:

CSUB=c4‾C_{\text{SUB}}=\overline{c_4}

Thus C=1 after subtraction means an unsigned borrow occurred (A<BA<B). The result bits still come from the same modulo-242^4 sum; only the flag polarity changes.

Overflow is not carry

Signed overflow concerns the two’s-complement range −8-8 through +7+7, while carry concerns the unsigned range 0 through 15.

For addition:

Vadd=A3⊕B3‾∧(A3⊕Y3)V_{\text{add}} = \overline{A_3\oplus B_3}\land(A_3\oplus Y_3)

For subtraction:

Vsub=(A3⊕B3)∧(A3⊕Y3)V_{\text{sub}} = (A_3\oplus B_3)\land(A_3\oplus Y_3)

Equivalently, for this adder/subtractor, V=c3⊕c4V=c_3\oplus c_4. 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:

ANDi=AiBiAND_i=A_iB_i ORi=Ai+BiOR_i=A_i+B_i XORi=Ai⊕BiXOR_i=A_i\oplus B_i NOTi=Ai‾NOT_i=\overline{A_i}

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:

tALU,max≈max⁡(tcandidate functions)+tresult select+troutingt_{\text{ALU,max}} \approx \max(t_{\text{candidate functions}}) +t_{\text{result select}} +t_{\text{routing}}

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:

Z=Y3∨Y2∨Y1∨Y0‾Z=\overline{Y_3\lor Y_2\lor Y_1\lor Y_0} N=Y3N=Y_3

C and V are operation-specific. A coherent policy for this teaching ALU is:

OperationCV
ADDfinal carry-outaddition overflow
SUBborrow (A<BA<B), the inverse of final carry-outsubtraction overflow
SHLbit shifted out of MSB0
SHRbit shifted out of LSB0
AND/OR/XOR/NOT00

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 N⊕VN\oplus V 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:

  1. source registers present A and B;
  2. the control unit selects OP;
  3. the ALU and flag-generation logic settle;
  4. a destination register captures Y on the active clock edge;
  5. 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:

  1. Build a four-stage adder/subtractor with B XOR SUB and c0=SUB.
  2. Add parallel AND, OR, XOR, and NOT lanes.
  3. Wire logical shifts and retain the shifted-out bits if C uses them.
  4. Select the result with an 8-way, four-bit multiplexer.
  5. Generate Z from the selected result and N from its MSB.
  6. Select C and V according to the operation table.
  7. 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.

References

Apply the concept in a simulator model.