← back

8-Bit Computer

2021


From late 2020 to mid 2021, this 8-bit computer was my first introduction to computer architecture and digital logic. I was initially inspired by Ben Eater's model, but here I've outlined several of the improvements and changes I've made to extend the architecture. I presented this project at the 2021 Greater Vancouver Science Fair, receiving Senior Bronze Divisional.

Keywords

  1. Soldering, circuit, and PCB design
  2. Prototyping, troubleshooting, and verification (oscilloscope, multimeter, etc.)
  3. Microprocessor architecture
  4. FPGA testing

1. Baseline

The baseline computer is derived from Ben Eater's model. The first version was made on an array of breadboards, which I later soldered onto circuit boards.

Breadboard version of the 8-bit CPU

Alongside the logic itself, I also had to make an auxiliary EEPROM programmer to implement the control logic. It's not as sophisticated as Ben Eater's shift register programmer, but it gets the job done.

EEPROM programmer using ATTINY85
An ATTINY85 (bottom left) was used to generate <1000 ns clock pulses to write to the AT28C16.

One regret I had in this stage was not using an FPGA or similar large IO device for the control logic. Debugging, writing, and reading EEPROM contents became increasingly challenging down the line, which ended up being one of the largest hurdles in this project.

My final build replaced the breadboards with perf-boards and chip sockets, soldering the components on for stability.

Final soldered version of the 8-bit CPU

2. Conditional programs

Implementing conditional execution was my largest improvement and what I presented at the Greater Vancouver Science Fair. Instead of a standalone module like RAM or registers, this device synthesizes the RAM, ALU, and program counter together.

Introducing jumps

The program counter (PC) is simply a binary counter. When it increments, it sends its count to the RAM, and the RAM returns what instruction should be next executed. Just how registers can load a value by toggling a control pin low, the PC can interrupt its counting, skip to a specified value on its inputs, and then resume counting.

Program counter loading diagram
When the counter loads, its input overwrites the current PC value. Once the load signal is off, it continues counting from the loaded value.

So our program counter has jumped to a specified value. In a sequential program, jumps hold little value since each operation is intended to follow the previous. But for conditional programs, they are essential.

print("starting")
x = 4
y = 3
z = x + y
print("done")
A sequential program — jumping is useless here.
x = 4
y = 3
z = 9
if x+y < z:
    print("in range")
print(z)
A conditional program — executing sequentially would run the if-block regardless.
Memory partitioning for conditional execution
The program partitioned in memory so each "chunk" lives at its own address.

Since x+y is indeed less than z, our CPU is told to jump to memory address 1000 to execute "in range". But we also need to jump back afterward, since print(z) should execute regardless of the condition.

Final conditional program flow

Implementing jumps

The ALU compares registers A and B, outputting a 2-bit code: 01 for A<B, 11 for A=B, 10 for A>B. Each 8-bit instruction encodes the condition in the instruction bits and the jump address in the data bits.

Jump circuit schematic
The jump circuit — deceptively simple, but the crux lies in partitioning memory correctly.

Example: a while loop

x = 0
while (x < 10):
    x += 1
print(x)
While loop compiled to 8-bit CPU instructions
The while loop compiled down to our 8-bit instruction set.

Since registers can't increment directly, we load 0xFF into register B and subtract — two's complement gives us an increment by 1. The program loops by jumping back to the comparison step until the condition fails.

Final block diagram with JUMP block
The final block diagram, including the newly minted JUMP? block.

3. Further development

There's a lot more that I've looked into and created for this computer. I was going through some old plans and found notes on how to implement a 256 byte RAM instead of the current 16 byte.

256 byte RAM sketch

Some other changes I made which I want to note:

  1. 16x2 character LCD implementation
  2. Reducing instruction fetch cycle by 66% via a dedicated instruction bus

I hope you enjoyed reading, and learned something new about how computers really work. Perhaps the idea of what a "program" really is has changed, like it did for me. Pursuing this project made me more appreciative of how much our processors do that we don't get to see — for example, the implementation of recursion.

Best,
Ebrahim