Platform Architecture

This page is the platform-level architecture narrative for axi-soc-ultra-plus-core. The platform provides the core infrastructure — AXI-Lite register bridge, DMA engine, RFDC wrapper, ring-buffer IP, and signal-generator IP — that every Simple-*-Example application repo builds upon. Readers looking for per-board details should consult Supported Boards and each application repo’s own documentation.

System overview

The platform connects a remote host machine to a Zynq UltraScale+ RFSoC device over TCP. The PS (Processing System) runs Linux; the PL (Programmable Logic) implements the FPGA design. All register access and DMA data transfers cross the PS-to-PL boundary through the AxiSocUltraPlusCore block.

┌─────────────────────────────────────────────────────────────────────────┐
│                     Host Machine (Remote Client)                         │
│  software/scripts/devGui.py  |  software/notebooks/rfsoc_example.ipynb  │
└───────────────────────┬──────────────────────────────────────────────────┘
                        │  TCP (ports 9000/9001 AXI-Lite, 9002 RFDC,
                        │       10000+ AXI Stream ring buffers)
┌───────────────────────▼──────────────────────────────────────────────────┐
│                   RFSoC Board (PS + PL, Zynq UltraScale+)                │
│                                                                           │
│   ┌────────────────────────────────────────────────────────────────────┐ │
│   │                          PL (FPGA)                                 │ │
│   │                                                                    │ │
│   │  ┌─────────────────┐  ┌───────────────┐  ┌─────────────────────┐ │ │
│   │  │  AxiSocUltra    │  │ RfDataConverter│  │    Application      │ │ │
│   │  │  PlusCore (HW)  │  │ (RFDC wrapper) │  │  (app-specific)     │ │ │
│   │  │  [submodule]    │  │                │  │                     │ │ │
│   │  │                 │  │                │  │  ┌─────────────┐   │ │ │
│   │  │  DMA engine     │  │  dspClk domain │  │  │AppRingBuffer│   │ │ │
│   │  │  axilClk domain │  │  axilClk domain│  │  └─────────────┘   │ │ │
│   │  └────────┬────────┘  └───────┬────────┘  │  ┌─────────────┐   │ │ │
│   │           │ AXI-Lite XBAR     │            │  │  SigGen     │   │ │ │
│   │           │ (3 masters)       │ dspAdc/Dac │  │ (DacSigGen) │   │ │ │
│   │           └───────────────────┴───────────►│  └─────────────┘   │ │ │
│   │                                            └─────────────────────┘ │ │
│   └────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘

Three major PL blocks make up the platform:

  • AxiSocUltraPlusCore — the platform core, provided as a git submodule. It implements the PS-to-PL AXI-Lite bridge, the DMA engine, and system monitor. Application repos do not modify this block.

  • RfDataConverter — wraps the Xilinx RF Data Converter IP core. Generates dspClk from the reference PLL, applies ADC P/N swap and polarity corrections, and gear-boxes the adcClock (416.667 MHz) RFDC output down to the 312.5 MHz DSP clock domain.

  • Application — the board-specific application logic. Contains the second-level AXI-Lite crossbar, AppRingBuffer (ADC/DAC capture), and DacSigGen (waveform generator). Each Simple-*-Example repo provides its own Application.vhd.

Clock domains

The design has three independent, asynchronous clock domains. The XDC constraints file in each application repo declares all three as asynchronous clock groups (lines 12–17 of SimpleRfSoc4x2Example.xdc).

axilClk — 100 MHz

The AXI-Lite control-plane clock. All register reads and writes cross the PS-to-PL boundary in this domain. The AxiSocUltraPlusCore block, the top-level crossbar, and the AXI-Lite clock-frequency constant AXIL_CLK_FREQ_C (100.0E+6, declared in AppPkg.vhd) all reference this domain.

dspClk — 312.5 MHz

The DSP and DAC sample-rate clock. The Application block, AppRingBuffer, DacSigGen, and the 256-bit Slv256Array sample bus all run on dspClk. RfDataConverter generates dspClk internally from a PLL driven by the RFDC reference clock input.

adcClock — 416.667 MHz

The RFDC ADC output clock. The Xilinx RF Data Converter IP delivers digitised samples on adcClock. The Ssr12ToSsr16Gearbox primitive converts 12 samples per cycle at 416.667 MHz into 16 samples per cycle at dspClk (312.5 MHz), crossing the domain boundary in the process.

All clock-domain crossings (CDC) must use a surf Synchronizer primitive or the Ssr12ToSsr16Gearbox. No direct flip-flop paths are permitted between the three async groups.

The Application PyRogue device must be enabled only after dspClk is stable. The Root.start() startup sequence enforces this order: InitClockDspRstWaitApplication.enableRfdc.Init → MTS sync → config load.

DMA model

The AxiSocUltraPlusCore DMA engine moves ADC and DAC ring-buffer data between the PL and PS DDR over AXI Stream. The lane count is set by the DMA_SIZE_C constant in each application’s AppPkg.vhd (conventional value: DMA_SIZE_C = 2).

Lane assignment:

  • Lane 0 — ADC/DAC ring-buffer data. AppRingBuffer writes captured samples into inbound DMA frames; the PS forwards them over TCP stream ports (10000 and above, one port per channel).

  • Lane 1 — Hard-wired loopback for DMA path debug. The top-level VHDL wires dmaIbMasters(1) <= dmaObMasters(1) directly.

On the Linux side, the aes-stream-drivers kernel module provides the device nodes used by the Rogue framework:

  • /dev/axi_memory_map — memory-mapped AXI-Lite register access

  • /dev/axi_stream_dma_0 — AXI Stream DMA channel for ring buffer data

The application Root (PyRogue) connects to the board through Rogue’s TCP bridge rather than through these device nodes directly. The firmware runs a TCP server on the board; the host connects as a TCP client. Host-side drop FIFOs (adcDropFifo, dacDropFifo, maxDepth=1) prevent host-side backpressure from stalling the firmware DMA path.

Adding DMA lanes requires updating DMA_SIZE_C in AppPkg.vhd and the corresponding stream wiring in the application Root._Root.py.

For the host-side Rogue stream pipeline and the RingBufferProcessor API, see PyRogue API Reference.

Sample bus

ADC and DAC sample data between the RFDC wrapper and the Application block flows on the Slv256Array bus — a 256-bit parallel bus clocked at dspClk (312.5 MHz). The type is defined in surf.StdRtlPkg.

Each 256-bit word carries SAMPLE_PER_CYCLE_C = 16 samples of 16 bits each:

-- 4 ADC input channels, one Slv256 per channel
dspAdc : in  Slv256Array(3 downto 0);
-- 2 DAC output channels, one Slv256 per channel
dspDac : out Slv256Array(1 downto 0);

The constant SAMPLE_PER_CYCLE_C (declared in AppPkg.vhd) links VHDL generics (ADC_SAMPLE_PER_CYCLE_G, DAC_SAMPLE_PER_CYCLE_G, SAMPLE_PER_CYCLE_G) to the Python Application device parameter smplPerCycle. Changing this constant requires synchronized updates in both the VHDL package and the Python device tree — the two values are not cross-checked automatically at build time.

For the full AppPkg constants layout and how they feed the crossbar configuration, see Register Map.

AXI-Lite hierarchy summary

All register access flows from the PS ARM core through the AxiSocUltraPlusCore AXI-Lite bridge into a two-level crossbar tree instantiated in the application’s top-level VHDL entity:

  1. Top-level crossbar decodes the address space into three slaves: HW_INDEX_C=0 (platform core registers), RFDC_INDEX_C=1 (RFDC IP registers), and APP_INDEX_C=2 (application logic).

  2. Application crossbar (inside Application.vhd) further decodes the application address space: RING_INDEX_C=0 (AppRingBuffer registers) and DAC_SIG_INDEX_C=1 (DacSigGen registers).

The Python pr.Device tree mirrors this hierarchy exactly. Each device’s offset argument must equal the AXIL_CONFIG_C(INDEX).baseAddr value generated by the genAxiLiteConfig VHDL call. For the full addressing pattern, constant definitions, and Python offset examples, see Register Map.

Further reading

  • Register Map — AXI-Lite addressing model, genAxiLiteConfig pattern, AppPkg constants, and the Slv256Array sample bus definition

  • PyRogue API Reference — platform PyRogue classes (AxiSocCore, AppRingBuffer, RingBufferProcessor, Rfdc, and the PyDM GUI launcher)

  • Supported Boards — list of Simple-*-Example application repos, board models, Xilinx part numbers, and target directory names