An instruction set architecture, or ISA, is the contract between software and hardware. It lists the registers a program can use, every instruction and exactly what it does, how each instruction is encoded in bits, and how memory behaves. A compiler writer needs nothing else to produce working code, and a processor designer must deliver exactly that behavior, by any means. This article explains what an ISA specifies and what it deliberately leaves open, how RISC-V organizes its base instructions and extensions, and what choosing an extension changes in real code.
14 min read | Beginner
What You'll Learn
- Explain what an instruction set architecture specifies and why it is a contract
- Distinguish what the ISA defines from what each processor design decides
- Describe the RISC-V base ISA, its standard extensions and their names
- Predict what happens when software and hardware disagree on the ISA
A Contract Between Software and Hardware
Software and processors are made by different people, often in different companies, years apart. A compiler written today must produce programs that run on processors designed next year, and a new processor must run programs compiled ten years ago. That only works if both sides follow the same written rules. The instruction set architecture (ISA) is that set of rules: a specification of everything a program can observe about the processor.
The ISA is written as a document, not as hardware. For RISC-V it is a pair of specifications, one for the instructions ordinary programs use and one for the privileged features used by operating systems. For x86 and Arm the equivalents are manuals of thousands of pages. A processor implements the ISA when it behaves exactly as the document says for every instruction, in every case.
The payoff is binary compatibility: a program compiled once runs unchanged on every processor that implements the same ISA. A small program compiled for RV32I would run on a tiny RISC-V microcontroller core and on a large application core, and print the same text on each, even though those processors are built in completely different ways.
What the ISA Specifies
An ISA covers a small number of things, but each one completely.
State
The architectural state is everything a program can see and change. In RV32I that is the 32 general-purpose registers x0 to x31, each 32 bits wide, with x0 always reading as zero; the program counter (PC); and memory. Larger processors have many more internal storage elements, such as pipeline registers, caches and branch predictor tables, but these are not architectural: no instruction can name them, and a program cannot tell whether they exist. Registers and the Register File describes the 32 registers.
Instructions and Their Exact Effect
For every instruction the ISA states its effect on the state. For add rd, rs1, rs2: read rs1 and rs2, add them as 32-bit values, ignore any carry out of bit 31, write the result to rd, and advance the PC by 4. The definition is exact, including the corner cases: what happens on overflow (the result wraps around, and nothing else happens), what a shift by more than 31 does (only the low 5 bits of the amount count), what a division by zero returns. A processor that returns a different value in any corner case does not implement the ISA, even if it gets every common case right.
Encoding
The ISA fixes how each instruction is written in bits: which bits hold the operation, which hold the register numbers, and how constants are packed in. add a0, a0, a5 is always the 32-bit word 0x00F50533, whoever built the processor. RISC-V Instruction Formats and Encoding takes the encodings apart bit by bit.
Memory
The ISA defines how memory is addressed and what loads and stores do: RISC-V memory is byte-addressed and little-endian, and loads and stores come in byte, halfword and word sizes. It also says what happens with misaligned accesses, which RISC-V allows each processor to handle in hardware or to report as an exception. For systems with several processors, a memory model defines in which order one processor's stores become visible to another. Memory, Addresses and Endianness covers addressing and byte order.
Exceptions and Privilege
Finally, the ISA defines what happens when something goes wrong or needs the operating system: an unknown instruction, a misaligned access, a system call. It defines privilege levels that keep ordinary programs from touching the operating system's data, and the special registers that control all of this. The Exceptions, Interrupts and Privilege chapter covers that part.
Quick Check
Q: A processor has a 16 KB data cache and a branch predictor. Are they part of the RV32I architectural state?
Show Answer
No. No RV32I instruction can name the cache or the predictor, and a program gets the same results with or without them; only the time changes. Architectural state is the 32 registers, the PC and memory.
What the ISA Leaves Open
Just as important is what an ISA does not say. It does not say how many cycles an instruction takes, how many instructions run at once, whether there is a pipeline or a cache, how fast the clock is, or how much power the processor uses. Those are decisions of the microarchitecture, the internal organization of one particular processor, and of its implementation, the circuits and the chip technology. ISA, Microarchitecture and Implementation explains the three levels.
This freedom is what lets one ISA serve very different products. A five-stage pipeline might average about 1.4 cycles per instruction. A processor that finished every instruction in a single long cycle would average exactly 1, at a much slower clock, and a large core that runs several instructions at once would average well under 1. All three must leave exactly the same values in the registers and in memory, and print exactly the same text. Timing is the one thing a program may see differ, which is why software must never depend on how long instructions take, and why hardware that needs a fixed time uses a timer, not an instruction count.
RISC-V: A Small Base and Optional Extensions
Many older ISAs grew by adding instructions over decades, and every processor had to carry all of them. RISC-V was designed in a modular way instead. There is a small mandatory base, and everything else is an optional extension that a processor may include or leave out.
The base used in these articles is RV32I: 32-bit registers and addresses ("32"), integer instructions only ("I"). It has about 40 instructions: integer arithmetic and logic, shifts, comparisons, loads and stores, branches and jumps, and a few for system purposes. That is enough to compile any C program. RV64I is the same base with 64-bit registers, and RV32E is a reduced version with 16 registers for the smallest cores.
The most common standard extensions are named by single letters:
| Letter | Extension | Adds |
|---|---|---|
| M | integer multiply and divide | mul, mulh, div, divu, rem, remu and variants |
| A | atomic instructions | read-modify-write operations on memory for multiprocessor synchronization |
| F, D | floating point | single and double precision registers and arithmetic |
| C | compressed instructions | 16-bit encodings of the most common instructions, for smaller code |
| V | vector | operations on whole arrays of values at once |
Newer extensions have longer names beginning with Z, such as Zicsr for the control and status register instructions or Zba and Zbb for bit manipulation. A processor's ISA string lists what it supports: rv32imc is RV32I plus multiply/divide and compressed instructions, a common choice for microcontrollers; rv64gc, where G stands for the general-purpose set IMAFD plus a few Z extensions, is typical for cores that run Linux.
What an Extension Changes: A Measurement
The compiler is told which ISA to target with the -march option, and it uses only instructions from that ISA. In one test, six small programs were compiled with optimization for four targets: plain rv32i, rv32i with M, with C, and with both.
Two different effects show up. The M extension changed nothing for four of the programs, because they never multiply or divide. It cut the code of the other two by about a third and two fifths, because without it the compiler must add library routines: __mulsi3 for multiplications, and a division routine for the remainder operation %. With M, each of those becomes a single instruction, mul or remu, and the routines disappear. The C extension, by contrast, never changed the number of instructions. It changed their size: most common instructions get a 16-bit encoding, and every program shrank by roughly a quarter. The two together nearly halved the programs that multiply.
The code size matters more than it may seem. A microcontroller's program lives in on-chip flash, and smaller code means a cheaper chip or more features in the same chip. And fewer bytes per instruction means fewer bytes to fetch, so the instruction memory or cache serves more instructions per access.
Where Does This Matter?
Choosing the ISA string is one of the first decisions when a team picks a RISC-V core for a product. A tiny controller in a sensor may be RV32EC; a motor controller RV32IMC with a hardware multiplier; an application processor RV64GC with the vector extension. The whole software stack, compiler settings, libraries and operating system, must then be built for the same string.
Quick Check
Q: Why did the C extension leave the instruction count of every program unchanged, while the M extension changed two of them?
Show Answer
C adds no new operations; it gives existing instructions a shorter 16-bit encoding, so the same instructions take fewer bytes. M adds new operations, multiply and divide, which replace whole library routines in programs that use them.
When Software and Hardware Disagree
A program compiled for rv32im contains mul instructions. If it runs on a core that implements only RV32I, the core meets a 32-bit word it does not recognize. The ISA defines what must happen: an illegal instruction exception. The processor stops the program at that instruction and jumps to an exception handler, usually part of the operating system, which can report the error, stop the program, or even perform the multiplication in software and continue, which is called emulation.
What must never happen is that the processor silently does something else. A core that treated an unknown instruction as, say, an addition would produce wrong results without any warning. Defined behavior for every bit pattern, including the invalid ones, is part of what makes an ISA a reliable contract.
Mismatches like this are common in practice, because the compiler's defaults may not match the target. Embedded projects therefore set -march and -mabi explicitly in their build system. The ABI option, ilp32 here, fixes the size of C types and how functions pass arguments; it must match too, so that code compiled separately can call each other.
Open and Proprietary ISAs
For decades almost every widely used ISA belonged to one company. x86, used in personal computers and servers, belongs to Intel and AMD, and only they build x86 processors. The Arm ISA, used in nearly every phone, is owned by Arm, which licenses it to chip companies: they can buy finished Arm cores or, with a more expensive license, design their own.
RISC-V is an open ISA. Its specifications are published by RISC-V International, a non-profit organization, and anyone may build a RISC-V processor without paying for the ISA or asking permission. That is why small companies, universities and open-source projects can design complete processors. It also allows custom extensions: a company can add its own instructions for a special task, in encoding space the ISA reserves for that purpose, without breaking compatibility with standard software. The price of openness is fragmentation risk, many cores with different extension sets, which RISC-V counters with profiles: named, fixed combinations of extensions that software can target, such as the profiles for application processors that run standard operating systems.
Common Mistakes
Mistake #1: Relying on timing the ISA does not promise
Code that waits a "fixed" number of instructions works on one core and fails on the next, because the ISA says nothing about cycles. Use a timer for delays.
Mistake #2: Compiling for the wrong -march
Code built with an extension the core lacks traps with illegal instruction exceptions; code built without an extension the core has wastes it, as a program that multiplies through a library routine shows. Match the compiler target to the core.
Mistake #3: Confusing architectural and microarchitectural state
Caches, predictors and pipeline registers are invisible to the ISA. Reasoning about a program's correctness should use only the registers, the PC and memory; the microarchitecture affects only speed.
Practice Exercise
Challenge: Choose the ISA String
Two programs have these code sizes. Program A: 444 bytes for rv32i and 308 bytes for rv32im. Program B: 584 bytes for rv32i, 336 bytes for rv32im and 468 bytes for rv32ic. (a) By what percentage does the M extension reduce the code size of each? (b) By what percentage does C reduce the code size of B? (c) A product's firmware is 60 KB when compiled for rv32i and fits in 64 KB of flash, leaving almost no room for updates. It multiplies rarely. Which extension would you ask the core supplier for first, and why?
Hint
Reduction = (old - new) / old. Think about which extension shrinks every program and which only helps programs that multiply or divide.
Solution
(a) A: (444 - 308) / 444 = 30.6 percent; B: (584 - 336) / 584 = 42.5 percent. (b) (584 - 468) / 584 = 19.9 percent. (c) C. It shrinks all code, by roughly a fifth to a quarter, which would bring 60 KB down to roughly 45 KB. M helps only code that multiplies or divides, which this firmware does rarely; it would mainly make those operations faster.
Summary
- The ISA is the contract between software and hardware: state, instructions and their exact effects, encodings, memory behavior, exceptions and privilege.
- It leaves timing and internal organization to the microarchitecture, so very different processors run the same binary.
- RISC-V has a small base (RV32I, about 40 instructions) and optional extensions named in an ISA string such as rv32imc.
- M shrinks programs that multiply or divide by replacing library routines; C keeps instruction counts and cuts code size by about a quarter.
- An instruction the core does not implement causes an illegal instruction exception, never silent misbehavior.