Introduction: Zybo - AXI DMA Inside Embedded Linux

As the title says, this tutorial explains how I did in order to be able to use the AXI DMA inside the embedded Linux on a Zybo board. Several other tutorials exist in order to install Linux on the Zybo platform (see references in the end of tutorial), so I won't cover that with much detail. However, I had to look through lots of forums and posts spread around the web in order to use the AXI DMA, and that is the reason why I am publishing this tutorial. If anything is wrong or if following the steps is not working for you, please comment and I'll do my best to help you out :)

As a side note, I did this with Vivado 2015.4 installed on Ubuntu 14.04 but I believe it is similar for other versions. Let's start then :)

Step 1: Install Linux on the Zybo

Follow this tutorial with the following exceptions:

  1. In Step 2, beyond modifying the indicated line of code, modify fdt_high and initrd_high addresses to 0x10000000.
  2. In Step 7, additionally add to the bootargs line of code this -> uio_pdrv_genirq.of_id=generic-uio mem=256M

What this does is make Linux see only 256MB of DDR memory (instead of all the 512MB capacity that the DDR has). This is required in order to have a chunk of memory (in this case, 512-256=256MB) reserved for using the AXI DMA engine. This chunk of memory is our way of sharing data between the PS and the PL in a fast way.

The generic-uio part is required for our peripherals to be recognized and will be explained later on.

Step 2: Install Required Tools in Zybo

First, run password command and define your password:

  1. passwd

It is not very safe playing around in the web with a root user without having a password. Then connect an Ethernet cable to connect to the web.

My way of communicating with the Zybo is via ssh so we have to install it. Additionally, we have to install the device-tree-compiler.

  1. sudo apt-get install openssh-server
  2. sudo apt-get install device-tree-compiler

The device-tree-compiler is required in order to enable us to change the device tree and make the Linux kernel to be aware of our peripherals. We will make the Linux kernel see our AXI DMA engine and our chunk of shared memory as peripherals.

Step 3: Create a Simple AXI DMA Loopback Vivado Project

Now, in our host computer that has Vivado installed, we will create a simple AXI DMA loopback project. Follow this link for instructions with the following exceptions:

  1. Don't use the FIFO and connect directly the MM2S port to the S2MM port in the AXI DMA block.
  2. Double click in the AXI DMA block and uncheck Scather-Gather engine.
  3. Stop after generating the bitstream, we do not need the SDK.

Step 4: Patch the Device Tree

Now, open a terminal and connect to the Zybo board via ssh (you will know your Zybo IP using ifconfig). We will patch the device tree.

  1. ssh root@YOUR_ZYBO_IP
  2. dtc -I fs -O dts /proc/device-tree -o devicetree.dts

This will dump the device tree in a human readable form. We will patch it in order for our DMA to be seen by the Linux kernel. Use the nano text editor and create "pl.dtsi" with the following content:

/ {
scratch_mem@10000000 {

#address-cells = <1>;

#size-cells = <1>;

reg = <0x10000000 0x10000000>;

compatible = "generic-uio";

interrupts = < 0 58 0 >;

interrupt-parent = <0x1>;

};

amba_pl: amba_pl {

ranges;

#size-cells = <0x1>;

#address-cells = <0x1>;

compatible = "simple-bus";

dma@40400000 {

#dma-cells = <1>;

compatible = "generic-uio";

interrupt-parent = <0x1>;

interrupts = <0 29 4 0 30 4>;

reg = <0x40400000 0x10000>;

};

};

};

Also, add the following line to the "devicetree.dts" file -> /include/ "pl.dtsi"

Now, we will recompile the device tree into binary form and substitute the original device tree. Then, our scratch_mem peripheral (located at 0x10000000) and our DMA engine peripheral (located at 0x40400000) will be recognized. Do the following:

  1. sudo mount /dev/mmcblk0p1 /boot
  2. sudo cp /boot/devicetree.dtb /boot/devicetree.dtb.orig
  3. sudo cp devicetree.dtb /boot/devicetree.dtb
  4. sudo sync
  5. sudo umount /boot
  6. reboot

Your ssh connection will close and your Zybo will reboot. Your device tree changes will be applied.

Step 5: Substitute the Bitstream and Enjoy :)

After the reboot, re-connect to Zybo via ssh. You can see that your new peripherals are being recognized by using the following commands:

  1. ls -l /dev/uio?
  2. ls -l /sys/class/uio/uio?/
  3. cat /sys/class/uio/uio?/name

Now, we will need to transfer our DMA loopback bitstream to the Zybo. Open another terminal and type:

  1. scp /PATH_TO_BITSTREAM_FOLDER/BITFILE_NAME.bit root@YOUR_ZYBO_IP:/root/

Back to your ssh session (the other terminal), you will replace the bitstream being used. Run the following command

  1. sudo dd if=BITFILE_NAME.bit of=/dev/xdevcfg

Now the bitstream being used is the one containing the DMA loopback design and you can use it with an application. I made use of this files: file1, file2 and file3. In order to use them you have to install python and numpy:

  1. sudo apt-get install python python-numpy

Finally, one more thing before just running test_dma.py. The interrupts were not working for me, so I used polling. In order to do so, I substituted this piece of code:

if dma.wait():

print('DMA Transfer Completed')

else:

print('DMA Transfer FAILED')

by

while(1):

if(dma.idle()):

break

Now, you can run test_dma.py and verify that the AXI DMA is working. You will not have cache problems since your scratch_mem is being used as a peripheral. After you are able to use this example, you may break the loopback and insert your own custom IP.

Step 6: (Optional) Change Clock Frequency

If your design needs a different PL clock frequency than 100MHz, you will need to change it separately, since this changes are not reflected in the bitstream. This is because the PL clocks are generated by the PS. Thus, in order to change the clock frequency of FCLK0 you run the following commands:

  1. echo fclk0 into /sys/{whatever_path_on_your_system}/fclk_export
  2. echo 1 into /sys/class/fclk/fclk0/enable
  3. echo {needed_frequency_in_Hz} into /sys/class/fclk/fclk0/set_rate

The same is applied to other clocks.

Step 7: (Mandatory) Read the References

In order to make it short, this tutorial is very simplistic. For you to know what you're doing, you need to read a lot. This references are what made me put the AXI DMA working:

  1. Embedded Linux Tutorial #1
  2. Embedded Linux Tutorial #2
  3. Embedded Linux Tutorial #3
  4. Divide the DDR memory in Linux
  5. Create an AXI device driver
  6. Create an AXI DMA device driver
  7. Dynamically change the clock frequency

Good luck and lots of patience for you all, this is not for the weak :)