Welcome

Firmware and gateware

Obviously, with so much programmable logic (the ESP32, the FPGA and the 486 itself), by itself the board does very little: the FPGA configuration, the code running on the ESP32 and the 486 startup code and BIOS do a lot of the heavy lifting.

For a PC, there's a few critical parts that are needed that are implemented by the FPGA. One of these is the RAM controller. Remember, the CPU has no concept of 'RAM', let alone of specific types or RAM: all it does is read and write data at addresses which may or may not be backed by RAM.

This means I needed to implement a RAM controller: something that takes those reads and writes and massages it into something the RAM understands. I'm using SDRAM, and that actually needs a fairly complicated protocol to read or write data: activate a row, wait a bit, do the read or write from a word inside the row, precharge the row to close it, wait a bit, repeat for next word. An added complication is that my SDRAM is 16-bit while the bus is 32-bit - for each read or write on the bus, I have to do two reads or writes on the SDRAM.


This all sounds like it's very slow - a 486 bus access that could finish in two bus cycles converts into a many-cycle SDRAM access. The saving grace is that the SDRAM runs at 100MHz meaning one 486 bus cycle (running at 33MHz) converts into 3 SDRAM bus cycles. This means that an entire DRAM cycle can complete in six 486 bus cycles, which is not optimal:


A paltry 16MByte per second is no world record: even an average contemporary 486 could get twice as much. But this slow memory speed is not a very large issue: the 486 CPU I chose has 16K of internal cache, and for games this compensates for a large amount of the slowness of the memory system. Aside from that, the games I prefer to play certainly don't require the CPU to run at top speed. If I ever change my mind, it's pretty simple to optimize this: I can optimize the activate/precharge for bytes that are close to eachother, and I can implement burst reading to return one word per clock cycle. However, for now that is not needed.

Aside from the memory subsystem, the FPGA also implements (the low-level parts of) all the peripherals. The PIC (Programmable Interrupt Controller), the PIT (Programmable Interval Timer, the DMA (Direct Memory Access) peripheral as well as the Sound Blaster Verilog were all copied from the ao486_MiSTer project. This is a project for the MiSTer FPGA board, which is a platform for FPGA-based emulation of consoles and systems. ao486 in particular implements a PC based around a 486SX-ish emulation core, so its needs and mine overlap, which means I can re-use components from it.

The FPGA also has peripherals that interface with a back-end on the ESP32 side. (MiSTer also has such a backend system, but because the architecture is entirely different, I couldn't easily copy these peripherals.) The FPGA and ESP32 work together to implement the IDE controller with attached hard disk and CDROM drive, the NE2000 network card which interfaces to a WiFi network, the MIDI synthesizer implemented in the ESP32, and of course the PS/2 controller and joystick ports which interface with any USB- or BLE-connected keyboards, mice and gamepads.

Aside from that, there's also some peripherals you wouldn't immediately find in a PC. There's a bus snooping peripheral that by default sends writes on IO port 80 to the ESP32, but can also forward information about other ports or port ranges. This is invaluable for debugging in case a given program does not play nice with the hardware in the FPGA. There also is an interface for the ESP32 to read and write from/to main memory and a special debug/configuration address space. This obviously is also useful for debugging, but in day-to-day life this is used to load the BIOS and video BIOS into SDRAM before starting the CPU. This means there's no need for a separate ROM peripheral.

Debugging didn't only happen using debug interfaces, by the way. Especially during BIOS bringup, I had lots of issues with the signals in the FPGA, and I needed to see how all the peripherals behaved while booting. Now, the usual way to get around that is to use simulation, for instance using Icarus Verilog but in this case a whole chunk of the system (namely the 486 CPU) was a black box that I don't have the Verilog for; how to get around that? Luckily, I've dealt with this before. Verilator is a tool that can take Verilog code and convert it into C++ while still allowing the usual simulation tools (i.e. dumping signals to vcd to inspect them) to work. Because the end result is C++, it's trivial to link it to other C++ code. In this case, I whipped up a thin wrapper around libx86emu, a library that emulates an x86 CPU in software to run firmware blobs. That wrapper could then be dropped in instead of the hardware 486, and that allowed me to debug the bringup stages of the BIOS.

The BIOS itself is a (lightly modified variant of) the BIOS used in the ao486 project, which itself is derived from the Bochs software PC emulator. It's a tried-and-tested BIOS, and while it's written in a bit of an odd C/assembly mix, it's pretty easy to debug and modify. The video chip is mostly seen as a standard VGA card, and it only needs a VGA BIOS to function. For the F65545 chip, a VGA BIOS still is available, and someone even shared the tools (look for cm110_cm112.zip) used by system integrators to customize the BIOS.


And with all that, I have a working 486 mainboard that can boot DOS and run games. Let's start with some benchmarks, all from Phils Computerlabs DOS Benchmark Pack:


Norton System information thinks this is a Cyrix 486 rather than an AMD one. It's also not impressed with the overall speed of the chip, assessing it as slower than a 486DX-33. I imagine this is because of the slow memory bus pulling down the score by a lot.


Superscape 3DBench 1.0c gives about 35.7FPS.


Doom, on max details, gives around 21FPS.


Quake timedemo does around 8FPS. Unsurprising, as Quake is written to target a Pentium and not a 486.

As mentioned before, there certainly is room for improvement when it comes to the overall speed of the platform: the simplistic memory controller is almost certainly holding the machine back a fair bit. As it's implemented in the FPGA, this could be improved without changing the actual hardware.

Okay, the prototype mainboard works, and you could viably make a desktop PC from this. However, I promised something portable. I'll explain how I made this mainboard into an actual product that makes it easy to pick up DOS games, even in modern times.

« Prev 4 Next »


© 2006-2026 Sprite_tm - Contact