JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

How Instructions Are Encoded in Bits

An instruction is just a 32-bit number, and the ISA is the rulebook that says which bits mean what. We crack a RISC-V word open field by field — opcode, registers, immediate — and trace the sign-extension trick that lets a stubby constant act like a full-width number.

An instruction is a number

Guide 1 introduced the contract; guide 2 met the registers and the program counter the contract names. Now we ask the most physical question of all: when the CPU fetches an instruction, what does it actually fetch? The honest answer, straight from the stored-program idea, is a number — a plain pattern of bits sitting in memory, no different in substance from the data the program crunches. A RISC-V add and the integer 0x00B50533 are the same 32 bits; the only thing that makes one an instruction is that the program counter points the machine at it and says "run this."

So the ISA needs a rulebook for reading that number: a way to carve a fixed-width word into named slots, each slot answering one question. Which operation is this? Which registers does it use? Is there a constant baked in? That rulebook is the instruction format, and decoding an instruction means nothing more than chopping its bits at the agreed boundaries and reading off each field. Get the boundaries wrong by even one bit and you would read a different, nonsensical instruction — which is exactly why the boundaries are fixed forever in the contract.

Cracking an R-type word open

Let's dismantle a real one. The R-type format handles register-to-register arithmetic — add, sub, and, xor and friends. RISC-V slices its 32 bits into six fields. From the low end up: a 7-bit opcode, then three 5-bit register numbers (destination rd, then sources rs1 and rs2), and two extra fields called funct3 (3 bits) and funct7 (7 bits) that, together with the opcode, pin down precisely which operation it is. Why 5 bits for each register? Because 2^5 is 32, and RISC-V has exactly 32 architectural registers to name. Nothing is arbitrary; every field width is sized to the thing it must hold.

Instruction:  add x10, x10, x11      ( x10 <- x10 + x11 )  =  0x00B50533

   31         25 24    20 19    15 14  12 11     7 6           0
  +------------+--------+--------+------+--------+-------------+
  |   funct7   |  rs2   |  rs1   |funct3|   rd   |   opcode    |
  | 0000000    | 01011  | 01010  | 000  | 01010  |  0110011    |
  +------------+--------+--------+------+--------+-------------+
       7 bits    5 bits   5 bits  3 bits  5 bits    7 bits
                  =11      =10              =10     = R-type ALU

  opcode 0110011 says 'register arithmetic';
  funct3=000 + funct7=0000000 together say 'this one is ADD'.
One R-type instruction, all 32 bits accounted for. The same word is the number 0x00B50533 — instruction and data are made of identical bits.

Read the diagram and the whole instruction is laid bare. The opcode 0110011 announces the broad family (register arithmetic); funct3 and funct7 narrow it to exactly add rather than sub or xor. The register fields hold 01010 and 01011 — the numbers 10 and 11 — so the machine reads registers x10 and x11, and writes the sum back into x10. There is no immediate field here at all, because both operands already live in registers. That is the R-type's defining trait: it is pure register-to-register work.

Where small constants live: the immediate

Plenty of instructions need a constant that is not in any register — add 10 to something, load from offset 8, jump 40 bytes ahead. RISC-V carries that constant inside the instruction itself as an immediate operand. To make room, the I-type format reuses the low fields (opcode, funct3, rd, rs1) but replaces rs2 and funct7 with a single 12-bit immediate in the top bits. So an instruction like addi x11, x11, 10 is one source register, one destination, and the literal 10 riding along in those 12 bits. No second register read, no memory access — the number is simply there.

But 12 bits is a stubby thing, and the machine computes on full-width 32-bit (or 64-bit) values. How does a 12-bit constant stand in for a 32-bit number? Through sign-extension, and here the two's-complement design we met in the data rung pays off beautifully. The top bit of the immediate is its sign bit; the hardware simply copies that bit leftward to fill all the missing high bits before using the value. Copy a 0 and the number stays positive; copy a 1 and it stays negative — and crucially, the same bit pattern means the same value at 12 bits or 32 bits. One little rule lets a short field carry the full range of small signed numbers.

Trace it concretely. The number -4 as a 12-bit immediate is 1111 1111 1100. Its leftmost bit is 1, so it is negative, and the hardware copies that 1 into all 20 missing high bits to make the 32-bit value 1111 1111 1111 1111 1111 1111 1111 1100 — still exactly -4, just wider. Feed it a positive immediate instead, like +5 (0000 0000 0101), and the leading 0 is copied: it stays +5 at full width. Same bits, same meaning, every time — that is the whole trick.

Why fixed-width, regular fields

RISC-V has a handful of formats — R, I, S (for stores), B (for branches), and a couple more — but they are deliberately almost the same. The opcode always sits in the very same low 7 bits, and the register-number fields rs1, rs2, and rd always sit in the same bit positions whenever they appear. This regularity is a gift, not an accident. Because rs1 is always in bits 15 through 19, the hardware can start fetching that register's value the instant the word arrives, before it has even finished decoding the opcode. The contract's tidiness becomes raw decode speed.

There is one wrinkle worth being honest about. The B-type branch immediate looks scrambled — its bits are stored out of order, not as a tidy contiguous field. That seems perverse until you see the reason: the encoding was chosen so that every immediate bit lands in the same wire it occupies in the I- and S-types wherever possible. The designers shuffled the branch immediate precisely so the sign-extension and routing hardware could be shared across formats. So even the one apparent mess exists to keep the hardware simpler. Regularity was the goal; the scramble serves it.

From encoded bits to action — and the CISC twist

Once the fields are sliced out, a small block of hardware — a decoder — turns them into the control signals that steer the rest of the chip: read these two registers, tell the ALU to add, sign-extend this immediate, write the result here. We will build that decoder properly in the datapath rung; for now hold the chain in your head as a tiny pipeline of meaning.

  1. Fetch: the program counter hands over an address; the 32-bit instruction word is read from memory.
  2. Decode: slice the word at the fixed boundaries — opcode, funct3/funct7, rd, rs1, rs2, immediate — and read off what each field means.
  3. Prepare operands: read the named registers, and sign-extend any immediate to full width.
  4. Execute and write back: the ALU does the named operation; the result lands in rd, and the program counter advances to the next word.

Now the honest twist that makes encoding matter for the RISC-versus-CISC story. RISC-V's fixed 32-bit words are trivial to slice — the next instruction always starts 4 bytes on. A CISC like x86 uses variable-length instructions, from 1 byte to 15, so the decoder cannot even tell where the next instruction begins until it has partly parsed this one. That makes wide, parallel decoding genuinely harder and costlier in power. It does not make x86 slow, though: modern x86 chips crack each complex instruction into small, uniform RISC-like micro-ops and run those on a fast internal engine.