diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..c493bd2 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,65 @@ +name: Docs + +on: + push: + branches: [main, doc-rst, github.io-docs-rst] + workflow_dispatch: {} + +# OIDC permissions: id-token:write lets GitHub exchange an OIDC token for +# Pages deployment auth; pages:write allows uploading to the Pages service. +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + + build: + runs-on: ubuntu-24.04 + steps: + + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v6 + with: + python-version: '3.12' + + - name: Install Sphinx + sphinx_rtd_theme + run: | + python -m pip install --upgrade pip + pip install -r docs/requirements.txt + + - name: Build documentation (strict) + run: sphinx-build -W --keep-going docs/ docs/_build/html + + - uses: actions/upload-pages-artifact@v5 + with: + path: 'docs/_build/html' + + deploy: + needs: build + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-24.04 + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + + # NOTE: the `enablement` input is intentionally omitted (defaults to + # false). Setting it true triggers a GET /pages -> POST /pages bootstrap + # path that intermittently fails: the GET hits "Bad credentials" and the + # fallback POST fails because GITHUB_TOKEN lacks `administration:write`. + # Pages is enabled out-of-band as a one-time manual setup step (see + # project docs); this action only needs to READ the existing Pages + # config, which works reliably with the `pages: write` scope declared + # above. + - uses: actions/configure-pages@v6 + + - id: deployment + uses: actions/deploy-pages@v5 diff --git a/.gitignore b/.gitignore index 1e2de28..3b5b42d 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ linux* *.bif *.elf *.dcp +.venv-docs/ diff --git a/README.md b/README.md index 40456c5..f811508 100644 --- a/README.md +++ b/README.md @@ -1,146 +1,16 @@ # Simple-ZCU208-Example - +Reference RFSoC ADC/DAC application for the SLAC SoC platform on the Xilinx ZCU208 evaluation board (`xczu48dr-fsvg1517-2-e`). -# Clone the GIT repository +**Application docs:** https://slaclab.github.io/Simple-ZCU208-Example/ -Install git large filesystems (git-lfs) in your .gitconfig (1-time step per unix environment) -```bash -$ git lfs install -``` -Clone the git repo with git-lfs enabled -```bash -$ git clone --recursive https://github.com/slaclab/Simple-ZCU208-Example.git -``` -Note: `recursive flag` used to initialize all submodules within the clone +**Shared workflow docs (clone, FW build, Yocto, SD card, Rogue install/launch, remote bitstream update):** https://slaclab.github.io/axi-soc-ultra-plus-core/ - +## Board-specific deltas -# How to generate the RFSoC .BIT and .XSA files - -1) Setup Xilinx PATH and licensing (if on SLAC S3DF network) else requires Vivado install and licensing on your local machine - -```bash -$ source Simple-ZCU208-Example/firmware/vivado_setup.sh -``` - -2) Go to the target directory and make the firmware: - -```bash -$ cd Simple-ZCU208-Example/firmware/targets/SimpleZcu208Example/ -$ make -``` - -3) Optional: Review the results in GUI mode - -```bash -$ make gui -``` - -The .bit and .XSA files are dumped into the SimpleZcu208Example/image directory: - -```bash -$ ls -lath SimpleZcu208Example/images/ -total 47M -drwxr-xr-x 5 ruckman re 2.0K Feb 7 07:13 .. -drwxr-xr-x 2 ruckman re 2.0K Feb 4 21:15 . --rw-r--r-- 1 ruckman re 14M Feb 4 21:15 SimpleZcu208Example-0x03000000-20250710093359-ruckman-XXXXXXX.xsa --rw-r--r-- 1 ruckman re 33M Feb 4 21:14 SimpleZcu208Example-0x03000000-20250710093359-ruckman-XXXXXXX.bit -``` - - - -# How to build Yocto Linux images - -1) Generate the .bit and .xsa files (refer to `How to generate the RFSoC .BIT and .XSA files` instructions). - -2) Setup Xilinx PATH and licensing (if on SLAC S3DF network) else requires Vivado install and licensing on your local machine - -```bash -# These setup scripts assume that you are on SLAC network -$ source Simple-ZCU208-Example/firmware/vivado_setup.sh -``` - -3) Go to the target directory and run the `BuildYoctoProject.sh` script with arg pointing to path of .XSA file: - -```bash -$ cd Simple-ZCU208-Example/firmware/targets/SimpleZcu208Example/ -$ source BuildYoctoProject.sh images/SimpleZcu208Example-0x03000000-20250710093359-ruckman-XXXXXXX.xsa -``` - - - -# How to make the SD memory card for the first time - -1) Creating Two Partitions. Refer to URL below - -https://xilinx-wiki.atlassian.net/wiki/x/EYMfAQ - -2) Copy For the boot images, simply copy the files to the FAT partition. -This typically will include system.bit, BOOT.BIN, image.ub, and boot.scr. Here's an example: - -Note: Assumes SD memory FAT32 is `/dev/sde1` in instructions below - -```bash -sudo mkdir -p boot -sudo mount /dev/sde1 boot -sudo cp Simple-ZCU208-Example/firmware/build/YoctoProjects/SimpleZcu208Example/images/linux/system.bit boot/. -sudo cp Simple-ZCU208-Example/firmware/build/YoctoProjects/SimpleZcu208Example/images/linux/BOOT.BIN boot/. -sudo cp Simple-ZCU208-Example/firmware/build/YoctoProjects/SimpleZcu208Example/images/linux/image.ub boot/. -sudo cp Simple-ZCU208-Example/firmware/build/YoctoProjects/SimpleZcu208Example/images/linux/boot.scr boot/. -sudo sync boot/ -sudo umount boot -``` - -3) Power down the RFSoC board - -4) Confirm the Mode SW2 [4:1] = 1110 (Mode Pins [3:0]). Note: Switch OFF = 1 = High; ON = 0 = Low. - -5) Power up the RFSoC board - -6) Confirm that you can ping the boot after it boots up - - - -# How to remote update the firmware bitstream - -- Assumes the DHCP assigned IP address is 10.0.0.10 - -1) Using "scp" to copy your .bit file to the SD memory card on the RFSoC. Here's an example: - -```bash -scp SimpleZcu208Example-0x03000000-20250710093359-ruckman-XXXXXXX.bit root@10.0.0.10:/boot/system.bit -``` - -2) Send a "sync" and "reboot" command to the RFSoC to load new firmware: Here's an example: - -```bash -ssh root@10.0.0.10 '/bin/sync; /sbin/reboot' -``` - - - -# How to install the Rogue With miniforge - -> https://slaclab.github.io/rogue/installing/miniforge.html - - - -# How to run the Rogue GUI - -- Assumes the DHCP assigned IP address is 10.0.0.10 - -1) Setup the rogue environment (if on SLAC AFS network) else install rogue (recommend miniforge method) on your local machine - -```bash -$ source Simple-ZCU208-Example/software/setup_env_slac.sh -``` - -2) Go to software directory and lauch the GUI: - -```bash -$ cd Simple-ZCU208-Example/software -$ python scripts/devGui.py --ip 10.0.0.10 -``` - - +- **Target directory:** `firmware/targets/SimpleZcu208Example/` +- **Default DHCP IP convention:** `10.0.0.10` (used in remote-update and GUI launch examples on the docs site) +- **Board:** Xilinx ZCU208 evaluation board; FPGA part: `XCZU48DR-FSVG1517-2-E`; firmware version: `v3.0.0.0` (`PRJ_VERSION = 0x03000000`) +- **Conda env (SLAC AFS):** `rogue_v6.6.1` +- **SD boot mode:** `Mode SW2 [4:1] = 1110` (switch OFF = 1 = High; ON = 0 = Low) +- **ZCU208-specific Yocto notes:** none beyond the shared procedure (the bare-metal-vs-Docker, build-output redirection, host-package prereqs all live on the hub). diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/_templates/.gitkeep b/docs/_templates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..8ee54d0 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,39 @@ +# Configuration file for the Sphinx documentation builder. +# https://www.sphinx-doc.org/en/master/usage/configuration.html +import subprocess + +project = 'Simple-ZCU208-Example' +author = 'SLAC National Accelerator Laboratory' +copyright = '2026, SLAC National Accelerator Laboratory' + +try: + release = subprocess.check_output( + ['git', 'describe', '--tags', '--abbrev=0'], + stderr=subprocess.DEVNULL, + ).decode().strip() +except Exception: + release = 'dev' +version = release + +extensions = [ + 'sphinx.ext.extlinks', + 'sphinx_copybutton', +] + +extlinks = { + 'repo': ('https://github.com/slaclab/Simple-ZCU208-Example/blob/main/%s', '%s'), + 'hub': ('https://slaclab.github.io/axi-soc-ultra-plus-core/%s', '%s'), +} + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +source_suffix = '.rst' +master_doc = 'index' +language = 'en' + +html_theme = 'sphinx_rtd_theme' +html_theme_options = {'titles_only': True, 'navigation_depth': -1} +html_title = 'Simple-ZCU208-Example' +html_baseurl = 'https://slaclab.github.io/Simple-ZCU208-Example/' +html_static_path = ['_static'] diff --git a/docs/explanation/architecture.rst b/docs/explanation/architecture.rst new file mode 100644 index 0000000..9128ef1 --- /dev/null +++ b/docs/explanation/architecture.rst @@ -0,0 +1,116 @@ +Architecture Overview +===================== + +This page describes the application-specific topology of +``Simple-ZCU208-Example`` on the Xilinx ZCU208 evaluation board +(``XCZU48DR-FSVG1517-2-E``). It covers what is unique to this application: the +8-channel ADC capture path, the 8-channel DAC output path, the +ring-buffer DMA model, the three local clock domains, and the startup +discipline that ties RTL and PyRogue together. + +For platform-level RFSoC architecture (clock-domain CDC philosophy, +DMA platform model, AXI-Lite hierarchy in general), see +:hub:`explanation/architecture.html`. + +Topology +-------- + +The application is built around the Xilinx RFDC IP wrapped by +``RfDataConverter``, an ``AppRingBuffer`` block for capture and +loopback, and a RAM-based ``SigGen`` for DAC waveforms. Data movement +is split across two DMA lanes: + +* **8-channel ADC capture path:** RFDC -> ``AppRingBuffer`` -> + DMA lane 0 -> host TCP stream. Per-channel ring buffers are armed, + triggered, and read back via PyRogue. +* **8-channel DAC output path:** DAC waveform RAM in ``SigGen`` -> + RFDC -> physical DAC pins. Loopback samples are also captured back + into ``AppRingBuffer`` for closed-loop debug. +* **Ring-buffer DMA model with** ``DMA_SIZE_C = 2``: lane 0 carries + the ADC and DAC ring-buffer data; lane 1 is hard-wired loopback for + debug. See :doc:`../reference/app_pkg` for the constants and + :doc:`../reference/rtl_top_entity` for the entity surface. + +Clock domains +------------- + +Three asynchronous clock domains drive this application: + +.. list-table:: + :header-rows: 1 + :widths: 20 25 55 + + * - Clock + - Frequency + - Role + * - ``axilClk`` + - 100 MHz + - Register access (PS-facing AXI-Lite); also drives the platform + core's ``auxClk`` / ``appClk`` inputs. + * - ``dspClk`` + - 312.5 MHz + - DSP and DAC sample bus (``Slv256Array``, 16 samples per cycle, + 8 channels ADC and 8 channels DAC). + * - ``adcClock`` + - 416.667 MHz + - RFDC ADC output. + +All three are declared as asynchronous clock groups in the XDC, so +Vivado's timing engine does not attempt to close timing across them. +CDC crossings between any two domains use surf primitives +(``Synchronizer`` for control/status, ``Ssr12ToSsr16Gearbox`` for the +ADC sample bus). For the platform-level CDC philosophy and the hub's +treatment of clock-group declarations, see +:hub:`explanation/architecture.html#clock-domains`. Concrete signal +names and the XDC excerpt live in :doc:`../reference/rtl_top_entity`. + +DMA model +--------- + +The application uses two DMA lanes (``DMA_SIZE_C = 2`` in +:doc:`../reference/app_pkg`): + +* **Lane 0** carries the ADC and DAC ring-buffer streams between the + ``AppRingBuffer`` block and the host. The PyRogue ``Root`` wires + this lane to per-channel ``RingBufferProcessor`` consumers and to + the data-writer file sink. +* **Lane 1** is a hard-wired loopback used for DMA debug; the host + pushes a frame down lane 1 and reads the same frame back, which + exercises the DMA engine independently of the RFDC and ring-buffer + logic. + +Adding lanes is a coordinated change: ``DMA_SIZE_C`` in +:repo:`firmware/shared/rtl/AppPkg.vhd` and the stream wiring in +:repo:`firmware/python/simple_zcu208_example/_Root.py` must be +updated together. For the platform-level DMA model (engine, AXI +Stream framing, host TCP-bridge convention), see +:hub:`explanation/architecture.html#dma-model`. + +Sample bus +---------- + +The parallel sample bus between RFDC and ``Application`` is a +``Slv256Array``: 16 samples * 16 bits = 256 bits per channel per +``dspClk`` cycle. The ``SAMPLE_PER_CYCLE_C`` constant in +:doc:`../reference/app_pkg` ties the RTL generics +(``ADC_SAMPLE_PER_CYCLE_G``, ``DAC_SAMPLE_PER_CYCLE_G``, +``SAMPLE_PER_CYCLE_G``) to the PyRogue ``smplPerCycle`` parameter. +The 16-sample width is fixed for this application; changing it +requires synchronized RTL and Python updates. The ZCU208 carries 8 +ADC channels and 8 DAC channels on ``dspAdc`` / ``dspDac`` (each a +``Slv256Array(7 downto 0)``). The PyRogue offsets and register +addresses tied to this bus are documented in +:doc:`../reference/register_map`. + +Startup discipline +------------------ + +The application's PyRogue ``Application`` device is instantiated with +``enabled=False`` and is only enabled after the ``dspClk`` is stable. +The ``Root.start()`` method enforces the order: user-logic reset, LMK +clock chip initialization, DSP-reset wait, application enable, RFDC +initialization, MTS sync, YAML config load, and finally the SigGen +waveform load. Bypassing this sequence (for example, enabling +``Application`` before ``dspClk`` is running, or loading the SigGen +waveform before MTS sync) is a known anti-pattern; registers that +depend on ``dspClk`` will read back zero or DECERR. diff --git a/docs/explanation/index.rst b/docs/explanation/index.rst new file mode 100644 index 0000000..aa24ae9 --- /dev/null +++ b/docs/explanation/index.rst @@ -0,0 +1,7 @@ +Explanation +=========== + +.. toctree:: + :maxdepth: 1 + + architecture diff --git a/docs/how-to/index.rst b/docs/how-to/index.rst new file mode 100644 index 0000000..8bf62d1 --- /dev/null +++ b/docs/how-to/index.rst @@ -0,0 +1,14 @@ +How-To Guides +============= + +Board-agnostic operational recipes (Rogue install, GUI launch, remote +bitstream update, SD card imaging, XSCT boot mode, QSPI flash) are hosted +on the shared platform docs site to avoid duplication. Follow the links +below for the canonical procedure; this page is intentionally a pointer. + +* :hub:`Remote bitstream update ` +* :hub:`Rogue install ` +* :hub:`Rogue GUI launch ` +* :hub:`XSCT boot mode ` +* :hub:`QSPI flash ` +* :hub:`SD card imaging ` diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..bb00e24 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,17 @@ +Simple-ZCU208-Example Documentation +===================================== + +Reference firmware and software example for the Xilinx ZCU208 evaluation +board (Xilinx Zynq UltraScale+ RFSoC, FPGA part +``XCZU48DR-FSVG1517-2-E``). Built on the SLAC ``axi-soc-ultra-plus-core`` +platform; shared workflow documentation lives on the +:hub:`platform docs site `. + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + tutorial/index + how-to/index + reference/index + explanation/index diff --git a/docs/reference/app_pkg.rst b/docs/reference/app_pkg.rst new file mode 100644 index 0000000..8d407b0 --- /dev/null +++ b/docs/reference/app_pkg.rst @@ -0,0 +1,54 @@ +AppPkg Constants +================ + +The ``AppPkg`` package declares application-wide VHDL constants tying RTL +generics to PyRogue parameters. The values below are sourced verbatim from +:repo:`firmware/shared/rtl/AppPkg.vhd`. + +.. list-table:: + :header-rows: 1 + :widths: 25 25 50 + + * - Constant + - Value + - Description + * - ``SAMPLE_PER_CYCLE_C`` + - ``16`` + - Samples per ``dspClk`` cycle on the 256-bit ``Slv256Array`` sample bus. + Linked to the PyRogue parameter ``smplPerCycle``. Changing this value + requires synchronized RTL and Python updates. + * - ``DMA_SIZE_C`` + - ``2`` + - Number of DMA lanes. Lane 0 carries ADC/DAC ring buffer data; + lane 1 is hard-wired loopback for debug. + * - ``AXIL_CLK_FREQ_C`` + - ``100.0E+6`` (Hz) + - AXI-Lite clock frequency, register-access domain. + * - ``AXIL_CLK_PERIOD_C`` + - ``1.0/AXIL_CLK_FREQ_C`` (s) + - Derived AXI-Lite clock period. + +Package declaration excerpt: + +.. code-block:: vhdl + + package AppPkg is + + constant SAMPLE_PER_CYCLE_C : positive := 16; + + ------------------------------------------------- + -- DMA[lane=0].inbound = ADC/DAC ring buffers + -- DMA[lane=1] = loopback debugging + ------------------------------------------------- + constant DMA_SIZE_C : positive := 2; + + constant AXIL_CLK_FREQ_C : real := 100.0E+6; -- Units of Hz + constant AXIL_CLK_PERIOD_C : real := (1.0/AXIL_CLK_FREQ_C); -- Units of seconds + + end package AppPkg; + +See :repo:`firmware/shared/rtl/AppPkg.vhd` for the full source. + +For the platform-level ``Slv256Array`` type definition and the ``surf`` +standard packages that ``AppPkg`` imports, see +:hub:`reference/register_map.html`. diff --git a/docs/reference/index.rst b/docs/reference/index.rst new file mode 100644 index 0000000..7ccabd4 --- /dev/null +++ b/docs/reference/index.rst @@ -0,0 +1,10 @@ +Reference +========= + +.. toctree:: + :maxdepth: 1 + + app_pkg + register_map + pyrogue_tree + rtl_top_entity diff --git a/docs/reference/pyrogue_tree.rst b/docs/reference/pyrogue_tree.rst new file mode 100644 index 0000000..0991af0 --- /dev/null +++ b/docs/reference/pyrogue_tree.rst @@ -0,0 +1,96 @@ +PyRogue Device Tree +=================== + +The application's PyRogue device tree mirrors the AXI-Lite crossbar hierarchy +in RTL. Each ``pr.Device`` carries an ``offset`` relative to its parent. The +values below are sourced from +:repo:`firmware/python/simple_zcu208_example/_Root.py`, +:repo:`firmware/python/simple_zcu208_example/_RFSoC.py`, and +:repo:`firmware/python/simple_zcu208_example/_Application.py`. + +Hierarchy +--------- + +* ``Root`` (``pr.Root``, see :repo:`firmware/python/simple_zcu208_example/_Root.py`) + + * ``Rfdc`` (RFDC API interface, platform-provided) + * ``RFSoC`` (offset ``0x04_0000_0000``, + see :repo:`firmware/python/simple_zcu208_example/_RFSoC.py`) + + * ``AxiSocCore`` (offset ``0x0000_0000``, platform core) + * ``Hardware`` (offset ``0x8000_0000``, ZCU208 board hardware control; + sourced from ``axi_soc_ultra_plus_core.hardware.XilinxZcu208``) + * ``Application`` (offset ``0xA000_0000``, + see :repo:`firmware/python/simple_zcu208_example/_Application.py`) + + * ``AppRingBuffer`` (offset ``0x00_000000``) + * ``DacSigGen`` (offset ``0x01_000000``, ``SigGen`` instance) + + * ``AdcProcessor[0..7]``, ``DacProcessor[0..7]`` (host-side + ``RingBufferProcessor`` instances; see :hub:`reference/pyrogue_api.html`) + * ``DataWriter`` (``pr.utilities.fileio.StreamWriter``) + +The ``Application`` device declares its children with ``offset`` values that +match the RTL Application crossbar (see :doc:`register_map`): + +.. code-block:: python + + self.add(rfsoc_utility.AppRingBuffer( + offset = 0x00_000000, + numAdcCh = 8, # Must match NUM_ADC_CH_G config + numDacCh = 8, # Must match NUM_DAC_CH_G config + )) + + self.add(rfsoc_utility.SigGen( + name = 'DacSigGen', + offset = 0x01_000000, + numCh = 8, # Must match NUM_CH_G config + ramWidth = 10, # Must match RAM_ADDR_WIDTH_G config + smplPerCycle = 16, # Must match SAMPLE_PER_CYCLE_G config + )) + +The ``smplPerCycle = 16`` parameter mirrors the RTL ``SAMPLE_PER_CYCLE_C`` +constant declared in :doc:`app_pkg`. + +Startup sequence +---------------- + +``Root.start()`` (in +:repo:`firmware/python/simple_zcu208_example/_Root.py`) executes the +following ordered sequence after the base ``pr.Root.start()`` runs: + +1. TCP connection setup (memory map + RFDC + ring-buffer streams). +2. Stream wiring (ADC/DAC ring buffer paths chained to ``DataWriter`` and to + per-channel host-side processors via ``>>``). +3. User-logic reset (``RFSoC.AxiSocCore.UserRst()``). +4. LMK clock chip initialization (``Hardware.InitClock``). +5. DSP-clock-stable wait (``RFSoC.AxiSocCore.DspRstWait()``). +6. Application enable (``RFSoC.Application.enable.set(True)``) — only after + the DSP clock is stable. +7. RFDC initialization and MTS sync (``Rfdc.Init()`` and ``Rfdc.Mts.*``). +8. Default YAML configuration load (``LoadConfig``). +9. DacSigGen waveform load (CSV file or ``LoadSingleTones`` fallback). + +The ``Application`` device is constructed with ``enabled=False`` and is +deliberately enabled only after step 5. Bypassing this order leaves the +application reading registers in an unknown clock state. + +For platform-level PyRogue patterns — the ``AxiSocCore`` interface, the +``Rfdc`` API, and the host-side ``RingBufferProcessor`` stream pipeline — see +:hub:`reference/pyrogue_api.html`. + +Public package surface +---------------------- + +The ``simple_zcu208_example`` package exposes its public classes via a +wildcard re-export pattern in +:repo:`firmware/python/simple_zcu208_example/__init__.py`: + +.. code-block:: python + + from simple_zcu208_example._Application import * + from simple_zcu208_example._RFSoC import * + from simple_zcu208_example._Root import * + +Private implementation files are underscore-prefixed (``_Application.py``, +``_RFSoC.py``, ``_Root.py``); ``__init__.py`` is the public face. diff --git a/docs/reference/register_map.rst b/docs/reference/register_map.rst new file mode 100644 index 0000000..543aa9e --- /dev/null +++ b/docs/reference/register_map.rst @@ -0,0 +1,92 @@ +Application Register Map +======================== + +The application exposes a two-level AXI-Lite crossbar hierarchy. A PS-facing +top-level crossbar in +:repo:`firmware/targets/SimpleZcu208Example/hdl/SimpleZcu208Example.vhd` +decodes the upper bits of the application address window into three slaves +(platform core, RFDC, application). The application-level crossbar in +:repo:`firmware/shared/rtl/Application.vhd` further decodes into the ring +buffer and DAC signal generator slaves. + +Top-level crossbar +------------------ + +Sourced from +:repo:`firmware/targets/SimpleZcu208Example/hdl/SimpleZcu208Example.vhd`. + +.. list-table:: + :header-rows: 1 + :widths: 30 30 40 + + * - Index constant + - Slave + - Notes + * - ``HW_INDEX_C = 0`` + - ``Hardware`` (ZCU208 board hardware) + - Board hardware control (LMK, I2C). Sourced from + ``axi_soc_ultra_plus_core.hardware.XilinxZcu208``. + * - ``RFDC_INDEX_C = 1`` + - ``RfDataConverter`` + - Wraps the Xilinx RFDC IP core; generates ``dspClk``. + * - ``APP_INDEX_C = 2`` + - ``Application`` + - Application logic; second-level crossbar. + +The crossbar is built from the surf ``genAxiLiteConfig`` macro: + +.. code-block:: vhdl + + constant AXIL_CONFIG_C : AxiLiteCrossbarMasterConfigArray(NUM_AXIL_MASTERS_C-1 downto 0) := + genAxiLiteConfig(NUM_AXIL_MASTERS_C, APP_ADDR_OFFSET_C, 31, 28); + +Application crossbar +-------------------- + +Sourced from :repo:`firmware/shared/rtl/Application.vhd`. + +.. list-table:: + :header-rows: 1 + :widths: 30 30 40 + + * - Index constant + - Slave + - Notes + * - ``RING_INDEX_C = 0`` + - ``AppRingBuffer`` + - ADC/DAC capture ring buffers; DMA inbound path (8 ADC + 8 DAC channels). + * - ``DAC_SIG_INDEX_C = 1`` + - ``SigGen`` (``DacSigGen``) + - RAM-based DAC waveform generator (8 channels). + +The Application-level configuration uses 28 address bits and 24 decode bits: + +.. code-block:: vhdl + + constant AXIL_CONFIG_C : AxiLiteCrossbarMasterConfigArray(NUM_AXIL_MASTERS_C-1 downto 0) := + genAxiLiteConfig(NUM_AXIL_MASTERS_C, AXIL_BASE_ADDR_G, 28, 24); + +Pattern: genAxiLiteConfig +------------------------- + +The ``genAxiLiteConfig(NUM_MASTERS, BASE_ADDR, ADDR_BITS, DECODE_BITS)`` macro +generates the ``MASTERS_CONFIG_G`` array consumed by ``surf.AxiLiteCrossbar``. +It evenly partitions the address window starting at ``BASE_ADDR``, sized by +``ADDR_BITS``, with each slave receiving ``2**DECODE_BITS`` bytes of address +space. For the platform-level macro definition and the ``AxiLitePkg`` package +that hosts it, see :hub:`reference/register_map.html`. + +Sample bus dimensions +--------------------- + +The RFDC and Application exchange samples over a 256-bit parallel bus carrying +16 samples per ``dspClk`` cycle. The bus width is fixed by the +:doc:`app_pkg` constant ``SAMPLE_PER_CYCLE_C = 16``; the sample width is 16-bit +signed integer per channel. + +* ADC bus: ``dspAdc : Slv256Array(7 downto 0)`` — 8 channels. +* DAC bus: ``dspDac : Slv256Array(7 downto 0)`` — 8 channels. + +For the platform-level ``Slv256Array`` type definition (declared in +``surf.StdRtlPkg``) and the broader sample-bus convention used across SLAC +RFSoC platforms, see :hub:`reference/register_map.html`. diff --git a/docs/reference/rtl_top_entity.rst b/docs/reference/rtl_top_entity.rst new file mode 100644 index 0000000..10cf8a4 --- /dev/null +++ b/docs/reference/rtl_top_entity.rst @@ -0,0 +1,94 @@ +Top-Level RTL Entity +==================== + +The top-level firmware target is ``SimpleZcu208Example`` (architecture +``top_level``). The full source lives in +:repo:`firmware/targets/SimpleZcu208Example/hdl/SimpleZcu208Example.vhd`. + +Entity surface +-------------- + +The entity is the top FPGA target loaded by the Processing System at boot. It +maps physical board I/O to internal AXI buses and three clock domains: + +* **LMK ports:** ``lmkSync``, ``clkMuxSel``, ``i2c1Scl``, ``i2c1Sda`` (I2C + to on-board LMK clock chip). +* **RF data converter ports:** ADC differential clock and data + (``adcClkP/N``, ``adcP/N[7:0]``); DAC differential clock and data + (``dacClkP/N``, ``dacP/N[7:0]``); SYSREF and PL clock differential pairs. +* **SYSMON ports:** ``vPIn``, ``vNIn``. + +Build-time generics are ``TPD_G`` (propagation-delay convention from surf) and +``BUILD_INFO_G`` (build metadata struct populated by ruckus). + +Instantiated blocks +------------------- + +.. list-table:: + :header-rows: 1 + :widths: 25 35 40 + + * - Block + - Source + - Role + * - ``AxiSocUltraPlusCore`` + - submodule (platform core) + - Platform core: DMA engine, AXI-Lite bridge to PS, SysMon. + * - ``Hardware`` (ZCU208) + - submodule (``axi_soc_ultra_plus_core.hardware.XilinxZcu208``) + - Board hardware control: LMK clock chip, I2C. + * - ``RfDataConverter`` + - :repo:`firmware/shared/rtl/RfDataConverter.vhd` + - Wraps the Xilinx RFDC IP; generates ``dspClk`` from the ``plClk`` PLL. + * - ``Application`` + - :repo:`firmware/shared/rtl/Application.vhd` + - Top-level application logic; second-level AXI-Lite crossbar. + +The top-level AXI-Lite crossbar splits the application address window across +the three blocks at indices ``HW_INDEX_C = 0``, ``RFDC_INDEX_C = 1``, and +``APP_INDEX_C = 2`` (see :doc:`register_map`). + +Clock domains +------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 25 55 + + * - Clock + - Frequency + - Role + * - ``axilClk`` + - 100 MHz + - Register access (PS-facing AXI-Lite); also drives ``auxClk`` / + ``appClk`` inputs of the platform core. + * - ``dspClk`` + - 312.5 MHz + - DSP / DAC sample bus (``Slv256Array``, 16 samples per cycle, 8 ch). + * - ``adcClock`` + - 416.667 MHz + - RFDC ADC output. + +All cross-domain crossings use surf ``Synchronizer`` or ``Ssr12ToSsr16Gearbox`` +primitives; the three domains are declared as asynchronous groups in the XDC. +For the platform-level CDC philosophy, see +:hub:`explanation/architecture.html#clock-domains`. + +Async clock groups (XDC) +------------------------ + +The XDC declares the three PLL outputs as asynchronous clock groups so the +Vivado timing engine does not attempt to close timing across domains: + +.. code-block:: text + + create_clock -name plClkP -period 2.0 [get_ports {plClkP}] + + set_clock_groups -asynchronous \ + -group [get_clocks -of_objects [get_pins U_Core/REAL_CPU.U_CPU/U_Pll/PllGen.U_Pll/CLKOUT0]] \ + -group [get_clocks -of_objects [get_pins U_Core/REAL_CPU.U_CPU/U_Pll/PllGen.U_Pll/CLKOUT1]] \ + -group [get_clocks -of_objects [get_pins U_RFDC/U_Pll/PllGen.U_Pll/CLKOUT0]] \ + -group [get_clocks -of_objects [get_pins U_RFDC/U_Pll/PllGen.U_Pll/CLKOUT1]] + +Sourced from +:repo:`firmware/targets/SimpleZcu208Example/hdl/SimpleZcu208Example.xdc`. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..7c4fb42 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +sphinx==8.1.3 +sphinx-rtd-theme==3.0.2 +sphinx-copybutton==0.5.2 diff --git a/docs/tutorial/first_build.rst b/docs/tutorial/first_build.rst new file mode 100644 index 0000000..3e18abc --- /dev/null +++ b/docs/tutorial/first_build.rst @@ -0,0 +1,192 @@ +First Build +=========== + +This tutorial is the end-to-end build procedure for the +``Simple-ZCU208-Example`` firmware on the Xilinx ZCU208 evaluation board +(FPGA part ``XCZU48DR-FSVG1517-2-E``, firmware version ``v3.0.0.0`` / +``PRJ_VERSION = 0x03000000``). It documents only the commands that differ +between this board and the platform-shared workflow; for host-prep details, +build-output redirection, the bare-metal-vs-Docker decision, and the +serial-console snippet — all of which are board-agnostic — sections below +deep-link to the corresponding anchors in the platform docs site +(:hub:`tutorial/first_soc_bringup.html`). + +.. admonition:: Reference toolchain + + This walkthrough was authored against: + + - **Vivado:** ``v2025.2`` (64-bit) + - **OS:** Ubuntu 22.04 LTS (x86_64) + - **Firmware version:** ``v3.0.0.0`` (``PRJ_VERSION = 0x03000000``) + - **Target FPGA part:** ``XCZU48DR-FSVG1517-2-E`` + - **Conda env:** ``rogue_v6.6.1`` + + Approximate end-to-end build time on a typical Linux build host with the + firmware tree on local-disk storage: **~60 min** total — firmware (~17 min) + plus Yocto (~45 min). + +Output filenames embed the build timestamp, the building user's username, +and the current git short-SHA, following the schema +``----``. The +```` placeholder is used below wherever the exact filename is +build-specific. + +Clone +----- + +Install `git-lfs `_ in your shell profile (one-time per +environment) before cloning, so any LFS-tracked binaries are fetched correctly: + +.. code-block:: bash + + git lfs install + +Clone the repository with all submodules: + +.. code-block:: bash + + git clone --recursive https://github.com/slaclab/Simple-ZCU208-Example.git + +The ``--recursive`` flag initialises the +:repo:`firmware/submodules/surf`, +:repo:`firmware/submodules/axi-soc-ultra-plus-core`, +:repo:`firmware/submodules/ruckus`, and +:repo:`firmware/submodules/aes-stream-drivers` submodules in one step. Omitting +it leaves the firmware build unable to find the required RTL and TCL sources. + +Setup environment +----------------- + +Source the Vivado 2025.2 environment, then activate the ``rogue_v6.6.1`` conda +environment: + +.. code-block:: bash + + source firmware/vivado_setup.sh + source software/setup_env_slac.sh + +The first script sets ``PATH``, ``LD_LIBRARY_PATH``, and the Xilinx licence +server variables required by ``make``. The second activates the +``rogue_v6.6.1`` conda environment used by the Python control layer. + +For host-package prerequisites, the non-SLAC Vivado/conda install path, Yocto +host-package list, and the build-output redirection step required before the +Yocto build, see :hub:`tutorial/first_soc_bringup.html#setup-environment`. + +Firmware build +-------------- + +Change into the target directory and run ``make``: + +.. code-block:: bash + + source firmware/vivado_setup.sh + cd firmware/targets/SimpleZcu208Example/ + make + +**Approximate timing:** ~17 min on a typical Linux build host with Vivado 2025.2. + +After a successful build, the ``.bit`` and ``.xsa`` artifacts are written to +``firmware/targets/SimpleZcu208Example/images/`` using the schema +``SimpleZcu208Example-0x03000000---``: + +.. code-block:: text + + firmware/targets/SimpleZcu208Example/images/SimpleZcu208Example-0x03000000---.bit (~33 MiB) + firmware/targets/SimpleZcu208Example/images/SimpleZcu208Example-0x03000000---.xsa (~3 MiB) + +.. note:: + + Your filename will differ — the build embeds the build timestamp, your + username, and the current git short-SHA. ``PRJ_VERSION = 0x03000000`` + corresponds to firmware version ``v3.0.0.0`` and is tracked in + :repo:`firmware/targets/shared_version.mk`. + +To review Vivado results in GUI mode after a successful build: + +.. code-block:: bash + + make gui + +For the platform-level firmware-build narrative (Vivado strategy, log locations, +common build failures), see :hub:`tutorial/first_soc_bringup.html#firmware-build`. + +Yocto build +----------- + +The Yocto build produces the embedded Linux boot images (``BOOT.BIN``, +``image.ub``, ``boot.scr``, ``system.bit``) that run on the ZCU208 Processing +System. From the target directory, pass the ``.xsa`` file produced by the +firmware build to ``BuildYoctoProject.sh``: + +.. code-block:: bash + + cd firmware/targets/SimpleZcu208Example/ + ./BuildYoctoProject.sh -f images/SimpleZcu208Example-0x03000000---.xsa + +**Approximate timing:** ~45 min on a typical Linux build host with the firmware +tree on local-disk storage (~7200 bitbake tasks). + +After a successful build, the boot images are in +``firmware/build/YoctoProjects/SimpleZcu208Example/linux/`` and a packaged +tarball is also produced at +``firmware/targets/SimpleZcu208Example/images/.linux.tar.gz``. + +For the bare-metal-vs-Docker decision, the Yocto host-package list, the +Dockerfile defect callout, and the deploy-path layout, see +:hub:`tutorial/first_soc_bringup.html#yocto-build`. + +SD card +------- + +Once the Yocto build is complete, write the boot images to an SD card. The +verified procedure — covering both the manual mount-and-copy recipe and the +scripted ``CreateDiskImage.sh`` path +(:hub:`how-to/sd_card_imaging.html`) — is documented on the platform +docs site at :hub:`tutorial/first_soc_bringup.html#sd-card`. The four files to +copy live under +``firmware/build/YoctoProjects/SimpleZcu208Example/linux/`` +(``BOOT.BIN``, ``image.ub``, ``boot.scr``, ``system.bit``). + +Boot +---- + +1. Power down the ZCU208 board. +2. Confirm ``Mode SW2 [4:1] = 1110`` (switch OFF = 1 = High; ON = 0 = Low) for SD boot. +3. Insert the SD card written in the previous step. +4. Power up the board. +5. Confirm the board is reachable on the default DHCP IP ``10.0.0.10``: + + .. code-block:: bash + + ping 10.0.0.10 + +For the serial-console snippet (USB-to-serial bridge, baud rate, terminal +program selection on Linux/Windows) and troubleshooting steps for boot or +network failures, see :hub:`tutorial/first_soc_bringup.html#boot`. + +Run the Rogue GUI +----------------- + +Once the board is reachable on the network, launch the PyDM GUI from the host: + +.. code-block:: bash + + python software/scripts/devGui.py --ip 10.0.0.10 + +This connects to the on-board ZMQ server, builds the PyRogue device tree, and +opens the default PyDM dashboard. + +For installing Rogue on a non-SLAC host, see :hub:`how-to/rogue_install.html`. +For the platform-level GUI launch how-to (advanced flags, alternative entry +points, headless operation), see :hub:`how-to/rogue_gui_launch.html`. + +Next steps +---------- + +- Update the bitstream on a running board without rebooting: + :hub:`how-to/remote_bitstream_update.html`. +- Flash the boot images to QSPI for SD-cardless boot: + :hub:`how-to/qspi_flash.html`. +- Recover from a bricked QSPI image using XSCT JTAG boot mode: + :hub:`how-to/xsct_boot_mode.html`. diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst new file mode 100644 index 0000000..586e419 --- /dev/null +++ b/docs/tutorial/index.rst @@ -0,0 +1,7 @@ +Tutorial +======== + +.. toctree:: + :maxdepth: 2 + + first_build