Computer Architecture and the Structure of a CPU: From Transistors to RISC-V
0. Key points
Section titled “0. Key points”- A computer is, at bottom, a network of switches. Two transistors make a NOT gate, four make a NAND gate, and NAND alone suffices to build every logic function (Corollary 2.4).
- There are only Boolean functions of variables, and every one of them can be written in disjunctive normal form (Theorem 2.3). What hardware can compute rests on this finiteness.
- An adder is a concrete implementation of a Boolean function. Its correctness is proved by induction on the carries (Theorem 4.2), and its speed is determined by the depth of the circuit (Proposition 4.4).
- The clock and the flip-flop are what create “time”. The upper bound on the operating frequency comes from the setup constraint (Proposition 5.2).
- The von Neumann architecture embodies one design decision: instructions and data live in the same memory, as the same kind of bit string. Generality is bought at the price of a narrow channel to memory, which becomes the bottleneck.
- An instruction set architecture (ISA) is the contract between hardware and software. RISC-V publishes that contract in a form anyone may implement, and
add x5, x6, x7encodes to the 32-bit word0x007302B3(Example 8.2).
1. Motivation: from rewiring the machine to reading a program
Section titled “1. Motivation: from rewiring the machine to reading a program”ENIAC, unveiled in 1946, was fast beyond comparison for its day. But changing what it computed meant physically replugging thousands of cables and switches by hand, a job that took days. The computation itself finished in seconds; specifying the computation took days. That asymmetry is the starting point for the next design.
In 1945 a draft report on the EDVAC, circulated under John von Neumann’s name, gave one answer to the problem. Instructions are numbers too; so put them in the same memory as the data and let the machine read them out itself. Rewiring was replaced by rewriting memory. This is the stored-program principle, and it is the basic design of almost every computer built since.
A naive question arises here. Why does a lump of sand (silicon) compute? Stones do not think. But a switch made of stone can distinguish two states, a high and a low voltage. Two distinguishable states let you represent truth values; truth values let you represent logic functions; logic functions let you represent arithmetic. Add a mechanism that remembers the previous value and a clock that marks which step you are on, and a static circuit becomes a computational process unfolding in time.
This article follows that construction from the bottom up, one level at a time.
- From switches (transistors) to logic gates (Theorem 2.3, Section 3)
- From logic gates to arithmetic (adders) (Section 4)
- From combinational circuits to memory and the clock (Section 5)
- The five functional units and the von Neumann architecture (Section 6)
- The internal structure of a CPU and the execution of an instruction (Section 7)
- The ISA as a contract between hardware and software, and RISC-V (Section 8)
2. Preliminaries: the language of Boolean functions and switches
Section titled “2. Preliminaries: the language of Boolean functions and switches”Before talking about circuits, let us fix in mathematical language what a circuit is trying to realise.
Definition 2.1(Boolean function)
A Boolean function of variables over is a map . On we define the operations
and call them AND, OR, NOT and XOR respectively.
Designing a circuit is nothing other than writing the desired Boolean function as a composite of these basic operations. Let us first count how many such targets there are.
Proposition 2.2(The number of Boolean functions)
For , the total number of Boolean functions of variables is exactly .
Proof(Proposition 2.2)
Specifying is the same as assigning the value or to each element of the domain . The domain has elements, since each of the coordinates has 2 possibilities. The assignments to distinct elements are independent, and each has 2 possibilities. Hence the total number of assignments is multiplied by itself times, that is, .
Concretely: 4 functions for , 16 for , 256 for , 65536 for , and about 4.3 billion for . The count grows doubly exponentially in , so functions of large cannot be handled by truth tables at all. That is precisely why we need a way to construct functions as formulas. The next theorem supplies one.
Theorem 2.3(Disjunctive normal form and the completeness of AND, OR, NOT)
Let and let be an arbitrary Boolean function. Then can be expressed by a formula using only the variables and the operations . Explicitly, for set
Then, provided is not identically ,
holds. If is identically , then .
Proof(Theorem 2.3)
We first verify two properties of .
(i) . The -th factor is . If the factor is ; if the factor is . In both cases it equals . Every factor is , so the AND is as well.
(ii) If then . Since , there is an index with . If then and the -th factor is ; if then and the -th factor is . A single factor equal to makes the AND equal to .
Now suppose is not identically and put for the right-hand side (this is a non-empty OR, since ). For every the following holds.
- If , then , so occurs among the terms of the OR, and by (i). An OR with even one term equal to is , so .
- If , then every occurring in the OR satisfies , hence , so by (ii) all the terms are . Therefore .
So and agree on every input, that is, ; and is written using only the variables and .
Finally, if is identically , then is both for and for , so it represents .
This theorem guarantees that any input-output table whatsoever can be realised by a circuit. Moreover the proof is constructive: write down the truth table and the formula falls out mechanically. On actual silicon, however, it is cheaper to get by with a single kind of gate rather than to build AND, OR and NOT directly. The following corollary supplies it.
Corollary 2.4(Functional completeness of NAND)
Define (NAND). Every Boolean function can be expressed by a formula using only the variables and NAND.
Proof(Corollary 2.4)
By Theorem 2.3, can be written as a formula in , so it suffices to build these three out of NAND alone.
- . Indeed , so the two sides agree.
- , applying the construction of from the previous line to .
- . The right-hand side is , which by De Morgan’s law equals .
Applying these three substitutions recursively to the formula for yields a formula using NAND only.
Example 2.5(Turning the 3-variable majority function into a formula)
We construct the function that returns when at least two of are . In the truth table, the inputs with are the four tuples . Writing out the construction of Theorem 2.3 verbatim gives
which uses four 3-input ANDs, one 4-input OR and three NOTs: eight gates in all. Now we simplify. Since , the last term may be used three times, so we may rewrite this as
Using on each line, the first line becomes , the second becomes , and the third becomes . Hence
which uses three 2-input ANDs and one 3-input OR: four gates, half the previous count. The same function can cost twice as much or half as much depending on how the formula is written. Reducing that cost is the job of the design step called logic synthesis.
3. Transistors: turning physics into logic
Section titled “3. Transistors: turning physics into logic”We now realise the of the previous section physically. The dominant device today is the MOSFET (metal-oxide-semiconductor field-effect transistor). Structurally it is a small capacitor: a metal (or polysilicon) electrode sitting on the silicon surface with a thin insulating film in between. Applying a voltage to this electrode (the gate) draws charge to the silicon surface underneath the insulator, and the region between the two flanking terminals (source and drain) becomes conducting. Voltage controls conduction. That is a switch.
For logic design it is enough to treat the device with the following idealised model.
- nMOS: conducts when the gate voltage is high (logical ) and blocks when it is low (logical ).
- pMOS: conducts when the gate voltage is low and blocks when it is high; complementary in behaviour to nMOS.
CMOS logic combines the two kinds by placing a network of pMOS transistors (the pull-up network) on the supply side and a network of nMOS transistors (the pull-down network) on the ground side, arranged so that for every input exactly one of the two conducts. The smallest example is the NOT gate.
If the input is , the pMOS conducts and the nMOS blocks, so the output is tied to the supply and equals . If the reverse happens and is tied to ground, giving . That is, : a NOT gate out of two transistors. The same idea with two nMOS in series and two pMOS in parallel gives NAND in four transistors, and swapping series for parallel gives NOR in four. Building an AND requires appending a NOT after the NAND, hence six. In CMOS the inverting version is always the cheaper one, which is why the NAND and NOR of Corollary 2.4 are the basic units of implementation.
From this structure we can derive a basic law about power consumption.
Proposition 3.1(Energy consumption of CMOS)
Consider a CMOS gate whose output drives a capacitance . Assume the pull-up and pull-down networks never conduct simultaneously and that transistor leakage current is negligible. Then:
- In the steady state, when the output is not changing, the current flowing from supply to ground is and the power consumption is as well.
- Each time the output makes a full round trip , the energy drawn from the supply and turned into heat is exactly , and this value does not depend on the resistance of the conducting path.
- If the clock frequency is and on average transitions occur per clock cycle, the average power consumption is .
Proof(Proposition 3.1)
(1) In the steady state, by assumption exactly one of the pull-up and pull-down networks conducts. Hence there is no conducting path joining supply to ground and no current flows. Power is the product of voltage and current, so it is .
(2) Consider first the charging . When the capacitance is charged from to , the charge delivered by the supply is . The supply voltage is constant at , so the energy it provides is
Meanwhile the energy stored in the capacitance is
so the difference is dissipated as heat in the on-resistance of the pMOS. Note that the resistance does not appear anywhere. Next, in the discharge , the stored in the capacitance is entirely dissipated as heat in the on-resistance of the nMOS, and the supply provides no energy. The round-trip total is .
(3) There are clock cycles per second and on average transitions per cycle, so there are round trips per second. By (2) each round trip costs , so the energy per unit time, that is the power, is .
The single most important feature of this formula is that enters squared.
Example 3.2(What happens when the supply voltage is lowered)
Consider a chip with signal wires. Take the capacitance of each wire to be , the operating frequency , the average transition rate per clock , and the supply voltage . By Proposition 3.1,
If we now raise only the supply voltage to , then grows by a factor of , reaching . Given that air cooling can shed on the order of 100 W, this difference is decisive. The reason supply voltages have been pushed down from 5 V to around 1 V as feature sizes shrank is exactly this square law.
Remark 3.3(Why binary?)
With ten distinct voltage levels, a single wire could carry one decimal digit. Two values are nonetheless used, for the sake of the margin against noise (the noise margin). At a supply voltage of , ten levels means the spacing to be distinguished is about , whereas two values give about . Moreover, as part (1) of Proposition 3.1 shows, CMOS consumes almost no power only when the output is driven fully to the supply voltage or to ground; intermediate voltages translate directly into wasted energy. Both reliability and power point to two values.
Remark 3.4(Homemade transistors, homemade chips)
The principle that a MOSFET is “a device controlling the conductivity of a silicon surface by means of an electrode behind an insulating film” is a simple one. As a result there are several cases of individuals making working transistors with home equipment. Jeri Ellsworth built and operated field-effect transistors using a homemade furnace and vacuum apparatus, and Sam Zeloof assembled a full photolithography setup in his home garage and fabricated homemade chips integrating several transistors. Going in the opposite direction, James Newman’s Megaprocessor is a 16-bit processor built from about 40,000 discrete off-the-shelf transistors, with every register bit lit by an LED so that the operation can be watched.
These are not practical devices, but they let one walk back down the ladder of abstraction and confirm the fact that computation is a physical phenomenon. The very characters you are reading now are the result of charge gathering and dispersing on some silicon surface.
4. From combinational circuits to arithmetic: the adder
Section titled “4. From combinational circuits to arithmetic: the adder”With logic gates in hand, we build arithmetic. Binary addition is realised by lining up, for each digit position, a component that produces “the sum digit” and “the carry to the next position”.
Definition 4.1(Full adder and ripple-carry adder)
A combinational circuit which, on inputs , outputs
is called a full adder. We call the sum bit and the carry out.
The circuit obtained by chaining full adders so that the carry out of the -th one feeds the carry in of the -st, with the inputs at digit and the carry into the least significant position, is called an -bit ripple-carry adder (carry-propagate adder).
Theorem 4.2(Correctness of the ripple-carry adder)
Let and let () and be arbitrary. If () are defined by the recurrence of Definition 4.1, then the integer identity
holds. That is, the sum of and read as -bit binary numbers, plus , equals the -bit number whose low bits are and whose most significant bit is .
Proof(Theorem 4.2)
Step 1: the one-digit identity. We show that for each , as integers,
The left-hand side is an integer between and . Since XOR is addition modulo (Definition 2.1), equals the remainder of the left-hand side on division by . Next we check by cases that equals the quotient of the left-hand side by , that is, “at least two of the three are ”.
- If : then , so . The left-hand side is , so the quotient is . They agree.
- If : then and , so . The left-hand side is , whose quotient equals . They agree.
- If : then and , so . The left-hand side is , so the quotient is . They agree.
Quotient and remainder agree, so the one-digit identity follows from the uniqueness of division with remainder.
Step 2: induction on the number of digits. We prove, for ,
For both sums are empty, so the left-hand side is and the right-hand side is ; thus holds.
Assume with . Adding to both sides, the left-hand side becomes the left-hand side of . The right-hand side becomes
where the second line uses the one-digit identity of Step 1. This is the right-hand side of . Hence holds, and by induction we obtain .
Example 4.3(Following a 4-bit addition to the end)
Take (), () and , and run the recurrence of Definition 4.1 from the least significant digit upward.
| 0 | 1 | 0 | 0 | 1 | 1 | 0 |
| 1 | 1 | 1 | 0 | 2 | 0 | 1 |
| 2 | 0 | 1 | 1 | 2 | 0 | 1 |
| 3 | 1 | 0 | 1 | 2 | 0 | 1 |
The result is , and indeed . The identity of Theorem 4.2 holds.
Reading the same circuit as signed (two’s complement) arithmetic, and , and the low four bits of the result, , give , which is also correct. The advantage of two’s complement is that the addition circuit can be reused verbatim. What does change is the test for validity: unsigned, the carry out signals overflow, whereas signed we compute , so there is no overflow.
In a ripple-carry adder the carry, as the name says, propagates from the low digits to the high ones in sequence. If the delay of one full adder is , then bits give a delay of , that is, . At 64 bits this would dictate the speed, so real CPUs use the following structure.
Proposition 4.4(Carry lookahead and prefix computation)
Setting and , the carry of Definition 4.1 can be written as . Define the binary operation on
Then the following hold.
- is associative and makes a monoid with identity element .
- Putting , we have .
- All the carries () can be computed by a circuit of fan-in-2 AND, OR and XOR gates of depth using gates.
Proof(Proposition 4.4)
First we verify . The defining formula in Definition 4.1 is ; substituting the definitions of gives , and commutativity of AND puts it in the stated form.
(1) Associativity. We compute both bracketings on three elements.
Since AND distributes over OR, the first component of the last expression equals , which matches the expression above. As for the identity, and .
(2) Induction on . For we have and . Assuming the claim for ,
where the second line uses distributivity and the last step uses .
(3) Depth. The pairs are the prefixes of an associative operation. In the Kogge–Stone construction one starts from and updates, for ,
(taking the identity when the index is negative). By associativity, induction on shows that equals the product over the interval . After stages we have and the whole interval is covered, giving . One application of is realised by two ANDs and one OR (depth 2), and each stage performs of them in parallel, so the overall depth is and the gate count is . Computing at the end costs one extra level of depth.
Comparing at 64 bits: the ripple carry takes 64 stages, whereas stages suffice. The gap between and translates directly into a gap in clock frequency.
5. Memory and the clock: sequential circuits
Section titled “5. Memory and the clock: sequential circuits”The circuits so far have no time in them. Give an input and, after a delay, the output is determined; that is all. To turn computation into a procedure we need a mechanism that remembers values, and it is obtained by introducing feedback into the circuit. Feeding the outputs of two NOR gates back into each other’s inputs yields an SR latch, which holds its previous state as long as both inputs are . Stacking two of these so that the input is captured only at the instant the clock signal rises gives an edge-triggered D flip-flop. A CPU register is simply as many D flip-flops as the register is wide, side by side.
Definition 5.1(Synchronous sequential circuits and timing parameters)
A circuit in which every memory element captures its value on the rising edge of one and the same clock signal is called a synchronous sequential circuit. For an edge-triggered D flip-flop we define the following quantities.
- (): the maximum (minimum) delay from the clock edge until the output Q settles.
- (setup time): how many seconds before the clock edge the input D must already have settled.
- (hold time): how many seconds after the clock edge the input D must be held.
We also write for the maximum delay and for the minimum delay of the combinational logic between flip-flops, and for the maximum spread in the times at which the clock arrives at the various flip-flops.
Proposition 5.2(Timing constraints and the maximum operating frequency)
In the setting of Definition 5.1, let the clock period be . In order for every flip-flop to capture its value correctly, the following two conditions are necessary and sufficient.
In particular the operating frequency is capped at . Since does not appear in the hold constraint, a hold violation cannot be removed by slowing down the clock.
Proof(Proposition 5.2)
Let time be the moment the clock edge arrives at the sending flip-flop.
Setup constraint. The output Q of the sender settles at time at the latest. From there the signal passes through the combinational logic, so the input D of the receiver settles at at the latest. On the other hand, allowing for the worst case of skew, the next clock edge reaches the receiver as early as . Since D must have settled at least before that edge, we need
and rearranging gives the first inequality. Conversely, if the first inequality holds then the above inequality holds and the condition is met.
Hold constraint. At the very same edge at time , the sender begins to emit its new value. That value begins to reach the receiver’s D input at at the earliest. For the receiver, allowing for the worst case of skew, this edge arrives at time at the latest. The receiver requires D not to change for after its own edge, so
is necessary, which is the second inequality. Note that does not appear in it. Lengthening the period does not alter the fact that a new value comes rushing in right after the edge. A hold violation can only be repaired by inserting buffers into the path so as to increase .
Example 5.3(Putting a number on the frequency)
Take , , , , , and combinational logic with and . By Proposition 5.2,
The hold constraint reads , which is false, so it is not satisfied. No matter how slowly this circuit is clocked, it will not work. Buffers must be inserted into the shortest path to bring up to at least .
Furthermore, if this combinational logic is split into two stages of equal delay with a flip-flop in between, then and
a frequency 1.65 times higher. This is the principle of pipelining, taken up again in Proposition 7.2.
6. The five functional units and the von Neumann architecture
Section titled “6. The five functional units and the von Neumann architecture”The parts are assembled. We now move to the machine as a whole. In the classical classification, a computer consists of the following five functional units.
| Unit | Role | Modern realisation |
|---|---|---|
| Control unit | Fetches and decodes instructions and sends control signals to the other units | Instruction decoder, control unit |
| Arithmetic unit | Performs arithmetic and logical operations | ALU, multiplier, floating-point unit |
| Memory unit | Holds instructions and data | Registers, cache, main memory (DRAM), SSD |
| Input unit | Brings information in from the outside world | Keyboard, sensors, network reception |
| Output unit | Sends information out to the outside world | Display, network transmission |
The control unit and the arithmetic unit together make up the CPU (central processing unit). In terms of the parts built in Sections 3 to 5, the arithmetic unit is a combinational circuit like the adder of Section 4, and registers, the fastest layer of the memory unit, are the rows of flip-flops of Section 5.
flowchart LR IN["Input unit"] --> MEM["Memory unit"] MEM --> OUT["Output unit"] MEM <-->|"data"| ALU["Arithmetic unit"] MEM -->|"instructions"| CU["Control unit"] CU -.->|"control signals"| ALU CU -.->|"control signals"| MEM
What is decisive in this diagram is that there is only one box for memory. Instructions and data alike are placed in the same memory, as the same kind of bit string.
Definition 6.1(The stored-program principle (von Neumann architecture))
A machine is said to follow the stored-program principle if it holds the sequence of instructions to be executed inside the same rewritable memory as the data, in the same format (a bit string), and reads them out one after another according to a register holding the address of the next instruction (the program counter), decoding and executing each in turn. A design based on this principle is called a von Neumann architecture.
From the single decision to put instructions in the same place as data, all of the following consequences follow.
- Programs can be treated as data. Compilers, linkers, loaders, virtual machines and JIT compilers are all “programs that take programs as input and produce programs as output”. This picture works only because an instruction is nothing but a bit string in memory. The same story, seen from the side of language implementations, is taken up in Programming Language Theory (translators and interpreters(Definition 3.1)[Programming Language Theory]).
- The distinction between instruction and data is only a matter of interpretation. Whether the bit string at a given address is an instruction or data is nothing but the difference between the CPU pointing at it with the program counter and reading it with a load instruction. A buffer overflow attack is the operation of getting a bit string written as data to be executed as an instruction; it is the flip side of this design. Modern operating systems and CPUs prevent it by withholding execute permission at page granularity (address translation by paging(Definition 5.1)[What an Operating System Does]). See The Role of the Operating System for details.
- The channel to memory becomes narrow. Since instructions and data travel the same route, the bandwidth of that route determines the overall speed no matter how fast the CPU becomes. John Backus called this the von Neumann bottleneck in his 1978 Turing Award lecture.
The remedy for the third point is the memory hierarchy and caching. Common measures are separating the caches for instructions and for data (making the inside of the CPU Harvard-style, as it is often put) and multi-level caches that exploit locality of reference. The average memory access time AMAT can be estimated as
With a hit time of 1 cycle, a miss rate of 3 % and a miss penalty of 100 cycles, this is cycles. Merely raising the miss rate from 3 % to 6 % gives 7 cycles, a factor of 1.75. CPU performance so often hinges on the memory access pattern of an algorithm because the coefficient in the second term of this formula is large. An estimate of the same shape applies to the cache that accelerates address translation (the TLB) as well (why the TLB works, in numbers(Example 5.6)[What an Operating System Does]).
7. Inside the CPU: datapath and control
Section titled “7. Inside the CPU: datapath and control”Opening up a CPU, we find broadly the following parts.
- Register file: a few dozen fast storage cells. In RV32I there are 32 registers of 32 bits. It is the row of flip-flops of Section 5 with address decoders for reading and writing attached.
- ALU (arithmetic logic unit): a combinational circuit performing addition and subtraction, AND, OR, XOR, shifts and comparisons. At its heart is the adder of Section 4.
- Program counter (PC): the register holding the address of the next instruction to execute.
- Control unit: the circuit that decodes the bit string of an instruction and generates the signals directing register-file reads and writes, the ALU operation, whether memory is accessed, and so on. By Theorem 2.3 this too can be realised as a family of Boolean functions taking the instruction bits as input.
The routes connecting these parts are called the datapath, and the control signals are what switch the multiplexers along those routes. It is standard to think of the execution of an instruction as divided into the following five stages.
Example 7.1(One add instruction, from start to finish)
Suppose the PC points at 0x00001000, that the machine word 0x007302B3 for add x5, x6, x7 is stored there, and that register x6 holds 12 and x7 holds 30.
- Instruction fetch (IF): the control unit presents the address
0x00001000to instruction memory and reads out0x007302B3. At the same time an adder computes PC + 4 =0x00001004. - Decode and register read (ID): from the low 7 bits
0110011we learn that this is an R-format arithmetic instruction, and from funct3 =000and funct7 =0000000that the operation is addition. The control unit raises the signals “ALU does addition”, “second operand is a register (not an immediate)”, “register write enabled” and “no memory access”. In parallel, rs1 = 6 and rs2 = 7 in the instruction cause the two read ports of the register file to deliver 12 and 30. - Execute (EX): the ALU computes . What is at work here is the adder of Theorem 4.2 and Proposition 4.4.
- Memory access (MEM): this instruction does nothing here. A load or store instruction would touch data memory at this stage.
- Write back (WB): 42 is written to rd = 5, that is, to x5. At the same time
0x00001004is written to the PC, and at the next clock edge the machine moves on to the next instruction.
A design in which all of this finishes in one clock cycle is called single-cycle. In that case in Proposition 5.2 is the delay through all five stages above, so the clock becomes slow.
Proposition 7.2(Speedup from pipelining)
Let be the total combinational delay needed to process one instruction, and suppose it is split into stages of equal delay with registers inserted in between. Write for the overhead of one register stage ( in Proposition 5.2). Assuming there are no stalls whatsoever due to data dependences or branches, the time to process instructions is
and as the speedup ratio converges to
In particular, as long as , increasing the number of stages never attains a speedup of .
Proof(Proposition 7.2)
Without splitting, the clock period must be at least by Proposition 5.2, and one instruction takes one cycle, so instructions take .
Splitting into stages, the logic delay of each stage is , so the period becomes . The first instruction leaves the last stage in cycle , and thereafter, under the assumption of no stalls, one instruction completes every cycle, so instructions take cycles. Hence is as claimed.
The speedup ratio is
and as the first factor converges to . Multiplying numerator and denominator of the second factor by gives . Taking its ratio to ,
and for with the denominator is the larger, so .
For instance, with , and , the period drops from to , giving . The ideal factor of 5 is out of reach. And in practice stalls do occur, because an instruction may use the result of the one just before it, or because the branch target is not known until it is resolved. The deeper the pipeline the larger these losses, which is why pipelines deeper than about 20 stages were reconsidered in the 2000s.
8. The instruction set architecture and RISC-V
Section titled “8. The instruction set architecture and RISC-V”In Section 7 we simply asserted that the machine word 0x007302B3 is an add instruction. That correspondence table is precisely the contract placed at the boundary between hardware and software.
Definition 8.1(Instruction set architecture (ISA))
The instruction set architecture (ISA) is the whole of the machine’s specification as observable from a program, namely (a) the set of instructions and their encoding into bit strings, (b) the number, width and roles of the registers, (c) the address space and the appearance of memory (alignment, byte order, ordering as seen from several agents of execution), (d) the treatment of exceptions and interrupts, and (e) the privilege levels. The same ISA can be implemented by several different circuits differing in pipeline depth and cache organisation. The manner of implementation is called the microarchitecture.
To say that an ISA is a contract means the following. A compiler emits code looking only at this contract, and a CPU designer may adopt any internal structure whatsoever so long as the contract is honoured. It is because of this separation that a binary written 20 years ago still runs on today’s CPUs.
RISC-V is an ISA that publishes this contract in a form anyone may freely implement and extend. The skeleton of the base integer instruction set RV32I is as follows.
- 32 registers of 32 bits (x0 through x31). x0 is hardwired to , so writing to it changes nothing.
- Instructions are a fixed 32 bits long. There are only six instruction formats: R, I, S, B, U and J.
- Only load and store instructions touch memory; arithmetic is performed between registers (or between a register and an immediate). This is the load-store style.
- There are only about forty base integer instructions. Multiplication and division are separated out into the M extension, atomic operations into the A extension, single- and double-precision floating point into the F and D extensions, and 16-bit compressed instructions into the C extension.
The single fact that x0 is always does a great deal of work in reducing the number of instructions. addi x5, x0, 7 becomes “put 7 into x5”, add x0, x0, x0 becomes an instruction that does nothing (a NOP), and beq x6, x0, L becomes “branch if x6 is 0”. No dedicated instructions are needed for any of these.
Example 8.2(Encoding add x5, x6, x7 into 32 bits)
An R-format instruction consists, from the high bits down, of funct7 (7 bits), rs2 (5 bits), rs1 (5 bits), funct3 (3 bits), rd (5 bits) and opcode (7 bits). For add the specification fixes opcode = 0110011, funct3 = 000 and funct7 = 0000000. The register numbers are rd = 5, rs1 = 6, rs2 = 7, that is, 00101, 00110 and 00111. Laying them out:
| funct7 | rs2 | rs1 | funct3 | rd | opcode |
|---|---|---|---|---|---|
0000000 | 00111 | 00110 | 000 | 00101 | 0110011 |
Written out in a row this is 00000000011100110000001010110011. Grouping into nibbles gives 0000 0000 0111 0011 0000 0010 1011 0011, which in hexadecimal is 0x007302B3. This is the bit string the CPU read in Example 7.1. The same procedure carried out for the I-format instruction addi can be found in encoding the addi instruction(Example 2.1)[Programming Language Theory].
The reading can also be run backwards. The rule “if the low 7 bits are 0110011 it is an R-format integer operation, and the pair funct3, funct7 determines which operation” is, seen from the hardware side, precisely the truth table of the control unit.
RISC-V came into wide use for reasons that are not only technical simplicity. Since using the specification requires neither licence fees nor permission, students can design an entire CPU in a university course, researchers can try out their own extensions, and companies can spin up application-specific processors. The property of an ISA being a “contract” works best when the contract is published.
The layer above the instructions, that is, how this contract is used to build processes (Definition 3.1[What an Operating System Does]), virtual memory and privilege modes (Definition 2.1[What an Operating System Does]), is treated in The Role of the Operating System, and the machinery that translates a high-level language down to the contract is treated in Programming Language Theory.
9. Exercises
Section titled “9. Exercises”Exercise 9.1Easy
Define (NOR). Express , and using NOR alone, and thereby show that NOR is functionally complete.
Solution
. Indeed , so .
. The first equality follows from , and the second applies the construction of from the previous paragraph to .
. The right-hand side is , which by De Morgan’s law equals .
Since can all be written with NOR, combining this with Theorem 2.3 shows that every Boolean function can be expressed using NOR alone. The line of argument is the same as in Corollary 2.4, only dualised.
Exercise 9.2Standard
Answer the following about 8-bit two’s complement representation. (1) Find the bit string for . (2) Compute with an 8-bit adder and determine and in the notation of Theorem 4.2. (3) Confirm, using this example and the example , that overflow of signed addition can be detected by .
Solution
(1) . Inverting every bit gives , and adding gives (0xD3 in hexadecimal) as the representation of . As a check, read as unsigned this is , which is correct.
(2) . We follow the carries from the least significant end.
| (67) | () | sum | ||||
|---|---|---|---|---|---|---|
| 0 | 1 | 1 | 0 | 2 | 0 | 1 |
| 1 | 1 | 1 | 1 | 3 | 1 | 1 |
| 2 | 0 | 0 | 1 | 1 | 1 | 0 |
| 3 | 0 | 0 | 0 | 0 | 0 | 0 |
| 4 | 0 | 1 | 0 | 1 | 1 | 0 |
| 5 | 0 | 0 | 0 | 0 | 0 | 0 |
| 6 | 1 | 1 | 0 | 2 | 0 | 1 |
| 7 | 0 | 1 | 1 | 2 | 0 | 1 |
The result is , matching . We have and .
(3) In this example , so no overflow is signalled, and indeed the answer is the correct value. Read as unsigned, is a carry out of range, but in the signed reading the carry into the most significant bit and the carry out of it cancel, so there is no problem.
Next take . Since , following from the bottom: a carry is generated at from ; at digit we get , so and ; at we get , so and ; at we get , so and . The result is , which read as 8-bit two’s complement is . The test gives , correctly detecting the overflow.
In general, means that the carry into the most significant bit and the carry out of it disagree. If those two agree, the carry into the top position is correctly cancelled against the weight of the sign bit, and the result stays within the representable range.
Exercise 9.3Standard
For a certain pipeline stage, take , , , and , with the delay of the combinational logic ranging from to . (1) Find the maximum operating frequency. (2) Determine whether the hold constraint is satisfied, and if not, state what must be done about it. (3) If the combinational logic of this stage is split into two stages of equal delay, by what factor does the frequency increase?
Solution
(1) By the setup constraint of Proposition 5.2,
so .
(2) The hold constraint is , that is, . This is false, so the constraint is not satisfied. The shortfall is , so buffers of delay at least must be inserted into the shortest path to make . As Proposition 5.2 states, does not appear in the hold constraint, so slowing the clock does not help.
(3) After splitting, , so
The factor is , short of 2. The reason is that an overhead of is unavoidably added per stage, and this is exactly what lies behind in Proposition 7.2.
Exercise 9.4Hard
An I-format RISC-V instruction consists, from the high bits down, of imm (12 bits, signed), rs1 (5 bits), funct3 (3 bits), rd (5 bits) and opcode (7 bits). For addi, opcode = 0010011 and funct3 = 000.
(1) Encode addi x5, x6, -1 into 32 bits.
(2) In RV32I the I-format immediate is limited to 12 bits. Explain how to place an arbitrary 32-bit constant into a register, using the U-format instruction lui (which places the upper 20 bits of the immediate into the upper 20 bits of the register and sets the lower 12 bits to ).
Solution
(1) The 12-bit two’s complement representation of is 111111111111. Since rs1 = 6 is 00110 and rd = 5 is 00101, laying them out:
| imm | rs1 | funct3 | rd | opcode |
|---|---|---|---|---|
111111111111 | 00110 | 000 | 00101 | 0010011 |
Written out in a row this is 11111111111100110000001010010011. Grouping into nibbles gives 1111 1111 1111 0011 0000 0010 1001 0011, which in hexadecimal is 0xFFF30293.
(2) Let be the desired constant. Place the upper 20 bits with lui x5, hi and then add the lower 12 bits with addi x5, x5, lo. However, the immediate of addi is signed, so when the value of the low 12 bits, , is at least , the addi adds the negative value instead. We therefore correct for this by carrying a into the upper part in advance:
For example, if the low 12 bits are 0x678, which is below , so no correction is needed and the two instructions are lui x5, 0x12345 and addi x5, x5, 0x678. If on the other hand , the low 12 bits are 0xFFF ( as a signed value), so we use lui x5, 0x12346 and addi x5, x5, -1. The assembler’s pseudo-instruction li (load immediate) performs this test automatically.
References
Section titled “References”- David A. Patterson, John L. Hennessy, Computer Organization and Design: The Hardware/Software Interface, RISC-V Edition, 2nd ed., Morgan Kaufmann, 2020 — Chapter 1 (performance), Chapter 2 (RISC-V instructions), Chapter 4 (datapath and pipelining), Chapter 5 (the memory hierarchy).
- Sarah L. Harris, David Money Harris, Digital Design and Computer Architecture, RISC-V Edition, Morgan Kaufmann, 2021 — Chapters 1–3 (Boolean algebra, CMOS, sequential circuits and timing), Chapter 5 (adders), Chapter 7 (microarchitecture).
- Noam Nisan, Shimon Schocken, The Elements of Computing Systems: Building a Modern Computer from First Principles, 2nd ed., MIT Press, 2021 — Chapters 1–5. A workbook that actually builds everything from NAND up to a CPU.
- John von Neumann, “First Draft of a Report on the EDVAC” (1945). Reprinted in IEEE Annals of the History of Computing 15(4) (1993), 27–75. DOI: 10.1109/85.238389
- John Backus, “Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs”, Communications of the ACM 21(8) (1978), 613–641. DOI: 10.1145/359576.359579 — the source of the phrase “von Neumann bottleneck”.
- Andrew Waterman, Krste Asanović (eds.), The RISC-V Instruction Set Manual, Volume I: Unprivileged ISA — published at riscv.org/technical/specifications/. The primary source for the instruction formats and their encoding.
Report an error in this article ・Operated by: Mugen Giken LLC ・Pricing ・Terms ・Legal notice
© 2026 夢現技研合同会社 ・Feeding the text to an LLM is welcome. Code samples are MIT licensed.