This post contains notes that I generated while bringing up Petalinux on my Cora Z7-10 Zynq board. First I get Petalinux working and do a couple of hello world examples. It can serve as a companion to the following resources:

In the second section, Petalinux is built from scratch, and I implement custom AXI-compliant IP on the FPGA fabric. A Xilinx tutorial is followed (with extra notes) to write a Linux device driver and an app to leverage the driver. The result? AXI-controlled blinky LEDs from Linux.

Topics covered:

  • SSH connections to the board
  • Adapting Xilinx tutorials for different target dev boards
  • Cross-compiling apps on the desktop and copying them to the board
  • Petalinux build and installation notes
  • Notes where existing tutorials weren’t correct (for my particular setup, anyway)

As usual, this article is not a standalone tutorial. It’s a supplement to the linked resources.

Note: some links are to Cora Z7-10 specific resources. If you have a different board, be sure to poke around and see if a similar resource exists for your platform.

Section 1 - Petalinux Bringup

Petalinux Install

I started by following the readme instructions in the repo linked below. I was not able to execute the petalinux installer from my root account, I had to make a new user, chmod +w the installation directory from the new user, and then run the installation script.

github repo for Cora Z7-10

I also added the source petalinux script call to the bottom of my .cshrc file so it will be sourced every time I make a new terminal. You could also make it an alias, since the command takes a couple of seconds to run, which is annoying when you want to bring up a new terminal quickly.

SD Card Partitioning

I partitioned my SD card with the following partitions:

  • Name: fpga, format: FAT, size: 1 GB
  • Name: bulk, format: EXT4, size: 7 GB

Go App

These notes accompany the article. I thought it would be interesting to write the first example in go, so I tried it out. Once I understood the flow, I went back to writing things in C.

  • Make a new folder with main.go inside it. Note that the final executable will have the same name as this directory.
  • cd into the directory you placed main.go in.
  • Run this command to compile: GOARCH=arm go build
  • An executable will be generated with the name of the current directory.
  • Copy it into the “bulk” partition on the SD card.

Petalinux Bringup

I used a pre-built Petalinux image from Digilent. As I learned later, this was a good call. Building Petalinux takes about an hour for me, so using the pre-compiled package made the process much quicker for an initial bringup.

  • Download the board support package from github, then place it in a directory called bsp somewhere.
  • Make a new directory to contain the petalinux project and cd into it.
  • Run the following command to generate the project: petalinux-create -t project -s <path to .bsp file>
  • As per the instructions, I copied the boot files onto the first partition of my microSD card and made the MAC address file. The MAC address on the board sticker needs a colon between every set of 2 characters in the file.
  • Plug in the SD card to the Cora, short jumper JP2.
  • Connected a terminal to /dev/ttyUSB1 at 115200 baud, 8-N-1.
  • Press SRST. I was soon greeted with a Zynq: prompt, which has several low-level commands that you can see by entering help. It was proving difficult to navigate and enter commands in this mode. I was getting filesystem configuration errors. So I cycled power and SRST. I was greeted with a root@Cora-Z7-10: prompt. Great, it’s working! In this mode, commands like ls work without error.
  • The bulk of my filesystem is on a 2nd partition of the SD card, which is also where I placed the demo go app executable.
  • Enter the following commands to mount to the partition: mkdir SDFS mount /dev/mmcblk0p2 ./SDFS
    cd SDFS
  • Now you can run the go app executable as usual: ./test-app
  • There was an issue with the demo app where it was set to run a huge number of iterations, so the app appears to do nothing after the initial print. Whoops. Don’t believe everything you see on the internet. However, the print worked, so at least we know the app worked.

C Hello World

GCC is installed under petalinux, so may as well make an app in C and check that it works too.

Still in the SDFS directory, I wrote a hello world file in c:

// hello.c
#include <stdio.h>

int main(){
    printf("Hello, world!\n");
    return 0;

Note that you’ll need to use vim to write the code since the only interface is a command prompt over a UART. I discuss writing apps on the PC and transferring them over a couple sections down in this article.

Compile it with: gcc hello.c.

Run it with: ./a.out.


Let’s SSH into the board so that files can be edited and compiled on the desktop, and then executables copied over over Ethernet.

  • Plug the board into your router with an Ethernet cable.
  • You should see a “link up” message soonafter
  • On the terminal connected over the UART, run ifconfig
  • The first inet addr is the ip address of the board, for me. I will use this address throughout this article. If you’re following along, always replace it with the IP address your board is given.

ifconfig output Above: output from ifconfig command

  • On the PC in a new terminal, SSH into the board using the following command: ssh root@ (replacing with the IP address you just determined).
  • You will be prompted for a password, it was root for me.

By now, you should see a command prompt appear for the board. SSH success! Use the logout command to close the SSH connection when you’re done.

SSH and Cross-compile Flow

Now let’s test out a more reasonable development flow where we write an app on the PC, cross-compile it there, then copy the executable to the device over ssh.

  1. Make a new directory on your PC called zynq-apps
  2. Inside, make an app project directory hello-world
  3. Inside the hello-world directory, add the following makefile:
# Cross compiler prefix: 
# many existing tutorials are not updated for Vivado

CC = arm-linux-gnueabihf-gcc
CFLAGS = -lm

all : hello-world

hello-world : hello-world.o
    ${CC} ${CFLAGS} $^ -o $@

clean :
    rm -rfv *.o
    rm -rf hello-world

    # this target will upload the executable to the board. Change to an IP address assigned to your board. 
    scp hello-world root@

.PHONY : clean
  1. Add the hello-world.c code from above, or add something more complex.
  2. Make the app with make
  3. Upload the app to the board using the uplink target with this command: make uplink. This will use scp to copy the executable over to the board. You will need to enter the password root when you run this command.
  4. SSH into the board again: ssh root@
  5. The hello-world app was copied into the root directory, so it can be executed with the following command: ./hello-world

Section 2: Connecting the ARM Cores and FPGA

The best tutorial I found for connecting the ARM cores and FPGA is this one from Xilinx. In this section, I include some notes for section 7 of the linked document, as I had to modify the process slightly to work with the Cora board.

The tutorial walks you through building an AXI-controlled IP block, adding a device driver to the petalinux build, and writing and running an application that uses the petalinux device driver.

Block Diagram Above: the block diagram for the system that will be created.


You should already have the Digilent board files installed, and you should be able to create a simple RTL project and load the bitstream on to the Cora board from Vivado. You should also be familiar with the Petalinux procedure discussed above, as it is used in this part too.

Writing HW Drivers

These notes correspond to the Creating Peripheral IP section of the tutorial linked above.

  1. Create a new vivado project and select the Cora board. This assumes that you’ve got the Digilent board descriptions installed.
  2. In the IP integrator, create a new block design. Add the Zynq PS IP to it.
  3. Double click the Zynq block, in the re-customize IP window, click Import XPS settings.
  4. Navigate to vivado-boards-master/new/board_files/cora-z7-10/B.0 and select preset.xml. Click OK.

XPS settings Above: setting up the XPS settings for the ARM processing subsystem.

This file comes from the vivado-boards repo from Digilent and allows the Zynq to be configured with all of the other hardware on the board.

  1. Run block automation.
  2. Connect the clocks (fig 2-8 in the tutorial). I get some warnings about negative clock skew. This is a known issue and can be ignored, apparently.
  3. Continue until step 10 of the Integrating Peripheral IP with PS GP Master Port section where the LED port settings are configured.
  4. Search for “leds” in the port list of the elaborated design. Refer to the Cora reference manual and set the Package pin for each bit of the led port. Set the IO standard to LVCMOS33.

XPS settings

  1. As per the tutorial, open up SDK.

Petalinux Driver Setup

  1. Execute the petalinux module creation commands in the Device Driver Development section.
  2. Copy the provided blink.c and blink.h files into project-spec/meta-user/recipes-modules/blink/files and update the .bb file. Don’t forget the trailing slash in the bb file, or the build process will throw an error.
  3. Run the command petalinux-build, not petalinux build as stated in the tutorial.
  4. Time for coffee! The build process took quite a while on my i5 machine, approximately an hour. It includes some very large file downloads - I guess this is where the 100 GB (!) free space requirement comes from.
  5. Generate the boot image from the command in the cora petalinux repo: petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/cora_z7_10_wrapper.bit --u-boot
  6. As described in the repo readme above, copy the newly generated image on to the SD card in the correct location.
  7. Insert the SD card into the board.

LED App Above: a preview of the working blinky app

SDK and FPGA Programming

These notes are for the Example Project: Loading a Module into Kernel and Executing the Application section.

  1. Open SDK, skip the TCF agent on the host machine step. I won’t be using the debugger right now.
  2. Make a new SDK project, set Linux as the OS in the wizard. I skipped the toolchain and system root configurations because the directories in the tooltips were not present on my system.
  3. Import the blink.h and linux_blinkled_app.c file from the LKM_App directory in the example code download from the Xilinx tutorial. Delete the helloworld.c file that’s included by default. It should automatically build without errors. The .elf file generated by SDK will be copied over to the board in a later step.
  4. Program the FPGA with Xilinx > Program FPGA. The board was auto-detected for me.

Testing out the Device

Now that everything is built and programmed, let’s try to run the app and communicate to custom hardware from a Linux device driver!

  • With the new linux image set, cycle power and connect up to the serial port.
  • Run the mknod commands on the board as described in the tutorial.
  • Create a directory /Apps

I decided to skip using the Xilinx tools for navigating on the device, and do it through SSH/the UART connection instead.

  • scp the .elf file from .sdk/linux-blinkled-app/Debug/linux-blinkled-app.elf from the PC onto the board in the /Apps directory using the following command on the PC: scp <path to elf file> root@

I had to remove the old SSH key stored for this IP address, since it changed when petalinux was rebuilt. Then I SSH’d into the board again.

I moved the .elf file into the Apps directory I made earlier, chmod +777’d it as is done in the tutorial, and executed. To my great surprise, the example fired right up and lights started blinking!

Blinking LEDs Above: blinking LEDs controlled by an AXI-enabled peripheral from a Petalinux device driver.

To recap, from scratch, we did the following:

  • built Petalinux
  • installed a custom device driver
  • implemented an AXI-compliant IP block and hooked it up to the Zynq processing system
  • implemented and built an app to use the driver to interface to the custom IP
  • learned the build flows, practiced communicating with the target board

That’s pretty good! All of the building blocks of a really interesting project are in place now. It’s time to look at the example code and examine how it can be used to bootstrap a more complex application and more complex custom IP.

Custom AXI IP [WIP]

  • make folder for IP in project directory
  • tools > create and package IP
  • setup the IP settings
  • If you don’t choose edit now, you can just click “add IP” in the block diagram and search for the IP name you just created. It will show up and then you can edit it.
  • Add it to the diagram
  • Edit in IP packager
  • Make the changes to the IP
  • In the IP packager flow, update the ports (should be an auto thing to do this). This occurs in the Package IP <IP Name> tab. Make your way down the left hand bar with all of the steps in it.
  • Re-Package IP

Now go back to the main design and re-sync with the IP. Now is also a good time to run connection and block automation.

  • Right click at the top of the sources tab and choose generate HDL wrapper.

Hook up IO Ports

  • RTL Analysis > Open Elabrorated Design // hookup-io picture
  • At the top, click the IO Ports
  • Near the bottom there is a search icon
  • Search for led and the pins will show up
  • Set the package pins to N15, G17, L15, M15 and LVCMOS33. Which LED is assigned which pin doesn’t really matter.

Using a Project From Git

  1. Open up the project in Vivado
  2. Generate bitstream
  3. When that’s done, File > Export Hardware to send the design to SDK
  4. File > Launch SDK
  5. When SDK opens, File > Import Projects From Filesystem

Navigate to repobase/projectname.sdk/sdkprojectname and select it. SDK should automatically detect that a project is present. Click OK to open up the project.

  1. Right click on the SDK project in the sidebar and choose Debug As > Debug Configurations...
  2. Select a Xilinx System Debugger type
  3. In the target setup tab, make sure the following settings are there:

    • Debug Type: Linux Application Debug
    • Connection: click the new button and enter the IP address of the board in the Host field.
  4. Press the Test Connection button. It should succeed.
  5. All good, time to flash the bitstream! Click the Program FPGA button.
  6. Once that’s done, you can debug the software application. Simply hit the Debug button and the debugger perspective should open up. Click the Start button when you are ready to run the code.