Skip to content

Commit 537ad26

Browse files
committed
test(hw): wire Talise profile + radio_on into ADRV9009 ZC706 DMA test
Two improvements over the previous "skip on refill timeout" behaviour: 1. Push a canonical iio-oscilloscope DC245p76 Talise filter profile to ``adrv9009-phy.profile_config`` and write ``radio_on`` to ``ensm_mode`` before any buffer ops. Same pattern test_adrv9009_zcu102_hw uses for its profile-reload loop, scoped here to "wake the radio" rather than "validate reload". Talise re-init walks JESD through SYNC -> ILAS -> DATA again, so we re-assert link DATA after the push. 2. Pre-check the IIO device list for the Kuiper-production names (``axi-adrv9009-rx-hpc`` / ``-obs-hpc``) before opening any DMA buffer. Our merged sdtgen DTB labels the JESD framing endpoint ``ad_ip_jesd204_tpl_adc`` instead, and the TPL device on its own does not back a refillable AXI-DMA buffer even with ``ensm_mode=radio_on`` — surfacing the buffered RX node under the Kuiper name is a merged-DTB IIO-naming change outside overlay-lifecycle scope. Skipping early avoids leaving a stalled libiio buffer that segfaulted later tests in the suite. Net effect on nemo: 5 passed, 1 skipped (DMA loopback) — same outcome as before, but the skip carries a precise diagnosis of what was tried and what's missing, and the suite no longer crashes during teardown.
1 parent 062fb74 commit 537ad26

1 file changed

Lines changed: 139 additions & 39 deletions

File tree

test/hw/xsa/test_adrv9009_zc706_overlay.py

Lines changed: 139 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,30 @@
1212
so U-Boot's ``tftp devicetree.dtb`` finds it; the kernel is
1313
``uImage`` (legacy U-Boot image, wrapped from the built ``zImage``
1414
by :func:`~test.hw.hw_helpers._wrap_zimage_as_uimage`).
15-
2. **DMA path** — the ADRV9009 ZC706 reference HDL has no internal
16-
DAC→ADC loopback (unlike AD9081's ``ad_ip_jesd204_tpl_*`` cores),
17-
so an injected TX DDS tone may not appear in the RX capture
18-
without an external SMA-to-SMA cable on the daughter card. The
19-
data-path verification therefore runs in two phases: a mandatory
20-
:func:`~test.hw.hw_helpers.assert_rx_capture_valid` smoke check
21-
plus an opportunistic FFT stage that asserts SNR > 10 dB on a
22-
non-DC peak only when one is clearly present, and otherwise logs
23-
the noise-only result and passes.
15+
2. **DMA path** — two adaptations vs the AD9081 variant:
16+
17+
* The default post-boot Talise state on ZC706 + ADRV9009 leaves
18+
buffered RX inert. The DMA test pushes a canonical
19+
iio-oscilloscope ``DC245p76`` Talise profile via
20+
``adrv9009-phy.profile_config`` first; that re-inits the radio
21+
to ``radio_on`` without changing the JESD lane rate.
22+
* The ADRV9009 ZC706 reference HDL has no internal DAC→ADC
23+
loopback (unlike AD9081's ``ad_ip_jesd204_tpl_*`` cores), so a
24+
TX DDS tone may not appear in the RX capture without an
25+
external SMA-to-SMA cable on the daughter card. The FFT stage
26+
asserts SNR > 10 dB on a non-DC peak only when one is clearly
27+
present and otherwise logs the noise-only result and passes.
2428
2529
LG_ENV: ``test/hw/env/nemo.yaml``.
2630
"""
2731

2832
from __future__ import annotations
2933

34+
import base64
3035
import os
3136
import shutil as _shutil
3237
import time
38+
import urllib.request
3339
from pathlib import Path
3440
from typing import Any
3541

@@ -79,6 +85,18 @@
7985
DDS_SCALE = 0.5
8086
RX_BUFFER_SIZE = 2**14
8187

88+
# Smallest of the canonical Talise filter profiles iio-oscilloscope
89+
# ships. All four use ``deviceClock=245.76 MHz`` (matches our cfg's
90+
# 245.76 MHz device clock), so pushing this profile re-initialises the
91+
# Talise radio to a state where buffered RX is enabled without
92+
# changing the JESD lane rate. ``test_adrv9009_zcu102_hw`` uses the
93+
# same source URL.
94+
TALISE_PROFILE_URL = (
95+
"https://raw.githubusercontent.com/analogdevicesinc/iio-oscilloscope/"
96+
"main/filters/adrv9009/"
97+
"Tx_BW100_IR122p88_Rx_BW100_OR122p88_ORx_BW100_OR122p88_DC245p76.txt"
98+
)
99+
82100
EXPECTED_IIO_NAMES_ANY = (
83101
# Kuiper-built DTBs use ``axi-adrv9009-rx-hpc``; the sdtgen-built
84102
# merged DTB the overlay test boots from labels the same buffered
@@ -239,6 +257,66 @@ def _apply_and_wait(shell) -> None:
239257
time.sleep(8.0)
240258

241259

260+
def _load_talise_profile(shell, cache_dir: Path) -> bool:
261+
"""Push a Talise filter profile to ``adrv9009-phy.profile_config``.
262+
263+
On ZC706 + ADRV9009 the default post-boot Talise state leaves the
264+
buffered RX path inert (unlike ZCU102 where the default profile
265+
is RX-capable). Pushing any profile triggers a Talise re-init
266+
that brings the radio up to ``radio_on``, after which DMA
267+
capture works. Returns ``True`` if the profile was applied,
268+
``False`` if the sysfs ``profile_config`` node could not be
269+
located (e.g. driver build without debugfs support).
270+
"""
271+
profile_sysfs = shell_out(
272+
shell,
273+
"find /sys/kernel/debug/iio /sys/bus/iio/devices "
274+
"-name profile_config 2>/dev/null | head -1",
275+
).strip()
276+
if not profile_sysfs:
277+
profile_sysfs = shell_out(
278+
shell,
279+
"find /sys -name profile_config 2>/dev/null | head -1",
280+
).strip()
281+
if not profile_sysfs:
282+
return False
283+
284+
cache_dir.mkdir(parents=True, exist_ok=True)
285+
cached = cache_dir / "talise_default.txt"
286+
if cached.exists() and cached.stat().st_size > 0:
287+
body = cached.read_text()
288+
else:
289+
with urllib.request.urlopen(TALISE_PROFILE_URL, timeout=30) as resp: # noqa: S310
290+
body = resp.read().decode("utf-8")
291+
cached.write_text(body)
292+
if not body.lstrip().startswith("<profile "):
293+
raise AssertionError(
294+
"Talise profile fetch returned non-XML content"
295+
f" (first 80 chars: {body[:80]!r})"
296+
)
297+
298+
b64 = base64.b64encode(body.encode()).decode()
299+
shell_out(shell, f"printf '%s' '{b64}' | base64 -d > /tmp/talise.txt")
300+
size_on_target = shell_out(shell, "stat -c%s /tmp/talise.txt").strip()
301+
assert size_on_target == str(len(body.encode())), (
302+
f"Talise profile partial push: target has {size_on_target},"
303+
f" expected {len(body.encode())}"
304+
)
305+
shell_out(shell, f"cat /tmp/talise.txt > {profile_sysfs}")
306+
# Talise re-init re-runs the JESD bring-up sequence; give the FSM
307+
# time to relock both links before any sysfs check.
308+
time.sleep(3.0)
309+
310+
# Profile push leaves the radio in ``calibrated`` (ENSM state 6) on
311+
# ZC706 builds — we need ``radio_on`` (state 7) before the buffered
312+
# RX path will deliver samples through DMA. ensm_mode is exposed
313+
# at the adrv9009-phy IIO device level (sibling to profile_config).
314+
phy_dir = profile_sysfs.rsplit("/", 1)[0]
315+
shell_out(shell, f"echo radio_on > {phy_dir}/ensm_mode")
316+
time.sleep(1.0)
317+
return True
318+
319+
242320
def _filter_si570_probe_noise(dmesg_txt: str) -> str:
243321
"""Strip the benign si570 -EIO probe lines.
244322
@@ -324,16 +402,19 @@ def test_load_overlay(booted_board, tmp_path):
324402

325403
@requires_lg
326404
@pytest.mark.lg_feature(["adrv9009", "zc706"])
327-
def test_dma_loopback(booted_board):
405+
def test_dma_loopback(booted_board, tmp_path):
328406
"""Verify DMA TX→RX data path.
329407
330-
Two phases:
408+
Setup: push a single Talise filter profile to wake the radio.
409+
The default post-boot Talise state on ZC706 + ADRV9009 leaves
410+
buffered RX inert; pushing any DC-245.76 MHz profile re-inits
411+
the chip into ``radio_on`` without changing the JESD lane rate.
412+
413+
Two verification phases:
331414
332415
* **Mandatory:** :func:`assert_rx_capture_valid` confirms that an
333-
RX buffer arrives with non-zero, non-latched samples. This is
334-
the same smoke check ``test_adrv9009_zcu102_hw`` runs before any
335-
Talise profile push and is sufficient evidence that the JESD +
336-
DMA path is alive.
416+
RX buffer arrives with non-zero, non-latched samples. Same
417+
smoke check ``test_adrv9009_zcu102_hw`` runs.
337418
* **Opportunistic FFT:** drive a DDS tone on TX and look for a
338419
coherent peak in the RX spectrum. If one is present (SNR
339420
> 10 dB), it must be non-DC and non-Nyquist. If no peak emerges
@@ -349,33 +430,52 @@ def test_dma_loopback(booted_board):
349430
if not overlay_is_loaded(shell, OVERLAY_NAME):
350431
pytest.skip("overlay not loaded — test_load_overlay must run first")
351432

433+
profile_loaded = _load_talise_profile(shell, tmp_path / "talise_cache")
434+
if profile_loaded:
435+
# Talise re-init walks JESD through SYNC → ILAS → DATA again.
436+
assert_jesd_links_data(shell, context="after Talise profile push")
437+
else:
438+
print(
439+
"Talise profile_config sysfs not found — proceeding with"
440+
" default post-boot Talise state"
441+
)
442+
352443
ctx, ip = open_iio_context(shell)
353444

354-
# Phase 1: bare data-path smoke check (independent of pyadi-iio).
355-
# On ZC706 + ADRV9009, the default Talise profile may leave the
356-
# buffered RX path inert until ``ensm_mode = radio_on`` is written
357-
# or a Talise profile is reloaded — neither of which is the
358-
# overlay-lifecycle test's responsibility. Treat a refill timeout
359-
# as "DMA path needs further setup", log it, and skip cleanly.
360-
try:
361-
assert_rx_capture_valid(
362-
ctx,
363-
(
364-
"axi-adrv9009-rx-hpc",
365-
"axi-adrv9009-rx-obs-hpc",
366-
"ad_ip_jesd204_tpl_adc",
367-
),
368-
n_samples=2**12,
369-
context="adrv9009 zc706 overlay",
445+
# Pre-check before touching any DMA buffer: only the production
446+
# Kuiper DTB names (``axi-adrv9009-rx-hpc`` / ``-obs-hpc``) back
447+
# a refillable AXI-DMA buffer. Our merged sdtgen DTB labels the
448+
# JESD framing core ``ad_ip_jesd204_tpl_adc`` instead, which is
449+
# the JESD framing test endpoint, not the buffered ADC frontend
450+
# — refilling its buffer hangs even after Talise re-init +
451+
# ``ensm_mode=radio_on``. Aligning the merged-DTB IIO names
452+
# with Kuiper is a separate merged-DTB IIO-naming change,
453+
# outside overlay-lifecycle scope. Skip the buffered-RX phase
454+
# cleanly when the right names are absent so we don't leave a
455+
# stalled libiio buffer that segfaults the rest of the suite.
456+
rx_dev_names = {d.name for d in ctx.devices if d.name}
457+
has_buffered_rx = bool(
458+
rx_dev_names & {"axi-adrv9009-rx-hpc", "axi-adrv9009-rx-obs-hpc"}
459+
)
460+
if not has_buffered_rx:
461+
del ctx
462+
pytest.skip(
463+
"merged-DTB exposes only ad_ip_jesd204_tpl_adc, not the"
464+
" axi-adrv9009-rx-hpc Kuiper name backed by AXI-DMA;"
465+
" buffered RX requires merged-DTB IIO-naming work"
466+
" outside overlay-lifecycle scope"
370467
)
371-
except AssertionError as exc:
372-
if "timed out" in str(exc).lower():
373-
pytest.skip(
374-
"ADRV9009 RX DMA refill timed out — buffered path needs"
375-
" radio-enable / profile load (lab-setup concern, not"
376-
f" overlay lifecycle): {exc}"
377-
)
378-
raise
468+
469+
# Phase 1: bare data-path smoke check.
470+
assert_rx_capture_valid(
471+
ctx,
472+
(
473+
"axi-adrv9009-rx-hpc",
474+
"axi-adrv9009-rx-obs-hpc",
475+
),
476+
n_samples=2**12,
477+
context="adrv9009 zc706 overlay",
478+
)
379479

380480
# Phase 2: opportunistic spectrum check via pyadi-iio.
381481
import adi

0 commit comments

Comments
 (0)