Wiring the Operations Together
The CPU's data path is the network of components — registers, the ALU, and the wires (and multiplexers) between them — through which operands flow and results return. The control logic sets the switches on that network so that, for each instruction, data flows along the right route.
Every CPU understands a fixed vocabulary of operations, its instruction set architecture (ISA). A machine instruction is a bit pattern split into fields. The opcode field says which operation to perform (add, load, store, jump); the operand fields say what to operate on — which registers, or an immediate value, or a memory address. Decoding an instruction means reading these fields and configuring the data path accordingly.
Instructions fall into a few families:
- Arithmetic/logic (e.g. add two registers) — route operands into the ALU, result back to a register.
- Data transfer (load, store) — move a value between a register and memory.
- Control flow (jump, branch) — change the program counter.
A key ISA distinction is between register operands (fast, on-chip) and immediate operands (a constant baked into the instruction itself). The control unit's job, on every cycle, is to translate the decoded opcode into the exact set of control signals — which register to read, whether the ALU adds or subtracts, where the result is written — that steer the data path.
Common pitfall: conflating the opcode (which operation) with the operands (what it acts on). "" has one opcode (ADD) and two operands (R1, R2). Reading the fields in the wrong roles is a classic decoding mistake — the opcode selects the operation; the operands feed it.
How the Fields Are Actually Laid Out
RISC-V makes the field structure described above unusually easy to see, because every instruction is exactly 32 bits and the fields sit at fixed positions.
An arithmetic instruction such as add x5, x6, x7 uses the R-type format, cut into six fields:
- opcode, 7 bits — the broad class of instruction
- rd, 5 bits — the destination register
- rs1 and rs2, 5 bits each — the two source registers
- funct3 (3 bits) and funct7 (7 bits) — which operation within that class, add or subtract for instance
Five bits is exactly what 32 registers require, since . An instruction that takes a constant rather than a second register uses the I-type format, which drops rs2 and funct7 and spends those bits on a 12-bit immediate value instead.
Two design choices are worth naming. Because rs1, rs2 and rd sit at the same bit positions in every format that has them, the hardware can start reading the register file before it has finished working out which instruction this is — decode and register read overlap. And RISC-V is a load–store architecture: arithmetic instructions touch only registers, and memory is reached solely through explicit load and store instructions, which keeps every other instruction short and uniform.
Common pitfall: reading a fixed instruction width as waste, on the grounds that a simple instruction "does not need" 32 bits. The width buys something specific: the fetch unit always knows where the next instruction begins, and every field is at a known position, so decoding is fast and uniform. Variable-length instruction sets trade that away in exchange for smaller code.