Usage of python bindings to xrfdc_functions.c

I’d like to make use of some of the python bindings to the C functions defined in xrfdc_functions.c. For example, the RFdcAdcBlock class has a member function GetCalCoefficients(self, CalibrationBlock, *CoeffPtr)which is bound to the C function u32 XRFdc_GetCalCoefficients(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, u32>
XRFdc_Calibration_Coefficients *CoeffPtr), but in this case I’m unsure of the canonical approach to constructing and passing the pointer *CoeffPtr to obtain the coefficients (I’m somewhat familiar with ctypes but am new to cffi).

Hi @ashwmk

You don’t need to do this directly with the xrfdc C functions. PYNQ abstracts this from the user, leaving simple interfaces for you to deal with.

Just by importing the xrfdc module, when you create an Overlay object, PYNQ will bind the xrfdc driver to the RF data converter IP in your design.

For example,

from pynq import Overlay
from pynq import xrfdc
from pynq import xrfclk

xrfclk.set_ref_clks()
ol = Overlay('your_bitstream.bit')
rfdc = ol.rfdc
rfdc.adc[0].block[0].GetCalCoefficients()

As you can see here, PYNQ deals with the C function parameters itself. rfdc contains the InstancePtr, and adc[0] and block[0] contain the Tile_Id and Block_Id, respectively. These are passed to the GetCalCoefficients() function in the background.

Again, you don’t need to pass these directly, PYNQ does this for you. All you need to do is make sure you’re pointing to the correct tile and block. The mapping for these depends on your board, but you should be able to figure it out from the board documentation. For example, the lowest ADC tile number will point to adc[0].

I’d recommend looking through the free RFSoc Book and supporting materials for more details on how to use the xrfdc driver.

1 Like

I must be running an older version of pynq, because I get an ImportError at from pynq import xrfdc. Also, rfdc does not appear as a member of my overlay, which is created from the base.bit bitstream. For reference, I’m running version 3.0.1 of pynq. What version is yours?

RFSoC-PYNQ supports both v3.0.1 and v3.1 versions of PYNQ. The xrfdc package should be available in the SD card images for both boards.

What board are you targeting? PYNQ v3.1 supports RFSoC4x2, ZCU208, and ZCU111. Images for these board can be found here: PYNQ supported boards and PYNQ pre-built images | PYNQ

I’m targeting the RFSoC 4x2. If there isn’t a good way to do what you’ve described above in v3.0.1, I’ll try setting things up with v3.1 and report back.

Okay, I’ve booted from the PYNQ 3.1.1 image, but I still can’t run from pynq import xrfdc:

In [1]: from pynq import Overlay

In [2]: from pynq import xrfdc

ImportError                               Traceback (most recent call last)
Input In [2], in <cell line: 1>()
----> 1 from pynq import xrfdc

ImportError: cannot import name ‘xrfdc’ from ‘pynq’ (/home/xilinx/pynq/init.py)

In [3]: import pynq

In [4]: pynq.__version__
Out[4]: ‘3.1.1’

from pynq import xrfclk fails similarly.

Hi @ashwmk,

For clarification, where are you running this code? Jupyter notebook? Terminal? Python Script?

Are you able to use the BaseOverlay?

Mario

The above was executed as root in an ipython console after sourcing the pynq environment:

xilinx@pynq:~$ su
Password:
root@pynq:/home/xilinx# source /etc/profile.d/pynq_venv.sh
(pynq-venv) root@pynq:/home/xilinx# ipython

Can you please try inside a Jupyter Notebook or from the terminal from Jupyter Lab?

I got the same result when run from a Jupyter Notebook.

Also, to answer your earlier question about using the base overlay, I have tried that, but I cannot use the contained rfdc object in the way that @joshgoldsmith described above:

from pynq.overlays.base import BaseOverlay

base = BaseOverlay('base.bit')

rfdc = base.radio.rfdc

rfdc.adc[0].block[0].GetCalCoefficients()

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [9], in <cell line: 1>()
----> 1 rfdc.adc[0].block[0].GetCalCoefficients()

AttributeError: 'RFdc' object has no attribute 'adc'

For the record, I was able to get this to work by constructing a XRFdc_Calibration_Coefficients * pointer using the xrfdc._ffi object:

from pynq.overlays.base import BaseOverlay
import xrfdc
base = BaseOverlay('base.bit')
coefs = xrfdc._ffi.new('XRFdc_Calibration_Coefficients *')
base.radio.receiver.channel_00.adc_block.GetCalCoefficients(0,coefs)
coefs.Coeff0
4291821514

But if this isn’t the preferred way of doing this, then we should probably keep this thread open.

Hi @ashwmk

Apologies, I got the code snippet wrong before. It should be import xrfdc not from pynq import xrfdc. It also seems that you do need a pointer for this specific function to work. It’s not one I’ve ever used before, so I missed that.

I’ve made a MWE for you that will print out the calibration coefficients for you below using the RFSoC4x2 base overlay:

from pynq.overlays.base import BaseOverlay
import xrfdc

ol = BaseOverlay("base.bit")

ADC_TILE = 0
ADC_BLOCK = 0
adc = ol.radio.rfdc.adc_tiles[ADC_TILE].blocks[ADC_BLOCK]

cal_blocks = ['OCB1', 'OCB2', 'GCB', 'TSCB']

for cal_block in range(len(cal_blocks)):
    coeff_struct = xrfdc._ffi.new("XRFdc_Calibration_Coefficients*")
    adc.GetCalCoefficients(cal_block, coeff_struct)
    coeffs = xrfdc._unpack_value(coeff_struct)
    print(f"{cal_blocks[cal_block]}: {coeffs}")

Just replace the ADC_TILE and ADC_BLOCK variables to the respective tile and block you are interested in.

This is based on the XRFdc_GetCalCoefficients documentation in PG269.

Thanks, @joshgoldsmith, that worked for me. I had a feeling the helper functions defined in xrfdc would be useful, but couldn’t find any examples of their usage in the PYNQ documentation.

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.