GPIO with ZYNQ and PetaLinux

Accessing GPIO controllers is pretty straightforward with PetaLinux, but there are a few tricks you need to know.

Locating the GPIO controller

In the example FPGA I am using, there are two GPIO controllers in the programmable logic. These are at address 0x4120_0000 and 0x4121_0000. If you look in the pl.dtsi file in your PetaLinux project, in the directory subsystems/linux/configs/device-tree, you will see entries for the GPIO devices. There’s no need to modify the entire device tree.

If you make a PetaLinux build and boot it, you can look in the /sys/class/gpio directory.

root@pz-7015-2016-2:~# ls /sys/class/gpio/                       
export       gpiochip901  gpiochip902  gpiochip906  unexport

You can see that there is a gpiochip directory for each GPIO controller. The gpiochip901 and gpiochip902 directories correspond to the PL controllers that I added in my design. The gpiochip906 directory is for the GPIO controller in the PS.

How will you know which is which, though? Each directory contains a label file which tells you the device tree label for the controller. You can go ahead and look at the contents:

root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip901/label 
/amba_pl/gpio@41210000
root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip902/label 
/amba_pl/gpio@41200000
root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip906/label 
zynq_gpio

Looking at it, you’ll see that gpiochip901 corresponds to my controller at 0x4120_0000 and gpiochip902 corresponds to the controller at 0x4121_0000. Gpiochip906 is different, and corresponds to the built-in controller on the ZYNQ. Why those numbers? In my FPGA, the first GPIO controller controls only a single GPIO bit, while the second controls four bits. We can tell how many bits each controller controls by looking in the ngpio file for the controller.

root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip901/ngpio 
1
root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip902/ngpio
4
root@pz-7015-2016-2:~# cat /sys/class/gpio/gpiochip906/ngpio
118

It looks to me like the numbering starts at 901. Since that controller has only a single GPIO bit, the next controller is 902. That one has four bits, so the ZYNQ PS controller goes at 906, which has 118 bits.

Enabling the GPIO bits

To access a GPIO bit, you need to enable the correct GPIO pin. You do that by writing to the export file in the /sys/class/gpio directory. Here is an example of enabling the LSB of my second controller:

root@pz-7015-2016-2:~# echo -n 902 > /sys/class/gpio/export 

Now if you look in the /sys/class/gpio directory, you will see a new directory created which allows you to control the individual GPIO pin.

root@pz-7015-2016-2:~# ls /sys/class/gpio
export       gpio902      gpiochip901  gpiochip902  gpiochip906  unexport

If you look in that directory you see a number of controls:

root@pz-7015-2016-2:~# ls /sys/class/gpio/gpio902
active_low  direction   power       subsystem   uevent      value

Accessing the GPIO bits

You can determine the GPIO direction by looking at the direction file. Since my GPIO pin is an output, it gives the value out.

root@pz-7015-2016-2:~# cat /sys/class/gpio/gpio902/direction
out

You can change the value to a 1 by writing to the value file.

root@pz-7015-2016-2:~# echo 1 > /sys/class/gpio/gpio902/value           

Conclusion

So there you have it. The “official” way to access GPIO on PetaLinux.

SPI with PetaLinux on ZYNQ

Recently, I spent a lot of time trying to get SPI working on a PicoZed ZYNQ board under Linux. It was absolutely shocking how complicated this ended up being. One issue, I think, is that the device tree options differ depending on which version of PetaLinux you’re using. In this post, I’m going to document here how to do it with PetaLinux 2016.2.

Modify the device tree

First, you need to modify the system-top.dts file located in your PetaLinux project’s subsystems/linux/configs/device-tree directory. You need to add an entry that extends the existing entry for the SPI device. In the example, I am using spi0 on the processor subsystem. You can see the base definition for the SPI interface in the zynq-7000.dtsi include file in the same directory.

It’s important to note that PetaLinux will create an entry for the SPI device when you configure Linux– however, you won’t get a device file unless you add the entry for your particular SPI device. The trick is to add the SPI device information to the file system-top.dts. The device tree specification syntax allows you to make changes to the automatic entry for the SPI device by labeling a a node, then overlaying additional information onto the labeled node in other parts of the device tree specification.

In our case, the processor built-in SPI devices are labeled spi0 and spi1. I wanted to use spi0, so I added an entry in the system-top.dts file to add to the spi0 definition. In the example below, I’ve added three devices.

&spi0 {
  is-decoded-cs = <0>;
  num-cs = <3>;
  status = "okay";
  spidev@0x00 {
    compatible = "spidev";
    spi-max-frequency = <1000000>;
    reg = <0>;
  };
  spidev@0x01 {
    compatible = "spidev";
    spi-max-frequency = <1000000>;
    reg = <1>;
  };
  spidev@0x02 {
    compatible = "spidev";
    spi-max-frequency = <1000000>;
    reg = <2>;
  };
};

Rebuild linux and reboot your PicoZed board and you can now see the device files.

root@pz-7015-2016-2:~# ls -l /dev/spi*
crw-rw----    1 root     root      153,   0 Jan  1 00:00 /dev/spidev32766.0
crw-rw----    1 root     root      153,   1 Jan  1 00:00 /dev/spidev32766.1
crw-rw----    1 root     root      153,   2 Jan  1 00:00 /dev/spidev32766.2

Testing the SPI interface

In order to test the SPI interface, I built an FPGA with the SPI ports marked for debug. This allows me to use the embedded logic analyzer to view the pin activity from Vivado. PetaLinux ships with a program to test the SPI interface called spidev_test. I compiled it with the following command:

arm-xilinx-linux-gnueabi-gcc -o spidev_test /tools/xilinx/petalinux-v2016.2-final/components/linux-kernel/xlnx-4.4/Documentation/spi/spidev_test.c

Then, I copied it to my board using ssh, configured the logic analyzer to capture SPI activity, and ran the following command:

root@pz-7015-2016-2:~# ./spidev_test -D /dev/spidev32766.0 --speed 10000000
spi mode: 0x0
bits per word: 8
max speed: 10000000 Hz (10000 KHz)
RX | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  | ................................

I could see the SPI pins wiggle in the logic analyzer view.

Conclusion

Anyway, I hope that this you save some time getting SPI to work.

PetaLinux for the PicoZed

I recently ran into a SNAFU when trying to build a PetaLinux image for a PicoZed board on a rev 2 FMC carrier board. The instructions assume that you have built an FPGA image and exported it from Vivado. You can check out my tutorial on PetaLinux for more background information, just in case you’re unfamiliar with the process.

You’re going to need to make sure that you have the board support package for the PicoZed board and carrier board you are using, which can download here. I like to keep the BSP in my Xilinx tool area, but you can store it anywhere you like. Be sure to unzip the download file.

Create the project

First, I source the settings file for PetaLinux. Don’t worry about the tftp warnings.

% . /tools/xilinx/petalinux-v2015.4-final/settings.sh

Now create your PetaLinux project. I created it as a sibling of the Vivado project directory.

% petalinux-create --type project --name pz_linux --source /tools/xilinx/pz_7015_2015_4.bsp

Configure the project

Next, you configure the project with the FPGA export. This sets the device tree for the build:

% petalinux-config --get-hw-description my_fpga/my_fpga.sdk --project pz_linux

Then, configure your filesystem:

% petalinux-config -c rootfs --project pz_linux

PetaLinux build fails

If we run petalinux-build right now, we run in to some trouble:

% petalinux-build --project pz_linux
INFO: Checking component...
INFO: Generating make files and build linux
INFO: Generating make files for the subcomponents of linux
INFO: Building linux
[INFO ] pre-build linux/rootfs/fwupgrade
[INFO ] pre-build linux/rootfs/gpio-demo
[INFO ] pre-build linux/rootfs/httpd_content
[INFO ] pre-build linux/rootfs/iperf3
[INFO ] pre-build linux/rootfs/peekpoke
[INFO ] pre-build linux/rootfs/weaved
[INFO ] build system.dtb
[ERROR] ERROR (phandle_references): Reference to non-existent node or label "usb_phy0"
[ERROR] ERROR: Input tree has errors, aborting (use -f to force output)
[ERROR] make[1]: *** [system.dtb] Error 255
ERROR: Failed to build linux

Fixing the device tree

The problem is that the device tree was overwritten by the petalinux-config. To fix that, we need to add the following lines to the system-conf.dtsi file located in pz_linux/subsystems/linux/configs/device-tree. Place the text following the entry for memory:

usb_phy0:phy0 {
  compatible="ulpi-phy";
  #phy-cells = ;
  reg = ;
  view-port = ;
  drv-vbus;
};

With this change in place, we can go ahead and run petalinix-build again, and the build will complete. I’m not exactly sure why this procedure is required or how to patch the PetaLinux install to fix this. I will explore that later.

Packaging

After building, you will need to package the distribution.

% petalinux-package --boot --format BIN --project pz_linux --fsbl pz_linux/images/linux/zynq_fsbl.elf --fpga my_fpga/impl_1/my_fpga.bit --u-boot

Copy the BOOT.BIN and pz_linux/images/linux/image.ub files to your MicroSD care and boot your PicoZed board.

Let me know if you have any ideas why this problem exists or how you can fix it.