Building a CPU From Scratch

CS 441 Lecture, Dr. Lawlor

Circuit Simulation via TkGate




Simple CPUs built from Digital Logic Circuits

adder circuit

add and multiply

tri-state bus circuit

CPU with manual control



CPU with instruction decode

So overall, this CPU's instructions look like:
    <constant: 8 bits>
CPU with instruction decode
Here's a philosophically similar CPU, but I'm using multiplexors instead of tristate busses. You can get an 8-bit mux by double-clicking the mux, clicking the "Port" tab, double-clicking on an input (or output) pin, and typing in the bit width you want (possibly after shuffling dialog boxes). The above CPU uses bits 7:6 as the destination register, bit 5 is reserved, bit 4 is the arithmetic operation, and bits 3:2 and 1:0 are both input register numbers.
So this mux-based CPU's instructions look like:
    <destination register: 2 bits>  <reserved: 1 bit>
<operation: 1 bit>
<source register A: 2 bits>  <source register B: 2 bits>
For example, the instruction (hex) "13" (binary 0001 0011) writes to register 0 the sum of register zero and register 3 (the constant).

CPU Design Philosophy

A CPU is actually a very simple circuit that continually does exactly two things:
  1. Figure out what to do next. (fetch)
  2. Do it. (decode and execute)
CPU's usually encode "what to do next" in a chunk of binary data called a machine language instruction, almost always stored in memory somewhere.  CPUs usually have a special register, the "program counter" or "instruction pointer", that points to the address in memory with the instruction to execute next.

So "Figure out what to do next" just means "Load the next instruction from the memory address at the instruction pointer."  The instruction pointer is usually moved to point to the next instruction as part of this  "fetch" phase.

"Do it" just means to decode the machine language instruction and figure out what it's telling you to do, then actually do what it says (execute).  We've basically got that under control in the TkGate circuit above.

A Trivially Simple CPU Emulator: UEMU

A full-fledged CPU emulator can be written in just a few dozen lines of C++.  For example, here's the core of "UEMU", a silly little made-up CPU I wrote a few years ago:
int CPU::run(void) {
while (1) {
int inst=mem[regs[0xf]++]; /* instruction fetch */
int opcode=(inst>>12)&0xf, i=(inst>>8)&0xf,
j=(inst>> 4)&0xf, k=(inst>>0)&0xf;
switch (opcode) { /* instruction decode */
case 0x1: regs[i]=(inst&0xff); break; /* "load Immediate" */
case 0xA: regs[i]=regs[j]+regs[k]; break; /* Addition */
case 0xC: if (regs[j]>=regs[k]) regs[i]++; break; /* Cond */
case 0xE: /* Emulator (or OS) call: */
switch (i) {
case 0x0: printf("%d%c",regs[k]," \n\t,"[j]); break;
case 0x1: printf("Enter a value:\n");
scanf("%i",&regs[k]); break;
case 0xD: dump(inst&0xff); break;
case 0xE: return regs[k]; /* Emulator Exit */
default: quit("Unknown OS-trap instruction 0x%X at 0x%X!\n",inst,regs[0xf]-1);
default: quit("Unknown instruction 0x%X at 0x%X!\n",inst,regs[0xf]-1);
(Try this in NetRun now!) or just download the source code and run it yourself.

"regs" is a little array of registers.  "mem" is another little array of memory locations.

If you look at the decode step, it should eventually become clear that executing an instruction like "0x1234" will be an "Immediate" load instruction (case 0x1:), loading into register 0x2 the value 0x34.

Similarly, "0xA234" will Add into register 2 the contents of registers 3 and 4.


Used only for storing program instructions.  UEMU is a bit odd in that memory consists of 16-bit instructions, not 8-bit bytes.  Normally a single instruction (or integer) stored in memory spans several bytes, but not in UEMU!


There are 16 registers numbered 0 through F (in hex).  Register F is the program counter--it stores the memory address of the next instruction to execute.  Note that loading a new value into register F will cause a jump!  All other registers are free for programmer use.


The instruction encoding is chosen such that everything's a multiple of 4 bits long, which makes it easy to write machine language programs in hex.  There are a ridiculously small number of instructions:
A tiny software emulator like this is a good way to get the feel for how to design a CPU instruction set.