Level 50: A 32-bit ALU

Open level →

Task

Goal: build the ALU of the educational RV32I subset. You build nine operations yourself — add/sub/sll/srl/slt/sltu/and/or/xor — while the tenth, sra, is ready-made: the sra32.v module is read-only. There are ten op codes (0–9), derived from funct3/funct7: 0 add, 1 sll, 2 slt, 3 sltu, 4 xor, 5 srl, 6 or, 7 and, 8 sub, 9 sra.

Build the arithmetic logic unit of the educational RV32I subset — the Alu32 block. You build nine operations yourself: add, sub, sll, srl, slt, sltu, and, or, xor. The tenth, sra (arithmetic shift right), is ready — the sra32.v module is read-only in the project and must be reused.

The project is multi-file:

  • alu.v — main file: the op decoder, all operation blocks and the final mux.
  • fa32.v — full adder (level 34): xor/and/or; you will build a 32-bit chain from it.
  • sra32.v — read-only: the ready-made arithmetic shift.

Alu32 interface: inputs a[31:0], b[31:0], op[3:0]; outputs r[31:0], zero.

The op codes are derived from the funct3/funct7 fields of the real instruction:

opOperationopOperation
0add5srl
1sll6or
2slt7and
3sltu8sub
4xor9sra

Codes 10–15 are unused: the reference outputs r=0 and zero=1 for them. Structural requirements: a Sra32 instance in alu.v and at least 32 FA32 instances in the carry chain. The checker tests the ALU on all ten operations against the reference model — with boundary values: 0, −1, 0x80000000, 0x7FFFFFFF, shifts by 0, 31 and 32 positions.

Solution for self-check

First try to build the ALU yourself. Below is a build checklist.

What you should get: The ALU matches the golden on all ten operations with the edge values 0, −1, 0x80000000, 0x7FFFFFFF and shifts by 0, 31 and 32; the zero flag is a nor-tree over the 32 result bits.

Step 1 — one-hot op decode. Four inverters on the op bits, and-chains for sel0…sel9. One active sel enables its block and its row in the final mux.

Step 2 — one adder path for addition and subtraction. A chain of 32 FA32. Subtraction uses two's complement: bx = b ^ binv, carry-in cin = binv, where binv = 1 for sub, slt and sltu (binv = op[1] | op[3]). Addition and subtraction share the same adder.

Step 3 — comparisons. sltu is NOT of the carry-out: unsigned "a is less than b" exactly when the subtraction borrows beyond the word width. slt is subtler: slt = sum[31] ^ ovf, where ovf = (a31^b31) & (sum31^a31) — two's-complement overflow. Both results are 32-bit: all zeros or …001. The subtraction path from step 2 is what makes both comparisons correct — swapping slt and sltu is the classic mistake of this level: they give different answers on negative numbers (the level prediction checks the pair a=0x80000000, b=1).

Step 4 — shifts. Barrel shift: 5 stages, stage k is controlled by bit b[k] — each stage either passes the bits through or shifts by 2^k. sll shifts left, srl shifts right with zeros. Connect sra with the ready-made instance Sra32(sr, a, b[4:0]) — the arithmetic shift copies the top bit, preserving the sign.

Step 5 — final mux and zero. Bitwise: 10 AND gates (one per sel) and an or-tree. zero is a nor-tree over all 32 bits of r: one when the result is zero. This is an internal signal of our ALU — it will be useful for beq/bne in levels 53–54. RV32I has no architectural flags register.

Check yourself

Why must slt and sltu not be mixed up?

sltu is the NOT of the subtract borrow-out, while slt accounts for two’s-complement overflow (slt = sum[31] ^ ovf). On negative numbers — say a = 0x80000000, b = 1 — they give different answers.