Continuing on from where I left off in Path II Programmable Blog 10 - Project Part 1 - Exploring PYNQ with the Ultra96v2 ...

 

IP to capture waveforms to, and generate waveforms from BRAM:

 

I created a new IP that can capture data from an input interface to BRAM, and transfer data from BRAM to an output interface. I won't go into too many details about how it works on the inside, but it's pretty simple:

  • An AXI-4 Lite slave interface that exposes 3 registers: 3 inputs (PS to IP) and 1 output (a IP to PS status register).
  • 3 FSMs: one main FSM for controlling the state (reset, input or output modes), and one each for the input & output modes. These 2 FSMs go through 3 states: IDLE, ACTIVE (where the counter is running) and DONE.

 

The PS can configure the number of samples to capture/generate to/from BRAM.

 

I wrote a testbench that puts the IP in different states to see whether it worked:

 

Imported the RTL into the Block Design, connected it to the second BRAM port, and also connected the ILA to help me debug. The 'interface_dxxx' are the input & output interfaces.

 

 

With all of this complete, I exported the bitstream and block_design.tcl to my Jupyter notebook. My first attempt at running it did not work, so I tried to use the ILA to debug. However, the PYNQ image wouldn't boot with the JTAG dongle connected, so I exported the hardware wrapper to Xilinx SDK and created a new project. I converted the testbench code into C, and executed the program - launching the program in Xilinx SDK allowed me to simultaneously use the Vivado ILA and fix one or two bugs.

With the hardware fixed, I booted up the PYNQ image, imported the bitstream & TCL file and wrote some Python that used the MMIO driver to access the IP.

 

I filled the memory-mapped BRAM with some data, put my IP into output mode and triggered it. The Vivado ILA shows a pattern being generated:

 

With all of the code added to the Jupyter notebook, here's what it looks like when you trigger a capture: the data is saved to BRAM, read back and plotted using matplotlib.

 

This completes my Path II Programmable project!

I don't plan on stopping here - I want to figure out how to get the R5s & A53s to talk, and explore more of the MMIO driver. I've looked at using the AXI DMA IP to transfer data from the BRAM to DDR4 (and vice-versa), so stay tuned for more!