Hardware-CI Runner Setup (no-os flash mode)¶
A no-os DUT repo opts into on-hardware CI with a project manifest and four
workflow inputs. The lab/toolchain logic (Vivado sourcing, the libtinfo shim,
the .xsa fetch from the Kuiper image) lives entirely in
adi-labgrid-plugins — the runner’s only hard requirement is a Vivado/Vitis
install.
Runner requirements¶
The per-project build+flash legs run on a self-hosted runner that must:
Have Vivado/Vitis installed (
/opt/Xilinx/Vivado/*/settings64.shor/tools/Xilinx/*/Vivado/settings64.sh).build-noosauto-detects the newest version. Override withVITIS_SETTINGS=<path/to/settings64.sh>if you have a non-standard install location.Be able to reach the JTAG cable wired to the target board (the runner is typically co-located on the exporter host, or uses the place’s
runnertag to route each leg to the right host).Have
adi-labgrid-pluginsinstalled (the workflow installs it per leg viauv; no manual prep required).
Register the runner with .github/scripts/register-hw-runners.sh. Use
--scopes to register one physical host across multiple GitHub scopes so a
single lab machine can serve both org and personal-account consumer repos:
./.github/scripts/register-hw-runners.sh \
--hosts-file ./hosts.tsv \
--scopes org:analogdevicesinc,repo:tfcollins/labgrid-plugins
The manifest¶
A no-os consumer repo places a project manifest at
tools/hw_ci/projects.yaml (override with the manifest workflow input):
projects:
- noos_project: adrv9009
part: adrv9009
carriers: [zc706]
validate_banner: "Successfully initialized" # optional; default shown
build_vars: {} # optional extra make vars
Each entry in the projects list accepts:
noos_project(required)The no-os project directory name under
projects/(e.g.adrv9009). Also used as the.elfstem.part(required)The canonical daughter-board part name, matched against the coordinator’s board catalog (same value used in
@pytest.mark.iio_hardware).carriers(required, >=1 entry)FPGA carrier boards to test against. One build+flash leg is generated per
(noos_project, carrier)pair that has a live flash-capable board on the coordinator.validate_banner(optional)Serial string the firmware must print after JTAG-flash; matched on the console as a pexpect pattern (an unanchored regex/substring search). Defaults to
"Successfully initialized"if omitted.build_vars(optional)Extra
makevariables forwarded to the no-os build asKEY=VALUEpairs.
The consumer workflow¶
Four inputs wire your no-os repo into the reusable noos-hw-request.yml
workflow:
jobs:
noos-hw-request:
uses: tfcollins/labgrid-plugins/.github/workflows/noos-hw-request.yml@main
with:
coordinator: ${{ vars.LG_COORDINATOR }}
manifest: "tools/hw_ci/projects.yaml"
runner-label: ${{ vars.HW_REQUEST_RUNNER }}
preflight-runner-label: ${{ vars.HW_PREFLIGHT_RUNNER }}
coordinatorCoordinator
host:portforGET /api/match?mode=flash. Set once as a repo or org variable.manifestPath to the projects manifest inside the consumer checkout. Default:
tools/hw_ci/projects.yaml.runner-labelFallback runner label for the per-project build+flash legs. Each leg prefers the runner co-located with its board (from the place’s
runnertag); this is the fallback.preflight-runner-labelRunner label for the discovery preflight step. Must be able to reach the coordinator REST API.
What happens per leg¶
Discovery (preflight).
adi-lg-hw-ci noos-matrixintersects the manifest’s(noos_project, part, carriers)entries with the coordinator’s live flash-capable boards (GET /api/match?mode=flash). Each matching(project, carrier)pair becomes one matrix leg. A project with no live board emits a::warning::annotation and is skipped.Build (
adi-lg-hw-ci build-noos). The build step runs on the leg’s assigned runner:Sources Vivado automatically from the system-wide install (or
$VITIS_SETTINGS).Creates the libtinfo
.so.5→.so.6shim under~/.local/xlnxshimso Vitis works on Ubuntu 22.04/24.04 without system-level package installs.Downloads the Kuiper Linux image (~3.5 GB) for the board’s release, cached once per release under
~/.labgrid/kuiper_releases.Extracts the board’s
system_top.xsafrom the Kuiper boot FAT partition, cached under~/.labgrid/kuiper_xsa.Sets
NOOS_VITIS_HSI_FLOW=1(the pure-HSI flow avoids the “Channel closed” crash that affects the default Vitis HWH flow) and runsmakeinprojects/<noos_project>/.Artifacts:
.elfinprojects/<noos_project>/build/, plussystem_top.bitandps7_init.tclinprojects/<noos_project>/build_hw/.
Flash + validate (
adi-lg request --mode flash). After the build completes, the leg reserves a matching flash-capable board on the coordinator (queuing up towaitseconds if the board is busy), then uses theBootNoOSJTAGstrategy to:JTAG-program the bitstream (
system_top.bit) onto the FPGA.Initialize the PS via
ps7_init.tcl.Download and run the
<project>.elf.Assert the
validate_bannerstring on the serial console.Release the board on exit (even on failure).
Troubleshooting¶
- “Channel closed” crash during ``make``
This is a known Vitis bug in the default HWH-based HSI flow.
build-noossetsNOOS_VITIS_HSI_FLOW=1automatically to use the pure-HSI flow, which avoids this crash. No manual workaround is needed.- libtinfo.so.5 / libncurses.so.5 not found
Vitis requires these legacy libraries that are absent on Ubuntu 22.04+.
build-nooscreates symlinks*.so.5 → *.so.6under~/.local/xlnxshimand prepends that directory toLD_LIBRARY_PATHautomatically. No system-level package installs are required.- Ambiguous Kuiper boot folder (multiple board matches in the FAT partition)
build-noossearches the Kuiper image’s FAT boot partition for a subdirectory matching the board name. If more than one folder matches, setflash.kuiper_xsa_dirin the coordinator board catalog for the relevant place, or pass--xsa-dir <path>toadi-lg-hw-ci build-noosdirectly to pin the exact folder path.- Flash fails with “board busy”
The default wait is 1800 s. Increase the
waitinput on the reusable workflow, or check whether a previous run left the board reserved:labgrid-client -x <coordinator> reservations.
See also¶
Hardware CI by part (hw-request) — the
adi-lg requestcommand and URI mode (Linux boot + IIO URI export).Hardware CI (v1, manifest-first) — the v1/v2 pytest-driven workflows (for repos whose tests talk the board over a libIIO network URI rather than direct JTAG).