Skip to content

Commit ddce484

Browse files
committed
feat(hw): add ADRV9009+ZC706 hardware test on labgrid node 'nemo'
Wires up a second ADRV9009+ZC706 board attached to labgrid node 'nemo' (TFTP boot, HomeAssistantOutlet switch.board_1_socket_1, Kuiper 2023_r2 zynq-zc706-adv7511-adrv9009). - test/hw/test_adrv9009_zc706_hw.py: XSA-pipeline hw test (mirrors the ADRV9371+ZC706 structure), with adrv9009_board GPIO overrides for the ZC706 (trx_reset=106, trx_sysref_req=112) since the ADRV9009Builder defaults to ZCU102 GPIO numbers. si570 probe -EIO is filtered because the optional Si570 clock chip sometimes doesn't ACK (present in production dmesg too). - test/hw/env/nemo.yaml: pytest env config for the nemo remote place (force-added past the env/ gitignore rule, same pattern as the existing bq/mini2/nuc configs). - test/hw/env/all.yaml: adds the nemo target block mirroring bq. - .github/hw-nodes.json: registers the nemo runner for the hw-nodes CI matrix. First-light verified 2026-04-22: all three JESD links (RX, RX_OS, TX) reach DATA state with the pyadi-dt XSA-generated DTB.
1 parent 9a7e2e2 commit ddce484

4 files changed

Lines changed: 285 additions & 0 deletions

File tree

.github/hw-nodes.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,11 @@
1919
"runner_label": "hw-nuc",
2020
"env_remote": "test/hw/env/nuc.yaml",
2121
"tests": ["test/hw/test_fmcdaq3_vcu118_hw.py"]
22+
},
23+
{
24+
"place": "nemo",
25+
"runner_label": "hw-nemo",
26+
"env_remote": "test/hw/env/nemo.yaml",
27+
"tests": ["test/hw/test_adrv9009_zc706_hw.py"]
2228
}
2329
]

test/hw/env/all.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,37 @@ targets:
8888
kernel_addr: '0x3000000'
8989
dtb_addr: '0x2A00000'
9090
bootargs: 'console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlycon rootfstype=ext4 rootwait clk_ignore_unused cpuidle.off=1'
91+
92+
nemo:
93+
features:
94+
- adrv9009
95+
- zc706
96+
resources:
97+
RemotePlace:
98+
name: nemo
99+
TFTPServerResource:
100+
address: 'auto'
101+
port: 3069
102+
root: /var/lib/tftpboot
103+
drivers:
104+
TFTPServerDriver: {}
105+
KuiperDLDriver: {}
106+
SerialDriver: {}
107+
HomeAssistantPowerDriver: {}
108+
SSHDriver: {}
109+
ADIShellDriver:
110+
prompt: 'root@.*'
111+
login_prompt: 'analog login: '
112+
username: 'root'
113+
password: 'analog'
114+
BootFPGASoCTFTP:
115+
wait_for_linux_prompt_timeout: 120
116+
reached_linux_marker: '@analog'
117+
# Zynq-7000 U-Boot settings
118+
uboot_prompt: 'Zynq>.*'
119+
kernel_image_name: 'uImage'
120+
dtb_image_name: 'devicetree.dtb'
121+
boot_cmd: 'bootm'
122+
kernel_addr: '0x3000000'
123+
dtb_addr: '0x2A00000'
124+
bootargs: 'console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlycon rootfstype=ext4 rootwait clk_ignore_unused cpuidle.off=1'

test/hw/env/nemo.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## Env config for ADRV9009 + ZC706 via nemo remote place (TFTP boot).
2+
##
3+
## Run:
4+
## LG_COORDINATOR=10.0.0.41:20408 \
5+
## LG_ENV=test/hw/env/nemo.yaml \
6+
## pytest test/hw/test_adrv9009_zc706_hw.py -v -s
7+
##
8+
## Tests must run on the `nemo` host itself (or a host with the same
9+
## /var/lib/tftpboot root), because the TFTPServerResource is consumed
10+
## locally by TFTPServerDriver.
11+
12+
targets:
13+
main:
14+
features:
15+
- adrv9009
16+
- zc706
17+
resources:
18+
RemotePlace:
19+
name: nemo
20+
TFTPServerResource:
21+
address: 'auto'
22+
port: 3069
23+
root: /var/lib/tftpboot
24+
drivers:
25+
TFTPServerDriver: {}
26+
KuiperDLDriver: {}
27+
SerialDriver: {}
28+
HomeAssistantPowerDriver: {}
29+
SSHDriver: {}
30+
ADIShellDriver:
31+
prompt: 'root@.*'
32+
login_prompt: 'analog login: '
33+
username: 'root'
34+
password: 'analog'
35+
BootFPGASoCTFTP:
36+
wait_for_linux_prompt_timeout: 120
37+
reached_linux_marker: '@analog'
38+
uboot_prompt: 'Zynq>.*'
39+
kernel_image_name: 'uImage'
40+
dtb_image_name: 'devicetree.dtb'
41+
boot_cmd: 'bootm'
42+
kernel_addr: '0x3000000'
43+
dtb_addr: '0x2A00000'
44+
bootargs: 'console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlycon rootfstype=ext4 rootwait clk_ignore_unused cpuidle.off=1'

test/hw/test_adrv9009_zc706_hw.py

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
"""ADRV9009 + ZC706 hardware test.
2+
3+
Drives the XSA pipeline (:class:`adidt.xsa.pipeline.XsaPipeline` +
4+
:class:`adidt.xsa.builders.adrv9009.ADRV9009Builder`) end-to-end on real
5+
hardware attached to the ``nemo`` labgrid place.
6+
7+
Boot strategy is :class:`BootFPGASoCTFTP` (Zynq-7000 TFTP boot). The DTB is
8+
renamed to ``devicetree.dtb`` before :meth:`KuiperDLDriver.add_files_to_target`
9+
so U-Boot's ``tftp devicetree.dtb`` finds it.
10+
11+
LG_ENV: lg_adrv9009_zc706_tftp.yaml
12+
"""
13+
14+
from __future__ import annotations
15+
16+
import os
17+
import shutil
18+
from pathlib import Path
19+
20+
import pytest
21+
22+
if not (os.environ.get("LG_COORDINATOR") or os.environ.get("LG_ENV")):
23+
pytest.skip(
24+
"set LG_COORDINATOR or LG_ENV for ADRV9009 ZC706 hardware test"
25+
" (see .env.example)",
26+
allow_module_level=True,
27+
)
28+
29+
from adidt.xsa.pipeline import XsaPipeline # noqa: E402
30+
from adidt.xsa.topology import XsaParser # noqa: E402
31+
from test.hw.hw_helpers import ( # noqa: E402
32+
DEFAULT_OUT_DIR,
33+
acquire_xsa,
34+
assert_ilas_aligned,
35+
assert_jesd_links_data,
36+
assert_no_kernel_faults,
37+
assert_no_probe_errors,
38+
check_jesd_framing_plausibility,
39+
collect_dmesg,
40+
compile_dts_to_dtb,
41+
deploy_and_boot,
42+
open_iio_context,
43+
parse_ilas_status,
44+
read_jesd_status,
45+
shell_out,
46+
)
47+
48+
49+
DEFAULT_KUIPER_RELEASE = "2023_r2"
50+
DEFAULT_KUIPER_PROJECT = "zynq-zc706-adv7511-adrv9009"
51+
52+
53+
def _stage_dtb_as_devicetree(dtb: Path, staging_dir: Path) -> Path:
54+
"""Copy *dtb* into *staging_dir* renamed to ``devicetree.dtb``.
55+
56+
BootFPGASoCTFTP's YAML sets ``dtb_image_name: devicetree.dtb`` — the
57+
file must have that exact basename when it lands in the TFTP root.
58+
"""
59+
staging_dir.mkdir(parents=True, exist_ok=True)
60+
staged = staging_dir / "devicetree.dtb"
61+
shutil.copyfile(dtb, staged)
62+
return staged
63+
64+
65+
@pytest.mark.lg_feature(["adrv9009", "zc706"])
66+
def test_adrv9009_zc706_xsa_hw(board, built_kernel_image_zynq, tmp_path):
67+
"""End-to-end pyadi-dt ADRV9009+ZC706 via the XSA pipeline."""
68+
out_dir = DEFAULT_OUT_DIR
69+
out_dir.mkdir(parents=True, exist_ok=True)
70+
71+
# --- 1. Acquire XSA (auto-downloads from Kuiper if not local) ---
72+
xsa_path = acquire_xsa(
73+
Path(__file__).parent / "xsa" / "system_top_adrv9009_zc706.xsa",
74+
DEFAULT_KUIPER_RELEASE,
75+
DEFAULT_KUIPER_PROJECT,
76+
tmp_path,
77+
)
78+
assert xsa_path.exists(), f"XSA not found: {xsa_path}"
79+
80+
# --- 2. Sanity-check topology ---
81+
topology = XsaParser().parse(xsa_path)
82+
assert topology.jesd204_rx, "No JESD204 RX instances in XSA topology"
83+
assert topology.jesd204_tx, "No JESD204 TX instances in XSA topology"
84+
print(
85+
f"XSA topology: {len(topology.converters)} converter(s), "
86+
f"{len(topology.jesd204_rx)} rx jesd, {len(topology.jesd204_tx)} tx jesd"
87+
)
88+
89+
# --- 3. Run the XSA pipeline ---
90+
# ADRV9009 default HDL framing: RX M=4 L=2 S=1 Np=16 -> F=4;
91+
# TX M=4 L=4 S=1 Np=16 -> F=2. Matches what
92+
# ``test_adrv9009_zcu102_hw._solve_adrv9009_config`` derives via
93+
# pyadi-jif and what the ZC706 ADRV9009 HDL reference design uses.
94+
#
95+
# ``adrv9009_board`` GPIO overrides: ADRV9009Builder defaults to
96+
# ZCU102 GPIO numbers (130/136); ZC706 wires the same signals to
97+
# gpio0:106 (reset) and gpio0:112 (sysref-req), matching the Kuiper
98+
# production zynq-zc706-adv7511-adrv9009 DT.
99+
cfg = {
100+
"adrv9009_board": {
101+
"trx_reset_gpio": 106,
102+
"trx_sysref_req_gpio": 112,
103+
},
104+
"jesd": {
105+
"rx": {"F": 4, "K": 32, "M": 4, "L": 2, "Np": 16, "S": 1},
106+
"tx": {"F": 2, "K": 32, "M": 4, "L": 4, "Np": 16, "S": 1},
107+
},
108+
"clock": {
109+
"rx_device_clk_label": "clkgen",
110+
"tx_device_clk_label": "clkgen",
111+
"hmc7044_rx_channel": 0,
112+
"hmc7044_tx_channel": 0,
113+
},
114+
}
115+
116+
framing_warnings = check_jesd_framing_plausibility(cfg["jesd"])
117+
assert not framing_warnings, (
118+
"JESD cfg is structurally inconsistent (will fail ILAS):\n "
119+
+ "\n ".join(framing_warnings)
120+
)
121+
122+
result = XsaPipeline().run(
123+
xsa_path=xsa_path,
124+
cfg=cfg,
125+
output_dir=out_dir,
126+
sdtgen_timeout=300,
127+
profile="adrv9009_zc706",
128+
)
129+
merged_dts = result["merged"]
130+
assert merged_dts.exists(), f"Merged DTS not written: {merged_dts}"
131+
132+
merged_content = merged_dts.read_text()
133+
assert 'compatible = "adi,adrv9009"' in merged_content, (
134+
"ADRV9009 compatible string missing from merged DTS"
135+
)
136+
137+
# --- 4. Compile to DTB, stage as devicetree.dtb ---
138+
dtb_raw = out_dir / "adrv9009_zc706.dtb"
139+
compile_dts_to_dtb(merged_dts, dtb_raw)
140+
dtb = _stage_dtb_as_devicetree(dtb_raw, out_dir / "tftp_staging_xsa")
141+
142+
# --- 5. Deploy + boot ---
143+
shell = deploy_and_boot(board, dtb, built_kernel_image_zynq)
144+
145+
# --- 6. Diagnostics + probe verification ---
146+
dmesg_txt = collect_dmesg(
147+
shell,
148+
out_dir,
149+
label="adrv9009_xsa",
150+
grep_pattern="adrv9009|hmc7044|ad9528|jesd204|talise|probe|failed|error",
151+
)
152+
assert_no_kernel_faults(dmesg_txt)
153+
# si570 probe -EIO is a benign hardware artifact present in production
154+
# too (the optional Si570 clock chip sometimes doesn't ACK on the
155+
# default i2c address); strip it before the probe-error check.
156+
dmesg_filtered = "\n".join(
157+
line for line in dmesg_txt.splitlines()
158+
if not ("si570" in line and "failed" in line)
159+
)
160+
assert_no_probe_errors(dmesg_filtered)
161+
assert "adrv9009" in dmesg_txt.lower() or "talise" in dmesg_txt.lower(), (
162+
"ADRV9009 driver probe signature not found in dmesg"
163+
)
164+
165+
ctx, _ = open_iio_context(shell)
166+
found = {d.name for d in ctx.devices if d.name}
167+
assert any("adrv9009" in n.lower() or "talise" in n.lower() for n in found), (
168+
f"No ADRV9009 IIO device found. Devices: {sorted(found)}"
169+
)
170+
assert any("9528" in n or "hmc7044" in n.lower() for n in found), (
171+
f"No AD9528/HMC7044 clock device found. Devices: {sorted(found)}"
172+
)
173+
174+
# --- 7. JESD link + ILAS diagnostics ---
175+
# Diagnostic: dump every registered clock's rate so we can see what
176+
# the MMCM / axi_clkgen / AD9528 actually produce per link. Compare
177+
# against production (TX = 61.44 MHz, RX/RX_OS = 122.88 MHz on the
178+
# "Reported Link Clock" sysfs line).
179+
print("=== /sys/kernel/debug/clk/clk_summary (adrv9009/ad9528/clkgen lines) ===")
180+
print(shell_out(shell, "grep -E 'ad9528|axi_adrv9009|axi_tx_clkgen|axi_rx_clkgen|jesd_|clk0|clkc|misc_clk' /sys/kernel/debug/clk/clk_summary 2>/dev/null | head -40"))
181+
print("=== jesd204 link rate debugfs ===")
182+
print(shell_out(shell, "ls /sys/kernel/debug/jesd204/ 2>/dev/null; ls /sys/kernel/debug/jesd204/*/ 2>/dev/null; ls /sys/kernel/debug/jesd204/topologies/0/ 2>/dev/null"))
183+
print("=== jesd204 per-link info ===")
184+
print(shell_out(shell, "for d in /sys/kernel/debug/jesd204/topologies/0/links/*; do echo \"=== $d ===\"; for f in $d/*; do [ -f \"$f\" ] && echo \"-- $(basename $f):\" && cat \"$f\" 2>/dev/null; done; done"))
185+
print("=== dmesg for TX rate clk_set_rate ===")
186+
print(shell_out(shell, "dmesg | grep -E 'axi-jesd204|jesd204_link|clk_set_rate|lane_rate|device_rate' | tail -n 40"))
187+
188+
rx_status, tx_status = read_jesd_status(shell)
189+
print("=== JESD204 RX status (sysfs) ===")
190+
print(rx_status)
191+
print("=== JESD204 TX status (sysfs) ===")
192+
print(tx_status)
193+
194+
ilas_report = parse_ilas_status(dmesg_txt)
195+
print("=== ADRV9009 ILAS report ===")
196+
print(ilas_report.summary())
197+
if ilas_report.fields:
198+
for name in ilas_report.fields:
199+
print(f" mismatched: {name}")
200+
assert_ilas_aligned(dmesg_txt, context="adrv9009_xsa")
201+
assert_jesd_links_data(shell, context="adrv9009_xsa")

0 commit comments

Comments
 (0)