This tutorial shows you how to generate a custom AXI4 IP with burst functionality in Vivado and how to connect it to the HP Port of the Zynq PS on the Zedboard and simulate it with Vivado xsim
.
- Vivado 2016.2
- Zedboard
-
Start Vivado.
-
Choose "Create New Project" in the Vivado welcome menu.
-
Click Next >.
-
Choose a name for your project and a location. The project name in this tutorial is
axi4_master_burst_example
. -
Choose RTL Project.
-
Don't add any HDL sources or netlists.
-
Don't add any existing IP components.
-
Don't add any constraints.
-
Choose Boards and select Zedboard Zynq Evaluation and Development Kit.
-
Click "Finish" to create the Vivado project.
-
Open: Menu -> Tools -> Create and Package IP.
-
Click Next >.
-
Choose Create a new AXI4 peripheral.
-
Choose a name, description and location for the new AXI4 peripheral. The name in this tutorial is
axi4_master_burst
and the location is[...]/ip_repo
. -
Keep the AXI4-Lite Slave interface.
-
Click on the green plus sign to add another interface.
-
The added interface should be an AXI4-Full master interface.
-
Choose Edit IP and click on Finish.
After the successful creation of the new IP a new Vivado project was opened. In this project you can find the Vivado generated Verilog code for the AXI4-Lite slave, AXI4-Full master, and a top module (wrapper) which contains those two components.
axi4_master_burst_v1_0
contains the top module.axi4_master_burst_v1_0_S00_AXI_inst
contains the Verilog code for the AXI4-Lite slave.axi4_master_burst_v1_0_M00_AXI_inst
contains the Verilog code for the AXI4-Full master.
The AXI4-Lite slave will be used to start and monitor a burst write/read of the AXI4-Full master from the Zynq PS. In order to do that you have to customize the AXI4-Lite slave a little.
Double-click on axi4_master_burst_v1_0_S00_AXI_inst
and navigate to the ports definition and add your own ports under // Users to add ports here
.
// Users to add ports here
output wire init_txn,
input wire txn_done,
input wire txn_error,
// User ports ends
The output wire init_txn
will later be connected to the AXI4-Full master to start a burst write/read. The input wires txn_done
and txn_error
will also be connected to the master and indicate if a write/read transaction was completed and if errors occured during a write/read transaction.
The output wire init_txn
will be connected to the slv_reg0
of the AXI4-Lite slave. Navigate to // Add user logic here
and add the following:
// Add user logic here
assign init_txn = slv_reg0[0:0];
// User logic ends
The input wires txn_done
and txn_error
are directly connected to the slv_reg1
and slv_reg2
registers of the AXI4-Lite slave. Navigate to // Address decoding for reading registers
and change the code like this:
// Implement memory mapped register select and read logic generation
// Slave register read enable is asserted when valid address is available
// and the slave is ready to accept the read address.
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= {{31{1'b0}},txn_done};
2'h2 : reg_data_out <= {{31{1'b0}},txn_error};
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
end
With those changes the Zynq PS is able to start a write/read transaction by setting the LSB of slv_reg0
to 1
and is also able to read from the LSB of slv_reg1
and slv_reg2
to see if the write/read transaction was completed successfully.
The newly added ports of the AXI4-Lite slave also have to be added to the module instantiation in the top module axi4_master_burst_v1_0
. Double click on axi4_master_burst_v1_0
and navigate to // Instantiation of Axi Bus Interface S00_AXI
and add the new ports to the port map:
// Instantiation of Axi Bus Interface S00_AXI
axi4_master_burst_v1_0_S00_AXI # (
.C_S_AXI_DATA_WIDTH(C_S00_AXI_DATA_WIDTH),
.C_S_AXI_ADDR_WIDTH(C_S00_AXI_ADDR_WIDTH)
) axi4_master_burst_v1_0_S00_AXI_inst (
.init_txn(m00_axi_init_axi_txn),
.txn_done(m00_axi_txn_done),
.txn_error(m00_axi_error),
Currently the wires m00_*
are output or input ports of the top module. Those have to be removed from the interface and added as wire in the top module. To do that navigate in axi4_master_burst_v1_0
to // Ports of Axi Master Bus Interface M00_AXI
and remove the following lines:
input wire m00_axi_init_axi_txn,
output wire m00_axi_txn_done,
output wire m00_axi_error,
Under the interface definition of the top module axi4_master_burst_v1_0
add the following lines:
wire m00_axi_init_axi_txn;
wire m00_axi_txn_done;
wire m00_axi_error;
Those m00_*
wires are connecting the the AXI4-Full master and the AXI4-Lite slave in the top module.
When you look at the verilog code of the AXI4-Full master in axi4_master_burst_v1_0_M00_AXI_inst
you will see a lot of finite state machines which handle the AXI4 transactions. One of those finite state machine is responsible for the overall functionality of the AXI4-Full master. You can find this state machine when you navigate to //implement master command interface state machine
. This state machine consists of four states:
IDLE
INIT_WRITE
INIT_READ
INIT_COMPARE
Initially the AXI4-Full master is in IDLE
until the init_txn_pulse
is set to high (this will be done by the AXI4-Lite slave slv_reg0
).
Then the AXI4-Full master will start to write C_NO_BURSTS_REQ
bursts consisting of C_M_AXI_BURST_LEN
data values to memory address C_M_TARGET_SLAVE_BASE_ADDR
.
After the burst writes are completed the AXI4-Full master starts burst reads on the same memory addresses and compares the read values to the previously written values.
If the comparison was successful the master goes back into IDLE
and waits for the next high signal on init_txn_pulse
. To complete the changes in the verilog code do the following:
-
Click on the tab Package IP - axi4_master_burst.
-
Click on Customization Parameters in Packaging Steps.
-
Click on Merge changes from Customization Parameters Wizard.
-
Click on Review and Package in Packaging Steps.
-
To finish the work on your custom AXI4 IP click on Re-Package IP.
-
Quit the project by click on Yes.
You can go back to the Verilog code by clicking on Flow Navigator -> Project Manager -> IP Catalog.
And navigate to User Repository -> AXI Peripheral -> axi4_master_burst_v1.0 and right-click to open the context menu an choose Edit in IP Packager.
That your custom AXI4 IP can be implemented on the Zynq PL and connected to the Zynq PS you have to create a block diagram in Vivado. The following steps will show you how to do that:
-
Click on Flow Navigator -> IP Integrator -> Create Block Diagram.
-
Choose a name, directory, and specify a source set for the block diagram. In this tutorial everything stays at the default.
-
Right-click on the white background of the Diagram tab and choose Add IP.
-
From the list of IPs choose ZYNQ7 Processing System (this is the Zynq PS) and double-click on it.
-
You can now see the Zynq PS in the block diagram. Click on Run Block Automation to connect the Zynq PS with the memory.
-
Leave everything at the default values and click on OK.
-
To connect the your custom AXI4 IP to the Zynq PS the Zynq PS needs an AXI4-Full slave high performance port. To enable this port double-click on the Zynq PS in the block diagram.
-
In the Re-customize IP window go to Page Navigator -> PS-PL Configuration.
-
Go to PS-PL Configuration -> HP Slave AXI Interface and check S AXI HP0 interface and click on OK.
-
In the next step add your custom AXI4 IP by right-clicking on the white background and choosing Add IP. Choose axi4_master_burst_v1.0 from the list of IPs and double-click on it.
-
To connect the custom AXI4 IP on the Zynq PL to the Zynq PS click on Run Connection Automation.
-
In the Run Connection Automation window click on S00_AXI and choose /processing_system7_0/FCLK_CLK0 for Clock Connection (...) and do the same with S_AXI_HP0.
-
Check all checkboxes on the left-hand side of the Run Connection Automation window to connect those ports automatically and click on OK.
-
After the auto connection is finished the block diagram should look like this:
-
Click on the Address Editor tab and unfold the axi4_master_burst_0 tree to see the address range of the S_AXI_HP0 port which is connected to the memory. On the Zedboard the address range goes from
0x00000000
to0x1FFFFFFF
. -
In the Diagram tab double-click on the axi4_master_burst_0 to open the Re-customize IP window. Set the C M 00 AXI TARGET SLAVE BASE ADDR to
0x10000000
(The default value0x40000000
is not within the range of the memory). -
In the Sources panel navigate to Design Sources -> design_1.
-
Right-click on design_1 and choose Create HDL Wrapper. This generates HDL code for the block diagram which is necessary for the synthesis.
-
Choose Let Vivado manage wrapper and auto-update and click OK. This will always update your HDL wrapper when the block diagram was changed.
-
After the HDL wrapper for block diagram was generated Vivado will ask if output products should be created. Those output products are necessary for the synthesis and the implementation of your custom AXI4 IP and the block diagram for the Zynq PL. Click on Generate to generate the output products.
To bring the custom AXI4 IP with the block diagram to the Zynq PL you have to synthesize and implement it.
-
Start the synthesis by click on Run Synthesis in Flow Navigator -> Synthesis.
-
After the synthesis is finished choose Run implementation and click on OK to run the implementation.
-
When the implementation is finished choose Generate Bitstream and click on OK to generate the bitstream which contains the configuration data for the Zynq PL.
-
Lastly, when the bitstream generation is finished you can look at the reports to see if all contraints are fulfilled. Choose View Reports and click OK. (However, this is not necessary here since the design is very simple).
To program the Zedboard and talk to it via UART you have to connect it to the power supply and connect two USB cables from your computer to the following USB ports on the Zedboard.
Make sure your Zedboard is turned on. If the green POWER led is on the Zedboard is turned on.
The C program which will be transferred to the Zynq PS will initiate an AXI4 read/write burst transaction over the AXI4-Lite slave interface of your custom AXI4 IP and afterwards it will verify the result.
-
You have to export the hardware configuration to the Xilinx SDK. Go to Menu -> File -> Export -> Export Hardware ....
-
Check Include bitstream and click OK.
-
To launch the Xilinx SDK go to Menu -> File -> Launch SDK.
-
When the Xilinx SDK is ready create a new project by going to Menu -> File -> New -> Application Project.
-
Choose a Project name and leave all other parameters at their default value and click on Next >. The Project name in this tutorial is axi4_master_burst_test.
-
Choose Hello World under Available Templates and click on finish. This creates a simple Hello World program for the Zynq PS.
-
After the project was successfully created open
helloworld.c
under Project Explorer -> axi4_master_burst_test -> src -> helloworld.c . -
Replace the Hello World C code with:
#include "platform.h" #include "xil_printf.h" #include "xbasic_types.h" #include "xparameters.h" int main() { init_platform(); xil_printf("== burst test== \n\r"); // pointer to address the AXI4-Lite slave volatile Xuint32 *slaveaddr_p = (Xuint32 *) XPAR_AXI4_MASTER_BURST_0_S00_AXI_BASEADDR; // pointer to memory address 0x10000000 volatile Xuint32 *data_p = (Xuint32 *) 0x10000000; // check status xil_printf("INIT_TXN\t0x%08x\n\r", *(slaveaddr_p+0)); xil_printf("TXN_DONE\t0x%08x\n\r", *(slaveaddr_p+1)); xil_printf("ERROR\t\t0x%08x\n\r", *(slaveaddr_p+2)); xil_printf("\n\r"); // start AXI4 write/read burst transaction *(slaveaddr_p+0) = 0x00000001; *(slaveaddr_p+0) = 0x00000000; // check status xil_printf("INIT_TXN\t0x%08x\n\r", *(slaveaddr_p+0)); xil_printf("TXN_DONE\t0x%08x\n\r", *(slaveaddr_p+1)); xil_printf("ERROR\t\t0x%08x\n\r", *(slaveaddr_p+2)); xil_printf("\n\r"); // print memory content int i; for(i = 0; i < 16; i++) { xil_printf("DATA+%d\t\t0x%08x\n\r", i, *(data_p+i)); } cleanup_platform(); return 0; }
-
Program the Zynq PL with the previously generated bitstream by going to Menu -> Xilinx Tools -> Program FPGA.
-
Click on Program.
-
The Zynq PS will write the content of all
xil_printf("...");
statments to the UART.On Linux you can connect to the UART of the Zedboard with the following
picocom
command:
picocom /dev/ttyACM0 -b 115200 -d 8 -y n -p 1
-
After you programed the Zynq PL you and connected it to the UART you can run the Program on the Zynq PS by clicking on the green button with the white triangle.
-
Choose Lauch on Hardware (System Debugger) and click on OK.
-
You should see the following output in
picocom
:
== burst test==
INIT_TXN 0x00000000
TXN_DONE 0x00000000
ERROR 0x00000000
INIT_TXN 0x00000000
TXN_DONE 0x00000001
ERROR 0x00000000
DATA+0 0x00000001
DATA+1 0x00000002
DATA+2 0x00000003
DATA+3 0x00000004
DATA+4 0x00000005
DATA+5 0x00000006
DATA+6 0x00000007
DATA+7 0x00000008
DATA+8 0x00000009
DATA+9 0x0000000A
DATA+10 0x0000000B
DATA+11 0x0000000C
DATA+12 0x0000000D
DATA+13 0x0000000E
DATA+14 0x0000000F
DATA+15 0x00000010
You can leave picocom
with [CTRL]+[A] [CTRL]+[Q].
If you would like to simulate your custom IP before synthesizing it you can either use the bus functional model for the AXI4-Bus provided by Xilinx if you have the licences or you can write you own testbench and simulate with Vivado xsim
. This tutorial provides a simple testbench for your previously generated custom AXI4 IP.
-
Go to the location of the Verilog files of you custom AXI4 IP. You find those in
[...]/ip_repo/ip_repo/axi4_master_burst_1.0/hdl
. -
Copy the testbench file from this tutorial
hdl/axi4_master_burst_v1_0_tb.sv
into the HDL directory[...]/ip_repo/ip_repo/axi4_master_burst_1.0/hdl
. The test bench initiates a burst write/read transaction by writing toslv_reg0
and simulates a memory module for the burst write/read transactions. -
Navigate with the command line to the HDL directory
[...]/ip_repo/ip_repo/axi4_master_burst_1.0/hdl
and execute the following commands:xvlog axi4_master_burst_v1_0_M00_AXI.v xvlog axi4_master_burst_v1_0_S00_AXI.v xvlog axi4_master_burst_v1_0.v xvlog -sv axi4_master_burst_v1_0_tb.sv xelab -debug typical axi4_master_burst_v1_0_tb -s tb xsim --gui tb
This opens the Vivado
xsim
simulator. -
Select all signals in the Objects section and right-click on them and choose Add To Wave Window.
-
Go into the Tcl Console and type
run 40000ns
and press [Enter] to start the simulation. In the Wave Window you will see all the signals of your custom AXI4 IP and their change over time. In the Tcl Console is a log of the testbench which tells you about all burst write/read transactions and all the slave write/read transactions. In the end the testbench prints the content of the memory.