Zynq Ultrascal+ RF data Converter to DMA

  • PYNQ version & Board name & Tool Version
    pynq version 2.7
    board name: ZCU111

I am trying to transfer data into the dac and receive back in the adc. However, the data does not come back. it is random data that is being store at the dma. I did an overlay with the dma along, and it works fine. it does not work when i add the data converters.
here is the dma setup:


ADC setup:

DAC setup:

overlay setup.pdf (135.8 KB)

code used:

from pynq import Overlay
from pynq import allocate

import numpy as np

import xrfclk
import xrfdc

# Set the reference clock frequency
xrfclk.set_all_ref_clks(409.6)

# Load the overlay
ol = Overlay('/home/xilinx/jupyter_notebooks/Own_experiments/assets/rf_data_wrapper1.bit')

# DMA RX
dma_read = ol.axi_dma_0.recvchannel

# DAC setup
dac_tile = ol.usp_rf_data_converter_0.dac_tiles[1]
dac_tile.DynamicPLLConfig(1, 409.6, 1228.8)
dac_block = dac_tile.blocks[2]

event = xrfdc.EVNT_SRC_TILE
dac_block.MixerSettings['Freq'] = 900
dac_block.MixerSettings['PhaseOffset'] = 0.0
dac_block.MixerSettings['EventSource'] = event
dac_block.UpdateEvent(event)

dac_tile.SetupFIFO(True)

# DMA TX
dma_write = ol.axi_dma_0.sendchannel

# ADC setup
adc_tile = ol.usp_rf_data_converter_0.adc_tiles[0]
adc_block = adc_tile.blocks[0]

event = xrfdc.EVNT_SRC_TILE
adc_block.MixerSettings['Freq'] = 900
adc_block.MixerSettings['PhaseOffset'] = 0.0
adc_block.MixerSettings['EventSource'] = event
adc_block.UpdateEvent(event)

adc_tile.SetupFIFO(True)

# Buffer length and sine wave parameters
buffer_length = 1500
amplitude = 32767  # Half of the maximum value for a 16-bit DAC
frequency = 250
sample_rate = 10000

# Generate time values
t = np.arange(0, buffer_length/sample_rate, 1/sample_rate)

sine_wave = amplitude * np.sin(2 * np.pi * frequency * t)

# Allocate buffers for DMA transfer
input_buffer = allocate(shape=(buffer_length,), dtype=np.float32)
output_buffer = allocate(shape=(buffer_length,), dtype=np.float32)


# # Copy the scaled data to the input buffer
# input_buffer[:] = scaled_data
# Copy the sine wave to the input buffer
input_buffer[:] = sine_wave
# Start DMA transfers
dma_write.start()
dma_read.start()
dma_write.transfer(input_buffer)
dma_read.transfer(output_buffer)
dma_write.wait()
dma_read.wait()

for i in range(10):
    print(output_buffer[i])
I try sending fix data, and it doesn't work either. 

if anyone can help me i will appreciated.
2 Likes

You shouldn’t use a float32 type for this.

`input_buffer = allocate(shape=(buffer_length,), dtype=np.float32)`

The data will be transferred to the PL and the ADC as a uint.

I’m also noting that you generate a signal at ~4GSPS, but sample at ~2GSPS.

It looks like you generate a real sinewave, but have selected I/Q in the RFDC ADC.
For the DAC you have Real → I/Q, and don’t connect the second AXI stream with the Q values.

I’d suggest you test with Real values first.

You will also need to generate the TLAST signal for the AXI stream coming from the DAC.
See this post: https://support.xilinx.com/s/question/0D52E00006iHq9TSAS/tlast-signal-with-zynq-ultrascale-rfsoc-rfdata-converter-on-zcu111?language=en_US

If you don’t do this, you will get a DMA error (DMAIntErr). You may already be getting this error with your DMA. Try check

ol.axi_dma_0.register_map

Cathal

1 Like

this is the output i get when I run ol.axi_dma_0.register_map


RegisterMap {
  MM2S_DMACR = Register(RS=0, Reset=0, Keyhole=0, Cyclic_BD_Enable=0, IOC_IrqEn=1, Dly_IrqEn=0, Err_IrqEn=0, IRQThreshold=1, IRQDelay=0),
  MM2S_DMASR = Register(Halted=1, Idle=0, SGIncld=0, DMAIntErr=0, DMASlvErr=0, DMADecErr=0, SGIntErr=0, SGSlvErr=0, SGDecErr=0, IOC_Irq=1, Dly_Irq=0, Err_Irq=0, IRQThresholdSts=0, IRQDelaySts=0),
  MM2S_CURDESC = Register(Current_Descriptor_Pointer=0),
  MM2S_CURDESC_MSB = Register(Current_Descriptor_Pointer=0),
  MM2S_TAILDESC = Register(Tail_Descriptor_Pointer=0),
  MM2S_TAILDESC_MSB = Register(Tail_Descriptor_Pointer=0),
  MM2S_SA = Register(Source_Address=2017181696),
  MM2S_SA_MSB = Register(Source_Address=0),
  MM2S_LENGTH = Register(Length=12000),
  SG_CTL = Register(SG_CACHE=0, SG_USER=0),
  S2MM_DMACR = Register(RS=0, Reset=0, Keyhole=0, Cyclic_BD_Enable=0, IOC_IrqEn=0, Dly_IrqEn=0, Err_IrqEn=0, IRQThreshold=1, IRQDelay=0),
  S2MM_DMASR = Register(Halted=1, Idle=0, SGIncld=0, DMAIntErr=1, DMASlvErr=0, DMADecErr=0, SGIntErr=0, SGSlvErr=0, SGDecErr=0, IOC_Irq=1, Dly_Irq=0, Err_Irq=1, IRQThresholdSts=0, IRQDelaySts=0),
  S2MM_CURDESC = Register(Current_Descriptor_Pointer=0),
  S2MM_CURDESC_MSB = Register(Current_Descriptor_Pointer=0),
  S2MM_TAILDESC = Register(Tail_Descriptor_Pointer=0),
  S2MM_TAILDESC_MSB = Register(Tail_Descriptor_Pointer=0),
  S2MM_DA = Register(Destination_Address=2017280000),
  S2MM_DA_MSB = Register(Destination_Address=0),
  S2MM_LENGTH = Register(Length=12000)
}

I did some changes to the data converters now to be a generated signal ~2GSPS for both the DAC and ADC. I do not understand the concept of the TLAST what I did was implement a AXI4-Stream DATA FIFO and enable the TLAST there:
new connection
M_AXIS_MM2S->axis_data_fifo_0->s12_axis
here is the setup:


new connection
m00_axis->axis_data_fifo_1-> S_AXIS_S2MM
here is the setup:

best,
JB

1 Like

I do not understand the concept of the TLAST what I did was implement a AXI4-Stream DATA FIFO and enable the TLAST there

This will only “pass through” TLAST. As there is no TLAST signal coming from the source, this will have no effect.
You need to manually generate a TLAST signal. For example, you could create a new IP that acts as a counter and generates a pulse after a certain number of valid signals. You would use the “pulse” as the TLAST signal.

It may help to look at this example design;
https://github.com/strath-sdr/pynq_nco/

In particular the axis_packet_controller IP
https://github.com/strath-sdr/pynq_nco/blob/main/boards/ip/iprepo/axis_packet_controller/hdl/tlast_generation.vhd

Cathal

2 Likes