Parallella FPGA Tutorial 2: AXI4 interface to BRAM


In Tutorial 1 I covered setting up an AXI4Lite interface in order to communicate with the FPGA via a set of registers.

An alternate interface is the full AXI4 interface, which I will be using to create an access path to internal FPGA memory. This is useful for operations which will operate on a larger data set than can be held in the register file (32 registers was the limit as I recall) via an AXI4Lite interface.

NOTE: This is still not a particularly efficient interface - I do intend to get the AXI4-Stream interface (DMA transfers to/from DDR) working, however there is a little work involved in this, including writing a driver to setup the DMA transfers - or porting a kernel to the Parallella which is compatible with the scant few examples out there.

Step 0: Setup new project

Like Tutorial 1, we will base this off the Tutorial 0 project files.

, which you can obtain from github if you do not wish to go through the tutorial. This is a companion repo to parallella-hw, as explained in that tutorial.

If you wan't to skip that, but use a ready-made project, you can checkout https://github.com/yanidubin/parallella-fpga-tutorials, place it in the same parent folder as parallella-hw. Tutorial000_BaseProject contains a base you should work from (planAhead: File >> Save Project As ). Tutorial002_AXI4 contains the completed project sources you should end up with at the end of this tutorial.

Step 1: Open planAhead, launch XPS

Exactly as per Tutorial 1 Step 1.

Step 2: Create and configure an AXI4 IP core

As with Tutorial 1, we will create an IP core - only this time it will use the full AXI4 interface.

[img] From the menu bar, go to Hardware >> Create or Import Peripheral.

[img] Click Next, then select Create templates for a new peripheral and click Next again.

[img] You then are asked where to create the core. I prefer to add all my cores to an EDK repository rather than the project. This is so I can re-use them in multiple projects.

[img] I recommend creating your own edk_user_repository - under parallella-hw/fpga/ if you are using a checkout/fork of parallella-hw, or better still in your own project repository (but at the top level as IP cores are designed to be shared between projects).

[img] Click Next, and give the module a Name, and click Next again.

[img] Select AXI4, and click Next.

[img] Leave IPIF Services on defaults, and click Next.

[img] Leave Slave Interface settings on defaults, and click Next.

[img] For User Memory Space, we will use only one BRAM bank for this example - although up to 8 can be added to the peripheral. Click Next.

[img] Leave IP Interconnect* on defaults, and click Next**.

[img] For (OPTIONAL) Peripheral Simulation Support, a license is required and I am not familiar with this - so I clicked Next again.

[img] You will then be asked about (OPTIONAL) Peripheral Implementation Support. Since I prefer to use VHDL rather than Verilog, and do not require any driver support, I simply clicked Next. While you may prefer Verilog, I chose to do the rest of this tutorial in VHDL - so if you plan to keep working through the final steps, stick with VHDL for now.

[img] Then click Finish and you are done.

Once again you have a new IP core which you can include in your projects - which won't do much in and of itself, however you will be able to read and write values from/to FPGA block memory. As usual, before we can use it, there are a few steps to go through. Only then will we customise it to make it do something a little more interesting.

Step 3: Reconfiguring the Processing System

Exactly as per Tutorial 1 Step 3. If you are using the HDMI build, or for some reason already have M_AXI_GP0 enabled, then you can skip this section (although it is instructional).

Step 4: Instantiate and configure an AXI4 IP core

Again this is identical to Step 4 of Tutorial 1, except for the name of the core.

Step 5: Build and test the resultant core

As in Tutorial 1, I have decided to leave the core customisation for a separate tutorial. So the test instructions for now are identical.

I appreciate that this is a little underwhelming - we have a new way of interfacing, but it looks the same as far as the simple tests we've looked at. Where you will see the difference is when we customise it to do something interesting.

This will be the next thing I cover in this series - so watch this space.

Step 6: So what really happened to the system?

Because I have been working in the background on porting the FuseSOC build tool to the Parallella, and intend to write an alternative version of these tutorials where you don't need to use the Xilinx GUI tools at all (and can jump straight into hacking VHDL running on the core), I thought I would show just how feasible this is in the context of what we just did.

I also offer this as it is a very handy way of working out what has been broken by the XPS when things don't work out for you - a fair bit of trial and error was involved in my own early attempts, before I got a reliable/repeatable process down.

There is a new PCore in your EDK folder, the result of all your clicking in Step 2 - but this is a very generic core, and you could have simply started by checking out the output as a "template". Since, as yet, you have not altered the HDL within.

As for the other steps (3 and 4), the sum total of your changes are covered in the following diff.

diff --git a/Tutorial002_AXI4/Tutorial002_AXI4.srcs/sources_1/system/system.mhs b/Tutorial002_AXI4/Tutorial002_AXI4.srcs/sources_1/system/system.mhs
index bd1f8e9..4617884 100644
--- a/Tutorial002_AXI4/Tutorial002_AXI4.srcs/sources_1/system/system.mhs
+++ b/Tutorial002_AXI4/Tutorial002_AXI4.srcs/sources_1/system/system.mhs
@@ -173,6 +173,9 @@ BEGIN processing_system7
  PARAMETER C_FCLK_CLK3_FREQ = 40000000
  PARAMETER C_USE_M_AXI_GP1 = 1
  PARAMETER C_USE_S_AXI_HP1 = 1
+ PARAMETER C_USE_M_AXI_GP0 = 1
+ PARAMETER C_USE_CR_FABRIC = 1
+ BUS_INTERFACE M_AXI_GP0 = axi_interconnect_1
  PORT MIO = processing_system7_0_MIO
  PORT PS_SRSTB = processing_system7_0_PS_SRSTB
  PORT PS_CLK = processing_system7_0_PS_CLK
@@ -281,5 +284,24 @@ BEGIN processing_system7
  PORT GPIO_I = processing_system7_0_GPIO_I_0
  PORT GPIO_O = processing_system7_0_GPIO_O
  PORT GPIO_T = processing_system7_0_GPIO_T
+ PORT FCLK_RESET0_N = processing_system7_0_FCLK_RESET0_N
+ PORT M_AXI_GP0_ACLK = processing_system7_0_FCLK_CLK0
+END
+
+BEGIN axi4_example
+ PARAMETER INSTANCE = axi4_example_0
+ PARAMETER HW_VER = 1.00.a
+ PARAMETER C_S_AXI_MEM0_BASEADDR = 0x60000000
+ PARAMETER C_S_AXI_MEM0_HIGHADDR = 0x60000FFF
+ BUS_INTERFACE S_AXI = axi_interconnect_1
+ PORT S_AXI_ACLK = processing_system7_0_FCLK_CLK0
+END
+
+BEGIN axi_interconnect
+ PARAMETER INSTANCE = axi_interconnect_1
+ PARAMETER HW_VER = 1.06.a
+ PARAMETER C_INTERCONNECT_CONNECTIVITY_MODE = 0
+ PORT INTERCONNECT_ACLK = processing_system7_0_FCLK_CLK0
+ PORT INTERCONNECT_ARESETN = processing_system7_0_FCLK_RESET0_N
 END

In the first part of the diff, we see in the first two lines the result of enabling the GP0 interface (Step 3). The next line connects GP0 to the axi_interconnect_1.

Looking at the second part of the diff, we see the clock and reset port definitions - in this case, they are connected to external PS7 ports - since these are used for the HDMI interface. We then see these connected to the AXI interconnect (clk, reset), and our core (clk only). Then there are the definitions of the core and the interconnect themselves. This was the result of Step 4, where we added the core to the system - and locked down the address range to be used.

Tagged as  HOWTO,  AXI4

Back to Blog