Recently, I've been covering various aspects of how to utilize the AXI interface utilized by Xilinx's intellectual property (IP) blocks and for the sake of making this series of project complete, I decided to throw together a project that I didn't see much guidance for elsewhere. This is the use of AXI peripherals in RTL versus the block design.
Which while I found it was possible, I would not recommend it unless absolutely necessary for some reason. There is so much automated configuration that Vivado does for you to make sure everything works well together in terms of connections of AXI interfaces and buses, that I would recommend trying to keep your AXI interfaces in the block design via the AXI IP packager tool or Add Module options I've demonstrated in my recent project posts.
Vivado ProjectI created a Vivado project targeting the Arty Z7. I'm using 2021.2, but the core of this project should easily translate to most past versions. This should also pretty easily translate to different target Xilinx-based FPGA boards as well.
As is normal protocol with Vivado projects for Zynq-based FPGAs, the first thing to do is create a new block design then add the Zynq processing system (PS) to it and run block automation to apply the target board presets (Arty Z7 in this case).
Next I added an AXI GPIO and ran connection automation just to get the AXI interconnect to be populated.
Once the AXI Interconnect structure has been populated, I deleted the AXI GPIO IP in the block design and made the Master AXI interface external so it is available to the RTL (right-click on the M00_AXI port and select Make External).
Since the Master AXI interface is interfacing to something outside of the block design, it must be manually associated with the Master AXI interface clock, M00_ACLK. Select the M00_AXI_0 port and under the Extended Interface Properties window, select M00_ACLK from the drop down menu for Clock Port.
Configure the Master AXI interface to match the configuration the AXI GPIO will needs that's going to be added in the RTL later. This configuration is normally handled automatically by Vivado when using AXI peripherals in the block design, but you must do it manually when talking to an AXI peripheral located outside of the block design in RTL. This includes settings such as address register width, if the endpoint master is expected to only produce INCR type bursts, if the endpoint master has cache, if the read response pin is present, and so on.
I'm using an AXI GPIO interface to control the four regular LEDs on the Arty Z7 board, so I will have a single channel AXI GPIO with four signals that are all outputs, and no interrupts. Thus, the configuration registers need to be configured to the following (found by selecting M00_AXI_0 port and under the Extended Interface Properties window, switch to the Properties tab and the CONFIG dropdown):
If you don't configure these AXI settings appropriately to match how you'll be calling it from the embedded application running on the Zynq PS, then everything will mostly likely hang and freeze as your embedded application is waiting for a signal on the AXI bus to respond in a certain way that the hardware isn't configured to respond to in that way.
To fully understand what settings/signals your AXI interface might need, refer to the AXI Reference Guide and AXI Register Slice User Guide.
I cheated by adding the AXI GPIO block and selecting the 4-bit LEDs on the Arty Z7 board, then looking at how Vivado configured its Master AXI interface and manually applying that to the Master AXI interface that
Create HDL WrapperNow that the block design is complete with the Master AXI interface made external to the block design for use in RTL, an HDL wrapper needs to be made to instantiate it in the project.
Unlike my other project posts, this HDL will not be auto-managed by Vivado so instead of using the Create HDL Wrapper option, I will use the View Instantiation Template to copy the block design instantiation over to an HDL wrapper I manually create.
In the Sources window, right-click on the block design file and select View Instantiation Template. This will open an HDL file showing the block design instantiation file to copy+paste.
Manually create an HDL wrapper by selecting Add Sources from the Flow Navigator and create a new file.
Copy+paster the block design instantiation over to the HDL wrapper file.
Do not however make the Master AXI interface signals external ports in the HDL wrapper. Just declare them as wire signals for now.
Add AXI GPIO in RTLTo add an AXI GPIO IP block to the RTL, open IP Catalog from the Flow Navigator and search for GPIO:
As I mentioned perviously, I'm using an AXI GPIO interface to control the four regular LEDs on the Arty Z7 board, so I will have a single channel AXI GPIO with four signals that are all outputs, and no interrupts.
The easy way to configure this for the Arty board is to just select leds_4bits in the Board tab for Board Interface for GPIO:
Select the option to generate the output products after configuring the AXI GPIO IP so Vivado can go ahead and synthesize that IP block.
You can find the instantiation template for the AXI GPIO block in the Sources window > IP Sources tab > axi_gpio > Instantiation Template. Copy+paste the AXI GPIO instantiation template into the HDL wrapper and match the Master AXI interface signals from the block design to the GPIO IP's Slave AXI interface signals:
Finally, assign the output (gpio_io_o) signals to the LED output port signal of the HDL wrapper.
Vivado ConstraintsWith the block design and HDL complete, the last thing to do before generating a bitstream is to add the constraints file with the package pinout for the 4 LEDs on the Arty Z7.
Create a new constraints file by selecting Add Sources from the Flow Navigator > Add or create constraints. Then you can copy+paste the LED pinout from the Arty Z7 master constraints file here.
Generate BitstreamRun synthesis, implementation (place & route), and generate a bitstream for the design. You can select Generate Bitstream from the Flow Navigator and it will automatically run synthesis and implementation before generating a bitstream.
I got these critical warnings about the parameter BOARD_PIN assignment that didn't make any sense. It wanted me to tie the output port pins for the LEDs to the trigger-state buffer select signals from the AXI GPIO IP block, which clearly isn't right. So I just ignored these critical warnings...
Export HardwareOnce the bitstream has been generated, export the hardware for use in Vitis by selecting File > Export > Export Hardware...
Be sure to select the option to include the bitstream in the exported hardware platform.
Launch Vitis (Tools > Launch Vitis IDE) and start by creating a new platform project based on the hardware platform exported from Vivado:
After creating the platform project, build it the create a new application project by selecting New > Application Project. Base the application project on the platform project just created and use the Hello World application template.
Now this is the next downfall of using AXI peripherals in RTL versus the block design: all C source files in Vitis have to be manually created and tied to the Master AXI interface base address specified in xparameters.h
Again, I cheated by copying the AXI GPIO source files from another Vitis workspace where the hardware platform did have an AXI GPIO IP in the block design. I only had to make a few modifications to manually change a couple of things such as the XGpio Configuration pointer, but otherwise the code was mostly the same for blinking an LED as an application project with an AXI GPIO IP in the block design.
I have attached the modified AXI GPIO source files along with the blink LED main function below that I imported into the application project.
Import AXI GPIO files by right-clicking on the src folder in the application project in the Explorer window and selecting Import Sources...
Point to the directory where the source files are located and copy them into the current application project.
I also threw an ILA on the Slave AXI interface of the AXI GPIO block in RTL and the LED output signals so I could verify my settings were correct in the AXI transactions (thus how I figured out that the configuration registers need to match to avoid an AXI transaction hanging on the bus).
After launching a debug run on hardware and opening the Hardware Manager in Vivado with a trigger on M00_AXI_0_awvalid I could see the LED being toggled on and off.
Led on:
Led off:
And the LED is indeed alive:
As I mentioned before, I would first try to add an AXI wrapper to my custom RTL with the IP packager in Vivado or adding it as a module directly to the block design prior to using this method as it requires so much extra manual configuration. However, it is still an option for situations where the previous two options aren't viable.
Comments