5.1.7 · D5Instruction Set Architecture (ISA)
Question bank — RISC-V base ISA (RV32I - RV64I)
Before we start, one shared vocabulary reminder so nothing below uses a word you haven't pinned down:
You will also meet the six instruction formats by name (R, I, S, B, U, J). Each is a fixed 32-bit template that chops the bits into named fields. Here is the vocabulary and layout for the four that appear on this page:

Now three pictures set up almost every trap below — study them first.




True or false — justify
Are RV32I and RV64I different in how many registers they have?
False — both have exactly 32 general-purpose registers (
x0–x31). Only the width of each register (XLEN) differs: 32 vs 64 bits (see Picture 1).The "32" in "RV32I" tells you how many registers exist.
False — that "32" is the register width (XLEN). It's a pure coincidence that the register count is also 32; that count is fixed in both RV32I and RV64I.
Writing a value to x0 changes it for the next instruction.
False —
x0 is the zero register, hardwired to zero. Writes are silently discarded and reads always return 0; that's what makes it a free source of the constant zero (see the vocabulary definition).RISC-V needs a dedicated "subtract immediate" instruction.
False — immediates are signed (Two's Complement), so
addi rd, rs, -5 already subtracts. One addi covers both directions because sign-extend (Picture 2) turns the negative field into a negative full-width value.Every RV32I instruction is the same length.
True — all base instructions are exactly 32 bits. Fixed length means fetch and decode are trivial and predictable, which helps Pipelining; the six format templates (see the formats figure) all share this width.
RISC-V has a flags register (carry, zero, overflow) like x86.
False — there is no flags register. As Picture 4 shows, a branch instruction feeds two registers straight into a comparator and jumps on the result, so the "did they match?" answer never needs to be stored anywhere (e.g.
bne x1, x0).addi x0, x0, 0 is a genuinely useful instruction.
True — it does nothing observable, which is exactly a
nop. The zero register (writes discarded) makes a do-nothing instruction fall out for free.RV64I's addi uses a 64-bit immediate because registers are 64 bits.
False — the immediate is still 12 bits in the instruction (see the I-type layout); it is just sign-extended to 64 bits instead of 32. Immediate field width and XLEN are independent.
A 20-bit J-type immediate lets jal jump only up to bytes.
False — that would be true if the field counted raw bytes, but the stored offset's bit 0 is implicit 0, so each step is 2 bytes. Restoring that dropped bit doubles the reach to bytes ( MiB), in steps of 2 (see Picture 3).
Spot the error
li x10, 0xEEF — does this leave 0xEEF in x10?
Error.
0xEEF has bit 11 set, so as a signed 12-bit immediate it is negative and sign-extends to 0xFFFF...FEEF (Picture 2). To get a clean positive 0xEEF you need a lui+addi pair."lui x10, 0xDEADB then addi x10, x10, 0xEEF gives 0xDEADBEEF." — right?
Error.
0xEEF sign-extends to a negative add. You must bump the upper part up by one (0xDEADB → 0xDEADC) so the negative low add lands exactly on 0xDEADBEEF. That correction is what the li pseudo-instruction does automatically — see Assembler & Pseudo-instructions."The S-type immediate is split just to confuse decoders."
Error. An S-type is a store instruction (like
sw); its 12-bit immediate is chopped into two pieces (bits 11:5 and 4:0, shown in the formats figure) precisely so rs1 and rs2 stay in fixed bit positions shared with other formats. The CPU can then read registers before decode finishes — the immediate reassembly MUX is the small price for that speed."bne x1, x0, loop needs a special compare-to-zero opcode."
Error. No such opcode exists or is needed:
x0 is the constant zero, so a normal bne comparing against x0 already means "branch if nonzero" (Picture 4 — the comparator does the work)."On RV64I, add on two 32-bit values gives correct C int wrap-around."
Error.
add works on full 64-bit registers and won't wrap at 32 bits. You need addw, which computes the low 32 bits modulo 2 to the 32 and then sign-extends to 64."A branch offset of imm = 1 moves the target 1 byte forward."
Error. Branch/jump offsets are in units of 2 bytes (bit 0 is implicit 0). The smallest nonzero step is 2 bytes, matching instruction alignment (Picture 3).
Why questions
Why is a constant-zero register (x0) such a good design choice?
One decision replaces a dozen special opcodes:
mv, li 0, nop, and compare-to-zero branches all reduce to normal instructions using x0.Why does RISC-V sign-extend the I-type immediate instead of zero-extending?
So that a negative small constant like
-1 becomes all-ones (0xFFFF...FF) and arithmetic "just works" — subtraction and negative offsets need no extra instructions (Two's Complement).Why make instructions fixed 32-bit length instead of variable like CISC?
Fixed length makes fetch/decode trivial and parallel, simplifying Pipelining. This is the core RISC vs CISC trade: simpler hardware, compiler does more work.
Why keep rs1/rs2/rd in the same bit positions across all formats?
The register file can be read before the immediate is decoded, shaving a step off the pipeline. Fixed field positions are a deliberate speed-over-prettiness choice (compare the I-, S-, B-, J-layouts in the formats figure — the register fields line up).
Why add *w instructions to RV64I rather than always masking by hand?
They give exact 32-bit modular arithmetic (then sign-extend) in one instruction, so C's
int behaves correctly on a 64-bit machine without manual masking.Why does making bit 0 of the branch offset implicit double the range?
A field of N bits counts 2 to the N different values. If those values are byte-offsets, reach is 2 to the N bytes. But if each stored value stands for two bytes (because the dropped bit 0 is always 0), the same N bits now cover 2 times 2 to the N bytes — a clean doubling for free (Picture 3).
Why does RISC-V leave multiply/divide out of the base I set?
To keep the mandatory core tiny so every chip (tiny embedded to server) can implement it cheaply. Multiply/divide is the optional M (Multiply/divide) extension — you write
RV32IM when a chip includes it.Edge cases
What does addi x0, x5, 100 actually do?
Nothing observable — the result would go to
x0, whose writes are discarded. It's effectively a nop that also wastes a computed sum.What's the largest and smallest value an I-type immediate can hold?
A 12-bit signed field has patterns, split exactly in half: 2048 negative values (
-2048 up to -1) and 2048 non-negative values (0 up to +2047). So the range is -2048 up to +2047. Anything outside needs lui+addi.Can a single branch reach anywhere in a program?
No — B-type offsets reach only about plus-or-minus 4 KiB; exactly, the target can go from -4096 up to +4094 bytes (even addresses only). Longer control transfers use
jal (about plus-or-minus 1 MiB) or jalr (register-relative, full range).How does the C (Compressed) extension interact with the "bit 0 is implicit 0" rule?
With C, instructions may be 16-bit and are aligned to 2 bytes, so addresses are still even — only bit 0 is guaranteed zero, not bit 1. That's why offsets scale by 2, not by 4; the base ISA deliberately keeps this door open for
C.On RV64I, after addw produces a result, what are the top 32 bits?
They are a sign-extension of bit 31 of the 32-bit result — all copies of that bit — so the 64-bit register holds a properly sign-extended 32-bit value.
If an immediate has its top (sign) bit set, is it positive or negative?
Negative — under Two's Complement, a set most-significant bit means the value is below zero. This is exactly why
0xEEF (bit 11 set) is -273, not +3823 (Picture 2).What happens if you try to lui a value that needs more than 20 upper bits?
You can't in one instruction —
lui only fills the upper 20 bits (bits 31:12). Larger constants on RV64I require additional shift/add steps, which the assembler expands for you.Is mv rd, rs a real RISC-V opcode?
No — it's a pseudo-instruction that assembles to
addi rd, rs, 0. See Assembler & Pseudo-instructions; the hardware never sees a "move" opcode.Recall The three villains, tied to their examples
- Villain 1 — the two "32"s (Picture 1): appears in the first two True/False items. Count = 32 always; width = XLEN.
- Villain 2 — signed 12-bit immediates (Picture 2): drives the
li x10, 0xEEFand0xDEADB-vs-0xDEADCerrors. Bit 11 set means negative. - Villain 3 — offsets in units of 2 bytes (Picture 3): behind the
imm = 1, J-type range, and C-extension items. Dropping bit 0 doubles reach.