Zynq UltraScale+ MPSoC OpenAMP


The purpose of this page is to describe the procedure involved in building and running the firmware for OpenAMP.

Software Requirements

The requirements for PetaLinux 2015.4 and XSDK 2015.4 must be met.

Prerequisites

User should have the basic understanding of Linux, PetaLinux, XSDK and how to boot a Xilinx board using JTAG boot.

Set up PetaLinux with OpenAMP

PetaLinux requires the following preparation before use:
1. Create the PetaLinux master project in a suitable directory (with no space in name).
In this guide it is named <master_root>:
$petalinux-create -t project -s <PATH_TO_PETALINUX_ZYNQMP_PROJECT_BSP>

2. Navigate to the <master_root> directory:
$cd <master_root>

3. Configure the kernel options to work with OpenAMP:
a. Start the PetaLinux kernel config tool:
$petalinux-config -c kernel
b. Enable loadable module support:
Kernel Configuration --->
[*] Enable loadable module support --->
c. Enable user space firmware loading support:
Kernel Configuration --->
Device Drivers --->
Generic Driver Options --->
<*> Userspace firmware loading support
[ ] Include in-kernel firmware blobs in kernel binary
() External firmware blobs to build into the kernel binary
d. Enable the ZynqMP R5 remoteproc driver support:
Kernel Configuration --->
Device Drivers --->
Remoteproc drivers --->
<M> ZynqMP_r5 remoteproc support

4. Enable all of the modules and applications in the RootFS:
IMPORTANT: These options are only available in the PetaLinux reference BSP.
The applications in this procedure are examples you can use.

a. Open the RootFS configuration menu:
$ petalinux-config -c rootfs
b. Ensure the OpenAMP apps are enabled:
Apps --->
[*] echo_test --->
[*] mat_mul_demo --->
[*] proxy_app --->
c. Ensure the OpenAMP modules are enabled:
Modules --->
[*] rpmsg_proxy_dev_driver --->
[*] rpmsg_user_dev_driver --->

Device Tree Settings

• The PetaLinux BSP includes a a Device Tree Binary (DTB) for OpenAMP located at:
pre-built/linux/images/openamp.dtb
• This is built from the Device Tree Source (DTS) located at:
subsystems/linux/configs/device-tree/openamp.dts
• This file is the same as the standard system-top.dts, except it has the following line incorporated:
/include/ "openamp-overlay.dtsi"
• This includes the DTS overlay which is in the PetaLinux BSP, located at:
subsystems/linux/configs/device-tree/openamp-overlay.dtsi
The overlay contains the following nodes, which OpenAMP requires in the device tree:
reserved-memory {
#address-cells = <2>;
#size-cells = <1>;
ranges;
};
amba {
test_r50: zynqmp_r5_rproc0@0 {
compatible = "xlnx,zynqmp-r5-remoteproc-1.0";
reg = <0x0 0xff340000 0x100>, <0x0
0xff9a0000 0x400>, <0x0 0xff5e0000 0x400>;
reg-names = "ipi", "rpu_base", "apb_base";
core_conf = "split0";
method = "direct";
interrupt-parent = <&gic>;
interrupts = <0 29 4>;
} ;
} ;

Build the Remote Firmware in XSDK

The remote firmware can be built using XSDK by following the steps below:

Create the Cortex-R5 BSP with the OpenAMP library

1. Create a new board support package
File -> New -> Board Support Package
a.When asked to specify the hardware platform support, specify a hardware file (*.hdf) for Zynq UltraScale+ MPSoC
This can be found in the subsystems/linux/hw-description folder in the PetaLinux directory
b.Specify the CPU as ‘psu_cortexr5_0’
c.Specify the Board Support Package OS as ‘standalone’
The Board Support Package Settings will appear

2. Navigate to the Board Support Package Settings Overview
Settings -> Overview
a.Select xilopenamp library

3. Navigate to the Board Support Package Settings Drivers
Settings -> Overview -> Drivers -> psu_cortexr5_0
a.Add -DUSEAMP=1 to the extra_compiler_flags (only for 2015.3 and 2015.4 releases)

Create the Cortex-R5 Firmware for OpenAMP

1. Create the application project
File -> New -> Application Projects
a.Specify the OS Platform as ‘standalone’
b. Specify the hardware platform (hardware file for the MPSoC created in the previous procedure).

c.Select the processor as ‘psu_cortexr5_0’
d.Select Use Existing to use the BSP created in the previous procedure or select Create New BSP to create a new BSB.


Note:
If you select Create New BSP the xilopenamp library is automatically included, but the USEAMP flag still needs to be set as described in step 4 of the previous procedure.
e.Click Next to select an available template (do not click Finish).

2. Select one of the three application templates available for OpenAMP remote bare metal from the Available Templates:
OpenAMP echo-test
OpenAMP matrix multiplication Demo
OpenAMP RPC Demo
3.Click Finish

Include the Cortex-R5 Firmware in the PetaLinux Project

After you have developed and built the remote firmware, the firmware needs to be included in the PetaLinux project.
1. Ensure that you are in the PetaLinux project root directory:
$cd <master_root>
2. Create a PetaLinux application inside the components/apps/<app_name> directory:
$petalinux-create -t apps --template install -n <app_name> --enable
3. Copy the firmware image built with XSDK into this directory:
$components/apps/<app_name>/data
4. Modify the Makefile to install the firmware in the RootFS. for example:
install:
$(TARGETINST) -d -p 755 data/<myfirmware> /lib/firmware/<myfirmware>
5. Rebuild the PetaLinux project.

TIP: If you are using the PetaLinux BSP and want to try one of the demo applications, you can copy the firmware to: components/apps/<app_name>/data/.

Build the Applications and Linux Project

Proceed as follows to build the applications and Linux project:
1. Ensure that you are in the PetaLinux project root directory:
$cd <master_root>
2. Build PetaLinux:
$petalinux-build

TIP: If you encounter any issues append –v to petalinux-build to see the respective textual output. If the build is successful, the images will be located in the image/linux folder:
<master_root>/images/linux

Boot the PetaLinux Project

You can boot the PetaLinux Project from QEMU or hardware.

Boot on QEMU

After a successful build, the PetaLinux project can be run on QEMU.
1. Navigate to the PetaLinux directory:
$cd <master_root>
2. Run PetaLinux boot:
$petalinux-boot --qemu --kernel

Boot on Hardware

After a successful build, the PetaLinux project can be run on hardware. Follow these procedures to boot OpenAMP on a board.
Set Up the Board
1. Connect the board to your computer, and ensure that it is powered on.
2. Program the relevant bitstreams to the board. Ensure that it is using RTL v5.2 This must be done separately from PetaLinux.
3. If the board is connected to a remote system, start hw_server on the remote system.
4. Open a console terminal and connect it to the board’s UART.
Download the Images
1. Navigate to the PetaLinux directory:
$cd <master_root>
2. Run the PetaLinux boot:
° Using a remote system:
$petalinux-boot --jtag --kernel --hw_server-url <remote_system>
° Using a local system:
$petalinux-boot --jtag --kernel

TIP: If you encounter any issues append –v to the above commands to see the textual output.

Run the Example Applications

After the system is up and running, log in with the username and password root. After logging in, the following example applications are available:
Run the Echo Test
1. Load the Echo Test firmware and driver. This loads the remoteproc and RPMsg modules:
modprobe zynqmp_r5_remoteproc firmware=r5_image_echo_test
modeprobe rpmsg_user_dev_driver
2. Run the test:
echo_test
3. The test starts, follow the on screen instructions to complete the test.
4. After you have completed the test, unload the application:
modeprobe –r rpmsg_user_dev_driver
modprobe –r zynqmp_r5_remoteproc

IMPORTANT: After you have exited the application, you must unload and re-load the module if you want to re-run the test.

Run the Matrix Multiplication Test
1. Load the Matrix Multiply application. This loads the remoteproc, RPMsg modules and applications”
modprobe zynqmp_r5_remoteproc firmware=r5_image_matrix_multiply
modeprobe rpmsg_user_dev_driver
2. Run the test:
mat_mul_demo
3. The test starts, follow the on screen instructions to complete the test.
4. After you have completed the test, unload the app:
modeprobe –r rpmsg_user_dev_driver
modprobe –r zynqmp_r5_remoteproc

IMPORTANT: After you have exited the application, you must unload and re-load the module if you want to re-run the test.

Run the Proxy Application
1. Load and run the proxy application in one step. The proxy application automatically loads the required modules:
proxy_app
2. When the application prompts you to Enter name enter a string.
3. When the application prompts you to Enter age enter an integer.
4. When the application prompts you to Enter value for p enter a floating point number.
5. The application then prompts you to re-run the test.
6. After you exit the application the module unloads automatically.

GOING FURTHER...

The following information describe the files and API to help you start and modify the above example applications or write your own

OpenAMP XSDK Key Source Files

Platform Info (platform_info.c)

This file contains APIs and hardcoded values which are used to get platform specific information for OpenAMP. There are two core functions, a number of hardcoded values and a platform specific array in the file. There is documentation included in the source file, some of which is shown below:

#define VRING1_IPI_INTR_VECT

This is the IPI interrupt vector for the Cortex-R5 (RPU).

struct hil_proc proc_table (Array)

This array provides definition of CPU nodes for master and remote context. It contains two nodes because the same file is intended to use with both master and remote configurations. On Zynq platform only one node definition is required for master/remote as there are only two cores present in the platform.

platform_get_processor_info

Copies the target info from the user defined data structures to HIL proc data structure.In case of remote contexts this function is called with the reserved CPU ID HIL_RSVD_CPU_ID, because for remotes there is only one master.
Usage
int platform_get_processor_info(struct hil_proc *proc , int cpu_id)
Arguments
Proc - HIL proc to populate
cpu_id - CPU ID
Returns
Status of execution
platform_get_processor_for_fw
Not used

Resource Table (rsc_table.c/.h)

The resource table contains entries that specify the memory and virtIO device resources. This contains information on the firmware ELF start address and size. The virtIO device contains device features, vring addresses, size and alignment information. The resource table entries are specified in rsc_table.c and the remote_resource_table structure is specified in rsc_table.h.

Bare Metal (baremetal.c/.h)

The bare metal files contain platform specific APIs that allow the remote application to communicate with the hardware. In this case it includes functions to initialize and control the GIC, interrupts, memory mappings, platform cache and IPI.

Platform (platform.c/.h)

These files contain the platform specific implementation of the Inter Process Communication (IPC) hardware layer interface. It contains platform specific operations, for controlling the IPI, CPUs and interrupts.



RPMsg Development

The RPMsg APIs provided by the OpenAMP framework allow bare metal or RTOS applications to perform Inter Process Communication (IPC) in an AMP configuration, running on either a master or remote processor.

API Functions

The key functions for the remoteproc OpenAMP framework are described in this section. This information is based on the documentation available in the remoteproc.h header file.

rpmsg_sendto

Sends a message containing data and payload length to the destination address of the remote processor respective to the rpdev channel using the rpdev's source address. If there
are no Tx buffers available, the function remains blocked until one becomes available, or a time-out of 15 seconds elapses. When the latter occurs, ERESTARTSYS is returned. Presently, this API can only be called from process context.
Usage
static inline int rpmsg_sendto( struct rpmsg_channel *rpdev,
void *data, int len, unsigned long dst)
Arguments
rpdev - the rpmsg channel
data - payload of message
len - length of payload
dst - destination address
Returns
Returns 0 on success and an appropriate error value on failure.

rpmsg_send

Sends a message containing data and payload length to the destination address of the remote processor respective to the rpdev channel using the rpdev's source and destination address. If there are no Tx buffers available, the function remains blocked until one becomes available, or a time-out of 15 seconds elapses. When the latter occurs, ERESTARTSYS is returned. Presently, this API can only be called from process context.
Usage
static inline int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len)
Arguments
rpdev - the rpmsg channel
data - payload of message
len - length of payload
Returns
Returns 0 on success and an appropriate error value on failure.

rpmsg_send_offchannel

Sends a message containing data and payload length to the destination address of the remote processor respective to the rpdev channel using src as the source address. If there
are no Tx buffers available, the function remains blocked until one becomes available, or a time-out of 15 seconds elapses. When the latter occurs, ERESTARTSYS is returned. Presently, this API can only be called from process context.
Usage
static inline int rpmsg_send_offchannel( struct rpmsg_channel *rpdev,
unsigned long src, unsigned long dst,
void *data, int len)
Arguments
rpdev - the rpmsg channel
src - source address
dst - destination address
data - payload of message
len - length of payload
Returns
Returns 0 on success and an appropriate error value on failure.

rpmsg_trysend

Sends a message containing data and payload length to the destination address of the remote processor respective to the rpdev channel using the rpdev's source and destination
addresses. If there are no Tx buffers available, the function immediately returns ENOMEM without waiting until one becomes available. Presently, this API can only be called from
process context.
Usage
static inline int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len)
Arguments
rpdev - the rpmsg channel
data - payload of message
len - length of payload
Returns
Returns 0 on success and an appropriate error value on failure.

rpmsg_trysendto

Sends a message containing data and payload length to the destination address of the remote processor respective to the rpdev channel using the rpdev's source addresses. If
there are no Tx buffers available, the function immediately returns ENOMEM without waiting until one becomes available. Presently, this API can only be called from process
context.
Usage
static inline int rpmsg_trysendto( struct rpmsg_channel *rpdev,
void *data, int len, unsigned long dst)
Arguments
rpdev - the rpmsg channel
data - payload of message
len - length of payload
dst - destination address
Returns
Returns 0 on success and an appropriate error value on failure.

rpmsg_trysend_offchannel

Sends a message containing data and payload length to the destination address of the remote processor respective to the rpdev channel using src as the source address. If there
are no Tx buffers available, the function immediately returns ENOMEM without waiting until one becomes available. Presently, this API can only be called from process context.
Usage
static inline int rpmsg_trysend_offchannel( struct rpmsg_channel *rpdev, unsigned long src,
unsigned long dst,
void *data, int len)
Arguments
rpdev - the rpmsg channel
src - source address
dst - destination address
data - payload of message
len - length of payload
Returns
Returns 0 on success and an appropriate error value on failure.

rpmsg_init

Allocates and initializes the rpmsg driver resources for a given device id (cpu id). The successful return from this function enables the IPC link.
Usage
int rpmsg_init( int dev_id, struct remote_device **rdev,
rpmsg_chnl_cb_t channel_created,
rpmsg_chnl_cb_t channel_destroyed,
rpmsg_rx_cb_t default_cb, int role);
Arguments
param dev_id - rpmsg remote device for which driver is to
* be initialized
* @param rdev - pointer to newly created remote device
* @param channel_created - callback function for channel creation
* @param channel_destroyed - callback function for channel deletion
* @default_cb - default callback for channel
* @param role - role of the other device, Master or Remote
Returns
Status of function execution

rpmsg_deinit

Releases the rpmsg driver resources for a given remote instance.
Usage
void rpmsg_deinit(struct remote_device *rdev);
Arguments
rdev - pointer to device de-init
Returns
None

rpmsg_get_buffer_size

Returns buffer size available for sending messages.
Usage
int rpmsg_get_buffer_size(struct rpmsg_channel *rp_chnl)
Arguments
Channel - pointer to rpmsg channel/device
Returns
Buffer size

rpmsg_create_channel

Creates RPMSG channel with the given name for remote device.

Usage

struct rpmsg_channel *rpmsg_create_channel(struct remote_device *rdev, char *name);

Arguments

rdev - pointer to rpmsg remote device
name - channel name

Returns

Pointer to new rpmsg channel

rpmsg_delete_channel

Deletes the given RPMSG channel. The channel must first be created with the rpmsg_create_channel API.

Usage

void rpmsg_delete_channel(struct rpmsg_channel *rp_chnl);

Arguments

rp_chnl - pointer to rpmsg channel to delete

Returns

None