rtl/alu.sv — Integer ALU (RV32IM)
Combinational base ALU (ADD/SUB/logic/shifts/compare) plus two multi-cycle units: a 3-cycle MUL/MULH and a 34-cycle DIV/DIVU/REM/REMU, both modeled as stall-counter state machines rather than pipelined datapaths.
Where it fits
Instantiated once by riscv_core.sv as alu_unit, driven from the EX stage.
Ports
| Direction | Name | Width | Description |
|---|---|---|---|
| in | clk, rst_n |
1, 1 | Clock / reset |
| in | a, b |
32, 32 | Operands |
| in | alu_control |
4 | Operation select (see table) |
| in | math_start |
1 | Fires once to kick off a MUL/MULH/DIV/DIVU/REM/REMU |
| out | math_busy |
1 | High for the op's full latency |
| out | result |
32 | Combinational result, or the registered MUL/DIV result once ready |
| out | zero |
1 | result == 0 |
Functionality — operation table
alu_control[3:0] |
Name | Operation | Latency |
|---|---|---|---|
4'd0 |
ALU_ADD | a + b |
0 (comb) |
4'd1 |
ALU_SUB | a - b |
0 |
4'd2 |
ALU_AND | a & b |
0 |
4'd3 |
ALU_OR | a \| b |
0 |
4'd4 |
ALU_XOR | a ^ b |
0 |
4'd5 |
ALU_SLT | signed a < b → 1/0 |
0 |
4'd6 |
ALU_SLTU | unsigned a < b → 1/0 |
0 |
4'd7 |
ALU_SLL | a << b[4:0] |
0 |
4'd8 |
ALU_SRL | a >> b[4:0] (logical) |
0 |
4'd9 |
ALU_SRA | a >>> b[4:0] (arithmetic) |
0 |
4'd10 |
ALU_MUL | a × b lower 32 bits |
3 cycles |
4'd11 |
ALU_MULH | a × b upper 32 bits (signed) |
3 cycles |
4'd12 |
ALU_DIV | signed quotient | 34 cycles |
4'd13 |
ALU_DIVU | unsigned quotient | 34 cycles |
4'd14 |
ALU_REM | signed remainder | 34 cycles |
4'd15 |
ALU_REMU | unsigned remainder | 34 cycles |
Critical timing rule: math_busy de-asserts on the same cycle the result becomes valid on result. The core samples result when math_busy==0 and the previous cycle it was 1 (math_done in riscv_core.sv).
Divider edge cases: division by zero → quotient 0xFFFFFFFF (-1), remainder = dividend. Signed overflow (INT_MIN / -1) → quotient INT_MIN, remainder 0. Uses Verilog's built-in //% operators for correctness (this module targets simulation, not synthesis — the counter provides the latency model, not a real iterative datapath).
Example — DIV latency
cycle 0: math_start=1, alu_control=ALU_DIV -> div_a_reg/div_b_reg latch, div_cnt=34
cycles 1-32: div_cnt counts down, math_busy=1 (pipeline frozen upstream)
cycle 33: div_cnt==1 -> div_result_reg = $signed(a) / $signed(b), div_result_valid=1
cycle 34: math_busy falls -> riscv_core samples `result` this cycle
Total observed latency from math_start to usable result: 34 cycles, matching docs/cpu.md's hazard table and the HALT_DRAIN_CYCLES=40 margin used in tb/tb_server_dispatch_unit.py to let a trailing DIV/REM fully retire before checking registers.
Related
- cpu.md — ALU section with hazard-table context
- reference.md — ALU control code quick-reference
- riscv_core.md — the only instantiation site