RFSoC ZCU111 Generate Custom Data in PS to send to PL RF Data Converter

Hello all,

I have a ZCU111 eval board and Vivado 2018.3. I have been able to successfully run the examples in the RFSoC Workshop git repo. I am trying to build something that is very similar, but essentially it allows me to send custom data to the RF data converter. I have written some code in python to generate a custom modulated message (sent continuously). This works in the pynq notebook that I have created. Now I want to create an overlay that will allow me to write data to the rf data converter. Based on my reading it looks like I need to write to a DMA and DDR4 memory so that the rf data converter can continuously grab data from the buffer. I made a block design in Vivado that I think should allow me to do this based (1.9 MB) n the tutorials I followed on this site. I have attached them for review.

I was able to import the overlay, but now I am confused on how to write the data to the memory to be transmitted out. Has anyone done this or can anyone help point me in the right direction?

Thank you for your time!!


Hello Sarah,
Did you find an answer to your problem, because i’m trying to do the same thing and i’m blocked.

Thank you,

Hi imad,

Unfortunately, no I have not yet been able to find the answer. I will update you once I have.


Hi Sarah,

First of all, I’m sorry for the late reply. I’ve been really busy lately and haven’t had a chance to look at this.

I have a few notes on your design in terms of PYNQ and our own QPSK design, and some follow up questions for you to help clarify what it is you’re trying to do.

In the block design tcl file you supplied you are using a PL DDR4 MIG IP. Is there a particular reason you want to use this as opposed to the PS DRAM? I haven’t used the DDR4 MIG IP before so am unsure about what the setup would look like for this. PS DRAM is the method we use to communicate via AXI-Stream in our own QPSK demo using the DMA IP. What example on this site did you base your design on?

On the subject of DMAs: Scatter Gather is not currently supported by PYNQ so you would need to disable this in order to use the DMA with PYNQ.

The xrfclk driver only supports certain PLL reference clock frequencies. There is a function in the xrfclk driver that will output the reference clock frequencies available to you. I don’t think the one you have picked (400 MHz) is supported. This frequency shouldn’t have any impact on your design therefore, if you pick a supported one should have the same effect. There is also a method to send your own register values to the clocks to set custom frequencies (this is only in the PYNQ 2.4.1 image), but this is not a particularly user-friendly option.

The Data Converter IP settings in your design show that you are using the DAC-C2C-Multi-2x2* preset. What are your reasons for using this? In your design you have no connections made to the 3 AXI-Stream inputs on the Data Converter IP, so no data would be able to get to the DACs. The AXI-Lite interface is used for configuration.

Generally, you would use PYNQ to write to memory using a DMA, and this DMA IP would be connected to a DAC using the respective IP AXI-Stream interfaces. If this was the approach you wanted to take, you could use 3 DMAs to send data to each of the 3 DAC inputs you have instantiated. Due to PS restrictions, PYNQ can only write to one DMA at a time though, meaning there would be idle time between each DMA transaction. Also, there’s not neceassrily a guarantee from Linux that data could be continuously sent to the DMAs. In our QPSK design we generated our random symbols on the PL so we avoided this obstacle.

If you did want to write continuous, looped data to the DACs, one option would be to use BRAMs on the PL filled with the data you generated in Python, then cycle through the BRAM address space using a counter. There is a ‘Single/Dual Port Ram’ IP in System Generator that would make this fairly easy to set up (although you would have to design the AXI-Stream Master signals yourself). You could then use PYNQ to write the data to this System Generator IP, with an AXI-Lite interface to control the counter and RAM blocks.

You can go to our GitHub repo for the QPSK design and look at how we put that together (we provide all our design files in there including System Generator and Vivado project files).

I will be able to look at this again in a week or two if you can provide a bit more information (and if you still need help of course).


Hello Josh,

I want to calculate the bit error rate for the QPSK design, I compared the sended and the received data and they are not synchronized.If you can help me with this it will be very kind of you.

Thank you for your reading.

Hi imad,

How are you getting the Tx and Rx data to measure BER? If you are using data from the ‘taps’ outputs then you will not be able to get an accurate result (i.e. if you are using Python to calculate BER).

The data from the taps is not guaranteed to be contiguous because of the way we have set up the System Generator IPs. Also, because the Tx IP is a monolithic block, containing all the stages from symbol creation to pulse shaping and interpolation, you would need to redesign the IP yourself to output a contiguous stream of unfiltered symbols - then test that against the output of the qpsk_rx_tsync IP.

The SD-FEC demo uses a similar method to test BER, so make sure to check out the IPI block design for that as well (you will need to create a license for youself in order to rebuild the design).

I’d also like to add that, because our demo is meant to be used with a cabled loopback (with no channel model), I’d be surprised if there were many errors after synchronisation.


Thanks for your reply Josh,i have another question,i was wondering if it’s possible to send my own data and use more dac and adc just by changing a few line of the python code which come in the QPSK or SD-FEC design.

Thank you very much in advance,


Hi Josh,

I will ask my question in reply to this answer, since it is related.
I am doing a similar example to Sara and for that I am using DMA scatter-gather mode that I have configured myself. My data is pre-caclulated and I only want to play it in a cyclic fashion on the DAC.
In python, I want to define a buffer in DDR with address that is 1) physically continuous, and 2) aligned to my DAM axi data-width (here is 128), since this is essential for the scatter-gather cyclic mode. I use xlnk.cma_array for this purpose, however, I cannot get addresses which are aligned with data width of 128.
Do you have a solution for it?