Tutorial 26: Controlling a SPI device using the ZYNQ SPI controller

In Tutorial 24, I covered controlling a SPI device by just taking control of the memory mapped GPIO and bit-banging the SPI without a driver. In this tutorial, we’ll do things the “official” way, and use the one of the hard IP SPI controllers present on the ZYNQ chip. For this tutorial I am using Vivado 2016.2 and PetaLinux 2016.2.

Enabling the SPI controller

First you need to enable the SPI controller on the ZYNQ subsystem. Double-click on the ZYNQ processing subsystem in your Block Design in the IP Integrator window. This will bring up the IP configuration window. Click on the Peripheral I/O Pins section of the Page Navigator and check the box next to SPI 0. Note that Quad SPI or QSPI is unrelated to this discussion.


Next, go to the MIO Configuration section of the Page Navigator. Expand the I/O Peripherals section and scroll down to SPI 0. In the IO column make sure that EMIO is selected. This programs the SPI control signals to use the Programmable Logic rather than pins dedicated to the Processor Subsystem.


Making the connections

Next, you’re going to need to make ports on the FPGA which will connect to the SPI controller. For a SPI master, you’ll need at least one chip select output, one clock output, and probably a data output to write to the SPI device. If you wish to read from the SPI device, you’ll also need a data input. In my case I am connecting to a MAX 5216 DAC which does not have read capability so I only have three output pins which I call MAX5216_SCLK, MAX5216_MOSI, and MAX5216_SSN. Wire them up to the corresponding pin on the SPI controller. Note: there will be many unused pins, since the controller can be used as a master, slave, or both. Also, the SPI0_SS_I pin must be connected to a logic 1 value due to a bug in the toolchain. To do this, instantiate a constant IP module and set the value to 1. Wire that to the SPI0_SS_I pin on the ARM processor. On my FPGA, I also connect to a DS3231 realtime clock over I2C and have some GPIOs connected as well. Don’t worry about not having those in your design. My block design is shown below:


Note that I have also marked the pins for debug. This can be really useful for debugging your software using the embedded logic analyzer feature of Vivado.

Now synthesize the design and use the I/O Planner to place the outputs where you want on your FPGA. Generate a bitstream and export your design to SDK. These steps are covered in other tutorials, so I won’t go into details here.

Build PetaLinux

You need to create a PetaLinux project and extract the hardware description from your project SDK directory as discussed in tutorial 23. Before you build PetaLinux, though, you need to modify the device tree to create the appropriate device file for your SPI device. This step is critical: without it, Linux won’t create the SPI device file to control your device.

The first step after creating your project is to use petalinux-config to read the hardware description that you exported from Vivado.

petalinux-config --get-hw-description <path to project.sdk directory>

Then you need to enable SPI support for your kernel. To configure the kernel run the following command.

petalinux-config -c kernel

Navigate to Device Drivers->SPI support and make sure that Cadence SPI controller, Xilinx SPI controller command module, Xilinx Zynq QSPI controller, and User mode SPI device driver support are all enabled. If you fail to enable the User mode SPI support then the SPI device files will not be created.


Edit the file subsystems/linux/configs/device-tree/system-top.dts in your PetaLinux project directory, then add the following lines.

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

Now you can run petalinux-build and petalinux-package to build and package Linux.

Verifying the SPI interface

Once you boot your board, you should be able to see the SPI device file in the /dev directory.

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

You can compile the spidev_test program to exercise the SPI interface and view the results on the logic analyzer. To compile the spidev_test program, use 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 copy the program to your board using ssh. Run the command, and if you use the embedded logic analyzer, you should see activity on the SPI interface.

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  | ................................

In a future tutorial, I will cover the software side of controlling a SPI device in Linux.

20 thoughts on “Tutorial 26: Controlling a SPI device using the ZYNQ SPI controller

  1. Hi,
    so I’ve been going through these tutorials and they have been very helpful!
    However i have been given a PLL, (LMX2592evm) to interface with the zynq7000.
    I don’t know how to go about it.
    Will the block design in vivado be the same as given in this tutorial?
    How can i get my fpga to control the LMX2592evm?
    I’ll be very grateful to any help available!

  2. You can use the SPI that is built-in to the PS on the ZYNQ chip like I show in tutorial 26 or you can use the bit-bang method I show in tutorial 24. Which SPI device you are using doesn’t really matter. The constraints and PS configuration will differ depending on how you are connecting the device to the board.

  3. Hi – thanks for writing all of this up – it’s invaluable!

    I’m trying to adapt the Xilinx SPI driver for use within a Zynq, but not using Petalinux. How can I get hold of a copy of system-top.dts? I tried to see your .c source from Tutorial 24 but the page is looking for login details. I have a .dts decompiled from a zedboard – I just want to compare it to yours.

  4. The dts file is really Linux specific. If you aren’t using Linux then it really doesn’t apply. You can use the techniques described in tutorial 24 to bit-bang the SPI interface. If you only need low-speed control of a SPI device this can often be the simplest solution. Or you can look at the Xilinx baremetal drivers page http://www.wiki.xilinx.com/Baremetal+Drivers+and+Libraries for the Cadence SPI or the AXI SPI. Of course, sometimes it is just easier to read the spec on the SPI IP block and just program the registers.

  5. I am using Linux, Xilinx Pulsar Linux in particular. I’ve since decompiled the .dtb that comes with the image into a .dts, so I don’t need system-top.dts anymore. Now I just need some insight into the MIO mapping syntax in the .dts, so that I can use all 3 chip selects on my only available SPI!

  6. Hi,

    Thanks for these helpful tutorial.
    Every steps look fine except spi check. There is no spidev in dev directory.
    When I power up the zedboard, ttyACM0 seems to be concerned, but when I configure gtkterm on this port, I can’t see anything.
    I followed your tutorial 24 and 26, I’m clearly newbie on embedded OS etc.. but I followed your tutorial 24 and 26 and nothing seemed to go wrong.
    Do you have any idea ?

  7. Sorry for the late response. Are you using the same version of Petalinux? There can be pretty large differences between versions and I have not run the tutorials with the latest versions.

  8. Hi,

    I started with your Tutorial 23, then I’m using petalinux 2015.4 with zedboard bsp 2015.4, but with Vivado 2017.4. However, I manage to open the serial connection, to boot (writing “boot” in the console) and to login with “root”.
    Thanks for these tutorials, and your answers.

  9. Hello,

    I am using microzed board with linux os.I want to communiacte with an adc connected to SPI0 controller in the microzed board.Can I use the spidev_test.c program to verify whether the device is responding and is it necessary to edit .dts file?

    Thanks in advance……..

  10. You need to edit the dots file to tell the Linux build process about your SPI device. Without the entry in its file Linux won’t have a spider device file in dev to talk to your device.

    Let me know if you have more questions.

  11. Hello,

    Thanks a lot for wonderfull sharings.
    At your last sentence you are saying, “In a future tutorial, I will cover the software side of controlling a SPI device in Linux.”
    Is there any progress about this tutorial?


  12. Saban,

    I should probably do that. Of course life has intervened since that statement was written and I have not gotten to it. I have however controlled a lot of SPI devices since then from Linux. If you have a particular question, or would like to see some example code dealing with a SPI device, let me know and I will try to help.


  13. Hi Pete,

    Thank you for your reply.
    I’m already controlling an ADC by using Petalinux spi_test driver as you described in your article.
    My next step is: configuring a lot of devices over SPI.
    Additional ADC, DAC, Flash..
    My ZYNQ need to be connected a lot of cards.
    It will also connect to a sub(low cost FPGA), and this fpga will configure some hardwares over SPI.
    It means ZYNQ can configure some devices directly over SPI and also it connects to low cost FPGA and this FPGA can configure hardwares on it.
    Have you ever implemented a design like that?
    Whay is your advice?


  14. Saban,

    Yes, I have implemented many systems like that. How are you connecting the ZYNQ to the other FPGA? How you do this will depend on a couple of factors. First, how fast does the interface need to run? Both in terms of throughput and latency. Do you have access to transceivers for the interface? If so then you can use a Xilinx chip to chip bridge. You can even use one if you don’t have those but only have an AXI streaming interface. Either a parallel interface between the chips, or you could make a serializer which takes the AXI streaming interface and maps it to a serial stream. Or you could use a SPI interface between the two FPGAs.

    Another route is that you can have a SPI register on the remote FPGA which controls a MUX that lets the SPI interface there, in turn, drive the SPI master interface on the remote FPGA. Essentially the register would serve as the source for the remote chip selects. I have done this when the remote FPGA is just a simple CPLD which is there pretty much to multiplex multiple SPI devices.

    Let me know if you have more questions. Feel free to send me an email on the topic since it is straying outside the scope of the tutorial.



  15. Hi Pete,

    Thanks for such nice tutorials. I am new to the world of Zynq and the first task I need to handle is the SPI interface on petalinux.
    What if I want to use the MIO pins and not the EMIOs? I have used MIO pins to run SPI on zedboard without the petalinux.
    Also if I am using SPI1 instead of SPI0, will be there any change in the .dts file ? will the &spi0 become &spi1 in my case? or any other modification? I have no idea 🙂


  16. Mohsin,

    To use the MIO pins you just need to configure the IO for the ZYNQ in the block design. If you change which SPI device you are using then the device tree also needs to change. And you need to make sure to enable the usermode SPI driver when you configure Petalinux. Make sure that once you boot you see the /dev/spidev* device files.


  17. Hi Pete,
    Thank you so much for this tutorial. I´m using the MIO Pins for the SPI. I already found the spidev file in the dev directory. But when i compiled the spidev_test i could see only this:
    spi mode: 0x0
    bits per word: 8
    max speed: 10000000 Hz (10000 KHz)
    Do you know what could be the reason for not seeing the RX 00 00 00 00 ?
    Thank you!!

  18. Imen,

    Unfortunately with the MIO pins you can’t see them on an ILA. Can you check with a scope on the board to see if the signals are changing? Are you sure you ran the spidev command with the correct arguments? Make sure you are using the correct device file too.


  19. Hi Pete,
    Thanks for detailed tutorial.
    I am using zc702 evaluation board and vivado 2019.1,petalinux 2019.1.
    I am trying to interface zc702 board to interface with adc(ad9434) via spi bus.

    I am using EMIO pins itself for spi.

    I already found the spidev file in the dev directory. But when i compiled the spidev_test i could see only this:
    spi mode: 0x0
    bits per word: 8
    max speed: 10000000 Hz (10000 KHz)
    Do you know what could be the reason for not seeing the RX 00 00 00 00 ?

    Could please help in this?

  20. Venkat,

    Sorry to take so long to respond. Vacations and California fires have been delaying things.

    Have you connected an internal logic analyzer to the pins you are using on the EMIO interface? You can also connect it to the AXI bus to see the memory transactions from the ZYNQ.
    Let me know if you have made any progress. And if you haven’t what you see on the ILA.


Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.