Intuition What this page is for
The parent note Instruction formats and encoding taught you the rules of packing an instruction into 32 bits. This page drills the rules until no scenario surprises you : positive and negative immediates, splitting bits, sign-extension, zero and maximum values, and the trap questions an exam loves. We build every worked example from raw bits — nothing is assumed.
Before anything, let us re-anchor the three words this whole page rests on, in plain language:
Definition The three words we will use constantly
A bit is a single 0 or 1. Picture a light switch: off = 0, on = 1.
A field is a fixed group of neighbouring bits with a job. Picture a row of switches fenced off with a label above it ("this row = destination register").
Encoding = you hold real numbers (register 5, immediate −8) and you fill in the switches. Decoding = the CPU reads the switches back into meaning.
Every number below is written in binary (base 2): reading right-to-left the columns are worth 1 , 2 , 4 , 8 , 16 , … (powers of two). So 00101 means 4 + 1 = 5 .
Definition The bracket notation
imm[a:b] — define it before we use it
When an immediate is split, we must name which bits of it live where. The notation ==imm [ a : b ] means "bits a down to b of the immediate value, counted from the right (bit 0 is the ones column)."== So imm [ 4 : 0 ] is the lowest five bits, and imm [ 11 : 5 ] is the seven bits just above them. A single bit is written imm [ 7 ] (just bit 7). Picture the immediate as a numbered ruler of switches; imm[a:b] fences off a labelled stretch of that ruler.
Two prerequisites we lean on and where to revise them: Registers and the Register File (why a register number is just a small integer) and Two's Complement and Sign Extension (how negative numbers live in bits).
Every encoding question you will ever see is one of these cells. The worked examples below are tagged with the cell they cover, and together they hit all of them.
#
Cell (case class)
What makes it tricky
Example
A
Simple positive R-type
just pack registers
Ex 1
B
Positive immediate (I-type)
immediate sits in one block
Ex 2
C
Negative immediate
top bit = sign, must sign-extend
Ex 3
D
Zero / degenerate input
register 0, immediate 0
Ex 4
E
Maximum / limiting value
biggest immediate a field holds
Ex 5
F
Split immediate (S-type)
bits scattered across two holes
Ex 6
G
Reordered + scaled (B-type)
immediate in units of 2 bytes
Ex 7
H
Decode direction (bits → meaning)
reverse the whole process
Ex 8
I
Field-width word problem
choose bit counts from requirements
Ex 9
J
Exam twist (opcode-budget trap)
more opcode ≠ more room
Ex 10
The R-type field order we will reuse (from the parent note) is the "phone number" 7-5-5-3-5-7 — read the figure below for the picture; the math line just names the same fences:
7 f u n c t 7 5 r s 2 5 r s 1 3 f u n c t 3 5 r d 7 o p co d e
Worked example Example 1 — Encode
add x5, x6, x7
Forecast first: the opcode for add is 0110011, funct3 = 000, funct7 = 0000000. Before reading on, guess: what 5-bit patterns do registers 5, 6, 7 become, and in what left-to-right order do they sit?
Step 1 — turn each register number into 5 bits.
5 = 4 + 1 → 00101 , 6 = 4 + 2 → 00110 , 7 = 4 + 2 + 1 → 00111 .
Why this step? Each register field is exactly 5 bits wide (32 registers need ⌈ log 2 32 ⌉ = 5 bits), so we must write each number in 5 columns, padding with leading zeros.
Step 2 — identify which register is which. In add rd, rs1, rs2: destination rd = x5, first source rs1 = x6, second source rs2 = x7.
Why this step? The assembly writes destination first, but the bits store rs2 far left and rd near the opcode — mixing these up is the #1 error. Look at the figure above: destination lives near the opcode (green), sources further left (blue).
Step 3 — drop each field into its 7-5-5-3-5-7 slot.
f u n c t 7 0000000 r s 2 = 7 00111 r s 1 = 6 00110 f u n c t 3 000 r d = 5 00101 o p co d e 0110011
Why this step? The Instruction Decode Stage always reads the lowest 7 bits as opcode, so we place fields exactly where decode looks.
Verify: count the bits: 7 + 5 + 5 + 3 + 5 + 7 = 32 . ✓ Full word as one 32-bit binary equals hex 0x007302B3 — checked in VERIFY.
Intuition What changes for I-type
An immediate is a number baked directly into the instruction (from the Latin "immediate" = right here, no lookup). For I-type we no longer need rs2 or funct7; we glue those 12 bits into one 12-bit immediate field. Using the bracket notation we just defined, all twelve bits imm [ 11 : 0 ] sit together:
12 imm [ 11 : 0 ] 5 r s 1 3 f u n c t 3 5 r d 7 o p co d e
Worked example Example 2 — Encode
addi x1, x2, 100
Forecast: 100 in 12 bits — guess how many leading zeros it needs.
Step 1 — write 100 in binary. 100 = 64 + 32 + 4 = 1100100 (7 bits).
Why? We need the raw value before we know how much padding a 12-bit field requires.
Step 2 — pad to 12 bits. 100 = 000001100100 .
Why? The immediate field is 12 columns wide; positive numbers pad with leading zeros. The top bit is 0, which (previewing Ex 3) means "positive".
Step 3 — place fields. addi opcode = 0010011, funct3 = 000, rs1 = x2 = 00010, rd = x1 = 00001.
imm = 100 000001100100 r s 1 00010 f u n c t 3 000 r d 00001 o p co d e 0010011
Verify: 12 + 5 + 3 + 5 + 7 = 32 ✓. Decode the immediate back: 00000110010 0 2 = 100 ✓ (checked in VERIFY).
Intuition Why negative numbers need a special rule
A 12-bit field holds only 12 switches, but the Instruction Decode Stage must widen it to 32 bits before the ALU adds. If we widened − 8 by simply padding with zeros, the top would read as a huge positive number . Sign extension copies the top bit outward, so a negative stays negative. This is exactly Two's Complement and Sign Extension .
Worked example Example 3 — Encode
addi x1, x2, -8, then sign-extend
Forecast: in 12-bit two's complement, will − 8 start with a 1 or a 0? Guess how the 32-bit widened value looks.
Step 1 — find − 8 in 12-bit two's complement. Take + 8 = 000000001000 , flip every bit → 111111110111 , add 1 → 111111111000 .
Why? Two's complement is defined as "invert and add one"; that gives the pattern whose value is − 8 .
Step 2 — read the sign bit. Bit 11 (leftmost) is 1 ⇒ negative. ✓ Forecast confirmed.
Why? In two's complement the top bit alone tells the sign — 1 means negative.
Step 3 — sign-extend to 32 bits. Copy the top bit (1) into the extra 20 bits:
20 copied ones 1111 1111 1111 1111 1111 − 8 in 12 bits 1111 1111 1000
Why? Copying the sign bit keeps the value − 8 instead of turning it into + 4088 . The figure shows the top bit fanning out to the left.
Verify: interpret the 32-bit result as signed: it equals − 8 . And + 4088 would be the wrong zero-extended answer — both checked in VERIFY.
Worked example Example 4 — Encode
add x0, x0, x0
Forecast: register 0 is special in RISC-V (it is hard-wired to always read 0). Guess what the whole instruction word looks like when everything is zero.
Step 1 — encode register 0. 0 = 00000 (five zeros). All three register fields are 00000.
Why? Zero is a valid register index; it just happens to be all zeros. Degenerate inputs still follow the normal rule.
Step 2 — assemble. add keeps opcode 0110011, funct3 000, funct7 0000000:
0000000 00000 00000 000 00000 0110011
Why? Nothing changes about the layout; only the values are zero.
Verify: as a 32-bit integer this is 0x00000033, not all-zero — because the opcode bits are still set. The lesson: a "do nothing on zeros" instruction is not the all-zero word. Checked in VERIFY. (The truly all-zero word 0x00000000 is an illegal instruction in RISC-V — a nice degenerate edge to remember.)
Worked example Example 5 — What is the largest and smallest immediate an I-type can hold?
Forecast: a 12-bit signed field — guess the two extremes.
Step 1 — split the range. With 12 bits, one bit is the sign, leaving 11 magnitude bits.
Why? In two's complement the range is asymmetric: negatives get one extra slot because 0 counts as a non-negative.
Step 2 — compute the extremes.
max = 2 11 − 1 = 2047 , min = − 2 11 = − 2048.
Why? The largest positive is all-ones-except-sign (011 … 1 ); the most negative is sign-bit only (100 … 0 ). The "− 1 " on the positive side is because we spent one pattern on zero.
Step 3 — pattern check. 2047 = 011111111111 , − 2048 = 100000000000 .
Why this step? We confirm the extremes are legal bit-patterns and not off by one: 011111111111 is every magnitude bit set with a 0 sign bit (biggest positive), and 100000000000 is the sign bit alone (most negative). Seeing the actual bits guards against the classic ± 1 range error.
Verify: 2 11 − 1 = 2047 and − 2 11 = − 2048 , and decoding 100000000000 as two's complement gives − 2048 — all checked in VERIFY. Limiting behaviour: ask for addi x1,x2,2048 and the assembler rejects it — it does not fit.
Intuition Why the immediate gets chopped
For a store sw x7, 12(x2) we still need both rs1 and rs2 in their usual seats so the register-read wiring never changes. That leaves no single 12-bit hole — so the 12-bit offset is split into two leftover gaps. The split is a hardware convenience , not a math change.
7 imm [ 11 : 5 ] 5 r s 2 5 r s 1 3 f u n c t 3 5 imm [ 4 : 0 ] 7 o p co d e
Worked example Example 6 — Encode the offset of
sw x7, 12(x2)
Forecast: the offset is 12. How do its bits divide into the top-7 and bottom-5 pieces?
Step 1 — write 12 in 12 bits. 12 = 8 + 4 = 000000001100 .
Why? We need all 12 bits before we cut them.
Step 2 — cut into imm[11:5] and imm[4:0].
imm [ 4 : 0 ] = lowest 5 bits = 01100
imm [ 11 : 5 ] = next 7 bits = 0000000
Why? Using the bracket notation from the top: we slice the same number 12 into two pieces that live in different fields.
Step 3 — place them. sw opcode 0100011, funct3 010, rs1 x2 = 00010, rs2 x7 = 00111:
imm [ 11 : 5 ] 0000000 r s 2 00111 r s 1 00010 f u n c t 3 010 imm [ 4 : 0 ] 01100 o p co d e 0100011
Verify: reassemble imm [ 11 : 5 ] ∥ imm [ 4 : 0 ] = 0000000 01100 = 12 ✓ (checked in VERIFY). The register fields rs1, rs2 sit in the same positions as R-type — that is the whole point.
Look at the figure below. The top row shows the plain 12-bit value 12; the coloured arrows carry its two slices down into the S-type template. Notice the green slice (imm [ 11 : 5 ] ) and the orange slice (imm [ 4 : 0 ] ) land in non-adjacent holes, while rs1 and rs2 (blue) stay exactly where R-type put them — that unchanged register wiring is the reason for the whole split.
Intuition The two extra twists of a branch
A branch offset (beq x1, x2, target) has two surprises beyond the S-type split:
Scaled by 2 — instructions are 2-byte aligned, so the offset is stored in units of 2 bytes ; bit 0 is always 0 and is not stored. A stored value of n means jump 2 n bytes.
Reordered — the bits are permuted so that the sign bit lands in the same top position as every other format, letting one sign-extend wire serve all formats.
Intuition The exact B-type field breakdown (which bit goes where)
A branch offset is a 13-bit signed number imm [ 12 : 0 ] , but because bit 0 is always 0 (2-byte alignment) only 12 bits are actually stored. Its sign bit is imm [ 12 ] , the top bit. RISC-V packs the stored bits like this (the two 5/7-wide holes of S-type, reshuffled):
7 imm [ 12 ] ∥ imm [ 10 : 5 ] 5 r s 2 5 r s 1 3 f u n c t 3 5 imm [ 4 : 1 ] ∥ imm [ 11 ] 7 o p co d e
Slice widths: imm [ 12 ] = 1 bit, imm [ 10 : 5 ] = 6 bits (fills the 7-bit top hole), imm [ 4 : 1 ] = 4 bits, imm [ 11 ] = 1 bit (fills the 5-bit low hole). Bit imm [ 12 ] (the sign) sits at the very top so one sign-extend wire works for every format.
Worked example Example 7 — Encode the branch offset of
beq x1, x2, +12 and confirm the distance
Forecast: guess before computing — does a stored value that means "12 bytes" put a 1 in the sign bit? Is the branch forward or backward?
Step 1 — write the byte offset as a 13-bit signed value. + 12 = 0 0000 0000 1100 as imm [ 12 : 0 ] (thirteen bits, bit 0 on the right).
Why? Branch offsets are 13-bit signed byte distances; we need every bit before we can pick slices out of it.
Step 2 — read the always-zero bottom bit. imm [ 0 ] = 0 . ✓
Why? Instructions are 2-byte aligned, so the byte offset is always even — bit 0 is guaranteed 0 and is therefore not stored (this is the "scaled by 2" trick). The stored halfword-count is 12/2 = 6 .
Step 3 — pull out each named slice. From imm [ 12 : 0 ] = 0 0000 0000 1100 :
imm [ 12 ] (sign) = 0
imm [ 11 ] = 0
imm [ 10 : 5 ] = 000000
imm [ 4 : 1 ] = 0110 (bits 4,3,2,1 = 0,1,1,0)
Why? These are exactly the four slices the B-type template asks for; we cut the 13-bit ruler into the pieces named in the breakdown callout above.
Step 4 — place slices into the 7-5-5-3-5-7 template. beq opcode 1100011, funct3 000, rs1 x1 = 00001, rs2 x2 = 00010:
imm [ 12 ] ∥ imm [ 10 : 5 ] 0 000000 r s 2 00010 r s 1 00001 f u n c t 3 000 imm [ 4 : 1 ] ∥ imm [ 11 ] 0110 0 o p co d e 1100011
Why? Each slice drops into its assigned hole so the sign bit imm [ 12 ] lands at the very top position, shared with every other format's sign bit.
Verify: the stored halfword count is 6 , so the effective jump is 6 × 2 = 12 bytes forward; the sign bit imm [ 12 ] = 0 confirms forward . Reassembling the four slices back into imm [ 12 : 0 ] gives 12 — all checked in VERIFY. This is exactly the parent-note mistake: the raw field ≠ the effective value. Ask "is it scaled or sign-extended?" — for B-type, both .
Worked example Example 8 — Decode the 32-bit word
0x00C58533
Forecast: we go backwards now. Guess: which field do we read first ?
Step 1 — write the hex as 32 bits. 0x00C58533 = 0000 0000 1100 0101 1000 0101 0011 0011.
Why? Every hex digit is 4 bits; we expand to see individual fields.
Step 2 — peel the lowest 7 bits (opcode). Lowest 7 = 0110011 → R-type add class.
Why? Decode always reads the opcode first, from the low end — it tells us which format the rest follows.
Step 3 — slice the remaining fields by 7-5-5-3-5-7. Reading up from the opcode:
rd (next 5) = 01010 = x10
funct3 (3) = 000
rs1 (5) = 01011 = x11
rs2 (5) = 01100 = x12
funct7 (7) = 0000000
Why? We reverse the packing: same fences, read values out instead of in.
Verify: the instruction is add x10, x11, x12. Re-encoding those fields gives back 0x00C58533 — checked in VERIFY.
Worked example Example 9 — Design a tiny ISA
Problem: A toy CPU has 16 registers and needs 40 distinct operations. Every instruction is [opcode][rd][rs1] (two registers). What is the minimum instruction width?
Forecast: guess whether it fits in one byte (8 bits).
Step 1 — opcode bits. ⌈ log 2 40 ⌉ : 2 5 = 32 < 40 , 2 6 = 64 ≥ 40 → 6 bits .
Why? A field of b bits encodes 2 b patterns; we need at least 40, so 6.
Step 2 — register bits. 16 registers → ⌈ log 2 16 ⌉ = 4 bits each, and there are two register fields (rd and rs1), so 4 + 4 = 8 bits.
Why? 2 4 = 16 exactly covers 16 registers, and the problem states two register operands.
Step 3 — total and round up. 6 + 4 + 4 = 14 bits. Real hardware fetches whole bytes, so round up to a byte boundary → 16 bits (2 bytes).
Why? A 14-bit word wastes 2 bits but is padded to 16 for clean byte-aligned fetching. This is the RISC vs CISC fixed-width philosophy in miniature.
Verify: ⌈ log 2 40 ⌉ = 6 , ⌈ log 2 16 ⌉ = 4 , sum = 14 , rounded to 16 — all checked in VERIFY. So the answer to the forecast is no , one byte is not enough.
Worked example Example 10 — The tempting wrong answer
Problem: "A student widens the primary opcode from 7 to 12 bits in a 32-bit fixed-width R-type to support more operations, keeping three 5-bit registers and a 3-bit funct3 and the 7-bit funct7. Does it still fit? What broke?"
Forecast: guess yes or no before adding.
Step 1 — sum the demanded bits. 12 ( opcode ) + 5 + 5 + 5 ( regs ) + 3 ( f u n c t 3 ) + 7 ( f u n c t 7 ) = 37 bits.
Why? The word is a fixed 32-bit budget ; every bit added to the opcode is stolen from somewhere else. We total what the design demands and compare to what we have .
Step 2 — compare to budget. 37 > 32 . It does not fit. Something must be dropped — likely funct7, losing the ability to distinguish add from sub.
Why? This is the parent-note mistake steel-manned: more opcode bits ≠ more usable instructions if they crowd out required fields.
Step 3 — the right fix. Keep the 7-bit opcode and expand operations through funct3/funct7 sub-fields, exactly as real RISC-V does — no budget overflow.
Why? Sub-fields multiply the operation count within the existing opcode class without spending scarce primary-opcode bits.
Verify: 12 + 5 + 5 + 5 + 3 + 7 = 37 > 32 ✓ (VERIFY). The trap answer "yes it fits" is provably wrong.
Recall Quick self-test on the whole matrix
Which cell involves multiplying the stored value by 2 before use? ::: Cell G (B-type branch, scaled by 2 bytes).
Why does S-type split its immediate? ::: So rs1 and rs2 stay in their normal positions and the register-read wiring never changes.
Largest value a 12-bit signed immediate holds? ::: + 2047 ; smallest is − 2048 .
Which bit of a B-type immediate is the sign bit? ::: imm [ 12 ] , the top bit, placed at the very top of the word so one sign-extend wire serves all formats.
Encoding add x0,x0,x0 — is the word all zeros? ::: No — the opcode bits 0110011 are still set (0x00000033).
First field the decoder reads? ::: The opcode, in the lowest 7 bits.
Mnemonic The scenario checklist before you answer any encoding question
"S-S-S-P" — is it S igned? S plit? S caled? and does it fit the P udget (budget)? Ask these four and no exam twist can ambush you.
See also: Addressing Modes (how the immediate becomes a memory address) and Pipelining (why fixed-width fields make decode fast enough to overlap).