OpenAMP Base Hardware Configurations

This document describes the base set of hardware required for OpenAMP to operate successfully as presented in Xilinx Vitis OpenAMP and libmetal template examples.

Table of Contents

Introduction

This document describes the base set of hardware required for OpenAMP to operate successfully as presented in Xilinx Vitis OpenAMP and libmetal template examples. It covers configurations for the RPU memory, shared memory for both the APU and RPU, generic interrupt controllers (GIC) and the inter-processor interconnect (IPI) interrupts. There are several examples of using Vitis, PetaLinux and OpenAMP, however this is not a tutorial for these tools. The reader is encouraged to refer to Xilinx user guides listed in the Related Links.

OpenAMP on Xilinx SoCs

Xilinx SoC products combine a set of heterogeneous hardware designs into one powerful and flexible platform that includes Arm Cortex-Ax, Cortex-R5 and Xilinx MicroBlaze processors. The OpenAMP project enables a distributed software architecture across this asymmetric multiprocessing platform (AMP).

There are two components of the OpenAMP framework:

  1. Life Cycle Management (LCM) : enables a master processor to load, start and stop a firmware on a remote processor.

  2. Inter-Processor Communication (IPC): interrupts, shared memory and message passing between the master and the remote processor.

Both the LCM and the IPC in the demos are using the remoteproc and the rpmsg drivers in the Linux kernel.  Xilinx SoCs based systems should provide access to a specific set of hardware resources in order to use the OpenAMP framework. Depending on the OpenAMP or libmetal demo this includes:

  1. Processing cores; need at least two:

    1. application processing unit (APU)

    2. real-time processing unit (RPU) for Versal and ZynqMP

  2. Memory for the processing system (PS): APU (master), RPU (remote) and shared memory:

    1. DDR

    2. Tightly-coupled memory (TCM)

    3. On-chip memory (OCM)

  3. Interrupts:

    1. APU GIC interrupts

    2. RPU GIC interrupts

    3. IPI interrupts

The next section tries to generalize each hardware component as it is used in the OpenAMP, followed by specific example of configuring some demos with the default values and another section showing how to change the defaults.

Zynq UltraScale+ MPSoC and Versal

LCM: Memory for RPU Firmware

Most demos use the DDR or the Tightly Coupled Memory (TCM) for RPU firmware text and data depending on the linker script (e.g.: linker_remote.ld) while the stack, heap and the interrupt vector table are in the TCM. The RPU is configured to enable the TCM interfaces from reset with base address 0 for TCMA. The Linux remoteproc driver on the APU will preload the TCMA with the boot code while the RPU is halted. When the halt is removed the RPU starts fetching instructions from the reset vector address in the normal way. The RPU's firmware .vectors section is loaded into TCMA at address 0 (see linker script). It starts with a set of branch instructions called _vector_table and a few code blocks labeled _boot, init and a function Init_MPU(). When the remoteproc driver requests RPU start the execution begins at address 0 in TCMA with a jump to _boot which calls Init_MPU(). All this is fetched from and executed in the TCMA. At the end the _boot jumps to _startup which could be in the DDR. The _startup prepares the "C runtime" and calls main().

Firmware load memory

ZynqMP / Versal

Firmware load memory

ZynqMP / Versal

RPU

0x3ED0_0000,  see linker script

APU kernel DT

0x3ED0_0000,  see rproc_0_reserved in the Linux Device Tree 

APU app

not used

IPC: Shared Memory

The OpenAMP demos have a .resource_table ELF section as in this example linker_remote.ld. It has list of system resources required by the Linux remoteproc, e.g.: virtIO device data used by rpmsg, see rsc_table.h and rsc_table.c.

The default location for shared memory is the DDR.

SHM_BASE_ADDR

ZynqMP / Versal

SHM_BASE_ADDR

ZynqMP / Versal

RPU

E.g. 0x3ED8_0000,  see metal_dev_table,  SHARED_MEM_PA, SHARED_MEM_SIZE, SHARED_BUF_OFFSET

APU kernel DT

E.g. 0x3ED8_0000, see reserved-memory nodes in the Linux Device Tree 

APU app: libmetal_amp_demo

UIO nodes names for zynqMP:

1 2 3 SHM_DEV_NAME "3ed80000.shm" IPI_DEV_NAME="ff340000.ipi" (if Versal=) "ff360000.ipi" TTC_DEV_NAME="ff110000.timer" (if Versal=) "ff0e0000.ttc0"

APU app: openamp demos

1 /sys/bus/rpmsg/devices/virtio0.rpmsg-openamp-demo-channel.-1.0

IPC: Interrupts

Recommended Reading

  •  UG1085                  Chapter 13: Interrupts

  •  Versal-ACAP-TRM  Chapter 52: Inter-Processor Interrupts

The OpenAMP and Libmetal demos use the IPI interrupts to enable one processor (source agent) to interrupt another processor (destination agent). The communications process uses the IPI interrupt register structure, the system interrupt structure, and the IPI message buffers. On the RPU’s firmware side these are represented by the struct metal_device ipi_device and IPI_CHN_BITMASK in platform_info.c.

GIC

IRQ System Interrupts: see IPI_IRQ_VECT_ID

GIC

IRQ System Interrupts: see IPI_IRQ_VECT_ID

Versal

63      See "IRQ System Interrupts" in Versal-ACAP-TRM for IPI1 "IPI 1 interrupt";

ZynqMP

65      or XPAR_XIPIPSU_0_INT_ID  See "system interrupts"  in UG1085 for RPU0 IPI_Ch1  

The demos need two IPI channels (and IPI masks) one for the APU and one for the RPU. These are selected from the set of seven reprogrammable IPIs. Other system users might be requesting IPI channels. Both the firmware and the Linux device tree should be checked to detect and avoid conflicts.


ZynqMP

IPI Channel

Base Addr

IPI_MASK  / IPI_CHN_BITMASK

See UG1087 Register Reference

References

Send to Ch_7

Send to Ch_1

RPU_0

Ch_1

0xFF31_0000

0x0100_0000 (set bit 24)



ipi_device, IPI_BASE_ADDR, IPI_CHN_BITMASK in platform_info.h

APU

Ch_7

0xFF34_0000



0x100 (set bit 8)



"xlnx,ipi-id" property, zynqmp_ipi1 node in ZynqMP .dtsi :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 zynqmp_ipi1 { compatible = "xlnx,zynqmp-ipi-mailbox"; interrupt-parent = <&gic>; interrupts = <0 29 4>; xlnx,ipi-id = <7>; #address-cells = <1>; #size-cells = <1>; ranges; /* APU<->RPU0 IPI mailbox controller */ ipi_mailbox_rpu0: mailbox@ff90000 { reg = <0xff990600 0x20>, <0xff990620 0x20>, <0xff9900c0 0x20>, <0xff9900e0 0x20>; reg-names = "local_request_region", "local_response_region", "remote_request_region", "remote_response_region"; #mbox-cells = <1>; xlnx,ipi-id = <1>; }; };

In Versal the channel names and numbers are different, see Versal-ACAP-TRM


Versal

IPI Channel

Base Addr

IPI_MASK / IPI_CHN_BITMASK

Versal ACAP Register Reference

References

Send to ipi3

Send to ipi1

RPU_0

ipi1

0xFF34_0000

0x20  (set bit 5)

 

ipi_device, IPI_BASE_ADDR, IPI_CHN_BITMASK in platform_info.h

APU

ipi3

0xFF36_0000



0x8 (set bit 3)

"xlnx,ipi-id" property, zynqmp_ipi1 node in Versal .dtsi. The mapping of ipi-id to channel is not obvious and it’s not the same as in ZynqMP.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 zynqmp_ipi1 { compatible = "xlnx,zynqmp-ipi-mailbox"; interrupt-parent = <&gic>; interrupts = <0 33 4>; xlnx,ipi-id = <5>; #address-cells = <1>; #size-cells = <1>; ranges; /* APU<->RPU0 IPI mailbox controller */ ipi_mailbox_rpu0: mailbox@ff990600 { reg = <0xff3f0ac0 0x20>, <0xff3f0ae0 0x20>, <0xff3f0740 0x20>, <0xff3f0760 0x20>; reg-names = "local_request_region", "local_response_region", "remote_request_region", "remote_response_region"; #mbox-cells = <1>; xlnx,ipi-id = <3>; }; };

Build and Run Demos

Build RPU firmware

Step 1. Launch Vitis IDE and select a workspace directory with enough free space and fast access. Local disk is prefered as some NFS can be slow.

Step 2. Create a new application project using "File > New Application Project".  This will start a wizard that will guide you through the process.

  • Create a new platform from hardware description XSA file. Select from the included XSA’s or use browse and import your own XSA.

  • Specify the application project name. E.g.: lm_amp and select psu_cortexr5_0 as the target processor from the list.

  • Select template for your project, e.g. "Libmetal AMP Application" and click "Finish":

Processor for the RPU firmware project

Domain: FreeRTOS or generic

Libmetal AMP Demo, OpenAMP echo-test,..

Processor for the RPU firmware project

Domain: FreeRTOS or generic

Libmetal AMP Demo, OpenAMP echo-test,..

Configuration of some hardware blocks used in this demo must match between the RPU’s firmware and APU’s Linux kernel, device tree and the user application. Please, see the "Hardware specification" tab to examine the address maps for APU and RPU processors:

  1. Memory: DDR and Tightly-coupled memory (TCM)

  2. Interrupts: PS-to-PS interrupts and Inter-processor interrupts (IPI)

  3. Timers: Triple Timer Counters (TTC)

Hardware specification: Memory Maps

IPI:  psu_ipi_1 example

Linker Script: lscript.ld

Hardware specification: Memory Maps

IPI:  psu_ipi_1 example

Linker Script: lscript.ld

Firmware memory map: Select lscript.ld file in the source Explorer. The summary view shows the memory regions for this application:

  1. The processor is configured to enable the TCM interfaces from reset with base address 0x0 for TCMA and 0x20000 for TCMB. This demo does not use TCMB.

  2. The stack, heap and the interrupt vector table are in psu_r5_atcm_MEM_0, TCMA.

  3. The text, read-only, initialized, uninitialized data sections, etc. are in psu_r5_ddr_0_MEM_0 starting at 0x3ED00000 with LENGTH=0x80000.

  4. OpenAMP demos have .resource_table ELF section. It has list of system resources required by the Linux remoteproc, e.g.: virtIO device data.

  5. The Linux remoteproc driver on the APU will preload the TCMA with the boot code while halting the RPU via nCPUHALTm pin. When the nCPUHALTm pin is deasserted, the processor starts fetching instructions from the reset vector address in the normal way. The FreeRTOS _vector_table is loaded into TCMA at address 0x0 followed by _boot and Init_MPU().

  6. The execution starts at address 0x0 in TCMA via a jump to _boot which calls Init_MPU(). All this is fetched from and executed in the TCMA. At the end the _boot jumps to _startup in the DDR. The _startup prepares the "C runtime" and calls main(). Starting from _startup the RPU fetches its instructions from the DDR while its stack and heap are in the TCMA.

Step 3. Build the application project: click lm_amp or lm_amp_system in the source Explorer and select "Project > Build Project". The build produces an ARM CortexR5 binary in the Debug (or Release) directory called lm_amp.elf. In this example this binary will be loaded to execute on the RPU (r5_0) via Xilinx remoteproc Linux driver.

Build PetaLinux

Create a PetaLinux project using the BSP where we took the XSA file for the RPU firmware:

  1. 1 2 source <path_to_petalinux_settings> petalinux-create -t project -s <path_to_petalinux_project_bsp>
  2. Depending on the selected demo you have to enable the following kernel config options (see: project-spec/meta-user/recipes-kernel/linux/linux-xlnx_%.bbappend and the kernel config file (e.g.: defconfig) it includes):

    1 2 3 4 5 6 7 8 CONFIG_UIO_PDRV_GENIRQ=m CONFIG_RPMSG_CHAR=m CONFIG_RPMSG_VIRTIO=m CONFIG_RPMSG=m CONFIG_VIRTIO=m CONFIG_REMOTEPROC=y CONFIG_ZYNQMP_R5_REMOTEPROC=m CONFIG_SPARSEMEM_VMEMMAP=y
  3. Copy one of the following device tree overlay files into <plnx-proj>/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi depending on the demo selected for the RPU firmware:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 / { reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; rproc_0_reserved: rproc@3ed000000 { no-map; reg = <0x0 0x3ed00000 0x0 0x40000>; }; }; zynqmp-rpu { compatible = "xlnx,zynqmp-r5-remoteproc-1.0"; #address-cells = <2>; #size-cells = <2>; ranges; core_conf = "lockstep"; reg = <0x0 0xFF9A0000 0x0 0x10000>; r5_0: r5@0 { #address-cells = <2>; #size-cells = <2>; ranges; memory-region = <&rproc_0_reserved>; pnode-id = <0x7>; tcm_0_a: tcm_0@0 { reg = <0x0 0xFFE00000 0x0 0x10000>; pnode-id = <0xf>; }; tcm_0_b: tcm_0@1 { reg = <0x0 0xFFE20000 0x0 0x10000>; pnode-id = <0x10>; }; }; }; /* Shared memory */ shm0: shm@0 { compatible = "shm"; reg = <0x0 0x3ed80000 0x0 0x1000000>; }; /* IPI device */ ipi0: ipi@0 { compatible = "ipi_uio"; reg = <0x0 0xff340000 0x0 0x1000>; interrupt-parent = <&gic>; interrupts = <0 29 4>; }; }; &ttc0 { /* compatible = "ttc"; timer@ff110000 */ status = "okay"; }; &ttc1 { /* compatible = "ttc"; timer@ff120000 */ status = "okay"; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 / { reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; rproc_0_reserved: rproc@3ed000000 { no-map; reg = <0x0 0x3ed00000 0x0 0x2000000>; }; }; zynqmp-rpu { compatible = "xlnx,zynqmp-r5-remoteproc-1.0"; #address-cells = <0x2>; #size-cells = <0x2>; ranges; core_conf = "lockstep"; reg = <0x0 0xff9a0000 0x0 0x10000>; r5_0: r5@0 { #address-cells = <0x2>; #size-cells = <0x2>; ranges; memory-region = <&rproc_0_reserved>; pnode-id = <0x18110005>; tcm_0@0 { reg = <0x0 0xffe00000 0x0 0x10000>; pnode-id = <0x1831800b>; }; tcm_0@1 { reg = <0x0 0xffe20000 0x0 0x10000>; pnode-id = <0x1831800c>; }; }; }; /* Shared memory */ shm0: shm@0 { compatible = "shm_uio"; reg = <0x0 0x3ed80000 0x0 0x1000000>; }; /* IPI device */ ipi_amp: ipi@ff360000 { compatible = "ipi_uio"; reg = <0x0 0xff360000 0x0 0x1000>; interrupt-parent = <&gic>; interrupts = <0 33 4>; }; ttc0 { compatible = "ttc-uio"; reg = <0x0 0xFF0E0000 0x0 0x1000>; }; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 / { reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; rpu0vdev0vring0: rpu0vdev0vring0@3ed40000 { no-map; reg = <0x0 0x3ed40000 0x0 0x4000>; }; rpu0vdev0vring1: rpu0vdev0vring1@3ed44000 { no-map; reg = <0x0 0x3ed44000 0x0 0x4000>; }; rpu0vdev0buffer: rpu0vdev0buffer@3ed48000 { no-map; reg = <0x0 0x3ed48000 0x0 0x100000>; }; rproc_0_reserved: rproc@3ed00000 { no-map; reg = <0x0 0x3ed00000 0x0 0x40000>; }; }; zynqmp-rpu { compatible = "xlnx,zynqmp-r5-remoteproc-1.0"; #address-cells = <2>; #size-cells = <2>; ranges; core_conf = "lockstep"; reg = <0x0 0xFF9A0000 0x0 0x10000>; r5_0: r5@0 { #address-cells = <2>; #size-cells = <2>; ranges; memory-region = <&rproc_0_reserved>, <&rpu0vdev0buffer>, <&rpu0vdev0vring0>, <&rpu0vdev0vring1>; pnode-id = <0x7>; mboxes = <&ipi_mailbox_rpu0 0>, <&ipi_mailbox_rpu0 1>; mbox-names = "tx", "rx"; tcm_0_a: tcm_0@0 { reg = <0x0 0xFFE00000 0x0 0x10000>; pnode-id = <0xf>; }; tcm_0_b: tcm_0@1 { reg = <0x0 0xFFE20000 0x0 0x10000>; pnode-id = <0x10>; }; }; }; zynqmp_ipi1 { compatible = "xlnx,zynqmp-ipi-mailbox"; interrupt-parent = <&gic>; interrupts = <0 29 4>; xlnx,ipi-id = <7>; #address-cells = <1>; #size-cells = <1>; ranges; /* APU<->RPU0 IPI mailbox controller */ ipi_mailbox_rpu0: mailbox@ff90000 { reg = <0xff990600 0x20>, <0xff990620 0x20>, <0xff9900c0 0x20>, <0xff9900e0 0x20>; reg-names = "local_request_region", "local_response_region", "remote_request_region", "remote_response_region"; #mbox-cells = <1>; xlnx,ipi-id = <1>; }; }; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 / { reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; rpu0vdev0vring0: rpu0vdev0vring0@3ed40000 { no-map; reg = <0x0 0x3ed40000 0x0 0x4000>; }; rpu0vdev0vring1: rpu0vdev0vring1@3ed44000 { no-map; reg = <0x0 0x3ed44000 0x0 0x4000>; }; rpu0vdev0buffer: rpu0vdev0buffer@3ed48000 { no-map; reg = <0x0 0x3ed48000 0x0 0x100000>; }; rproc_0_reserved: rproc@3ed00000 { no-map; reg = <0x0 0x3ed00000 0x0 0x40000>; }; }; zynqmp-rpu { compatible = "xlnx,zynqmp-r5-remoteproc-1.0"; #address-cells = <2>; #size-cells = <2>; ranges; core_conf = "lockstep"; reg = <0x0 0xFF9A0000 0x0 0x10000>; r5_0: r5@0 { #address-cells = <2>; #size-cells = <2>; ranges; memory-region = <&rproc_0_reserved>, <&rpu0vdev0buffer>, <&rpu0vdev0vring0>, <&rpu0vdev0vring1>; pnode-id = <0x18110005>; mboxes = <&ipi_mailbox_rpu0 0>, <&ipi_mailbox_rpu0 1>; mbox-names = "tx", "rx"; tcm_0_a: tcm_0@0 { reg = <0x0 0xFFE00000 0x0 0x10000>; pnode-id = <0x1831800b>; }; tcm_0_b: tcm_0@1 { reg = <0x0 0xFFE20000 0x0 0x10000>; pnode-id = <0x1831800c>; }; }; }; zynqmp_ipi1 { compatible = "xlnx,zynqmp-ipi-mailbox"; interrupt-parent = <&gic>; interrupts = <0 33 4>; xlnx,ipi-id = <5>; #address-cells = <1>; #size-cells = <1>; ranges; /* APU<->RPU0 IPI mailbox controller */ ipi_mailbox_rpu0: mailbox@ff990600 { reg = <0xff3f0ac0 0x20>, <0xff3f0ae0 0x20>, <0xff3f0740 0x20>, <0xff3f0760 0x20>; reg-names = "local_request_region", "local_response_region", "remote_request_region", "remote_response_region"; #mbox-cells = <1>; xlnx,ipi-id = <3>; }; }; };

The following descriptions refer to the "ZyncMP Libmetal AMP Demo" device tree overlay (.dtsi).

  • The <reg> property of the rproc_0_reserved node has the same memory region as the one selected in the lscript.ld in the Vitis application. This allows Xilinx Linux remoteproc driver to take our lm_amp CortexR5 binary (from /lib/firmware), parse its ELF headers and set up the segments in the DDR where the RPU can read and execute the text and access the shared data.

  • The <reg> property of shm0 node is included for Linux UIO to let user space demo to talk to our firmware application lm_amp on the RPU.

  • Cross-reference for Shared memory: the Linux device tree (system-user.dtsi), RPU linker script (lscript.ld), metal_dev_table from freertos/zynqmp_r5/zynqmp_amp_demo/sys_init.c

 

firmware ELF: text, data, etc. sections

shared memory

RPU

lscript.ld: ORIGIN = 0x3ED00000, LENGTH = 0x00080000

/* 0x80000 is excessive. Can be 0x40000 as in the DT */

SHM_BASE_ADDR 0x3ED80000

.size = 0x1000000    /* sys_init.c  metal_dev_table  */

APU kernel DT

rproc_0_reserved: reg = <0x0 0x3ed00000 0x0 0x40000>;

shm0:  reg = <0x0 0x3ed80000 0x0 0x1000000>;

APU app

/* not referenced via UIO */

SHM_DEV_NAME "3ed80000.shm"      /* common.h */

  • Cross-reference for the IPI. The ipi0 node for Linux UIO uses IPI Channel 7 with base address 0xFF34_0000, while the RPU uses its default IPI Channel-1 with BASE_ADDRESS 0xFF31_0000.

 

IPI Channel

Base Addr

IPI_MASK

References

Send IPI to Ch_7

Send IPI to Ch_1

RPU_0

Ch_1

0xFF31_0000

0x100_0000 (set bit 24)

 

IPI_MASK   0x1000000      in  common.h

APU

Ch_7

0xFF34_0000

 

0x100  (set bit 8)

-DCONFIG_IPI_MASK=0x100  in  CMakeLists.txt

  • The RPU code uses metal_dev_table from sys_init.c, common.h, CMakeLists.txt

RPU

IPI_BASE_ADDR=0xFF310000;             /* metal_dev_table, CMakeLists.txt */

APU kernel DT

reg = <0x0 0xff340000 0x0 0x1000>;        /*   ipi0  node */

APU app

IPI_DEV_NAME="ff340000.ipi"         /* common.h, CMakeLists.txt   */

  • Cross-reference for the TTCs, mapped via UIO for the Linux demo app

RPU

TTC0_BASE_ADDR=0xFF0E0000;      /* metal_dev_table, CMakeLists.txt */

APU kernel DT

&ttc0                             /* enable TTC0 via the reference to ttc0 node */

APU app

TTC_DEV_NAME="ff110000.timer"       /* common.h, CMakeLists.txt   */

Build the PetaLinux project and package the BOOT.bin for Versal

1 2 petalinux-build petalinux-package --boot --plm --psmfw --u-boot --dtb

For other architectures please see UG1144 PetaLinux Tools.

Changing the Defaults

LCM: TCM for RPU Firmware

Some OpenAMP demos are configured to have their text segment in the DDR memory. This allows larger firmware to run on RPU, but it might be slower compared to running from the TCM. According to Cortex-R5 Technical Reference Manual: “A ATCM typically holds interrupt or exception code that must be accessed at high speed, without any potential delay resulting from a cache miss.  A BTCM typically holds a block of data for intensive processing, such as audio or video processing.” Another reason to run from the TCM is power usage, i.e. if the APU decides  to suspend the DDR can be powered down or put into retention mode to save power.

The linker script controls the location of the ELF sections of the RPU firmware. For example, to run with .vectors, .text and .note.gnu.build-id  in psu_r5_atcm_MEM_0, and  with everything else in psu_r5_btcm_MEM_0, use the following linker script:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 _STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x2000; _HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x4000; _ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024; _SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048; _IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024; _FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024; _UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024; /* Define Memories in the system */ MEMORY { psu_r5_atcm_MEM_0 : ORIGIN = 0x0, LENGTH = 0x10000 psu_r5_btcm_MEM_0 : ORIGIN = 0x20000, LENGTH = 0x10000 psu_r5_ddr_0_MEM_0 : ORIGIN = 0x3ed00000, LENGTH = 0x80000 } /* Specify the default entry point to the program */ ENTRY(_boot) /* Define the sections, and where they are mapped in memory */ SECTIONS { .vectors : { KEEP (*(.vectors)) *(.boot) } > psu_r5_atcm_MEM_0 .text : { *(.text) *(.text.*) *(.gnu.linkonce.t.*) *(.plt) *(.gnu_warning) *(.gcc_execpt_table) *(.glue_7) *(.glue_7t) *(.vfp11_veneer) *(.ARM.extab) *(.gnu.linkonce.armextab.*) *(.note.gnu.build-id) } > psu_r5_atcm_MEM_0 .note.gnu.build-id : { KEEP (*(.note.gnu.build-id)) } > psu_r5_atcm_MEM_0 .init : { KEEP (*(.init)) } > psu_r5_btcm_MEM_0 .fini : { KEEP (*(.fini)) } > psu_r5_btcm_MEM_0 .interp : { KEEP (*(.interp)) } > psu_r5_btcm_MEM_0 .note-ABI-tag : { KEEP (*(.note-ABI-tag)) } > psu_r5_btcm_MEM_0 .rodata : { __rodata_start = .; *(.rodata) *(.rodata.*) *(.gnu.linkonce.r.*) __rodata_end = .; } > psu_r5_btcm_MEM_0 .rodata1 : { __rodata1_start = .; *(.rodata1) *(.rodata1.*) __rodata1_end = .; } > psu_r5_btcm_MEM_0 .sdata2 : { __sdata2_start = .; *(.sdata2) *(.sdata2.*) *(.gnu.linkonce.s2.*) __sdata2_end = .; } > psu_r5_btcm_MEM_0 .sbss2 : { __sbss2_start = .; *(.sbss2) *(.sbss2.*) *(.gnu.linkonce.sb2.*) __sbss2_end = .; } > psu_r5_btcm_MEM_0 .data : { __data_start = .; *(.data) *(.data.*) *(.gnu.linkonce.d.*) *(.jcr) *(.got) *(.got.plt) __data_end = .; } > psu_r5_btcm_MEM_0 .data1 : { __data1_start = .; *(.data1) *(.data1.*) __data1_end = .; } > psu_r5_btcm_MEM_0 .got : { *(.got) } > psu_r5_btcm_MEM_0 .ctors : { __CTOR_LIST__ = .; ___CTORS_LIST___ = .; KEEP (*crtbegin.o(.ctors)) KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) __CTOR_END__ = .; ___CTORS_END___ = .; } > psu_r5_btcm_MEM_0 .dtors : { __DTOR_LIST__ = .; ___DTORS_LIST___ = .; KEEP (*crtbegin.o(.dtors)) KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) __DTOR_END__ = .; ___DTORS_END___ = .; } > psu_r5_btcm_MEM_0 .fixup : { __fixup_start = .; *(.fixup) __fixup_end = .; } > psu_r5_btcm_MEM_0 .eh_frame : { *(.eh_frame) } > psu_r5_btcm_MEM_0 .eh_framehdr : { __eh_framehdr_start = .; *(.eh_framehdr) __eh_framehdr_end = .; } > psu_r5_btcm_MEM_0 .gcc_except_table : { *(.gcc_except_table) } > psu_r5_btcm_MEM_0 .mmu_tbl (ALIGN(16384)) : { __mmu_tbl_start = .; *(.mmu_tbl) __mmu_tbl_end = .; } > psu_r5_btcm_MEM_0 .ARM.exidx : { __exidx_start = .; *(.ARM.exidx*) *(.gnu.linkonce.armexidix.*.*) __exidx_end = .; } > psu_r5_btcm_MEM_0 .preinit_array : { __preinit_array_start = .; KEEP (*(SORT(.preinit_array.*))) KEEP (*(.preinit_array)) __preinit_array_end = .; } > psu_r5_btcm_MEM_0 .init_array : { __init_array_start = .; KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array)) __init_array_end = .; } > psu_r5_btcm_MEM_0 .fini_array : { __fini_array_start = .; KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array)) __fini_array_end = .; } > psu_r5_btcm_MEM_0 .ARM.attributes : { __ARM.attributes_start = .; *(.ARM.attributes) __ARM.attributes_end = .; } > psu_r5_btcm_MEM_0 .sdata : { __sdata_start = .; *(.sdata) *(.sdata.*) *(.gnu.linkonce.s.*) __sdata_end = .; } > psu_r5_btcm_MEM_0 .sbss (NOLOAD) : { __sbss_start = .; *(.sbss) *(.sbss.*) *(.gnu.linkonce.sb.*) __sbss_end = .; } > psu_r5_btcm_MEM_0 .tdata : { __tdata_start = .; *(.tdata) *(.tdata.*) *(.gnu.linkonce.td.*) __tdata_end = .; } > psu_r5_btcm_MEM_0 .tbss : { __tbss_start = .; *(.tbss) *(.tbss.*) *(.gnu.linkonce.tb.*) __tbss_end = .; } > psu_r5_btcm_MEM_0 .bss (NOLOAD) : { . = ALIGN(4); __bss_start__ = .; *(.bss) *(.bss.*) *(.gnu.linkonce.b.*) *(COMMON) . = ALIGN(4); __bss_end__ = .; } > psu_r5_btcm_MEM_0 _SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 ); _SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 ); /* Generate Stack and Heap definitions */ .heap (NOLOAD) : { . = ALIGN(16); _heap = .; HeapBase = .; _heap_start = .; . += _HEAP_SIZE; _heap_end = .; HeapLimit = .; } > psu_r5_btcm_MEM_0 .stack (NOLOAD) : { . = ALIGN(16); _stack_end = .; . += _STACK_SIZE; _stack = .; __stack = _stack; . = ALIGN(16); _irq_stack_end = .; . += _IRQ_STACK_SIZE; __irq_stack = .; _supervisor_stack_end = .; . += _SUPERVISOR_STACK_SIZE; . = ALIGN(16); __supervisor_stack = .; _abort_stack_end = .; . += _ABORT_STACK_SIZE; . = ALIGN(16); __abort_stack = .; _fiq_stack_end = .; . += _FIQ_STACK_SIZE; . = ALIGN(16); __fiq_stack = .; _undef_stack_end = .; . += _UNDEF_STACK_SIZE; . = ALIGN(16); __undef_stack = .; } > psu_r5_btcm_MEM_0 _end = .; }

IPC: Shared Memory

Shared memory parameter changes need to be coordinated between the RPU firmware code and the APU's Linux device tree as shown in this table:



Default

Change to



Default

Change to

RPU      SHARED_MEM_PA

0x3ED40000

0x3EF40000

RPU      SHARED_MEM_SIZE

0x100000

no change

RPU      SHARED_BUF_OFFSET

0x8000

no change

Linux DT:   rpu0vdev0vring0

reg = <0x0 0x3ed40000 0x0 0x4000>;

reg = <0x0 0x3ef40000 0x0 0x4000>;

Linux DT:   rpu0vdev0vring1

reg = <0x0 0x3ed44000 0x0 0x4000>;

reg = <0x0 0x3ef44000 0x0 0x4000>;

Linux DT:   rpu0vdev0buffer

reg = <0x0 0x3ed48000 0x0 0x100000>;

reg = <0x0 0x3ef40000 0x0 0x100000>;

IPC: IPI Channels

Selecting a different IPI channel also requires coordinating changes in the RPU firmware and the APU's Linux device tree:

Versal

Default IPI1

Change to IPI2

Versal

Default IPI1

Change to IPI2

RPU     IPI_BASE_ADDR

0xFF340000

0xFF350000

RPU  IPI_CHN_BITMASK

0x00000020

0x00000040

RPU    IPI_IRQ_VECT_ID

63

64

Linux DT:    xlnx,ipi-id

3

4

Please, see the IPI Interrupt Channel Architecture:

Additional Information

Timers for the Libmetal AMP Demo

The timers are used in the "libmetal AMP demo" and are not needed for any of the OpenAMP demos. ZynqMP and Versal have four Triple Timer Counters (TTC[0-3]). The code in the demo uses TTC0 by default. However any of the four TTCs can be used.

Triple Timer Counter

ZynqMP

Versal

Triple Timer Counter

ZynqMP

Versal

TTC0_BASE_ADDR

0xFF11_0000

0xFF0E_0000

TTC_DEV_NAME

ff110000.timer

ff0e0000.ttc0

For example, what code changes are needed to use a different TTC  in zynqmp_amp_demo? Find  the base address of TTC1 in ug1085 zu+ TRM (it's 0xFF12_0000). Unfortunately, some values for TTC0 are hard coded and require source code changes.

Zynq-7000

Zynq-7000 has two Arm Cortex-A9 MPCore CPUs. Our demos use one core A9_0 to run Linux master and A9_1 to run a baremetal or FreeRTOS remote firmware.

Shared Memory

There are both on-chip memory (OCM) and external memory interfaces (DDR). The demos use the DDR as a default shared memory location.

 

Zynq

 

Zynq

SHARED_MEM_PA

0x3e800000

SHARED_MEM_SIZE

0x80000

SHARED_BUF_OFFSET

0x80000

Interrupts

Generic Interrupt Controller (PL390)

 

Zynq

 

Zynq

 SCUGIC_PERIPH_BASE

0x3e800000

SGI_TO_NOTIFY

15

SGI_NOTIFICATION

14

Related Links

  1. UG1186 - Libmetal and OpenAMP User Guide (v2020.2)

  2. UG1416 - Vitis Unified Software Development Platform (v2020.2)

  3. UG1085 - Zynq UltraScale+ Device Technical Reference Manual (v2.2)

  4. UG1087 - Zynq UltraScale+ MPSoC Register Reference

  5. UG1304 - Versal ACAP System Software Developers Guide (v2020.2)

  6. Versal ACAP Technical Reference Manual

  7. Versal ACAP Register Reference