FPGA implementation

I targeted a Xilinx Zynq-7000 on a Digilent PYNQ-Z2 board for hardware bring-up. This page documents what synthesis cost, where the timing closed, where the design sat on the fabric, and the live demo running on the board. The bug from bring-up moved to the Verification postmortems, where it shares context with the rest of the verification work.

482/482 RISCOF RV32I pass against Spike
1972 LUTs (3.71% of Xilinx Zynq-7000 (PYNQ-Z2))
120/120 Regression suite on every commit

Toolchain and target

Resource utilization

The full RV32I core, register file, instruction and data memories, MMIO, and the PYNQ-Z2 top-level fit comfortably in the Zynq-7000 with substantial headroom for future extensions (caches, additional MMIO, peripherals).

Vivado utilization table summarizing LUTs, flip-flops, and BRAM usage on the Zynq-7000 Vivado post-implementation utilization for the full design.
Vivado utilization graph broken out by module hierarchy Same utilization, broken out by module hierarchy: the register file and ALU dominate, the memories are small.

Timing closure

The clock was constrained to 100 ns (10 MHz). At that target Vivado closes with positive worst-negative-slack across all paths; the timing summary below is the post-implementation report.

Vivado design timing summary showing met setup, hold, and pulse-width with positive worst-negative-slack Setup, hold, and pulse-width all met at 10 MHz.

Fmax is open. The 10 MHz constraint was deliberately conservative for bring-up; rerunning synthesis with the constraint loosened (or removed) and reading the resulting WNS would let me quote a real maximum. That’s a TODO; until I do, the fmax card in the results strip at the top of this page stays hidden.

Implementation layout

The placed-and-routed design on the Zynq-7000 fabric, captured from the Vivado device view. The placement is sparse, which is the visual confirmation of the utilization numbers above.

Vivado implementation device view: placed cells on the Zynq-7000 FPGA fabric, with most of the device unused Post-implementation placement on the Zynq-7000 fabric. The lit-up region shows the riscv-5i core; the rest of the device is unused.

Lessons from bring-up

Two pieces of advice I would give myself if I were starting again:

  1. Use the Xilinx Clocking Wizard for any non-trivial clock. The first revision of the PYNQ-Z2 top-level used a small logic-based clock divider to derive the CPU clock from the board’s 125 MHz reference. That worked in simulation, but on the board the JTAG debug hub became unstable and the ILA dropped triggers intermittently. Switching to a Clocking Wizard IP block stabilized the clock tree, fixed the JTAG flakiness, and made the ILA reliable. Worth the IP-block bureaucracy.
  2. Memory timing is the first thing to verify on real hardware. The bug documented in the postmortem was a sim-vs-synth mismatch: the simulation model of the instruction memory was combinational, the FPGA implementation used synchronous block RAM. The pipeline’s IF stage assumes single-cycle fetch, and that assumption only failed on the board. Anything that simulates one way and synthesizes another deserves an explicit equivalence check before bring-up.

Hardware demo

src/pynq_z2_top.sv wires the lower four bits of the MMIO LED register at 0x8000_0000 to the board’s four user LEDs. Any RISC-V program that writes to that address shows up on the LEDs the next cycle. The demo program is a Fibonacci generator that writes successive terms; because the LEDs are four bits, values larger than 15 wrap modulo 16, so the visible sequence rolls over after 13, 21, 34.

Sequence Displayed on LEDs:
  * 1  -> 0001 (1)
  * 2  -> 0010 (2)
  * 3  -> 0011 (3)
  * 5  -> 0101 (5)
  * 8  -> 1000 (8)
  * 13 -> 1101 (13)
  * 21 -> 0101 (5)  (21 is 10101 binary; bottom 4 bits are 0101)
  * 34 -> 0010 (2)  (34 is 100010 binary; bottom 4 bits are 0010)

A short video of the program running on the board. The same Fibonacci binary that produces this sequence on hardware also passes its regression testbench in simulation.

Reproducing the build