4.1.7 · D2Computer Architecture (Deep)

Visual walkthrough — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)

2,570 words12 min readBack to topic

Step 1 — What is a "32-bit number", really?

WHAT. A MIPS instruction is one row of 32 tiny on/off switches. Each switch is a bit — a single 0 or 1. Nothing more. The CPU has no idea what any switch means until we agree on a rulebook.

WHY start here. Because the whole problem of "instruction formats" is just: how do we carve this row of 32 switches into meaningful chunks? If we don't fix the carving in advance, the hardware cannot know whether switches 21–25 name a locker or spell part of a number.

PICTURE. Below: 32 empty boxes, numbered from bit 31 (left, most significant) down to bit 0 (right). This is the blank canvas every instruction is painted on.

Figure — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)

Step 2 — The first question: "how many things can a chunk name?"

WHAT. Before carving anything, learn the one counting rule that governs every field width on this page: a chunk of bits can spell exactly different patterns.

WHY this tool and not another. We keep asking "how wide must this field be to name X different things?" The inverse of is the logarithm base 2, written . That's the tool: answers "how many bits do I need to name X items?" We use (not division, not ) because doubling the bits doubles the choices — a multiplicative, not additive, growth.

  • — take a chunk of switches, count how many distinct 0/1 pictures it can show.
  • — the reverse question: given that we must name things, how many switches?
  • — the "round up" brackets (a ceiling), because you can't have a fraction of a switch.

PICTURE. A staircase: 1 bit → 2 patterns, 2 bits → 4, 3 bits → 8… and the reverse arrow reading it as "to name 32 things, climb to 5 bits."

Figure — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)
Recall Why 5 bits for a register?

MIPS has 32 registers → . See MIPS Register File and Conventions. How many bits to name 32 registers? ::: 5, because .


Step 3 — Reserve the master key: the opcode

WHAT. Slice off the top 6 bits and call them the opcode. This chunk's only job is to answer "which rulebook (format) does the rest of this instruction follow?"

WHY 6 bits, and why first?

  • First — so the decoder can read it immediately, before it knows anything else, and pick the right rulebook. (See Control Unit and Decoding.)
  • 6 bits — this is a design choice giving possible opcodes. That's a comfortable menu of operations while leaving 26 bits below for everything else. Remember 6; it never changes across the three formats.

PICTURE. The 32-box strip with the leftmost 6 boxes shaded blue and labelled "opcode → which rulebook?". The remaining 26 boxes are a gray "to be decided" region.

Figure — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)

Step 4 — R-type: carve the 26 leftover bits for "three lockers + which math"

WHAT. We want instructions like add $t0,$s1,$s2 — read two registers, combine them, write a third. That needs to name three registers and say which combining operation.

WHY these exact widths — watch them fall out.

  • Three register names. Each register (from Step 2) needs bits. Three of them = bits. Call them rs, rt (the two sources) and rd (the destination).
  • A shift amount. Shift instructions (sll) can move a 32-bit word by 0–31 places → bits. Call it shamt.
  • "Which math?" Because opcode was spent as 0, we need a second selector. We have bits left — exactly enough for R-type operations. Call it funct.

Term by term: opcode = 0 announces "R-type"; rs, rt = the two source lockers to read; rd = the destination locker to write; shamt = how far to shift (0 for non-shift ops); funct = the real operation code. Add them: . ✓ Nothing left over, nothing missing.

PICTURE. The strip fully carved into six colored fields with widths labelled, and a checksum "6+5+5+5+5+6 = 32" underneath.

Figure — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)

Step 5 — I-type: trade two fields for one 16-bit number

WHAT. Now we want a constant baked in — addi $t0,$s1,10, lw $t0,32($s3), beq. We keep the opcode (6) and only need two registers now, but we need room for a number.

WHY the number is 16 bits. Start from R-type and merge the three trailing fields rd + shamt + funct = bits into a single immediate field. That's where 16 comes from — it's not chosen, it's the leftover after opcode + two registers:

Term by term: rs = one source (for loads it's the base address); rt = now the destination for addi/lw (it receives the result), but a source for sw/beq; immediate = the 16-bit constant/offset. Because a constant can be negative (e.g. addi $t0,$t0,-1), the top bit of the immediate is a sign, copied leftward when used — that's Sign Extension and Immediates.

PICTURE. Two strips stacked: R-type on top, and I-type below with rd|shamt|funct shown collapsing (arrows) into one wide 16-bit "immediate" block.

Figure — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)

Step 6 — The branch trick: how 16 bits reach far away

WHAT. beq $rs,$rt,LABEL must name a target address — but addresses are 32 bits and we only have 16 left. Deriving the escape.

WHY multiply by 4, and why relative. Two observations do all the work:

  1. Every instruction is 4 bytes, so every instruction address is a multiple of 4 — its low 2 bits are always 00. Storing those two zeros is a waste. So the 16 bits count words (groups of 4 bytes), not bytes. Hardware shifts left by 2 (i.e. ) to restore the bytes.
  2. Branches jump nearby (loops, ifs), so instead of an absolute address we store a signed offset from the current spot — that's why the immediate is signed (positive = forward, negative = backward).

  • — the address of the instruction after the branch (the CPU already advanced the program counter by 4).
  • — the 16-bit signed word offset; negative reaches backward.
  • — undo the "counted in words" saving, converting back to bytes.

Effective reach: 16 signed bits + 2 implied = 18-bit signed byte reach ( KB). See PC-relative vs Absolute Addressing.

PICTURE. A vertical column of instruction addresses (…P, P+4, P+8…). An arrow from beq at P curves to P+4 + imm×4, with a forward (green) and backward (red) example.

Figure — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)

Step 7 — J-type: spend almost everything on an address

WHAT. j LABEL needs no registers at all — just "go there." Keep the opcode (6), give the other 26 bits to the address.

WHY 26 can still reach a 32-bit target. Same two tricks, pushed harder:

  • Low 2 bits are 00 (word-aligned) → append them, buying 2 bits for free.
  • The top 4 bits are not stored — they're copied straight from the current PC. So a jump can only move within the same 256 MB region the PC is already in.

  • — the four high bits taken from the program counter (not from the instruction).
  • — "glue together" (concatenate) the bit groups.
  • — the 26 bits we actually stored.
  • — the two always-zero low bits, appended by hardware.

Count restored bits: . ✓ Effective byte reach: bits (256 MB region).

PICTURE. A 32-bit target bar split into three colored zones — top 4 "from PC" (gray), middle 26 "stored" (blue), bottom 2 "implied 00" (orange) — with braces showing 4+26+2=32.

Figure — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)

Step 8 — Degenerate & edge cases (never leave a gap)

WHAT / WHY. Check the corners so nothing surprises you later.

  • nop = all zeros. 00000000...0 decodes as opcode 0 (R-type), funct 0 = sll $0,$0,0 — a shift of register 0 by 0. It does nothing, harmlessly. So the all-off instruction is legal.
  • shamt = 0 for non-shift R-types. add, sub ignore shamt; it's simply zero. The field always exists even when unused.
  • Immediate = 0. addi $t,$s,0 is a valid copy; beq $x,$y,0 targets the very next instruction ().
  • Negative immediate. Top bit of the 16-bit field = 1 means negative; it is sign-extended to 32 bits before use, so -1 becomes 0xFFFFFFFF. Forgetting this is the classic offset bug.
  • Register 0. $zero (register number 0) always reads 0 and ignores writes — even if it lands in rd. See MIPS Register File and Conventions.

PICTURE. A small table-figure: each edge case as a row (nop, shamt=0, imm=0, imm<0, $zero), with its 32-bit strip and a one-word verdict (safe / sign-extend / no-op).

Figure — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)

The one-picture summary

All three formats sharing the same top three fields (opcode, rs, rt live in identical positions) so the same wires feed the register file — that shared alignment is the whole point of RISC regularity. Below them, the divergence: R keeps three registers + funct, I merges the tail into a 16-bit number, J merges almost everything into a 26-bit address.

Figure — Instruction formats — R-type, I-type, J-type (MIPS - RISC-V)

opcode 0

lw addi beq etc

j jal

Fetch 32 bits at PC

Read top 6 bits opcode

R-type: read funct for the op

I-type: 16-bit immediate

J-type: 26-bit address

rs rt rd shamt funct

rs rt immediate

address then shift

Recall Feynman retelling — the whole walkthrough in plain words

Picture 32 light switches in a row. We had to agree on a rulebook or the machine couldn't tell a locker-number from a plain number. So we made the first 6 switches a code word naming which of three rulebooks we're using — that's the only field that never moves.

Then we asked one repeated question: "how many switches to name X things?" Naming one of 32 lockers takes 5 switches (because 5 switches make 32 patterns). Three lockers = 15 switches; add 5 for "how far to shift" and the 6 left over become "which math" — that's R-type, and the numbers 5-5-5-5-6 weren't chosen, they were forced.

Want a number baked in instead of a third locker? Glue the last three fields into one 16-wide slot for a constant — that's I-type, and 16 is just whatever was left over. For branches we're clever: since every instruction sits at a multiple of 4, the low two bits are always off, so we don't store them — we count in steps of 4 and measure from where we are (forward or backward), stretching 16 bits into an 18-bit reach.

Want to jump far with no lockers at all? Give 26 switches to the address, steal the low 2 (always off) for free, and borrow the top 4 from where we currently are — that's J-type, reaching one big neighborhood. Same 32 switches, three rulebooks, first 6 switches always tell you which. Every width on the page is just the answer to "how many switches do I need?" — nothing memorized.


Flashcards

Where does every field width on this page ultimately come from?
The question "how many bits to name X things?" answered by .
Why are register fields 5 bits?
; MIPS has 32 registers.
Why 6-bit opcode read first?
operations, and the decoder needs it before anything else to pick the format.
Where does the 16-bit immediate width come from?
Leftover: after opcode + two registers.
Why multiply branch/jump offsets by 4?
Instructions are word-aligned (low 2 bits always 0), so offsets count words; hardware shifts left by 2.
Branch target formula?
, imm signed.
J-type target reconstruction?
top4 from PC 26 stored 00 = 32 bits.
What is the all-zero instruction?
nop, i.e. sll $0,$0,0 — does nothing.
Why can't j reach all of memory?
Its top 4 bits are copied from the current PC (256 MB region).