ARM AArch64-ELF Topics

This appendix describes topics relevant to GNAT for bareboard AArch64 and also presents a tutorial on building, running, and debugging an Ada application on an embedded AArch64 board.

Supported Processors and Run-Time Libraries

GNAT for bareboard ARM supports processors from the ARMv8 architecture family.

Run-time support is provided for the following processor families:

  • Xilinx Zynq UltraScale+ MPSoC

For these processor families, pre-built run-time libraries are provided that represent subsets of the Ada language appropriate for the intended application domain and capabilities of the processors. Due to the range of individual processors within these families and the different configurations options available for a particular processor, these pre-built libraries target a specific processor or developer board.

For the Xilinx Zynq UltraScale+ MPSoC, the run-time libraries are targeted at the Avnet UltraZed board and as a bare metal guest on Wind River Helix Virtualization Platform. The following pre-built run-time libraries are available:

  • light-zynqmp

  • light-tasking-zynqmp

  • embedded-zynqmp

These runtime supports the running on the quad cortex-A53 SMP APU block of the UltraScale+ MPSoC:

  • boot from Exception Level 3-1: the boot process is configuring the various EL down to 1 in 64-bit mode. The runtime mode used is EL1 non-secure.

  • MMU: the mmu configuration is expecting 2GB of DDRAM and configures the memory access in 32-bit mode.

  • SMP: the four cores are supported by the runtime in SMP mode.

  • Interrupts: 16 level of interrupt priority are supported, and thus nested interruptions are supported as well. Finally, the use of the FPU is always supported, in tasks and in interrupt handlers.

Getting Started with the UltraZed and GNAT

This guide will demonstrate how to run an Ada application on the Avnet UltraZed. The process for other UltraScale+ boards is largely similar, except you will have to provide a Hardware Platform Specification and a BSP tailored for your board.

To begin, you will need to obtain and install from Xilinx:

  • Xilinx Software Development Kit (XSDK) 2017.4 or later

and from GNAT Tracker:

  • Gnatbench plugin for Eclipse CDT

  • GNAT for aarch64-elf

Preparation

GNAT installation

Before running XSDK, make sure that GNAT is installed and accessible from your environment (PATH). To test, type from your command line:

$ gprconfig --show-targets

The list should include aarch64-elf.

Hardware Platform Specification

As the UltraScale+ MPSoC is a configurable MPSoC, it requires a Hardware Platform Specification. For the Avnet UltraZed, visit their website and obtain the Board Definition Files for Vivado 2017.2 and later and follow the included tutorial. For this guide, the Hardware Platform Specification is called design_1_wrapper_hw_platform_0 specification.

Open XSDK and create a standalone BSP from the File -> New -> Board Support Package. While this BSP is not strictly needed to build the application, it provides XSDK the information how to run or debug the application. Use “standalone bsp” as the project name and do not include any extra libraries.

Gnatbench Installation

With XSDK open:

  • Click on the menu item Help->Install New Software

  • Click on “Add”, select “Archive”, select the gnatbench zip file

  • Click on “OK”

  • Select AdaCore Plugins for Eclipse/CDT

  • Click on Next and finish the installation

Restart XSDK before proceeding to the next step.

Create an Ada Project

Click on the menu File -> Other, and select Ada -> Ada Project

In the dialog:

Project name:

hello, use default location (click Next)

Project unit name:

Hello, Single Project

Main Program Settings:

Hello, Generate the file as a Hello World application

Directories settings:

leave default

Ada Builder settings:

click on Scan, select aarch64-elf, make sure no other toolchain is selected.

Click on Finish.

Right click on the project, select “Properties”. In the “Project References” section, add a dependency over “standalone_bsp”. Close the dialog.

Right-click on the project hello, choose “Build main” -> “hello.adb”.

Run/Debug the Application

Emulator-based Debugging

Click on the Menu “Debug” > “Debug Configurations”

Select Xilinx C/C++ application (System Debugger on QEMU), and press the “New” button.

  • Target setup:

    • Debug Type: “Standalone Application Debug”

    • Connection: “QEMU”

    • Hardware Platform: “design_1_wrapper_hw_platform_0”

  • Application:

    • Project Name: click on Browse, select hello

    • Application: click on Search, select hello

    • Download checked for psu_cortexa53_0

    • (optional): check Stop at program entry

Click Apply, then Debug.

This should launch QEMU and switch to the Debug perspective, and stop at the entry point (or at main). Clicking on the ‘Resume’ button will display “Hello World!” from the Emulator Console, and reset the simulator.

On-board Debugging

To begin:

  • set the board to start in JTAG mode

  • connect the JTAG module to your host

  • connect the UART to your host and open a terminal (115200 bauds)

Now click on the Menu “Debug” -> “Debug Configurations”

Select Xilinx C/C++ application (System Debugger), and press the “New” button.

  • Target setup:

    • Debug Type: “Standalone Application Debug”

    • Connection: “Local”

    • Hardware Platform: “design_1_wrapper_hw_platform_0”

    • Initialization File: “psu_init.tcl”

    • Run psu_init is checked

  • Application:

    • Project Name: hello

    • Application: select hello

    • Download checked for “psu_cortexa53_0”

    • Check “Reset processor”

    • (optional) check Stop at program entry if you need to debug the crt0

Click Apply, then Debug.

The debug view should open automatically and the Cortex-A53 #0 should stop at main. The main entry point of the hello world example will be displayed. Press the continue button and the output “Hello World!” should print on your serial console.

Resources used by the runtime

  • The GIC is configured to redirect the exceptions to the Ada runtime.

  • The Software generated interrupt 0 is reserved by the runtime and should not be used by user code.

  • The UART0 is used as standard input/output by the runtime.

Memory mapping

<!-- DDR, 2GB -->
<region access="rwx---" cache="wb"
        virt="0x00000000" size="0x80000000" name="ram"/>

<!-- PL -->
<region access="rw-rw-" cache="nc"
        virt="0x80000000" size="0x40000000" name="pl"/>

<!-- QSPI, lower PCIe -->
<region access="rw-rw-" cache="nc"
        virt="0xC0000000" size="0x20000000" name="qspi"/>

<!-- Peripherals -->
<region access="rw-rw-" cache="nc"
        virt="0xF8000000" size="0x08000000" name="io"/>

On the Xilinx Zynq Ultrascale+ the runtime configures the MMU to contain 4KB guard pages around the CPU stacks. These guard pages are unmapped and will trigger a CPU exception if accessed. The guard pages are defined in the common.ld linker script. If you have a custom linker script, please refer to common.ld on how to define the CPU stacks and their guard pages in your own script. Failure to do so may result in incorrect MMU configuration that results in an unexpected data exception during initialization.

If a custom linker script is used these pages need to be added. Otherwise the MMU will place these pages inside the stack and BSS sections causing an exception when initializing the memory.

Note: as a Helix Virtualization Platform bare metal guest the application cannot access addresses lower than 0x100000 and the DDR region needs to be adapted to guest configuration on the hypervisor.

Boot

By default the runtime is booting from RAM, with the entry point set to 0x00000. It can also be booted from QSPI with the entry point at 0xC0000000. As a Helix guest the runtime will boot from 0x100000. To specify the boot environment set the LOADER project variable to RAM, QSPI or HELIX. The LOADER variable can be specified as a switch to GPRBuild:

gprbuild -P proj.gpr -XLOADER=QSPI

or by adding the following to your project file:

type LOADER_Type is ("RAM", "QSPI", "HELIX");
LOADER : LOADER_Type := external ("LOADER", "QSPI");

Debugging

Halting time when debugging

There may be scenarios when debugging where you need to halt Ada.Real_Time.Clock and the runtime timer services when the debugger halts a processor. To achieve this, the underlying system counter needs to be halted in response to a debug event. As the system counter is implementation defined in the ARMv8-A architecture, please refer to your processor’s documentation for further information on the system counter provided and whether it supports halt-on-debug.

Do note that typical ARMv8-A processor implementations use the Generic Timer defined by the architecture to provide the system counter. This timer provides support for halt-on-debugging via an HDBG flag, however once again, it is up to the implementation whether this flag is supported or not. Furthermore, how debug events are routed to the Generic Timer is also left to the processor’s implementation. Refer to the ARMv8-A Architecture Reference Manual and your processor’s documentation for details.

Xilinx Zynq Ultrascale+

The Xilinx Zynq Ultrascale+ uses the ARMv8-A Generic Timer as the basis of its system counter (alternatively referred to as IOU_SCNTRS Module or System Timestamp Generator in the Xilinx documentation, referred to here as the system timer). The system timer supports halt-on-debug (HDBG). To enable this feature, the HDBG flag needs to be set and the CoreSight embedded cross trigger (ECT) configured to route the debug trigger from the desired core to the system timer.

Xilinx SDK

To enable the HDBG system timer flag while debugging from the Xilinx SDK, open the generated psu_unit.tcl file found within ZCU102_hw_platform project. This file initializes the Xilinx Processor System and can be used to enable the HDBG flag without changing your Ada application. Modifying psu_unit.tcl also ensures your application does not ship with the HDBG enabled.

To enable the HDBG flag, find the following line in psu_unit.tcl`:

mask_write 0XFF260000 0x00000001 0x00000001

and replace it with:

mask_write 0XFF260000 0x00000003 0x00000003

To configure the ECT:

  1. Open the Debug Configurations dialog (Run->Debug Configurations…).

  2. From the left side of the dialog, select your debug configuration.

  3. Under the Target Setup tab, enable the option Enable Cross-Triggering and click on the ellipsis button that is on the right of it.

  4. Click the Create… button on the Cross Trigger Breakpoints window.

  5. Under Cross Trigger Signals (Input), locate the processor core that your application is running on (typically A53-0) and click the checkbox next to DBGTRIGGER.

  6. Under Cross Trigger Signals (Output), scroll down until you find FTM-STM. Select the checkbox next to HALT SYSTEM TIMER.

  7. Click OK to close the Cross Trigger Breakpoints dialog boxes.

You can now click Debug to debug your application with system counter halt-on-debug enabled. If you wish to disable this feature, open the debug configuration and deselect Enable Cross-Triggering.

Lauterbach

If you are using a Lauterbach probe, add to your TRACE32 script the following lines to enable the HDBG flag and configure the ECT:

::

SYStem.Option CTITimerStop ZYNQULTRASCALE Data.Set 0xFF260000 0x00000003

For more information about this solution, please contact Lauterbach.