Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ linux*
*.bif
*.elf
*.dcp
.venv-docs/
150 changes: 10 additions & 140 deletions README.md
Original file line number Diff line number Diff line change
@@ -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).
Empty file added docs/_static/.gitkeep
Empty file.
Empty file added docs/_templates/.gitkeep
Empty file.
39 changes: 39 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -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']
116 changes: 116 additions & 0 deletions docs/explanation/architecture.rst
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 7 additions & 0 deletions docs/explanation/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Explanation
===========

.. toctree::
:maxdepth: 1

architecture
Loading