Post Go back to editing

ANTSDR E310 (AD9363, CMOS 1R1T) PetaLinux 2023.2 Migration — dig_tune fixed via skip-mode=2, but DMA interrupt (GIC SPI 63) never reaches CPU

Thread Summary

The user encountered issues with DMA interrupts not reaching the CPU on a Zynq Z7020 SoC when migrating from Buildroot to PetaLinux 2023.2. Despite the DMA transfer completing and the interrupt being asserted, the GIC never sees it as pending. The final answer indicates that official support for the ANTSDR E310 Rev.C board should be sought from the vendor, not ADI, as it is not an ADI product. The user hypothesizes that the PetaLinux-generated FSBL might be misconfiguring the IRQ_F2P signals.
AI Generated Content
Category: Software
Software Version: 2023.2

Hello ADI community,

I am a student engineer currently working on a Software Defined Radio (SDR)
project using the ANTSDR E310 Rev.C board by MicroPhase. My task is to migrate
the board's firmware from an existing working Buildroot setup to PetaLinux 2023.2,
with the goal of streaming RF data to IIO Oscilloscope via iiod over the network.

I would like to share a detailed account of my progress, the issues I encountered,
the fixes I have applied, and the problem I am currently stuck on — in hopes that
someone in the community may have seen something similar or can point me in the
right direction.

--------------------------------------------------------------------------------
HARDWARE AND SOFTWARE OVERVIEW
--------------------------------------------------------------------------------

Hardware:
- Board : ANTSDR E310 Rev.C (MicroPhase)
- SoC : Xilinx Zynq Z7020 (ARM Cortex-A9 dual-core)
- RF Chip : AD9363 (similar to AD9361 but narrower frequency range)
- Interface : CMOS full-port, 1R1T mode (single RX, single TX)
- Bitstream : From antsdr-fw-patch HDL project (CMOS mode, MODE_1R1T=0,
ADC_INIT_DELAY=21) — verified MD5 identical between
Buildroot and PetaLinux setups

Data path:
AD9363 (RF)
→ CMOS 12-bit parallel interface (DATA_CLK_P, RX_FRAME_P, P0_D[5:0])
→ AXI ADC core (cf-ad9361-lpc @ 0x79020000, v10.03)
→ AXI DMAC (adi,axi-dmac-1.00.a @ 0x7c400000)
→ DDR memory (IIO buffer)
→ iiod → IIO Oscilloscope

Software (Buildroot — working baseline):
- Kernel : 6.1.0-23073-gf3da30df6004-dirty (ADI linux main branch)
- iiod : running with -D -n 3 -F /dev/iio_ffs
 - Status : AD9361 initializes, IIO Oscilloscope receives RF signal White check mark

Software (PetaLinux — migration target):
- PetaLinux : 2023.2 with meta-adi-xilinx layer
- Kernel : 6.1.70adi-v2023.2 (ADI linux 2023_R2 branch)
- iiod : running with -D (network mode)

--------------------------------------------------------------------------------
PROBLEM 1: dig_tune FAILED [STATUS: RESOLVED White check mark]
--------------------------------------------------------------------------------

After first boot of PetaLinux, the kernel log showed:

ad9361 spi0.0: ad9361_dig_tune_delay: Tuning RX FAILED!

This was immediately followed by DMA timeout when trying to read data:

$ iio_readdev -s 4096 cf-ad9361-lpc voltage0 voltage1
Unable to refill buffer: Connection timed out (110)

--- Root Cause Analysis ---

The dig_tune() function in the ADI kernel driver works by:
1. Injecting a PN9 test pattern into the AD9363 RX path (BIST mode)
2. Sweeping all 256 combinations of clock_delay (0-15) and data_delay (0-15)
3. For each combination, calling check_pn() to verify the PN9 pattern matches
4. Finding the longest consecutive "valid" window and choosing the center value
5. Writing the optimal delays back to AD9363 registers

The problem was in check_pn(), which first checks the ADI_STATUS bit:

static int ad9361_check_pn(struct axiadc_converter *conv, bool tx, unsigned delay)
{
...
if (!tx && !(axiadc_read(st, ADI_REG_STATUS) & ADI_STATUS))
return 1; // returns error immediately if ADI_STATUS is not set
...
}

In PetaLinux (ADI 2023_R2 kernel), ADI_STATUS was never set during the sweep,
so every single delay combination returned as invalid → no valid window found
→ "Tuning FAILED" → returns -EIO.

The Buildroot kernel (ADI main branch v6.1-23073) succeeds because it brings
the AXI ADC core to a state where ADI_STATUS is set before tuning begins.
I verified that the source code of ad9361_dig_tune() itself is identical
between the two kernels line-by-line; the difference lies in the AXI ADC core
initialization sequence between the two branch versions.

--- Fix Applied ---

Since dig_tune fails in PetaLinux 2023_R2, and manually forcing it also fails,
I chose to skip the tuning entirely and hard-code the optimal delay values,
which I read directly from the AD9363 registers on the working Buildroot board
after a successful dig_tune run:

$ iio_reg ad9361-phy 0x006 → 0x08
bits[7:4] = 0 → rx-data-clock-delay = 0
bits[3:0] = 8 → rx-data-delay = 8

$ iio_reg ad9361-phy 0x007 → 0xa0
bits[7:4] = 10 → tx-fb-clock-delay = 10
bits[3:0] = 0 → tx-data-delay = 0

DTS change in zynq-antsdr-e310.dtsi:

Before:
adi,digital-interface-tune-skip-mode = <0>; /* run dig_tune → FAIL */
adi,tx-fb-clock-delay = <0>;
adi,tx-data-delay = <9>;
/* no rx-data-delay or rx-data-clock-delay */

After:
adi,digital-interface-tune-skip-mode = <2>; /* skip, use manual values */
adi,rx-data-clock-delay = <0>;
adi,rx-data-delay = <8>;
adi,tx-fb-clock-delay = <10>;
adi,tx-data-delay = <0>;

(skip-mode=2 means: skip both RX and TX tuning, use the DTS-specified delays)

--- Result ---

After rebuild and flash to SD card, the boot log showed:

White check mark ad9361 spi0.0: ad9361_probe : AD936x Rev 0 successfully initialized

(Note: the following kernel log message contains legacy driver terminology)
White check mark cf_axi_adc 79020000.cf-ad9361-lpc: ADI AIM (10.03.) probed ADC AD9364 as [primary]

White check mark Starting IIO Daemon: iiod

No "Tuning FAILED" message. AD9361 and cf_axi_adc probe successfully.

--------------------------------------------------------------------------------
PROBLEM 2: DMA INTERRUPT NEVER REACHES CPU [STATUS: UNRESOLVED X]
--------------------------------------------------------------------------------

After resolving the dig_tune issue, iio_readdev still fails with timeout:

$ iio_readdev -u local: -s 4096 -b 1024 cf-ad9361-lpc voltage0 voltage1
Unable to refill buffer: Connection timed out (110)

--- Investigation ---

I verified the AXI ADC core registers look healthy:

Address 0x79020000 (cf-ad9361-lpc):
 RSTN (0x040) = 0x3 → released from reset White check mark
 STATUS (0x05C) = 0x5 → ADI_STATUS bit set White check mark
 CNTRL (0x044) = 0x4 → R1_MODE (1R1T CMOS) White check mark
 CH0 CTRL (0x400) = 0x251 → FORMAT_SIGNEXT|FORMAT_EN|ENABLE|IQCOR_ENB White check mark

I also verified the DMA transfer completes successfully at the hardware level.

NOTE: There are two AXI DMACs in this system. The correct one for RX is at
0x7c400000 (from DTB: dmas = <0x1a 0x00>, dma-names = "rx").
The TX DMAC is at 0x7c420000. I initially read the wrong one.

Also NOTE: DMAC register offsets are at base+0x400 (not base+0x080):
CTRL = base + 0x400
TRANSFER_ID = base + 0x404
TRANSFER_DONE = base + 0x428
ACTIVE_ID = base + 0x42c
IRQ_MASK = base + 0x080
IRQ_PENDING = base + 0x084
IRQ_SOURCE = base + 0x088

RX DMAC (0x7c400000) registers after enabling the IIO buffer:

CTRL (0x400) = 0x1 → DMAC enabled White check mark
 TRANSFER_DONE (0x428) = 0x1 → DMA transfer COMPLETED White check mark
 IRQ_PENDING (0x084) = 0x3 → bits 0 and 1 both pending White check mark
 IRQ_SOURCE (0x088) = 0x3 → interrupt NOT masked White check mark

The DMA transfer completes. The DMAC is asserting its interrupt output.

However, the interrupt NEVER reaches the CPU:

/proc/interrupts:
31: 0 0 GIC-0 63 Level 7c400000.dma ← always 0, never increments

GIC Distributor registers:
 ISENABLER1 (0xF8F01104) bit 31 = 1 → SPI 63 is enabled at GIC White check mark
 ISPENDR1 (0xF8F01204) bit 31 = 0 → SPI 63 is NOT pending at GIC X

This means: the DMAC is asserting the interrupt signal (IRQ_SOURCE=3),
but the GIC never sees it as pending (ISPENDR=0), even though the GIC
has SPI 63 enabled.

The IIO buffer framework uses interrupt-driven DMA. When the buffer is enabled,
the kernel submits a DMA transfer and waits for the interrupt callback to mark
the buffer as "data ready". Without the interrupt firing, the buffer is never
marked ready, and iio_readdev blocks forever until timeout.

--- What I have verified ---

Bitstream (PetaLinux vs Buildroot) : MD5 identical White check mark
 dma-axi-dmac.c source (both kernels) : Identical line-by-line White check mark
 SLCR INT_MASK (0xF8000620) : 0x0 (nothing masked) White check mark
 GIC ISENABLER1 bit 31 (SPI 63) : 1 (enabled) White check mark
 GIC ISPENDR1 bit 31 : 0 (NOT pending) X
 PS interrupts (UART, Ethernet, MMC) : All working normally White check mark
 DMAC TRANSFER_DONE : 1 (transfer completes) White check mark
 DMAC IRQ_SOURCE : 3 (interrupt asserted) White check mark
 CPU IRQ count (/proc/interrupts) : 0 always X

Everything in the chain is verified correct except the signal does not appear
to cross from the PL (FPGA) to the PS (GIC) despite SLCR not masking it.

--- Hypothesis ---

The only meaningful difference remaining between Buildroot and PetaLinux is
the BOOT.BIN file, which contains:
1. FSBL (First Stage Boot Loader) — initializes PS hardware at power-on
2. Bitstream — loaded into PL
3. U-Boot

The PetaLinux FSBL is generated from the PetaLinux project's XSA file.
The Buildroot FSBL was generated from the Vivado SDK for this board.

My hypothesis is that the PetaLinux-generated FSBL uses a different ps7_init.c
configuration that does not properly enable or route the IRQ_F2P signals from
PL to PS, causing GIC SPI 63 (IRQ_F2P[2]) to never receive the assertion from
the AXI DMAC.

--- Next step I plan to try ---

Boot the board using Buildroot's BOOT.BIN (Buildroot FSBL + same bitstream +
Buildroot U-Boot) while keeping the PetaLinux kernel and rootfs on the SD card.
If the DMA interrupt starts working, this confirms the FSBL is the root cause.

--------------------------------------------------------------------------------
QUESTIONS FOR THE COMMUNITY
--------------------------------------------------------------------------------

1. Has anyone experienced PL-to-PS interrupts failing silently on Zynq-7000
where IRQ_SOURCE is asserted at the DMAC but GIC ISPENDR is never set —
specifically when using a PetaLinux-generated FSBL?

2. Are there specific ps7_init registers (beyond SLCR INT_MASK) that control
IRQ_F2P[x] routing or enable that could cause this behavior?

3. Is there a known issue or workaround for ADI AXI DMAC interrupt delivery
under PetaLinux 2023.2 on Zynq-7000 (Z7020)?

4. Would it be reasonable to patch the DMA driver to use polling on
TRANSFER_DONE (0x428) instead of waiting for the interrupt? And if so,
is there a recommended way to do this within the IIO buffer framework?

Any guidance, pointers to related threads, or suggestions would be very
much appreciated. Thank you for your time.

-FPGAStarter

Thread Notes