The monitor finally talks, and the flaky boot was the level shifters

/ Intel 8008 VHDL / from intel-8008-vhdl 8008 b8008 monitor txs0108 ecpbram cdc selftest ecp5

What I tried

Fetch-capture instrumentation in the FPGA: latch every T3’s address and data onto the LEDs, and read them out when the CPU wedges. A black-box recorder that holds the killing instruction. Then a standalone rom_diag firmware that hex-dumps the CPU’s own view of its first 256 ROM bytes plus a RAM write and readback pattern, forever, with no receive path and no parsing. That proved the CPU, BRAM and UART transmit chain clean on their own.

What broke

Several independent things, which is why it took months.

The HW-221 breakouts are TXS0108E auto-direction shifters. I was running the 5 V EEPROM’s outputs into a bank whose VCCB rail was at 3.3 V. That’s an absolute-maximum violation on every read. Those parts wedge into a bad state until power-cycled, which is why boot reliability changed when I power-cycled the shifter board and nothing else. Auto-direction one-shot shifters were the wrong part for a memory bus regardless.

The bootstrap FSM was clocked on the derived phi2 signal with an async reset, so every path into it was unconstrained. Glitches re-jammed RST 0 (spontaneous restart to the banner) or re-fired the post-bootstrap hardware break (freeze). It now advances on a phi2 rising-edge enable inside the 25 MHz domain. No clock crossing remains.

The D command printed every low nibble as 0. send_hex_byte parked its byte in B, and char_delay counts B down to zero. Saved in C instead.

Weeks of “memory corruption” in the monitor turned out to be my backspace keystrokes going into the command buffer as raw 0x08 bytes. The hex parser stopped at them and mangled addresses. The CPU was never wrong about that one.

What changed

ROM went back inside the FPGA. The Yosys ROM problem from January got solved properly this time: a synchronous-read ROM that Yosys infers as DP16KD block RAM, with the one-clock latency invisible to the 8008. Firmware updates now go through ecpbram, which patches ROM contents into the post-PnR config. Unused ROM space is seeded with random bytes so ecpbram has a unique pattern to match, and it fails loudly if the pattern is missing, so the old silent corruption can’t come back silently.

The data RAM also went from async-read distributed LUTRAM with ‘Z’ outputs to synchronous block RAM. That LUTRAM was the design’s critical path.

Auto-start: a synthetic run press 2 ms after reset release, so the board boots hands-free. A W command for writing bytes. A boot testbench over the full top with a PLL stub and UART decoder, power-on to banner byte.

What I measured

ThingBeforeAfter
Firmware iteration4 min resynthesis0.66 s ecpbram
fmax46 MHz116 MHz
T-state~4.4 us (real chip 4 us at 500 kHz)
ISA self-test on silicon42/42

The self-test ROM runs on the FPGA and reports per test over serial: every ALU op in register, immediate and memory form, carry and borrow chains, all rotates, sign, parity, zero and carry branches, conditional CALL and RET both ways, 6-deep call nesting, a RAM pattern walk. The only bring-up failure was a wrong expected value in my own test. The CPU summed 0x11 times 28 to 0x1DC and the test wanted 0x1BC.

Next

L to load Intel HEX over serial and G to run it. First payload is the 1974 SCELBI Mandelbrot from Mike Willegal’s page.