It's the early 1980s, it's nearly Christmas, and everyone seems to be releasing new computers. Management has decided we need to release a new computer too. I have one month to create a new computer from scratch. Some corners may need to be cut.
Yesterday I accidentally tried to plug my laptop power supply into my FPGA board. There was a spark as I tried to do so, and now the board doesn't work properly. Oddly enough, it still loads the test configuration off the SD card and flashes an LED, but the JTAG interface no longer works, and the Zynq chip gets burning hot within a few seconds of turning it on. Which is a shame, as I was just getting the hang of VHDL again. It also means I'm going to have to bow out of this year's RetroChallenge (https://www.retrochallenge.org/), although I am tempted to dig out my Z80 breadboard project and do something with that instead.
I got my AUG1 computer to flash an LED a while back now, but getting it to do anything more complicated has stumped me until today, when I got a couple of PMODs: one with eight LEDs, and another with four slide switches. It's so much easier trying to debug VHDL when you have more than one input and one output. It still took all evening to persuade it to work. I both love and hate working with VHDL. When it works, it's amazing what you can do with it, but when it doesn't work it's a nightmare. I'm still not totally sure which of my changes fixed it, but it now works.
This video shows a flashing red LED which changes speed depending on the binary number input on the switches. And this isn't plain VHDL — it's my AUG1 CPU running a small programme which reads the switch values once per loop and adjusts the loop length accordingly.
It feels slightly backward at this point — when I have all the power of an FPGA which can do thousands of things simultaneously, and I'm configuring it to execute my programme sequentially, using a very badly designed CPU. But it's very satisfying getting it to do that!
I'm not sure what I should work on next. One thing I'd like to do is to get HDMI output working. I've found some VHDL code which successfully displays a test image, so I want to try to combine it with my CPU to output text. Another thing I want to do is to work out how to get a programme into the ZYNQ's ARM processor and how to make that communicate with the FPGA side of it, and then get my CPU to use the on board DRAM instead of using block RAM, so that I can quickly upload new AUG1 programmes without needing to rebuild the entire bitstream, which takes forever. I don't know which of these tasks will be easier — the HDMI task seems hard, but to get the ZYNQ PS working requires reading Xilinx documentation, and that's never a pleasant task.
For a long time I've thought that designing a CPU would be quite interesting. Every so often I try, usually with some lofty aim to make an architecture which is really elegant. So I spend ages designing it on paper, and eventually give up because I can't quite get it to work within those parameters.
So this time I decided the only requirements would be that it would be a largely 8 bit processor, and it would work, even if it wasn't particularly nice to use, or elegant, or efficient.
So this is what I ended up with. (As I'm a Z80 person, there are a few similarities with that CPU).
An 8 bit data bus and a 16 bit address bus – but I'm tempted to include banked memory later. Memory mapped IO only at the moment. As yet, no interrupt handling, but I think that will have to be added at some point. There are control lines to indicate whether a memory operation is for instructions, data or the stack, so in theory each of these could be in separate, isolated areas of memory.
8 8-bit registers, r0..r7, which can also be used as 4 16-bit registers, w0..w3. Separate 16-bit programme counter and stack pointer. A separate flags register with four flags: zero, carry, overflow and negative.
The instruction set is comprised of these instructions:
r<r> = r0..r7
r<s> = r1..r7
w<w> = w0..w3
<c> = z/nz/c/nc/o/no/n/nn
<nn> = 8 bit number
<nnnn> = 16 bit number
This leaves 54 opcodes currently unused, so there's plenty of room for expansion at the moment.
r0 is used as the accumulator. You can jump/call using the w3 register or a direct value only — there's no ability to use the other w registers for jumps/calls.
Unlike on the Z80, the 16 bit inc and dec instructions also affect the zero flag. But we have no instruction for move data directly between registers — everything currently has to go via r0, which is a bit of a pain. You can also only load values from memory into r0.
The VHDL implementation is currently very crude, and most instructions take quite a few clock cycles to complete. However, since my FPGA board runs at 125MHz it's still much faster than it needs to be.