Decoding an RV32I instruction by hand is a mechanical procedure. If you always follow the same order, the formats with “scrambled” bits stop being a problem. This guide gives the method and applies it to one instruction of each format. All the tables are generated by the site’s decoder.
The five-step method
- Convert the word to binary and number the bits from 31 (left) to 0 (right). Each hex digit is 4 bits.
- Read the opcode (bits 6–0). It gives the group and, with it, the format. Without this step you do not know what the other bits mean.
- Read the fixed fields that format has: rd (11–7), funct3 (14–12), rs1 (19–15), rs2 (24–20) and funct7 (31–25).
- Rebuild the immediate by joining its pieces in the order the format defines, and sign-extend from bit 31.
- Pick the mnemonic from funct3 (and funct7 when present) and write the instruction with the ABI register names.
RV32I opcode table
| opcode | Group | Format | Instructions |
|---|---|---|---|
| 0110011 | OP | R | add, sub, and, or, xor, sll, srl, sra, slt, sltu |
| 0010011 | OP-IMM | I | addi, andi, ori, xori, slti, sltiu, slli, srli, srai |
| 0000011 | LOAD | I | lb, lh, lw, lbu, lhu |
| 1100111 | JALR | I | jalr |
| 0100011 | STORE | S | sb, sh, sw |
| 1100011 | BRANCH | B | beq, bne, blt, bge, bltu, bgeu |
| 0110111 | LUI | U | lui |
| 0010111 | AUIPC | U | auipc |
| 1101111 | JAL | J | jal |
Three base instructions are missing: FENCE (0001111) and ECALL/EBREAK (1110011). The site’s decoder does not recognize them.
R format: 0x40B50533
Opcode 0110011 → OP group. The R format has no immediate: the 32 bits are shared by two source registers, one destination and two function fields. funct3 = 000 means add or sub, and funct7 decides: 0100000 is sub.
| Field | Bits | Binary | Meaning |
|---|---|---|---|
| funct7 | [31:25] | 0100000 | distinguishes variants (0100000 = sub/sra) |
| rs2 | [24:20] | 01011 | register x11 (a1) |
| rs1 | [19:15] | 01010 | register x10 (a0) |
| funct3 | [14:12] | 000 | selects the operation within the group |
| rd | [11:7] | 01010 | register x10 (a0) |
| opcode | [6:0] | 0110011 | group OP |
| Instruction | sub a0, a0, a1 | ||
I format: 0xFFF28293
Opcode 0010011 → OP-IMM. Bits 31–20 are the whole immediate: 111111111111. Since bit 31 is 1, extending it to 32 bits fills with ones and the value is −1. Result: addi t0, t0, -1.
| Field | Bits | Binary | Meaning |
|---|---|---|---|
| imm[11:0] | [31:20] | 111111111111 | piece of the immediate |
| rs1 | [19:15] | 00101 | register x5 (t0) |
| funct3 | [14:12] | 000 | selects the operation within the group |
| rd | [11:7] | 00101 | register x5 (t0) |
| opcode | [6:0] | 0010011 | group OP-IMM |
| imm | -1 (0xFFFFFFFF) | rebuilt immediate (signed) | |
| Instruction | addi t0, t0, -1 | ||
In immediate shifts (slli, srli, srai) bits 24–20 are the shift amount and bits 31–25 act as funct7: 0100000 tells srai from srli.
S format: 0xFEA42E23
Opcode 0100011 → STORE. A store writes no register, so the rd slot (bits 11–7) holds the 5 low bits of the immediate. The rest is in bits 31–25: imm[11:5] = 1111111 and imm[4:0] = 11100. Together: 111111111100 = −4.
| Field | Bits | Binary | Meaning |
|---|---|---|---|
| imm[11:5] | [31:25] | 1111111 | piece of the immediate |
| rs2 | [24:20] | 01010 | register x10 (a0) |
| rs1 | [19:15] | 01000 | register x8 (s0) |
| funct3 | [14:12] | 010 | selects the operation within the group |
| imm[4:0] | [11:7] | 11100 | piece of the immediate |
| opcode | [6:0] | 0100011 | group STORE |
| imm | -4 (0xFFFFFFFC) | rebuilt immediate (signed) | |
| Instruction | sw a0, -4(s0) | ||
The instruction is sw a0, -4(s0): it stores a0 at address s0 − 4. Note that rs2 (a0) is the data and rs1 (s0) is the address base.
B format: 0x00050663
Opcode 1100011 → BRANCH. A branch immediate is a multiple of 2 bytes, so its bit 0 is not stored. The other 12 bits are split into four pieces: imm[12] in bit 31, imm[10:5] in 30–25, imm[4:1] in 11–8 and imm[11] in bit 7.
| Field | Bits | Binary | Meaning |
|---|---|---|---|
| imm[12] | [31] | 0 | piece of the immediate |
| imm[10:5] | [30:25] | 000000 | piece of the immediate |
| rs2 | [24:20] | 00000 | register x0 (zero) |
| rs1 | [19:15] | 01010 | register x10 (a0) |
| funct3 | [14:12] | 000 | selects the operation within the group |
| imm[4:1] | [11:8] | 0110 | piece of the immediate |
| imm[11] | [7] | 0 | piece of the immediate |
| opcode | [6:0] | 1100011 | group BRANCH |
| imm | 12 (0xC) | rebuilt immediate (signed) | |
| Instruction | beq a0, zero, 12 | ||
In order: imm[12] = 0, imm[11] = 0, imm[10:5] = 000000, imm[4:1] = 0110, plus the implicit final 0: 0 0 000000 0110 0 = 12. beq a0, zero, 12 jumps 12 bytes (3 instructions) forward if a0 is 0.
U format: 0x123452B7
Opcode 0110111 → LUI. The upper 20 bits are the immediate and go straight into bits 31–12 of the destination register; the low 12 bits become zero. Here t0 becomes 0x12345000.
| Field | Bits | Binary | Meaning |
|---|---|---|---|
| imm[31:12] | [31:12] | 00010010001101000101 | piece of the immediate |
| rd | [11:7] | 00101 | register x5 (t0) |
| opcode | [6:0] | 0110111 | group LUI |
| imm | 74565 (0x12345) | rebuilt immediate (signed) | |
| Instruction | lui t0, 0x12345 | ||
J format: 0x010000EF
Opcode 1101111 → JAL. As in B, bit 0 is not stored and the rest is split: imm[20] in bit 31, imm[10:1] in 30–21, imm[11] in bit 20 and imm[19:12] in 19–12. Here only imm[4] is 1: the jump is 16 bytes and the return address is saved in ra.
| Field | Bits | Binary | Meaning |
|---|---|---|---|
| imm[20] | [31] | 0 | piece of the immediate |
| imm[10:1] | [30:21] | 0000001000 | piece of the immediate |
| imm[11] | [20] | 0 | piece of the immediate |
| imm[19:12] | [19:12] | 00000000 | piece of the immediate |
| rd | [11:7] | 00001 | register x1 (ra) |
| opcode | [6:0] | 1101111 | group JAL |
| imm | 16 (0x10) | rebuilt immediate (signed) | |
| Instruction | jal ra, 16 | ||
Why are immediates scattered?
RISC-V fixes two things in every format: rs1, rs2 and rd are always in the same positions, and the immediate’s sign bit is always bit 31. The hardware can therefore read registers before knowing which instruction it is, and sign extension always uses the same wire. The price is that S, B and J immediates are chopped up. Between S and B very little changes: the bits are reused shifted by one position, because B does not need bit 0.
Common mistakes
- Reading bits 31–20 as the immediate before checking the opcode: that only holds for the I format.
- Forgetting the implicit 0 at the end of B and J immediates (the offset comes out halved).
- Not sign-extending: 0xFFF in an I immediate is −1, not 4095.
- For lui, forgetting that the immediate fills the upper bits: lui t0, 1 loads 4096, not 1.
- Mixing up rs1 and rs2 in stores: the data is rs2 and the base is rs1.