Migen Gigatron

2019-04-14 fpga / python
An attempt to implement the Gigatron TTL Microcomputer on an FPGA, in Migen.

An attempt to implement the Gigatron TTL Microcomputer on an FPGA, specifically the TinyFPGA BX using Migen.

Why?

To learn Migen and a bit more about FPGA programming. I’ve previously messed around with FuPy but it’s a pretty complicated place to dive in.

Also I’m just blown away by what the Gigatron creators have achieved from so few parts and how much this would have changed the history of personal computing if Woz had homebrewed something like this instead of the MOS 6502 in the Apple 1.

It’s already been done …

Yeah, but in Verilog.

Repository

Migen Gigatron on Github

Building

Part of the point of this project is to work out how to build the FPGA gateware from scratch, without running weird environments which I don’t understand.

So I’m not using conda, or apio, or litex-buildenv or any of that stuff.

Tools

Other than a bunch of Ubuntu packages, and some of the Python libraries, everything is pulled in as submodules and built within the tree, or at least that’s the idea.

    git submodule update --init --recursive

    cat ubuntu-packages.txt | xargs sudo apt install -y

    make -j$(nproc)

    pip install -e migen/
    pip install tinyprog

Gateware

    PATH=$PWD/build/bin:$PATH 
    python3 test.py

Programming

First make sure systemd modemmanager isn’t messing with you

Then you can run tinyprog to copy the gateway onto the FPGA. Press the reset button and then:

    tinyprog -p build/test.bin

Hardware

I/O

The Gigatron board is almost all logic, which can be fit into the FPGA gateware. There’s some I/O circuitry around the edges, eg: for the audio and video outputs, but that can be built on strip-board easily enough, with the TinyFPGA board fitting into headers.

There are 24 user I/O pins easily available (and another 12 hidden underneath the module) and the Gigatron only needs 8 digital outputs for video, another 8 for blinkenlights and audio and a single input pin for the game controller (SER_DATA)

Some of the pins and analogue circuitry could be dispensed with by adding in a PWM circuit for the audio output.

Memory

RAM

Here we hit the first hurdle: the ICE40LP8K FPGA used on the TinyFPGA BX board has only 128Kbit = 16KByte of block RAM and the Gigatron needs 32KByte = 256Kbit of RAM. Ooops. So our options are:

  1. Add an external static RAM to the project.
  2. Wait around for the TinyFPGA EX or the FleaFPGA Ohm to be ready, which should have plenty of RAM on board (both Lattice ECP5 which is supported by the Open Source SymbiFlow tool.
  3. Develop on a slightly less tiny platform, eg: Artix A7 or similar.
  4. Make a cut-down version of Gigatron for the moment.

Part of the attraction of using Gigatron for this project was compatibility with the Gigatron ROM but I think I’ll run with option 4 for the moment, and make a cut down gigatron with the intention of expanding it by methods 1 or 2 later.

Option 3 would mean a whole different and non-Open Source toolchain, so I’m not keen on that.

ROM

There’s also a substantial amount of ROM required … 64Kwords = 128Kbytes of it! The Gigatron is a Harvard Architecture processor, so the ROM is separate from RAM and in this case is 16 bits wide as opposed to the 8 bit wide RAM.

When running, the soft-CPU will need to retrieve its ROM code from the SPI Flash on the TinyFPGA BX board.

Temporary Solution

For now, I’ll set up two blocks of on-chip SRAM: one 8 bit x 8192 word read-write memory for the RAM, and one 16 bit x 4096 word read-only memory for the ROM.

At least for now we can get the CPU sorted out and hopefully get the vCPU and maybe even Gigatron BASIC working.

With only 8K of RAM it’s not going to be able to generate 160x120x6bit video though, that needs a lot more space! Perhaps a boring UART/teletype interface will have to do.

ROM Compression

There’s also bigger versions of the ICE40 with more RAM available, for example the “UltraPlus” parts like the ICE40-UP5K … these have an extra 16 bit x 64Kword worth of “SPRAM” (Single Ported RAM) which would be rather handy. Sadly that’s still not quite enough for both RAM and ROM, but it’s closer!

It’s pretty obvious looking at it that the ROM is sparse: there’s no “copy from ROM” instruction so data has to get loaded in using LD $XX instructions using a weird pipelining trick.

To make it a bit clearer, here’s a graphical representation of the ROM, with green pixels representing “LD” instructions (opcode=0) and blue pixels representing zero words (opcode=0, data=0)

ROM Map

I’m thinking some kind of lookup table could pack these pages a bit more tightly in the internal RAM, thus scavenging back some storage to fit in the actual RAM.

One interesting thing to note from the memory map is that most of the code, including a menus and a video driver fonts and a 16-bit virtual CPU, fit into the first 25 rows (3.2 kWord) and then the two pictures (parrot and jupiter) take up 120 rows (16 kWord) each.