Architecture

Retro-Active is building toward a common bus standard so that peripheral cards, backplanes, and host-CPU boards from different contributors can plug together regardless of how each one is built — discrete logic, PAL/GAL, FPGA, or whatever comes next. This page is an early draft of that standard: it documents where we are today, the thinking behind the current signals, and where the open questions still live. Expect it to change as the community weighs in.

A Protocol, Not a Pinout DRAFT

Most retro expansion buses — the Apple II slot, the original IBM PC ISA bus, and a lot of homebrew 68k buses — are essentially the host CPU’s pins brought out to a connector. That makes cards simple, but it locks the bus to one CPU family forever. A card designed for a 6502 machine can’t drop into a 68000 machine and vice versa, even when the card’s actual job (driving a UART, blitting pixels, talking to an SD card) has nothing to do with the host CPU.

Retro-Active’s bus is closer in spirit to PCI: it defines its own signals, its own timing, and its own transactions, independent of any specific CPU. Each host connects through a small bridge — a handful of 74-series chips, a PAL/GAL, or a simple FPGA block — that translates the host’s native cycles into bus transactions. The bridge is the only CPU-specific part. Past it, every card behaves identically.

We’re not building PCI. The complexity target matches the era — simpler timing, slower clocks, a smaller signal count, and bridges you can draw on a napkin. But the core idea — a CPU-independent protocol with a bridge per host — is what makes the same card work with an 8-, 16-, or 32-bit host, and what lets one driver (recompiled for each CPU via GCC) target them all.

Design Goals

Signal Lines (Planned)

The bus splits into two kinds of lines: shared lines that run across the entire backplane and every slot sees in common, and per-slot lines that each slot connector carries on its own pins. The tables below are the current working sketch, drawn from the reference implementation. Widths, directions, and even the existence of some lines are likely to change once more than one card is prototyped on more than one host.

Shared backplane lines — one wire, every slot sees the same value:

Signal Direction Width Purpose
CLK host → cards 1 System clock (25 MHz reference)
RESET_N host → cards 1 Active-low global reset
ADDR host → cards 24 Address bus (16 MB window)
DATA bidir 32 Data bus (driven by the host on writes, by the selected slot on reads)
WSTRB host → cards 4 Write strobe, one bit per byte lane
RSTRB host → cards 1 Read strobe
READY cards → host 1 Transaction complete / wait-state release (wire-OR; only the selected slot drives)

Per-slot connector pins — one wire per physical slot position:

Signal Direction Width Purpose
SLOT_SEL_n backplane → card 1 Active-low chip-select for this slot, decoded from ADDR[19:16] by the backplane
IRQ_n card → host 1 Active-low interrupt request owned exclusively by this slot

One open design choice: the bus can either have a fixed native width (with bridges narrowing or widening for smaller or larger hosts, like PCI does) or scale with the host. The current sketch shows a 32-bit data bus; an 8-bit host’s bridge would serialise accesses over multiple cycles. Feedback welcome.

Bridging to a Host CPU

Every host talks to the bus through a bridge. The bridge waits for the CPU to start a memory or IO cycle aimed at a bus address range, translates that cycle into a bus transaction (address, direction, width, data if writing), drives the bus signals with the right timing, and returns data and any wait states back to the CPU when the bus answers.

For slow hosts (a 1 MHz 6502, a 4 MHz Z80) the bridge can be a handful of gates and a PAL. For faster hosts (a 10 MHz 68000, a 25 MHz 386) a bundle of 74-series parts or a small FPGA handles the pipelining. The bridge absorbs every CPU-specific quirk so the rest of the system doesn’t have to care.

Slots and Backplane

The bus is organised around a physical backplane with up to 16 slots. Each slot is identified by a number in the range 0–15 and gets a 64 KB register window in the host's address space.

Slot numbers are encoded directly into the upper bits of the bus address:

Address bits Purpose
ADDR[23:20] IO-region tag — must be 0x4 for a card cycle
ADDR[19:16] Slot number (0 – 15)
ADDR[15:0] Per-slot register space (64 KB per card)

The backplane decodes ADDR[19:16] through a single 74LS154 (4-to-16 line decoder) to produce 16 active-low SLOT_SEL_n lines, one per connector. The decoder is enabled only when ADDR[23:20] equals the IO tag and at least one strobe (WSTRB or RSTRB) is active. Cards therefore do not need to decode the address themselves; they just wait for SLOT_SEL_n to go low and use ADDR[15:0] to pick a register. That keeps the card's decode logic to a single AND gate or two.

Each card exposes a small bank of memory-mapped registers behind its slot window. A 16-bit register offset gives plenty of room — typical cards use only a handful of registers near the bottom of the window and leave the rest reserved. Writes are accepted via WSTRB; reads are driven onto DATA on the same cycle READY is asserted.

See the backplane reference document for the full slot pinout, the decoder schematic, and a connector-pin budget.

Interrupts

Each slot owns its own IRQ_n line back to the host. That gives up to 16 independently-reported interrupt sources on a 16-slot backplane — no polling required to tell cards apart, no wire-ORing, no daisy-chain priority schemes. A card pulls its own IRQ_n low whenever it has something to report, and releases it when the host acknowledges.

What happens on the host side depends on the CPU. Each host bridge is paired with a small interrupt controller peripheral that aggregates the per-slot IRQ lines into whatever the CPU's interrupt pins expect:

The interrupt controller is a required host-side peripheral — it's part of the platform every bridge assumes — but it lives outside the bridge proper. That separation keeps the bridge's chip count honest (nine chips for a 6502, twelve for a 68k) regardless of how many interrupt sources the system ultimately supports, and it lets a builder put the interrupt controller on its own card if they prefer.

Within a card, multiple internal interrupt sources collapse onto the single IRQ_n pin via a simple wire-OR or open-drain gate. The host's interrupt controller handles the "who raised me?" question at slot granularity; anything finer (which ring buffer filled? which timer expired?) is the card's responsibility, usually answered by a read of one of its own status registers.

Example Memory Map

This is one valid memory layout — the one used by the reference implementation. Builders are free to map memory however they want, as long as the IO region's slot encoding (ADDR[23:20] = 0x4, ADDR[19:16] = slot) and the per-slot register window match the bus spec. The diagram and table below show what ships on the current reference board today: a 24-bit address space split between boot ROM, memory-mapped IO, kernel/program RAM, a framebuffer, and a stack at the top of RAM.

Zooming in on the IO region: 0x400000 is slot 0's base, 0x410000 is slot 1's base, and so on through 0x4F0000 for slot 15. Each card's registers live in the low 64 KB of its window.

Vertical memory map diagram showing BRAM BIOS at 0x000000, IO devices at 0x400000+, kernel space at 0x800000, program space at 0x810000, framebuffer at 0xA00000, free heap, and stack at the top of SDRAM ending at 0xFFFFF0.
Range Size Purpose
0x000000 – 0x007FFF 32 KB BRAM: BIOS / boot ROM
0x400000 – 0x4FFFFF 1 MB IO region — 16 slots × 64 KB each, slot n at 0x4n0000
0x800000 – 0x80FFFF 64 KB Kernel space
0x810000 – 0x9FFFFF ~2 MB Program space
0xA00000 – 0xA7CFFF 512 KB Framebuffer (640 × 400 × 16 bpp)
0xA7D000 – 0xEFFFFF ~4.5 MB Free heap
0xF00000 – 0xFFFFF0 1 MB Stack (SDRAM, grows down)

Reference Implementation

The first working example of this architecture is a Colorlight i5 board (ECP5) running the FemtoRV RV32IMFC soft core at 25 MHz, with an HDMI GPU, a 4-voice 4-operator FM synthesizer plus sampled audio, a PS/2 keyboard controller, and an SD card FAT32 driver. This implementation is FPGA-based because that’s the fastest way to iterate on the spec today — but it’s an implementation, not the spec. A discrete-logic reference build is planned alongside it so the two can keep each other honest: anything that’s easy in the FPGA but impossible in 74-logic needs to change.

Source for the reference implementation lives in the benpayne/learn-fpga repository (branch colorlight_i5_support).

Standardization Roadmap