… although I spent months tinkering with a Z80 chip in a previous life - long, long ago.
But with an FPGA, that can change, at least in the virtual sense: there are several “soft cores” implementing an 8080 chip in a HDL, including memory and peripherals.
The light8080 project at OpenCores is one such implementation (in Verilog), and it’s quite an interesting beast because it uses a microcode architecture. Microcode is like “the lowest of low-level stored-program execution models”: it sits between machine instructions and the gates, registers, and memory comprising its implementation.
There’s a repository on GitHub with the
implementation, which I have modified slightly to set up my own
demo (calling
it “lite8080” to avoid confusion). I’ve also set up a lite8080.qpf
(Quartus
Project File) there, which will build the final bitstream for my EP4CE6-based
FPGA board.
Here’s the result:
That’s 10% of the FPGA, with 2 KB used for the microcode ROM and 4 KB as actual 8080 memory (this particular FPGA could provide up to 28 KB). We could in fact fit multiple 8080’s in there, if we wanted!
According to the Quartus timing analysis, this design should work up to 100 MHz.
Which is all nice and well, but how do you get 8080 code into this thing?
It turns out to be quite simple. Part of the bitstream can contain initialisation data for RAM memory. This is in fact how ROM works in an FPGA (such as the microcode in this design): preset the memory with data and don’t implement write access at all.
In this case, I’ve taken the easy way out and kept the demo code already present in the original light8080 project: a small “hello world” and serial echo demo, written in C.
This can then be compiled to 8080 code by the legendary Small-C compiler, and turned into a hex file of just over 1 KB. Then it can be converted into Verilog code (there are also other ways to do this), as shown here.
There were two small problems I had to fix:
The code was written for an FPGA running at 30 MHz, whereas my setup runs at 50 MHz. This affects the baud rate calculation, so I ended up patching the hex values to run at 19,200 baud.
The external interrupts I connected to some outputs on an unused PMOD header were constantly generating interrupts. So I decided to completely disable interrupt handling for now.
But after that: bingo, success!
$ picocom -b 19200 /dev/cu.usbserial-00002414
picocom v2.1
[...]
Terminal ready
Hello World!!!
Dec value: 5678
Hex value: 0x4D2
Echoing received bytes:
asdasd
Thanks for using picocom
$
There are more complete implementations on FPGAs, such as the MultiComp design I’ve built a while back, which runs CP/M and has a whopping 64 KB of ram. But this “light” implementation of the Intel 8080 chip is a nice illustration of how small these designs can be and how microcode works.