The AXI-Streaming interface is important for designs that need to process a stream of data, such as samples coming from an ADC, or images coming from a camera. In this tutorial, we go through the steps to create a custom IP in Vivado with both a slave and master AXI-Streaming interface. The custom IP will be written in Verilog and it will simply buffer the incoming data at the slave interface and make it available at the master interface – in other words, it will be a FIFO. We’ll test the custom IP using a DMA which we’ll use to push streaming data into the IP and pull data out of the IP. We’ll use an SDK application to setup these DMA transfers and compare the sent data with the received data. The hardware we use for testing this will be the MicroZed 7010, so this is a Zynq-7000 design.
The above image is a basic block diagram of our Vivado design, it shows how the DMA connects to the Zynq Processing System, and also how the custom IP connects to the AXI-Streaming interfaces of the DMA. If you are not familiar with the DMA IP, you should checkout this tutorial on using the DMA.
Source code for the custom IP
The Verilog code for our custom IP is based on an asynchronous AXI-Streaming FIFO written by Alex Forencich. You can find the original code on his Github repo, as well as a bunch of other useful modules. I’ve had to slightly modify the code for this project and you’ll be able to copy and paste it from below:
Remember, when you create the custom IP, Vivado will auto-generate a top level wrapper (filename is axis_fifo_v1_0.v) and some code to drive the slave and master AXI-Streaming interfaces. You’ll have to paste the above code over the top module source code (axis_fifo_v1_0.v) of the auto-generated IP. The other two auto-generated source files can be left as they are – they will be removed from the hierarchy as soon as you replace and save the top module code, because they will no longer be instantiated by the top module.
MicroZed Board Preset issue
When building our Vivado design, just after generating a HDL wrapper for the block design, you will see some critical warnings related to timing of the DDR interface. These critical warnings can be ignored and they are related to some values in the board files. See this forum post for more information:
The test application for SDK
We test the custom IP by making the DMA push data through the AXI-Streaming slave interface and to pull data out of the AXI-Streaming master interface of our custom IP. The application we will use for this is one of the example applications for the DMA that can be found in the Xilinx SDK installation files. You will find it on this path:
In this tutorial, we use the scatter gather poll example (xaxidma_example_sg_poll.c), but as we hooked up the interrupts in the Vivado design, we could have also used the interrupt based one (xaxidma_example_sg_intr.c).
What to try
Once you’ve gotten this working, I suggest you try modifying the test application in the SDK to print out what is actually being sent and received. You could then modify your Verilog code to do some kind of manipulation of the incoming data, rebuild everything and verify with your test application that the data coming out is what you expected. Another useful thing to do when building custom IP blocks like this is to write a test bench and simulate the custom IP, this will be the topic of a future tutorial.