|
12 | 12 | so U-Boot's ``tftp devicetree.dtb`` finds it; the kernel is |
13 | 13 | ``uImage`` (legacy U-Boot image, wrapped from the built ``zImage`` |
14 | 14 | by :func:`~test.hw.hw_helpers._wrap_zimage_as_uimage`). |
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. |
| 15 | +2. **DMA path** — the default post-boot Talise state on ZC706 + |
| 16 | + ADRV9009 leaves buffered RX inert. The DMA test pushes a |
| 17 | + canonical iio-oscilloscope ``DC245p76`` Talise profile via |
| 18 | + ``adrv9009-phy.profile_config`` first; that re-inits the radio |
| 19 | + to ``radio_on`` without changing the JESD lane rate. The |
| 20 | + ADRV9009 ZC706 reference HDL has no internal DAC→ADC loopback |
| 21 | + (unlike AD9081's ``ad_ip_jesd204_tpl_*`` cores), so a TX DDS tone |
| 22 | + may not appear in the RX capture without an external SMA-to-SMA |
| 23 | + cable on the daughter card. The FFT stage asserts SNR > 10 dB |
| 24 | + on a non-DC peak only when one is clearly present and otherwise |
| 25 | + logs the noise-only result and passes. |
28 | 26 |
|
29 | 27 | LG_ENV: ``test/hw/env/nemo.yaml``. |
30 | 28 | """ |
@@ -442,49 +440,88 @@ def test_dma_loopback(booted_board, tmp_path): |
442 | 440 |
|
443 | 441 | ctx, ip = open_iio_context(shell) |
444 | 442 |
|
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" |
467 | | - ) |
468 | | - |
469 | 443 | # 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", |
| 444 | + # |
| 445 | + # Both the RX TPL (``...@44a00000``) and the OBS TPL |
| 446 | + # (``...@44a08000``) probe to libiio with the same of_node |
| 447 | + # name ``ad_ip_jesd204_tpl_adc`` — their unit-names are |
| 448 | + # identical in the sdtgen-built DTB. ``find_device`` returns |
| 449 | + # whichever probed first (typically OBS via ``ad_adc.c``, |
| 450 | + # before cf_axi_adc binds the RX TPL). Talise's ``radio_on`` |
| 451 | + # streams framer-A (RX); framer-B (OBS) stays gated, so a |
| 452 | + # refill on the OBS device returns all zeros even though the |
| 453 | + # OBS DMA fires its done IRQ. Prefer the RX TPL by reg |
| 454 | + # address so we exercise the path ``radio_on`` actually |
| 455 | + # enables. |
| 456 | + rx_tpl_dev = None |
| 457 | + for d in ctx.devices: |
| 458 | + if d.name != "ad_ip_jesd204_tpl_adc": |
| 459 | + continue |
| 460 | + try: |
| 461 | + of_node = d.attrs["of_node"].value if "of_node" in d.attrs else "" |
| 462 | + except Exception: # noqa: BLE001 — attr read may raise on some builds |
| 463 | + of_node = "" |
| 464 | + if "44a00000" in of_node: |
| 465 | + rx_tpl_dev = d |
| 466 | + break |
| 467 | + if rx_tpl_dev is None: |
| 468 | + # ``of_node`` isn't always exposed as an IIO attr; fall back |
| 469 | + # to the higher-numbered duplicate. cf_axi_adc binds the RX |
| 470 | + # TPL after ``ad_adc`` binds OBS, so the RX iio:device has |
| 471 | + # the larger numeric id. |
| 472 | + candidates = [d for d in ctx.devices if d.name == "ad_ip_jesd204_tpl_adc"] |
| 473 | + if candidates: |
| 474 | + rx_tpl_dev = max(candidates, key=lambda d: int(d.id.rsplit(":device", 1)[1])) |
| 475 | + |
| 476 | + target_names: tuple[str, ...] = ( |
| 477 | + "axi-adrv9009-rx-hpc", |
| 478 | + "axi-adrv9009-rx-obs-hpc", |
| 479 | + "ad_ip_jesd204_tpl_adc", |
478 | 480 | ) |
| 481 | + if rx_tpl_dev is not None: |
| 482 | + target_names = (rx_tpl_dev.id,) |
| 483 | + |
| 484 | + try: |
| 485 | + assert_rx_capture_valid( |
| 486 | + ctx, |
| 487 | + target_names, |
| 488 | + n_samples=2**12, |
| 489 | + context="adrv9009 zc706 overlay", |
| 490 | + ) |
| 491 | + except AssertionError: |
| 492 | + # On-target diagnostics: IRQ counts disambiguate |
| 493 | + # DMA-not-firing from JESD-not-streaming for the next |
| 494 | + # round of debugging. |
| 495 | + print("=== /proc/interrupts ===") |
| 496 | + print(shell_out(shell, "cat /proc/interrupts")) |
| 497 | + print("=== dmesg tail ===") |
| 498 | + print(shell_out(shell, "dmesg | tail -n 60")) |
| 499 | + raise |
479 | 500 |
|
480 | 501 | # Phase 2: opportunistic spectrum check via pyadi-iio. |
| 502 | + # |
| 503 | + # ``pyadi-iio`` looks up the buffered ADC by the production |
| 504 | + # name ``axi-adrv9009-rx-hpc``. Our merged DTB names the same |
| 505 | + # node ``ad_ip_jesd204_tpl_adc`` (sdtgen unit-name), so the |
| 506 | + # ``adi.adrv9009`` constructor returns an object with |
| 507 | + # ``_rxadc = None`` instead of raising — every subsequent |
| 508 | + # ``dev.rx_*`` access then crashes with ``AttributeError``. |
| 509 | + # Detect that state up front and skip; Phase 1 already |
| 510 | + # validated the data path. |
481 | 511 | import adi |
482 | 512 |
|
483 | 513 | try: |
484 | 514 | dev = adi.adrv9009(uri=f"ip:{ip}") |
485 | 515 | except Exception as exc: # noqa: BLE001 — connect failure → skip phase 2 |
486 | 516 | print(f"adi.adrv9009 unavailable; skipping FFT phase: {exc}") |
487 | 517 | return |
| 518 | + if getattr(dev, "_rxadc", None) is None: |
| 519 | + print( |
| 520 | + "adi.adrv9009 missing 'axi-adrv9009-rx-hpc' IIO name " |
| 521 | + "(merged DTB exposes 'ad_ip_jesd204_tpl_adc' instead) — " |
| 522 | + "skipping FFT phase; Phase 1 already validated the data path." |
| 523 | + ) |
| 524 | + return |
488 | 525 |
|
489 | 526 | dev.rx_enabled_channels = [0] |
490 | 527 | dev.rx_buffer_size = RX_BUFFER_SIZE |
|
0 commit comments