One clock: phi1 and phi2 become enables, then a PLL
What I tried
The core used to clock its modules on phi1 and phi2 as real clock signals, the way the schematic reads. That’s several derived clock domains on an FPGA, and the debug controller added a LUT on the clock path to stop and step the CPU. Timing reports couldn’t say anything useful about any of it.
On 2026-04-20, in six phases: phase_clocks now emits one-cycle edge pulses for phi1 and phi2 rising and falling. The program counter migrated first, from rising_edge(phi1) to clk with a phi1_rising enable. Then the leaf modules, then the control modules, then the top-level processes. RAM moved onto the raw clock. Last, the gated clock in debug_clock_control went away. The CPU always runs on the master clock, and the debug controller outputs a run_enable hold that freezes the phase_clocks state machine when you stop or step. Every downstream flop is gated on the pulses, so holding the pulses holds the CPU.
The testbench baseline had to be repaired first so I could tell the refactor from a regression.
What I measured
After phase 6, clk is the only clock net in the design. On 04-21 I constrained it to 100 MHz so the slack number in the report meant something. On 05-06 I dropped that to an honest 50 MHz, because 100 wasn’t closing without multicycle path annotations I didn’t want to write.
What changed on 05-06
An ECP5 on-chip PLL divides the 100 MHz oscillator to 25 MHz and everything runs on that: CPU, UART, debouncers. Timing closes with margin and no multicycle constraints. Power-on reset waits for PLL lock. A CLK_FREQ_HZ generic on b8008 and phase_clocks scales the phi pulse widths, so the 8008’s 0.8 and 0.6 microsecond phases hold at 25 MHz or any other system clock.
Next
The board still doesn’t boot reliably from the EEPROM. This refactor removed one candidate. The others are still out there.