The Ultra96 board provides both developers and makers with a heterogeneous Zynq MPSoC. This combines quad-core ARM A53 64-bit processors, dual-core R5 32-bit processors, a Mali GPU and Programmable Logic along with WiFi, Bluetooth, and other IO for interfacing.

This makes it a very useful board for a range of applications from IoT & IIoT to image and signal processing and a lot of applications in between.

For for a more detailed introduction, check out the MicroZed Chronicles blog on the Ultra96.

Background

The Zynq MPSoC is called a heterogeneous SoC as it consists of a Processor System (PS) with several different processor types and Programmable Logic (PL).

The PS contains the both processor groups and the GPU. While the programmable logic is where we implement our accelerated functions or processing pipelines.

Zynq MPSoC Context Diagram

To exploit all of the capability provided by the Zynq MPSoC and the Ultra96 board we need to use a high level operating system such as Linux on the quad-core ARM A53 unit which we refer to as the Application Processing Unit (APU).

Using Linux we can then use easily leverage WiFi, Bluetooth, and the GPU along with several open source industry standard languages and frames works such as Python and OpenCV.

One additional benefit of the Zynq MPSoC is that it also provides a Real time Processing Unit (RPU) with dual core lockstep ARM R5 32-bit processors. While we cannot run Linux on these processors we can develop FreeRTOS or BareMetal applications. As such the RPU provides the ability for real time control which is a very useful feature for IoT and IIoT applications e.g. Robotics.

The APU and RPU can be integrated and communicate with each other using the OpenAMP framework with the APU as the master.

The programmable logic (PL) design can then be accessed from either the A53 or R5 depending upon the content of the PL.

To get all of this working correctly for an application we need to initially create the following:

1) Programmable Logic design and configuration of the PS using Xilinx Vivado

2) PetaLinux Application to run on the PS APU

Once these two elements are generated, we can then develop our higher level application for the APU and implement OpenAMP functions if needed for the RPU.

To generate these two building blocks we have the choice of two approaches. Either a bottom up bare bones approach which we can use to build our own complete solution from the ground up.

Alternatively we can leverage the existing Ultra96 framework and add too it, updated hardware design elements in the PL if we so desire.

We will examine both starting with the ground up approach.

Method One: From Scratch

Step One: Creating the Vivado Design

Implementing the hardware design is straight forward, the first thing we need to do is download the board definition files and include them within our Vivado project.

This enables Vivado to understand the configuration of the the PS and the connectivity of its Multiplexed IO. You can download the board definition files from here.

Once downloaded these files should be installed in your Vivado directory under the following path:

<Install path>/Vivado/<version>/data/boards/board_files/

This will allow you to then select the Ultra96 board as your target board for the creation of a new Vivado project.

Once the project has been created, create a block diagram and add in a Zynq MPSoC processing system.

With the Processing System added, you will see the design automation assistance offer to run block automation. Running this applies the preset Ultra96 configuration to the board.

Running block automation

With the block automation complete, we are going to create a simple example for this project which connects a GPIO to the fan control signal.

Vivado Block Design

Before we build the project we need to add in a simple IO constraint file for the fan output as below:

set_property PACKAGE_PIN F4 [get_ports {fan_out }]; # "F4.FAN_PWM" # Set the bank voltage for IO Bank 26 to 1.8V set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 26]]; # Set the bank voltage for IO Bank 65 to 1.2V set_property IOSTANDARD LVCMOS12 [get_ports -of_objects [get_iobanks 65]]; # Set the bank voltage for IO Bank 66 to 1.2V set_property IOSTANDARD LVCMOS12 [get_ports -of_objects [get_iobanks 66]];

With the constraints entered we can generate the bit file and then export the hardware design.

Exporting the hardware design

It is this hardware design we will use for the development of the PetaLinux OS.

However, before I do that I want to make sure the basics of the design work properly.

If they do not identifying and addressing issues at this stage, is much faster and easier than if we wait until we have PetaLinux running.

Step Two: Verifying the Design in Bare Metal

The next step is to use the exported hardware design first in a baremetal SDK application to ensure I can read commands and data sent out over the UART.

One of the reasons I am doing this is initially using a bare bones approach the UART output is the only way I can tell PetaLinux has booted correctly until I get the WiFi up and running.

The Ultra96 board provides both UART and JTAG connections, to connect to, both require the use of flying leads.

UART connection

JTAG Connection

I connected a logic level USB to RS232 connector to the UART connection and used the flying leads connection from my SmartLynq to establish a JTAG connection.

If you want to do the same and do not have a SmartLynq you can always build the project and create a first stage boot loader and test the application booting from the SD Card. The MicroZed Chronicles demonstrates how to do this in its first blogs.

The first step is to create a new application project, which targets the APU processor 0. We can use the hello world template for the application as all we want to do is check the UART is working properly.

Creating the hello world project

Selecting the Hello World Template

With the hello world project created, it will also create a board support package which contains the drivers for the system.

We need to regenerate the BSP to use UART 1 as the STD IN/OUT as that is the PS UART connected to the UART pins. We can do this by opening the MSS file located under the BSP and selecting modify BSP settings.

Once this is completed the project will need to be recompiled and we are then ready for testing.

For this we need to create a debug configuration which will download the FPGA bit file, configure the PS and download the test application. We do this by clicking on the bug icon on the SDK menu and selecting debug configurations.

Here we want to create a new system debugger application debugger and configure it for standalone operation. This will by default populate the options needed to download the files and run on the Ultra96.

Creating the Debug Configuration

Once the debugger has completed the download and initialization, it will wait at the entry point to the main function for you to run the application.

Before we run the application we should first connect the SDK terminal to the UART so we can see the output from the application.

connecting the UART to SDK Terminal

Once that is complete we can run the application and you will see the hello world in the SDK terminal window.

This proves out the hardware design and our connection to the UART as such we can then continue to develop the PetaLinux.

Step Three: Creating the PetaLinx OS

To create a petalinux application we need to have the matching version of petalinux for our Vivado application. That is if like in this example I generated the project using Vivado 2018.1 I should use the matching PetaLinux version of 2018.1.

The first thing we need to do on our linux machine or linux virtual machine once PetaLinux is installed is to create a new project and import the hardware definition to configure the PetaLinux project.

The Hardware Definition File or HDF contains all of the necessary information (including the bitfile) for the PS and PL to be configured correctly.

This file will be located within the <project name>.sdk directory of your Vivado project if you have exported the hardware. Remember when you export the hardware to include the bitfile.

Using the commands below we can create the project and import the HDF to configure the Petalinux project.

petalinux-create --type project --template zynqMP --name ultra96 cd ultra96 petalinux-config --get-hw-description=/home/ad/shared/u96_linux_hackster/u96_linux_hackster.sdk

Once the project has been created and configured Petalinux will open a system configuration dialog which we can use to configure the PetaLinux installation.

PetaLinux Configuration

All we need to do within this configuration is to update the design to use UART1 as the STD IO. We do this under the subsystem and hardware settings.

Selecting Serial interfaces

Selecting UART 1

Once this is completed exit the dialog saving any changes, this will take you back to the command prompt.

Where we can enter the following command to build PetaLinux:

petalinux-build

Once the build has finished (it may take some time) we can then change directory to the <project> images/linux

Within this directory you will see the PetaLinux image (image.ub) and all of the other files necessary to build the application.

However, we still need to create a boot.bin which combines the FSBl, FPGA bit file, UBoot and of course the PMU software. We do this using the command below, make sure the filenames are correct for each element.

petalinux-package --boot --fsbl zynqmp_fsbl.elf --u-boot u-boot.elf --pmufw pmufw.elf --fpga system.bit

This will create the boot.bin file we can use on the SD card with the image.ub file, copy these onto a SD Card and insert it into the Ultra96.

Once we power on the Ultra96 board we will be able to see the PetaLinux OS booting over the UART output.

Start of PetaLinux Boot Sequence

End of PetaLinux Boot Sequence

With PetaLinux up and running we can log in (use root, root is asked) and look under the sys/class directory to see that we can see the GPIO which we added into the PL

GPIO as detected by the PetaLinux Build

We can confirm this is the correct address by double checking against the Vivado project

Vivado Memory Map confirming PetaLinux Address.

We can then write a user space Linux application which can drive the GPIO. Although of course we want to be able to drive even more of the PL than just a GPIO and we want to be able to make use of the WiFi & Bluetooth which are not enabled in this build. If we want we can add them in the Linux Kernel configuration.

While we could install the WiFi drivers easily, some applications may also want to just add too the existing Ultra96 frame work. We can do this using the Ultra96 BSP.

Method Two: Using the Ultra96 BSP

The Ultra96 BSP provides everything we need to update the reference design which comes with the Ultra96 out of the box.

This means we can add functions into the programmable logic to accelerate design and then access it over using application which use WiFi or Bluetooth quickly and easily.

The BSP is available here

Step One: Updating the Hardware

Once the BSP is downloaded, if you want to make changes to the hardware you will need to unzip the BSP until you can see the hardware directory.

UnZipped BSP allowing hardware to be modified.

Using Vivado you can then open the project file contained below this directory and add in your desired PL functionality.

For this example I added in a BRAM controller and block memory, the rest is exactly as configured in the BSP.

With this completed implement this project using Vivado and export the hardware containing the bit file into a HDF.

Updated design to the Ultra96 BSP

What we need to do now is to create an new project based on the BSP and then update the PetaLinux projects hardware definition to reflect our new design.

Step Two: Updating and Building the BSP

To create the PetaLinux project we need to use the BSP still in its compressed form in our Linux development environment.

To create the project, use the command:

petalinux-create -t project -s xilinx-ultra96-reva-v2018.2-final.bsp

This will create a project based upon the unmodified hardware design.

To update the PetaLinux project to reflect the newly built hardware, we need to read in the HDF we just exported in Vivado.

As before we update the PetaLinux hardware definition using the command:

petalinux-config --get-hw-description=/home/ad/shared petalinux-build petalinux-package --boot --fsbl zynqmp_fsbl.elf --u-boot u-boot.elf --pmufw pmufw.elf --fpga system.bit

We can then build the PetaLinux system and copy the packaged files onto the SD Card and test the system also as before.

However, this time we also need to ensure the SD Card has two partitions one named boot and the other rootfs, this needs to be at least 4GB. Into the boot partition we place the Boot.bin and the image.ub

Booting the system and connecting the Ultra96 to a WiFi network, we can then log in via Putty and test the presence of the BRAM. (Use root and root as the username and password)

When the BRAM was added into the PL using Vivado this was assigned to the address space 0x0080000000 in the system memory map.

We can use use devmem to read and write memory mapped locations.

Therefore, using devmem we can first read the BRAM address to ensure it is 0x00000000.

Once we have confirmed the memory location is empty we can use devmem to write data into the memory and read it back to confirm it was written correctly.

In the example below I wrote 0x55aa55aa into address 0x0080000000 this was successful.

If desired we can write to and test other memory locations in the BRAM.

Resultant Ultra96 application accessing BRAM over WiFi link

This approach using the BSP allows us to take advantage of the full range of Ultra96 capabilities with the greatest ease.

You can find the files associated with this project here:

https://github.com/ATaylorCEngFIET/Hackster

See previous projects here.

More on on Xilinx using FPGA development weekly at MicroZed Chronicles.