retro-silicon

Notes

The Zynq's HP ports: FPGA logic reaching the ARM's memory

How a core in the PYNQ-Z2's programmable logic uses the processor side's DDR3, with no operating system: the HP ports, and setting up the DDR controller over JTAG.

· PYNQ-Z2

The PYNQ-Z2’s chip, a Xilinx (now AMD) Zynq XC7Z020, is two things on one die: an ARM processing system and a block of FPGA fabric [1]. retro-silicon’s cores live in the fabric. Until now the NES there has kept its cartridge in the fabric’s own block RAM, which runs out at a few hundred kilobytes. The board’s 512 MB of DDR3 [3] is wired to the processor side, so to use it a core has to cross over. This note explains the crossing retro-silicon uses, the HP ports, and how the DDR is brought up without running an operating system.

Two halves of one chip

Processing system (PS)Programmable logic (PL)
What it isTwo ARM Cortex-A9 cores with caches, on-chip RAM, a boot ROM, the DDR controller, and peripherals such as USB, Ethernet and SD [1]FPGA fabric of the Xilinx 7 series [1]: on the XC7Z020, 53,200 six-input LUTs, 140 36 Kb block RAMs and 220 DSP slices
On the PYNQ-Z2Cores at 650 MHz [3]Where the NES and its HDMI output run

The two halves meet at a set of fixed interfaces on the boundary [1]. Every signal crossing it passes through level shifters, which the processor side has to turn on first.

Five ways across

InterfaceHow manyWho starts a transferWidthWhat it’s for [1]
GP master2The ARM32-bitThe ARM reading and writing registers in the fabric
GP slave2The fabric32-bitThe fabric reaching the processor’s peripherals
HP4The fabric32 or 64-bitHigh-bandwidth access to DDR and on-chip RAM, through FIFOs and a memory interconnect of their own
ACP1The fabric64-bitAccess kept coherent with the ARM’s caches

All of them speak AXI, Arm’s on-chip bus protocol [2]. AXI splits a transfer into independent channels: read address, read data, write address, write data and write response, each with its own valid/ready handshake. The fabric connects to the processor side’s ports with ordinary wires, so anything in the fabric that speaks AXI can use them.

retro-silicon uses two of them.

The NES’s path to DDR3

retro-silicon’s cores don’t speak AXI themselves. The NES asks for memory through a simple port: an address, a request, an acknowledge and the data. The same core runs:

The new bridge turns the core’s requests into AXI transactions on an HP port. Nothing in the NES changes. A core keeps one memory port, and each board adapts it at the edge.

DDR3 is far slower to answer than block RAM: a read crosses the bridge, the HP FIFOs, the interconnect and the DDR controller, and the time varies with refresh and other traffic. The NES already copes with memory like that. On the 20K, a CPU cycle’s last dot waits for its PRG byte if it isn’t back yet, and the PPU’s tile data is fetched a dot ahead. A wait changes nothing the emulated system does, only how long the FPGA takes to do it, so the frames and their checksums stay the same as on every other board.

Bringing up the DDR without an operating system

At power-up, the DDR controller is held in reset and the level shifters are off. Normally a first-stage boot loader on the ARM sets up the clocks and the DDR controller from an SD card or flash, and then starts Linux. retro-silicon doesn’t need any of that to use the memory.

The rig’s PYNQ-Z2 boots in JTAG mode. The ARM’s boot ROM then simply waits, and the processor side’s registers can be reached through the chip’s debug port. Over that port, using OpenOCD, the rig does four things:

  1. sets up the DDR clocks and the controller’s timings for the board’s memory;
  2. runs the controller’s training;
  3. turns on the level shifters;
  4. releases the fabric’s resets.

After that the ARM has nothing more to do. No SD card, no boot image, and nothing that depends on an operating system or a Linux distribution.

The settings come from two places: the Zynq’s reference manual [1], which describes every register of the DDR controller, and the board’s memory.

The PYNQ-Z2’s DDR3 [3]
PartOne x16 device, 4 Gbit (512 MB), on a 16-bit bus
SpeedDDR3-1066F, clocked at 525 MHz
LatenciesCAS 7, CAS write 6
Geometry15 row, 10 column and 3 bank address bits
TimingstRCD and tRP 13.125 ns, tRAS 35 ns, tRC 50.625 ns, tFAW 40 ns
TrainingWrite leveling, read gate and data eye

retro-silicon writes its own setup code from these, rather than using Xilinx’s generated one, so the library stays under its own licence.

Beyond the PYNQ-Z2

The split carries over to other chips that pair ARM cores with an FPGA:

On any of them, a core with a memory port reaches the board’s DDR through the same bridge component. Only the step that sets up the processor side’s DDR controller is different for each chip and board.

Where this stands

Milestone 9.5 started on October 30, 2026, with a go/no-go check of the open toolchain:

Next come the bridge and the DDR setup. Results will appear on the NES’s page, and in the memory/axi-bridge component’s status once it exists.

Sources

  1. AMD, Zynq 7000 SoC Technical Reference Manual (UG585): the processing system, the PS–PL interfaces, the DDR controller. https://docs.amd.com/r/en-US/ug585-zynq-7000-SoC-TRM
  2. Arm, AMBA AXI Protocol Specification (IHI 0022). https://developer.arm.com/documentation/ihi0022/latest
  3. The PYNQ project, the PYNQ-Z2’s base design, v3.0.1 (boards/Pynq-Z2/base/base.tcl): its processor and DDR3 settings. https://github.com/Xilinx/PYNQ/blob/v3.0.1/boards/Pynq-Z2/base/base.tcl

The XC7Z020’s LUT, block RAM and DSP counts are as nextpnr-xilinx reports the device.