PYNQ3.0.1 RFDC Output Bad

Hello,

I just upgraded my ZCU111 from PYNQ2.7 to PYNQ3.0.1 and I can’t figure out why my RFDC DAC output looks bad. I have a design built using Vivado 2022.1 that uses an HLS URAM block to store a waveform which is played out of the DAC. We have a driver which writes values from Python to the URAM. I am using the same .bit and .hwh and HLS driver. It works great in PYNQ2.7-- I can generate a single sine wave an get a nice tone out of the DAC. In PYNQ3.0.1 this is what I’m getting on a Spectrum Analyzer:

At first I thought maybe it was a clocking issue. I used xrfclk to program the PLLs to the correct value (I have 409.6 set as the PLL reference in Vivado).

xrfclk.set_ref_clks(lmk_freq=122.88, lmx_freq=409.6)

I can see the PLL LEDs come on when I run this. Here is the output of my clock dict:

{0: {'enable': 1, 'divisor0': 15, 'divisor1': 1},
 1: {'enable': 0, 'divisor0': 4, 'divisor1': 1},
 2: {'enable': 0, 'divisor0': 4, 'divisor1': 1},
 3: {'enable': 0, 'divisor0': 4, 'divisor1': 1}}

I’m sure they are programmed because I can run the rest of my design without the board hanging.

Here is some sample python including the driver code internals:

import pynq
import numpy as np
import xrfclk


xrfclk.set_ref_clks(lmk_freq=122.88, lmx_freq=409.6)

## Generate a sine wave
fs = 4.096e9 # DAC Samle Rate
n = 2**19 # URAM Table is 2**19 complex samples
freq = 300e6 # Hz
freq_res = fs / n # DAC frequency resolution
quant_freq = np.round(freq / freq_res) * freq_res # quantize frequency
t = 2 * np.pi * np.arange(n)/fs
sine_wave = 1*np.exp(1j * t * quant_freq)

## Prepare values for the HLS IP Run Config
data = (sine_wave*8192).round().clip(-8192, 8191)*16 # quantize data
replay_len = data.size // 16
tlast_every = int(256)
tlast= True

## Fill Buffer with Samples in Proper Order
buffer = pynq.allocate(2 ** 20, dtype=np.uint16)
iload = data.real.astype(np.int16)
qload = data.imag.astype(np.int16)
for i in range(16):
        buffer[i:data.size * 2:32] = iload[i::16]
        buffer[i+16:data.size * 2:32] = qload[i::16]

## Update DAC Table Register Map with Values
ol.dac_table.register_map.a_1 = buffer.device_address
ol.dac_table.register_map.length_r = replay_len - 1  # length counter
ol.dac_table.register_map.tlast = bool(tlast)
ol.dac_table.register_map.replay_length = tlast_every-1
ol.dac_table.register_map.run = 1
ol.dac_table.register_map.CTRL.AP_START = 1 # start

I downloaded the PYNQ2.6 registers.py so I can read the “write-only” registers in the dac_table register map and the values look correct:

RegisterMap {
  CTRL = Register(AP_START=1, AP_DONE=0, AP_IDLE=0, AP_READY=0, RESERVED_1=0, AUTO_RESTART=0, RESERVED_2=0, RESERVED_3=0, RESERVED_4=0),
  GIER = Register(Enable=0, RESERVED=0),
  IP_IER = Register(CHAN0_INT_EN=0, CHAN1_INT_EN=0, RESERVED=0),
  IP_ISR = Register(CHAN0_INT_ST=0, CHAN1_INT_ST=0, RESERVED=0),
  a_1 = Register(a=2017460224),
  a_2 = Register(a=0),
  length_r = Register(length_r=32767, RESERVED=0),
  tlast = Register(tlast=1, RESERVED=0),
  replay_length = Register(replay_length=255, RESERVED=0),
  run = Register(run=1, RESERVED=0)
}

I’ve been looking at the change log and commit history for PYNQ2.7 → PYNQ3.0.1 and nothing is jumping out at me that might relate to this.

If anyone has further suggestions or debugging ideas please let me know!

Thanks,
Jenny

Test .hwh and .bit attached.
test.zip (6.1 MB)

1 Like

Hi Jenny,

Are the results you see consistent, including from a fresh boot?

One thing that could give some useful info is dmesg log, it would let us know if there’s a memory issue again or something else in the system is failing causing things in python land to fail silently…

You running as a script over ssh or in jupyter? Could try to run your code in separate cells or add some sleeps between clock programming, allocations, register writing, in case there’s some weird race condition going on.

I’m reaching here a bit, but as a sanity check have you tried plotting the sinewave just before it gets loaded into the DAC? It doesn’t seem like you’re using anything exotic, but it is newer python and numpy versions in the v3.0 image.

Thanks
Shawn

2 Likes

Hi, I don’t have enough data to offer any precise solution here.

But I can say I’ve had HLS not generate correct logic before. The C-test bench was perfect but the logic in the part did not behave the same. It was for some conditional triggering type of logic. Most likely it was some optimization gojng on that I needed to add a pragma somewhere for to make it behave correctly. I ended up re-writing the code in a different manner to work around it. Really undermined my confidence in HLS, up till then it was flawless. I used the ILA to finally convince myself that the logic was flawed.

Your saying it looks like the software is doing it’s job correctly. Can you add the ILA to your own design and take a peek inside at some of the logic? Maybe gather some of those samples going into the DAC and work back from there?

Sorry can’t offer anymore solution than that at this time.

-Fred

2 Likes

My bad everyone it was a simple quantization error. You’re right @stf I thought I checked the waveform before it was loaded to the URAM but I didn’t look close enough.

This is not correct:

data = (sine_wave*8192).round().clip(-8192, 8191)*16

It should be

data = (sine_wave*8192).round().clip(-8192, 8191)*4

Now everything is working :blush:. Apologies for the false alarm I forgot the test that change before I upgraded. Thanks for the input regardless.

Best,
Jenny

2 Likes