This blog briefly explains the AXI4 Memory Mapped I/O protocol. The memory-mapped system is a common technique to access I/O and memory in processor systems. AXI protocol is a memory-mapped communication scheme among IPs in an embedded system. AXI protocol divides IP ports into two groups: Masters and Slaves. Then an interconnect switch or Network-on-Chip (NOC) connects them together. Whereas masters send commands to start a read or write transaction, the slaves wait to answer the received commands.
If you want to learn more about the AXI4 protocol and all the design details in this post, please refer to my online course, “High-Level Synthesis for FPGA, Part 3 – Advanced“.
To explain how AXI4 works, here I explain how to use AXI4 protocol to connect slide switches to LEDs available on the Basys 3 FPGA platform.
Consider a group of switches as inputs and a set of LEDs as outputs. We aim to design an HLS IP with two master ports to read the switch status and illuminate the LEDs correspondingly. To convert the AXI master protocol signals to simple wires to be connected to switches and LEDs, we need two IPs with AXI slave and ap_none interfaces.
AXI4 Address Space
Before designing the HLS IP with two master ports, we should note that each slave port has its own address space; the address spaces of the two slave ports should be mapped on the master ports’ address spaces. Let’s see these addresses in more detail.
The following figure shows the two address spaces associated with the slave ports on the two AXI GPIO IPs. We assume that the two master AXI ports on the master IP (the IP in the middle of the previous figure) share the same address space. The slave address spaces should be mapped on the shared master space. For this purpose, we should define the offset address of each slave in the master address space. Let’s assume the offset address of the slave IP connected to the switches is 0, and the offset address of the slave IP connected to the LEDs is 0x10000. We should consider these offset addresses in our HLS code.
So let’s write the HLS code for the master IP. It is helpful to have a look at the master address space again. The offset address of the AXI slave port corresponding to switches is zero, and the register at address 0 contains the switch status.
The offset address of the AXI slave port corresponding to LEDs is 0x10000, and the register at address 0x10000 contains the LEDs data. In our HLS code, we should read data from address 0 and copy that into address 0x10000. The master IP code starts with defining the arguments. We should have two arguments. One is for receiving the switches status, and the other is for sending out the LEDs data.
Then we should apply the m_axi interface to both arguments. As we assumed that these two master ports share the same address space, initially, both point to the address 0 of this address space. As the offset address of the switch data register is zero, we can read directly from the SW port. However, to write into the LED port, we should consider the corresponding offset address, which is 0x10000.
Note that we cannot add this offset address directly to the LED argument. We should consider the C/C++ rule of adding an integer value to a pointer. C/C++ always multiply the integer value by the size of the pointer type in bytes and then adds that to the pointer. So first, we should divide 10000 hex by the size of the pointer data type in our code. In this case, this size is 2 for the unsigned short int. After understanding the basic concepts, let’s develop the design in Vitis-HLS and Vivado toolsets.
Create a Vitis-HLS project and name it “memory_mapped_inout_output-vitishls”. Don’t forget to choose the Basys3 board as the target FPGA platform. Create a new design source file with the “memory_mapped_inout_output.cpp” name. Then add the master IP code as shown the previous figure. As the size of unsigned short int data types is two bytes, I have considered dividing by 2 here in this code. Synthesise the code and generate the corresponding RTL/IP. Now we are ready to create a Vivado project.
Create a new Vivado project with the “memory_mapped_input_output-Vivado” name. Choose the Basys3 board as the target FPGA platform. Create a new block design. Click on the settings option on the left and add the Vitis-HLS RTL/IP paths to the Vivado repository.
Add the memory_mapped_input_output IP generated by Vitis-HLS to the design area. Go to the board tab on the left and drag-drop the system clock into the design area. Then click on the Run Connection Automation link. After selecting the reset option, press OK. Then add a processor System Reset IP to the system. Click on the Run Connection Automation link again. After selecting all the options on the left, press OK.
Connect the locked port on the Clocking Wizard IP to the dcm_locked on the Processor_system_reset IP. Then connect the ap_clk on the added Vitis-HLS IP to the clk_out1 of the clocking wizard IP. Also, connect the ap_rst_n to the peripheral_aresetn on the Processor system reset IP. In order to provide a start signal for the design, add a debouncer and a pulse generator IP, as usual, and connect them to the design as shown.
To add an AXI GPIO IP, we can search it in the Vivado repository and add the IP, or we can go to the board tab on the left and drag and drop the 16 switches GPIO into the design area. The added IP will be configured and connected automatically to slide switches on the board.
Then click on the Run Connection Automation link again. Then we should add another GPIO IP to be connected to LEDs. We can add that manually by searching the IP in the Vivado Repository. After adding the IP, we should customise that. So double-click on the added IP. Then select led 16bit as the board interface for the GPIO interface. Then click on the Run Connection Automation link again.
We should also define the led_16bits port as output. For this purpose, double-click on the AXI GPIO again and change the Default Tri-State Value to 0. Now we should set the AXI master and slave addresses. For this purpose, go to the address editor and expand all the interfaces, then select the unincluded interfaces, then right-click and select the include option.
Then go to the Address Map tab and check the slave AXI offset addresses. Now we should add a constraint file to connect the ap_start input signal to the UP push button. Note that the AXI GPI IO output ports will be connected automatically to the slide switches and LEDs on the board. Finally, create the bit stream and program the board.
On the board, if you set a binary pattern on slide switches, you will see the pattern on LEDs after pressing the up-push button.
If you want to learn more about the AXI4 protocol and all the design details in this post, please refer to my online course,”High-Level Synthesis for FPGA, Part 3 – Advanced“.