G27
Self-checkedTier 1 14 min readcoa.alu-design

ALU design

How an arithmetic and logic unit is built from one-bit slices: AND, OR, adder, B inversion and a multiplexer, the flags it produces, set-less-than, the zero detector, shifters, and what limits its speed.

Why it exists

Every arithmetic instruction, every comparison, every address calculation and every branch decision passes through the ALU. It is a purely combinational circuit: two operand inputs, a few control inputs that choose the operation, a result and a handful of status bits (flags). It is also the block that sets the clock speed of the simple processor, because its worst-case delay (the carry through the adder) is the longest path in the datapath. GATE asks you to build an ALU from adders and gates, to give the control signals for an operation, to say what the flags mean, to design the comparison, and to estimate its delay and hardware.

Prerequisites

You need the adder, the adder-subtractor and the carry/overflow flags from adders and subtractors and fixed-point arithmetic, and the multiplexer from multiplexers.

Precise definitions

ALU. A combinational circuit with operands AA and BB (each nn bits), a control input that selects the operation, a result nn bits wide and status outputs. The common operations are add, subtract, AND, OR, (NOR, XOR), set-on-less-than and sometimes shifts. Multiplication and division are usually done by a separate unit or by repeated use of the ALU (Booth multiplication).

The one-bit slice. An nn-bit ALU is nn identical slices connected by the carry chain. Each slice contains

  • an XOR gate (or multiplexer) that inverts BB when a control line BinvertB_{invert} is 1,
  • an AND gate and an OR gate on AA and the (possibly inverted) BB,
  • a full adder with carry in and carry out,
  • a multiplexer that picks which of AND, OR, the adder's sum or the special "less" input becomes the result bit.

Operations by control setting (this page's design; BinvertB_{invert} also feeds the carry in of the lowest slice):

OperationBinvertB_{invert}multiplexer selectscarry in
AND0AND0
OR0OR0
ADD0sum0
SUB1sum1
SLT (set on less than)1"less"1

Subtraction. A−B=A+Bˉ+1A - B = A + \bar{B} + 1: invert every bit of BB and set the carry in to 1 (two's complement negation).

Flags. ZZ (zero): every result bit is 0, computed by a NOR over all result bits (for nn bits a tree of n−1n - 1 two-input gates with ⌈log⁡2n⌉\lceil \log_2 n \rceil levels). NN (negative): the top result bit. CC (carry): the carry out of the top slice (after a subtraction a carry of 1 means A≥BA \ge B unsigned, i.e. no borrow). VV (overflow): the carry into the top slice XOR the carry out of it.

Set on less than. The result is 1 if A<BA < B as signed numbers, else 0. It is computed by subtracting and taking sign of (A−B)⊕V\text{sign of } (A - B) \oplus V (that is N⊕VN \oplus V): the sign bit of the difference alone is wrong when the subtraction overflows. The result is placed in bit 0 and all other bits are 0; the top slice produces the "less" value and feeds slice 0's "less" input. For an unsigned comparison use the carry: A<BA < B exactly when C=0C = 0 after the subtraction.

Equality is Z=1Z = 1 after a subtraction.

Shifters. A barrel shifter shifts by any amount in one step: it has ⌈log⁡2n⌉\lceil \log_2 n \rceil stages, stage kk shifts by 2k2^k or passes the value unchanged under control of bit kk of the shift amount, each stage being nn two-input multiplexers: n⌈log⁡2n⌉n \lceil \log_2 n \rceil multiplexers in all (160 for 32 bits). A shift register shifts by one position per clock, so shifting by kk takes kk cycles.

Control codes. With mm operations the control field needs ⌈log⁡2m⌉\lceil \log_2 m \rceil bits. The classic textbook MIPS-style design uses a 4-bit ALU control: 0000 AND, 0001 OR, 0010 add, 0110 subtract, 0111 set on less than, 1100 NOR, where, in that design, bit 3 inverts AA, bit 2 inverts BB (so 0110 inverts BB and adds with carry in 1) and the low two bits choose AND, OR, add or the "less" input; NOR is AND of both inverted inputs.

Speed. The critical path is the carry chain of a ripple adder (about 2n2n gate delays) plus the B-inversion XOR and the output multiplexer. Carry lookahead or a faster adder makes it close to constant; this is why the ALU delay appears in the clock-period calculation of the datapath.

How it works

The animation runs five operations on 8-bit operands with their flags. Look at the two subtractions: with A=100A = 100 and B=−100B = -100 the true difference 200 does not fit, the result bits read −56-56 (so N=1N = 1), the overflow flag VV is 1, and "set on less than" correctly returns 0 because N⊕V=0N \oplus V = 0.

An 8-bit ALU: operations and flags: AND

Computed exactly, not typed

Working

result = A AND B, bit by bit

Value, format and bits (changed bits in amber)

Atwos-complement= 106
01101010
Btwos-complement= 53
00110101
resulttwos-complement= 32
00100000
Z (result is zero)= 0
N (sign of result)= 0
C (carry out)= 0
V (signed overflow)= 0

Step 1

Each result bit is the AND of the two input bits at the same position. The carry and overflow flags are not defined for a logic operation and are shown as 0.

1 / 7
computed by script: a simulation of the machine, one register transfer per step. Python 3.15.1, darwin arm64. python3 scripts/gen-coa-traces.py

Worked examples

EX 1The ALU, built from slices, in a program

Easy

The C program builds an 8-bit ALU from eight copies of the slice (B inverter, AND, OR, full adder, output multiplexer), runs it on the animation's operands, then checks it against the C operators for all 65536 operand pairs and five operations, and checks the logarithmic shifter for all inputs and shift amounts.

#include <stdio.h>
#include <stdint.h>
 
/* a 1-bit ALU slice: AND, OR, add (full adder) and a "less" input, with an optional inverter on B */
typedef struct { int out, cout, sum; } Slice;
 
static Slice slice(int a, int b, int cin, int binv, int op, int less) {
    int bb = b ^ binv;
    Slice s;
    s.sum = a ^ bb ^ cin;
    s.cout = (a & bb) | (cin & (a ^ bb));
    int r[4] = { a & bb, a | bb, s.sum, less };
    s.out = r[op];
    return s;
}
 
enum { AND, OR, ADD, SUB, SLT };
 
/* an 8-bit ALU made of 8 slices; returns the result and sets the flags */
static unsigned alu8(unsigned a, unsigned b, int op, int *z, int *n, int *c, int *v) {
    int binv = (op == SUB || op == SLT), carry = binv;
    int sel = op == AND ? 0 : op == OR ? 1 : 2;
    unsigned res = 0;
    int carry_into_sign = 0, sign_sum = 0;
    for (int i = 0; i < 8; i++) {
        if (i == 7) carry_into_sign = carry;
        Slice s = slice((a >> i) & 1, (b >> i) & 1, carry, binv, sel, 0);
        res |= (unsigned)s.out << i;
        if (i == 7) sign_sum = s.sum;
        carry = s.cout;
    }
    *c = carry;
    *v = carry_into_sign ^ carry;
    *n = (int)((res >> 7) & 1);
    *z = res == 0;
    if (op == SLT) return (unsigned)(sign_sum ^ *v);      /* set-less-than: sign of the difference xor overflow */
    return res;
}
 
/* a logarithmic shifter: stage k shifts by 2^k when bit k of the shift amount is 1 */
static unsigned shl8(unsigned x, int amount) {
    for (int k = 0; k < 3; k++)
        if ((amount >> k) & 1) x = (x << (1 << k)) & 0xFF;
    return x;
}
 
int main(void) {
    int z, n, c, v;
    unsigned r = alu8(106, 53, ADD, &z, &n, &c, &v);
    printf("106 + 53 = %u  N=%d C=%d V=%d\n", r, n, c, v);
    r = alu8(106, 53, SUB, &z, &n, &c, &v);
    printf("106 - 53 = %u  N=%d C=%d V=%d\n", r, n, c, v);
    r = alu8(100, (unsigned)(-100) & 0xFF, SUB, &z, &n, &c, &v);
    printf("100 - (-100): bits %02X  N=%d V=%d, so N xor V = %d\n", r, n, v, n ^ v);
    r = alu8(100, (unsigned)(-100) & 0xFF, SLT, &z, &n, &c, &v);
    printf("100 < -100 (signed)? %u\n", r);
    int bad = 0;
    for (unsigned a = 0; a < 256; a++)
        for (unsigned b = 0; b < 256; b++) {
            bad += alu8(a, b, AND, &z, &n, &c, &v) != (a & b);
            bad += alu8(a, b, OR, &z, &n, &c, &v) != (a | b);
            bad += alu8(a, b, ADD, &z, &n, &c, &v) != ((a + b) & 0xFF) || c != (int)((a + b) >> 8);
            bad += alu8(a, b, SUB, &z, &n, &c, &v) != ((a - b) & 0xFF) || c != (a >= b) || z != (a == b);
            bad += alu8(a, b, SLT, &z, &n, &c, &v) != (unsigned)((int8_t)a < (int8_t)b);
        }
    int bad_shift = 0;
    for (unsigned x = 0; x < 256; x++)
        for (int k = 0; k < 8; k++) bad_shift += shl8(x, k) != ((x << k) & 0xFF);
    printf("slice ALU wrong on %d of 327680 operations; shifter wrong on %d of 2048 cases\n", bad, bad_shift);
    return 0;
}
106 + 53 = 159  N=1 C=0 V=1
106 - 53 = 53  N=0 C=1 V=0
100 - (-100): bits C8  N=1 V=1, so N xor V = 0
100 < -100 (signed)? 0
slice ALU wrong on 0 of 327680 operations; shifter wrong on 0 of 2048 cases

Reading it. 106+53=159106 + 53 = 159 does not fit in a signed byte: N=1N = 1 and V=1V = 1 (as an unsigned sum it is fine, C=0C = 0). 106−53=53106 - 53 = 53 has C=1C = 1 (no borrow). 100−(−100)100 - (-100) gives the bit pattern C8, N=1N = 1, V=1V = 1, so the signed comparison N⊕V=0N \oplus V = 0 and the set-less-than result is 0: 100 is not less than −100-100.

EX 2Control settings for each operation

Easy

To subtract, set Binvert=1B_{invert} = 1 and carry in =1= 1, and select the adder output. To AND, set Binvert=0B_{invert} = 0 and select the AND output. For the NOR operation invert both inputs (or invert the AND of inverted inputs): A NOR B=Aˉ⋅BˉA\ \text{NOR}\ B = \bar{A} \cdot \bar{B}, which needs a second inverter on AA in this slice, so a design that supports NOR adds an AinvertA_{invert} line. The number of control lines for 8 operations is ⌈log⁡28⌉=3\lceil \log_2 8 \rceil = 3 (plus the inversion lines if they are separate).

EX 3Why set-less-than uses the overflow flag

Medium

Take 8-bit A=100A = 100 and B=−100B = -100. A−B=200A - B = 200, which wraps to −56-56 (11001000). The sign bit is 1, so a designer who returns the sign of the difference would report A<BA < B, which is wrong. The overflow flag is 1, and N⊕V=1⊕1=0N \oplus V = 1 \oplus 1 = 0 gives the right answer. If A=−100A = -100 and B=100B = 100: A−B=−200A - B = -200 wraps to +56+56 (sign 0) with V=1V = 1, and N⊕V=0⊕1=1N \oplus V = 0 \oplus 1 = 1: correct, −100<100-100 < 100.

EX 4Shifter and zero-detector cost

Medium

A 32-bit barrel shifter has ⌈log⁡232⌉=5\lceil \log_2 32 \rceil = 5 stages of 32 two-input multiplexers: 160 multiplexers, and the delay is 5 multiplexer delays whatever the shift amount. A 64-bit one has 6 stages and 384 multiplexers. The zero flag of 32 result bits needs a 32-input NOR: 31 two-input gates in 5 levels (OR gates in a tree followed by an inverter).

EX 5Delay of an n-bit ALU

GATE level

Suppose the slice's XOR (B inversion) takes 1 gate delay, each carry stage (AND then OR) takes 2, the sum XOR takes 1 and the output multiplexer takes 1. For a ripple ALU of nn bits the critical path from BB to the top result bit is about 1+2n+1+1=2n+31 + 2n + 1 + 1 = 2n + 3 gate delays; for n=32n = 32 this is 67. With carry lookahead the carry path becomes a constant (a few gate delays), so the ALU delay hardly depends on nn. The question normally supplies its own delays: add them along the longest path.

EX 6Which flags decide which branch?

GATE level

After SUB: branch-if-equal looks at ZZ; signed branch-if-less looks at N⊕VN \oplus V; signed branch-if-greater-or-equal looks at N⊕V‾\overline{N \oplus V}; unsigned branch-if-lower looks at C=0C = 0 (borrow); unsigned branch-if-higher-or-same looks at C=1C = 1. The same subtraction serves all of them, so the ALU does not need a separate comparator.

Cheat box

Cheat box

  • Slice: XOR on B (BinvertB_{invert}), AND, OR, full adder, output multiplexer. nn slices linked by the carry.
  • SUB: Binvert=1B_{invert} = 1, carry in =1= 1. A−B=A+Bˉ+1A - B = A + \bar{B} + 1.
  • Flags: ZZ = NOR of result; NN = top bit; CC = carry out (after SUB: 1 means A≥BA \ge B unsigned); VV = carry into XOR carry out of the top bit.
  • Signed A<BA < B: N⊕VN \oplus V after SUB. Unsigned: C=0C = 0. Equal: ZZ.
  • Barrel shifter: ⌈log⁡2n⌉\lceil \log_2 n \rceil stages × n\times\ n two-input multiplexers.
  • Control bits for mm operations: ⌈log⁡2m⌉\lceil \log_2 m \rceil.
  • Ripple ALU delay grows with nn; lookahead makes it nearly constant.

Traps and GATE patterns

Connections

The adder, subtractor and their flags are built in adders and subtractors and interpreted in fixed-point arithmetic. The result multiplexer is a multiplexer. The ALU is one block of the datapath, where it sits between the registers and the bus, and its delay sets the clock period that pipelining tries to shorten. Multiplication uses it repeatedly.

PYQs

No PYQs attached to this concept yet

Previous year questions are being added section by section from your PYQ book, each with my own re-checked answer. Nothing is shown from memory.

Self-check

Question 1

What control settings make the ALU compute A−BA - B?

Show answer

Invert B (Binvert=1B_{invert} = 1), set the carry in to 1 and select the adder output: A+Bˉ+1A + \bar{B} + 1.

Question 2

An 8-bit subtraction gives N=0N = 0, V=1V = 1. Is A<BA < B (signed)?

Show answer

N⊕V=0⊕1=1N \oplus V = 0 \oplus 1 = 1, so yes, A<BA < B (the true difference is negative, but it overflowed to a positive-looking pattern).

Question 3

How many 2-input multiplexers are in a barrel shifter for 16-bit words?

Show answer

⌈log⁡216⌉=4\lceil \log_2 16 \rceil = 4 stages of 16 multiplexers: 64.

Question 4

After SUB the carry flag is 0. What does that mean for unsigned operands?

Show answer

A borrow occurred: A<BA < B (unsigned).

Revision card

One-screen revision

  • Slice: B-invert XOR, AND, OR, full adder, output multiplexer; SUB = invert B + carry in 1.
  • ZZ = NOR of result, NN = top bit, CC = carry out, VV = carry into XOR carry out of the sign bit.
  • Signed less-than =N⊕V= N \oplus V; unsigned less-than =(C=0)= (C = 0); equal =Z= Z.
  • Barrel shifter: ⌈log⁡2n⌉\lceil \log_2 n \rceil stages of nn multiplexers. Control bits =⌈log⁡2m⌉= \lceil \log_2 m \rceil.

References

  • ·Hennessy and Patterson, Computer Organization and Design, Chapter 3 Arithmetic for Computers (Appendix: the arithmetic logic unit)
  • ·Morris Mano, Computer System Architecture, 3rd ed., Chapter 10 Computer Arithmetic and Chapter 4 Register Transfer and Microoperations (arithmetic logic shift unit)
  • ·Hamacher, Vranesic, Zaky, Computer Organization, 5th ed., Chapter 6 Arithmetic

Verification

The slice-based ALU is implemented twice (a Python model with exhaustive checks for 3, 4 and 5 bits, scripts/enum-checks/coa_alu.py, and the C program on the page, which checks all 65536 operand pairs of 8 bits for five operations against the language operators). The animation's values and flags are recomputed independently in tests/coa-traces.test.ts. The barrel-shifter and zero-detector counts are checked in the script. The control-code numbers of the MIPS-style ALU quoted in the text are from memory of the textbook design and not checked against a manual. Textbook chapter titles are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand (long C programs were not re-read line by line: they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose). No defect found. Status raised to self-checked; verified needs a textbook or human check.

last verified: never