5.1.2 · D5Instruction Set Architecture (ISA)

Question bank — Instruction formats and encoding

2,388 words11 min readBack to topic

First — a picture and a jargon refresher (so this page stands alone)

Everything below assumes you can see a 32-bit instruction word: a row of 32 tiny boxes, each holding one bit (a 0 or a 1). We chop that row into fields — labelled coloured stretches of boxes. Look at the diagram before reading any question.

Figure — Instruction formats and encoding

True or false — justify

True or false: In a fixed-length ISA (Instruction Set Architecture), every instruction uses every field.
False. Every instruction is the same width, but a format may leave fields unused or repurpose their bits (e.g. I-type turns rs2+funct7 into a 12-bit immediate). Fixed width ≠ fixed field usage.
True or false: The opcode is always the first thing written on the page (leftmost bits).
False. In RISC-V the opcode lives in the lowest 7 bits (rightmost when written), and the decoder reads it there. "Fixed position" means fixed, not necessarily leftmost — see Instruction Decode Stage.
True or false: A 32-instruction ISA needs a 5-bit opcode.
True as a lower bound: . But real ISAs often use a wider primary opcode plus funct sub-fields so they can add operations later without redesigning the word.
True or false: Two's-complement immediates are needed because subtraction is a separate instruction.
False. They're needed so a single field can hold negative numbers (like addi x1, x2, -8); the top bit is the sign bit. See Two's Complement and Sign Extension.
True or false: Variable-length encoding is objectively worse than fixed-length.
False — it's a trade-off. Variable (x86) gives denser code and better instruction-cache hits; fixed (RISC-V, MIPS, ARM) gives fast, parallel decode. Neither dominates; see RISC vs CISC.
True or false: Because R-type and I-type share opcode/rd/funct3/rs1 positions, the register-read hardware can be identical for both.
True. Keeping rs1 (and rs2 where present) in fixed bit positions across formats means the Registers and the Register File read ports are wired the same way; only the immediate-assembly logic changes.
True or false: If an ISA has fewer than 128 operations, using a 7-bit opcode wastes bits.
Partly false. The "spare" patterns aren't waste — they're reserved room for future instructions, and 7 bits keeps tidy. Spare opcode space is a feature, not leakage.
True or false: U-type and J-type both carry a 20-bit immediate, so they use the immediate the same way.
False. U-type (lui, auipc) places its 20 bits directly into the upper part of a 32-bit value (bits 31:12); J-type (jal) reorders and scales its 20 bits into a signed jump offset. Same width, very different plumbing.
True or false: The immediate bits stored in a B-type (branch) instruction equal the byte offset the CPU jumps by.
False. The B-type immediate is stored scaled, sign-extended, and its bits are reordered/scattered. The raw field must be reassembled before it becomes the real offset — see the B-type walkthrough below.

Spot the error

Spot the error: "We can add more registers just by widening the register fields, no other cost."
Widening rs1/rs2/rd steals bits from the same fixed word. Going from 32 to 64 registers (5→6 bits) eats 3 extra bits across three fields, shrinking the immediate or opcode room. The budget is fixed.
Spot the error: "S-type splits its immediate because the designers ran out of space to be lazy."
The split exists so rs1 and rs2 stay in their usual positions. Keeping registers fixed simplifies the read logic; the immediate takes whatever leftover holes remain. It's a deliberate hardware win, not laziness.
Spot the error: "To decode add x5,x6,x7, first read the funct7 field to know it's an add."
The decoder reads the opcode (low 7 bits) first to learn the class, then funct3/funct7 disambiguate add vs sub within that class. Opcode leads; funct refines.
Spot the error: "A 12-bit signed immediate can represent 0 to 4095."
That's the unsigned range. Signed two's-complement 12 bits covers to — the same 4096 patterns, but half are negative because the top bit is a sign bit.
Spot the error: "Sign-extension just pads the immediate with zeros to reach 32 bits."
Zero-padding would turn into a huge positive number. Sign-extension copies the sign bit into the new high bits, preserving value: stays . See Two's Complement and Sign Extension.
Spot the error: "Since decode finds the opcode instantly, variable-length x86 decodes just as fast as RISC-V."
In variable-length encoding you can't locate instruction until you know the length of instruction — a serial dependency. That length-finding step is the bottleneck fixed-length avoids.
Spot the error: "funct3 and funct7 are extra opcodes, so they count toward how many major operations the ISA has."
They're sub-selectors that expand operations within a primary opcode class, without growing the primary opcode. They let you multiply operations while spending the primary opcode budget only once.
Spot the error: "lui (U-type) loads its 20-bit immediate as a small number 0–1048575 into the register."
lui places those 20 bits into bits 31:12 and zero-fills the low 12, so the value is the immediate shifted left by 12 — a large number, used to build full 32-bit constants together with a following addi.

Why questions

Why does the opcode sit in a fixed bit position across all formats?
So the decoder can always find what kind of instruction this is before it knows anything else, letting it choose how to interpret the remaining bits. Fixed opcode position = decode can start immediately and in parallel.
Why must a 12-bit immediate be sign-extended to 32 bits before the ALU (Arithmetic Logic Unit) sees it?
The ALU operates on full 32-bit values. A 12-bit field must be widened while preserving its numeric meaning, so the sign bit is replicated — otherwise negatives would silently become large positives.
Why does giving more bits to the opcode reduce the possible immediate range?
The 32-bit word is a fixed budget. Every bit spent on the opcode is unavailable to the immediate/register fields, so a bigger opcode directly shrinks how large an immediate you can encode.
Why does variable-length encoding improve instruction-cache behaviour?
Common tiny operations fit in 1 byte instead of 4, so programs occupy less memory. Denser code means more instructions fit in the same cache, raising the hit rate and easing memory bandwidth.
Why can't a single rigid instruction layout serve add, addi, and jal well?
They need different things — three registers vs. two registers plus a number vs. one register plus a big jump distance. One rigid layout would either waste bits or run short on immediate space, so multiple formats reuse the same 32 bits differently.
Why do U-type and J-type both dedicate 20 bits to the immediate?
They exist to build big values: U-type sets the high 20 bits of a 32-bit constant/address, and J-type reaches far-away jump targets. Neither needs rs2, so those freed bits are handed to a wide immediate instead.
Why is the B-type branch immediate stored scaled (not as a raw byte count)?
Because valid branch targets always land on an aligned instruction boundary, the lowest offset bit(s) are known to be 0 and needn't be stored — so the same stored bits reach farther. Caveat: in base RV32I instructions are 4-byte aligned, so the true useful step is even coarser; the "units of 2 bytes" scaling in the spec exists because the optional compressed (C) extension allows 2-byte-aligned instructions. The walkthrough below unpacks how the scattered bits reassemble.
Figure — Instruction formats and encoding

Edge cases

Edge case: An ISA has exactly 1 instruction. How many opcode bits?
bits — you need no opcode to distinguish one thing from itself. In practice you'd still reserve bits for future growth, but mathematically zero suffices.
Edge case: An ISA has exactly a power of two, say 64, operations. Opcode width?
bits, used with no spare patterns — every one of the codes is assigned. Adding even one more operation would force a 7th bit.
Edge case: What does an all-zeros immediate mean in a signed field vs. an unsigned one?
Zero in both cases — the sign bit is 0, so signed and unsigned agree at 0. The interpretations only diverge once the top bit is 1.
Edge case: A store needs rs1, rs2, and an offset, but the word has no contiguous 12-bit hole. What happens?
The immediate is split across the two leftover regions (S-type), keeping rs1/rs2 fixed. The decoder reassembles the scattered pieces into one 12-bit value before use.
Edge case: You must encode addi x1, x2, +2048. Does it fit a 12-bit signed immediate?
No. The maximum is (); overflows the positive end. You'd need a different sequence (e.g. lui + addi) — a real limit assemblers handle for you.
Edge case: And the lower bound — does addi x1, x2, -2049 fit?
No. The most-negative 12-bit two's-complement value is ; falls one step below it and overflows the negative end. The signed window is asymmetric: it reaches one further negative () than positive ().
Edge case: A U-type immediate is 20 bits — what value range does lui actually place into the register?
The 20 bits fill bits 31:12, so the register gets that field shifted left by 12: multiples of from up to . It sets the upper part; the low 12 bits are zero until a later addi fills them.
Edge case: Is an all-zeros 32-bit word always an illegal/reserved instruction?
No — it depends on the ISA. In RISC-V opcode 0000000 is unassigned, so an all-zeros word traps as illegal (a useful safety net against jumping into zeroed memory). But other ISAs deliberately define the all-zeros pattern as a NOP (do nothing). The rule is ISA-specific, not universal.

Recall One-line summary of the traps

The fixed word is a budget, the opcode is anchored, immediates are signed, asymmetric, and sometimes reshaped/reordered (S, B, U, J), and fixed-vs-variable is a trade-off — most errors come from forgetting one of these four.

Return to Instruction formats and encoding.