ARM-ELF Topics and Tutorial

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

Supported Processors and Run-Time Libraries

GNAT for bareboard ARM supports processors from the ARMv7 architecture family, specifically the Cortex-M3, Cortex-M4, Cortex-R4 and Cortex-A9 series.

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

  • ST Microelectronics STM32F4
  • Texas Instruments TMS570
  • Xilinx Zynq-7000

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 as detailed next.

For the ST Microelectronics STM32F4, the run-time libraries are targeted at the STM32F4-Discovery and STM32F429I-Discovery kits. The following run-time libraries are pre-built:

  • ravenscar-full-stm32f4
  • ravenscar-sfp-stm32f4
  • zfp-stm32f4

For the Texas Instruments TMS570, the following run-time libraries are pre-built:

  • ravenscar-full-tms570
  • ravenscar-sfp-tms570
  • zfp-tms570

For the Xilinx Zynq-7000, the following run-time libraries are pre-built:

  • ravenscar-full-zynq7000
  • ravenscar-sfp-zynq7000
  • zfp-zynq7000

All pre-built run-time libraries include the necessary low-level hardware startup and initialization functionality required to run applications on the developer board. In many cases, the pre-built run-time libraries will run on other developer boards and processors if they share a similar configuration to these pre-built run-times. If your processor or board uses a different configuration you can modify and rebuild the pre-built run-time to meet your needs, or you wish to target a different processor within the supported Cortex families you can re-target the run-time. The run-time libraries are largely independent of the underlying hardware, simplifying this process.

Getting Started

Developing with GNAT for bareboard ARM is similar to native GNAT development with two important differences. First, when building for a bareboard target you need to specify both the target processor and the run-time library. This can be defined in project file (either directly or via the project properties dialog in GPS) or on the command line. For details on how to specify the target and run-time library for your project see Specifying the Run-Time Library and Target. For all supported ARM boards, the target ‘arm-eabi’ needs to be specified.

For example, the following project file fragment shows both the target and the run-time library specified via their respective attributes. In this example an ARM target and the ‘Zero Foot Print’ STM32F4 run-time library is specified:

project Demo is
   ...
   for Runtime ("Ada") use "zfp-stm32f4";
   for Target use "arm-eabi";
   ...
end Demo;

The second difference is how programs are run and debugged. This is dependent on your setup and the tools you use. For example, a debug probe can be used to download programs to an ARM microcontroller’s embedded flash or RAM. In this instance refer to the documentation accompanying the probe. In many cases, GPS can be used to debug your program if your debug probe provides a GDB server. See Debugging for details.

If you are using the ST-LINK debug probe (embedded in the STM32F4x Discovery Kits), GPS integrates support that allows you to download programs to STM32 microcontrollers and debug them direct from GPS. The following guide Getting Started with the STM32F429-Discovery provides an introduction to setting up the required software to interact with the ST-LINK debug probe and how to download and debug programs on STM32F microcontrollers. While the guide uses the STM32F429 Discovery Kit, this introduction is applicable for other STM Discovery Kits or other boards utilising STM32F microcontrollers.

Getting Started with the STM32F429-Discovery

This guide describes how to setup, build, run, and debug an Ada application on the STM32F4x Discovery kits. Setup for Windows and Linux hosts are covered. While this section focuses on the STM32F429-Discovery Kit, much of the steps contained within this section are indpendent of the application and the target hardware itself.

The example used by this guide, led_flasher-stm32f4, can be found in the directory:

<GNAT_install_directory>/share/examples/gnat-cross/led_flasher-stm32f4

This simple example flashes the two LEDs on the developer board, toggling between a standard flashing rate and a quicker flashing rate at the press of the blue User button on the board. The example provides a demonstration of the Ravenscar Run-Time, protected procedures as interrupt handlers and how to access the registers of a microcontroller. While the example targets the STM32F429-Discovery Kit, the ravenscar-full-stm32f4 run-time the example uses supports other STM32F4 Discovery Kits. Thus, this example can easily be run on other Discovery Kits by modifying the example to reflect the LED pin assignments for your Discovery Kit. See the lights.ads file on how to perform this modification.

To begin this guide, it is assumed GNAT for bareboard ARM has been installed and the install bin directory is on the PATH environment variable. To complete the setup of the developer tools, the drivers and tools needed to interact with the debug probe included in the Discovery Kits need to be install as described in the following sections.

Host Setup

Windows

GNAT Pro for bareboard ARM on Windows includes the software tools used to interact with the ST-LINK debug probe. To complete installation, the USB driver for the probe needs to be obtained and installed.

To obtain the USB device driver for the ST-LINK debug probe go to http://www.st.com/content/st_com/en/products/embedded-software/development-tool-software/stsw-link009.html and click on the large Get Software box. A table headed Get Software will appear into view. Click on the Get Software under the Order from ST column of the table to obtain the driver. To download the drivers you will need to accept ST Micro’s license agreement and either have an account with myST or enter your contact details.

Once downloaded unzip the USB device driver and run the installer, accepting all the defaults.

Linux

GNAT Pro for bareboard ARM on Linux requires the installation of the open source stlink project. To obtain the project you will need to have a C compiler, with the gcc or clang compilers that are provided on your Linux distribution suitable for the task. You may need to install some additional developer tools depending on your setup and Linux distribution. On Ubuntu (and other Debian based distributions) you can make sure you have the required tools by running:

sudo apt-get install libtool
sudo apt-get install git-core
sudo apt-get install cmake

Once the require developer tools are installed you need to install the following libraries (using Ubuntu/Debian for example again):

sudo apt-get install libusb-1.0-0-dev

stlink can now be obtained, built and installed by performing the following:

git clone https://github.com/texane/stlink stlink

cd stlink
mkdir build
cd build

cmake ..
make
sudo make install
ldconfig /usr/local/lib

Once installed ensure the installation bin directory (by default /usr/local/bin) is on the environment PATH.

Opening the Led_Flasher Project

The led_flasher-stm32f4 example can be found in the directory:

<GNAT_install_directory>/share/examples/gnat-cross/led_flasher-stm32f4

and has the following layout:

led_flasher-stm32f4:
 |--> README.txt
 |--> flasher.gpr
 |--> gnat.adc
 |--> obj:
 \--> src:
       |--> button.adb
       |--> button.ads
       |--> led_flasher.adb
       |--> lights.adb
       |--> lights.ads
       |--> st.ads
       |--> st-stm32f4.ads
       |--> st-stm32f4-exti.ads
       |--> st-stm32f4-gpio.ads
       \--> st-stm32f4-rcc.ads

If you have installed GNAT in a shared location you may first need to copy the example to a directory that you have write access to (e.g. in you home directory).

The example has a typical project layout, capturing the sources for the project within the src directory while the object and other generated files are stored in the obj directory. The gnat.adc file contains the configuration pragmas for the project, while the README.txt provides an introduction to the example.

flasher.gpr is the GPR project file for the example that captures the project settings, including the target and run-time used by the project. On Windows you can open the project in GPS by double clicking on the project file in Windows Explorer. Otherwise you can launch GPS from the Start Menu and open the project from within GPS. On Linux you can open the project with the following shell command from the led_flasher-stm32f4.adc directory:

$ gps -P flasher.gpr

Note if this is your first time running GPS, GPS will create a configuration directory and will alert you via a pop-up dialog box. Press ‘OK’ to close that box and continue. A ‘Tip of the Day’ dialog will appear. Press Close to dismiss it. A Welcome to GPS page will appear in the center of the GPS work area. Use the ‘x’ that will appear to the right of the title in the tab to close that page.

You can now explore the project in the Project View on the left side of GPS.

Building

Within GPS, build the project using either the Build button on the toolbar or the Build->Project->flasher_led.adb menu entry. In the latter case, press the Execute button on the window that appears to build with the default setting.

As GPS builds the project it will display the Messages view at the bottom of the GPS window that will list the files as they compile. On a successful build, the Messages view will output the memory usage of the led_flasher program and indicate that the build was successful:

     Memory region         Used Size  Region Size  %age Used
           flash:       58992 B         1 MB      5.63%
          sram12:       34168 B       128 KB     26.07%
             ccm:          0 GB        64 KB      0.00%
[2016-10-18 17:48:26] process terminated successfully, elapsed time: 00.95s

GPS will also display the memory usage of the program in a view on the left side of the main GPS window.

Running the Program

To run the compiled program, connect the STM Discovery Kit to the computer with a USB cable. Then click on the Flash to Board button on the toolbar. This will download the program to the STM Discovery Kit. Once the program has been downloaded the Message view will report:

Flashing image to board.
Flashing complete. You may need to reset (or cycle power).

Press the black Reset button on the Discovery Kit to reset the board and run the program. The two user LEDs should now flash at different rates. Pressing the blue User button will cause the LEDs to flash at twice their rate. Pressing the User button again will cause the LEDs to flash at their normal rate.

Debugging the Program

With the board connected, click on the Debug on Board button on the toolbar. This will start a GPS debug session and launch the necessary tools required to debug the program on the board from GPS. The program will also be automatically loaded onto the microcontroller. Debugging a program on a board through GPS is similar to debugging a native program:

  • The program can continue to the next break point by pressing the Continue button on the toolbar or by using the Debug->Continue menu item.
  • The program can be stepped through statements using the corresponding menu items in Debug menu and buttons on the toolbar.
  • The program can be interrupted by pressing <ctrl>-\ or though the Debug->Interrupt menu.
  • Breakpoints can be set via the Breakpoints view, by clicking on the line numbers in the source editor while the program is interrupted or by right-clicking on the desired line and selecting Set breakpoint on line ... from the Debug menu.
  • Variables can be queried from the Variables view or by hovering the mouse over a variable with the program interrupted.

GPS also provides a Debugger Console view that allows shell access to the underlying GDB client that is debugging the program. The GDB commands that correspond to the above debugging tasks can be entered into the console directly if desired.

The program can be restarted by either using the black reset button on the board or via the Debugger Console with the command:

monitor reset

The debugging session can be exited via the Debug->Terminate menu or issuing the quit command in the Debugger Console.

Debugging Walkthrough

If you do not have an active GPS debug session, click on the Debug on Board button on the toolbar. When the GPS session loads, the editor will switch to the main procedure in led_flasher.adb and the Debugger Console will report the initial output of gdb:

file /GNAT/share/examples/gnat-cross/led_flasher-stm32f4/obj/led_flasher
Reading symbols from GNAT/share/examples/gnat-cross/led_flasher-stm32f4/obj/led_flasher...done.
remote debugging using localhost:4242
(gdb) load
Loading section .text, size 0xafa4 lma 0x8000000
Loading section .ARM.extab, size 0x8c0 lma 0x800afa4
Loading section .ARM.exidx, size 0xf28 lma 0x800b864
Loading section .rodata, size 0x1ff4 lma 0x800c790
Loading section .data, size 0xe3c lma 0x800e784
Start address 0x80036fc, load size 62908
Transfer rate: 14 KB/sec, 8986 bytes/write.
(gdb) monitor reset halt

Here GDB has loaded the example program led_flasher and connected to the remote GDB server (the program relaying the debugging commands from GDB to the board). Once GDB is connected with the board, GPS issues the load command to load the program onto the board and follows with the monitor reset halt command to reset the microcontroller to a debuggable state. If the Debugging Console does not display the above, there is a communication problem with the board. If this occurs, quit GPS and replug the Developer Kit to the your computer.

Staying within the Debugger View, at the GDB command prompt (gdb) type

c

to run the program. The LEDs on the Developer Kit will flash at different speeds. Press the blue User button to change the speed of the LEDs.

Now press <ctrl>-\ to interrupt the program. When you do this the program on the microcontroller stops running and the Debugger Console will report the instruction address and subprogram the microcontroller has stopped on:

Program received signal SIGTRAP, Trace/breakpoint trap.
0x080035ec in __gnat_sys_tick_trap ()

GPS will switch the Editor view to the line of code where the program stopped if it can locate the source files. Since led_flasher program is compiled with the debug flag this will occur when the debugger stops inside the program code. However, in the above case the program has stopped in the run-time code that has not been compiled with the debug flag and thus GPS will not switch to the run-time source code.

We will now stop the debugger in a line in the example’s source code. With the Project view visible expand the src directory and open lights.adb. Scroll to line 47 and click on the line number. A mark will appear over the line number to indicate that the breakpoint has been set. You can also confirm the breakpoint in the Breakpoints View on the lower left of the GPS window.

You can also add breakpoints from the Breakpoints view. In the view, click on the plus icon. In the Breakpoint editor windows that appears, enter button.adb into the file field and change the line field to 29. Click the OK button to close the window and to add the breakpoint. Double-clicking on the new breakpoint in the Breakpoints view will open the Button package and highlight the line the breakpoint is set on. For now in the Breakpoints view deselect the Enable checkbox next to the newly created breakpoint 2.

In the Debugger Console enter the command c to run the program to the next break point or press the continue button on the toolbar. The program will run to the selected breakpoint and GPS will switch the editor to the file location the program has stopped on (lights.adb:47 in this case). With the program stopped, you can query parts of the program state. For example, you can query the value of the local Divider object on line 51 by placing the cursor over the variable name. The value of the object will appear in a tooltip next to the variable (in this case it will show 1). You can also query the state of the protected object Speed_Button on line 47 by the same method, where it will show the Speed_State component as normal.

Now press the blue User button on the board and continue the program. When the program stops, look at the state of the protected object: it should now be double.

You can step through the statements of the program by pressing the step or next statement buttons on the GPS toolbar or by entering the s n commands into Debugger Console. Step (s) cause the debugger to stop at a new line of source code, while Next (n) will cause the the debugger to step over subprogram calls. Try using these commands now and notice how the editor highlights the current position of the program.

We will now change how long one of the LEDs on the board stay on for. First delete the breakpoints we created by selecting the breakpoints in the Breakpoints view and pressing the minus button on the view’s toolbar. Next, open lights.ads and goto line 45. This task object declaration creates a task that flashes the user LED PG13. Change the On_Time discriminate of the task to 75. Build the application again using the build button on the toolbar. Once the program has been built, we now need to load the updated program onto the microcontroller. Go back to the Debugger Console view and enter at the GDB prompt:

load

If the program has been successfully downloaded the console will print

`/GNAT/share/examples/gnat-cross/led_flasher-stm32f4/obj/led_flasher' has changed; re-reading symbols.
Loading section .text, size 0xafa4 lma 0x8000000
Loading section .ARM.extab, size 0x8c0 lma 0x800afa4
Loading section .ARM.exidx, size 0xf28 lma 0x800b864
Loading section .rodata, size 0x1ff4 lma 0x800c790
Loading section .data, size 0xe3c lma 0x800e784
Start address 0x80036fc, load size 62908
Transfer rate: 14 KB/sec, 8986 bytes/write.

Now you can reset the microcontroller and run the program by entering the following:

monitor reset
c

Notice now that LED PG13 stay on for longer. To conclude the walkthrough, exit GPS’s debugging session by either typing quit in the Debugger Console or by using the Debug->Terminate menu item.

Target Restrictions

Task Priority Restriction

Due to a restriction stemming from the hardware model of interrupt handling and context switching in the ARMv7-M architecture, Ada tasks cannot have a base priority specified that is an interrupt priority value.

This restriction applies only to ARMv7-M processors. It does not apply to the ARMv7-A or ARMv7-R architectures.

Debugging

The means for debugging applications on bareboard targets vary with the target board and debugging probe used. This section addresses both general GPS debugging topics and the use of the debugging probe embedded on the ST Microelectronics Discovery Kits.

Debugging with GPS

GPS offers a similar debugging experience when targeting an embedded ARM board as a native target, with GPS using the GDB client included with GNAT. The difference between native and remote debugging is remote debugging requires a GDB server to pass commands and data between the debug probe and GDB. An example of a GDB server is the st-util utility that communicates with the ST-LINK debug probe found on the STM Discovery Kits and used by GPS in the Getting Started tutorial.

GPS can be set to automatically connect to your probe’s GDB server when you launch a debugging session in GPS on a per-project basis. To do this open the properties for your project in GPS by clicking on the pencil icon in the Project view and navigate to the Embedded properties sheet. Under Communication Settings enter the host address and port of your GDB server in the Program Host field as detailed by your probe’s documentation. For example, if you are using st-util as your GDB server and running it on your computer you would enter:

localhost:4242

In the Protocol field choose remote from the drop down menu.

If you are using the st-util GDB server, GPS provides built-in support for launching st-util when the user initializes a debug session. You can tell GPS that you are using this tool by selecting it from the Connection Tool drop down menu. Otherwise, you will need to ensure that you have your GDB server running before starting a debugging session in GPS.

ST Microelectronics Discovery Kits

This section provides details on using the ST-Link debugging probe embedded in ST Microelectronics Discovery Kits that enables symbolic debugging with GDB (on the command line or within GPS) over USB. Normally, GPS will handle the details of launching and connecting to the GDB server and thus this section is useful if you are not using GPS or run into problems while using GPS.

Interaction with the on-board JTAG converter is accomplished by a stand-alone command-line executable named st-util that runs on the host. This executable is a third-party utility that provides a number of capabilities, including that of a GDB server. It is pre-built on the Windows hosted ARM-ELF toolchain but you must build it on Linux hosts. See Host Setup in the Getting Started section on installing and setting up st-util.

Using st-util involves manually invoking it on the command line, without parameters, in a command shell. You must invoke it before GDB connects to the board, each time a connection is to be made. This includes the case in which a previous invocation and connection has terminated, because terminating the debugger session disconnects the gdb-server that ‘st-util’ provides.

Note that if you just want to download your application to the board and let it run immediately, without debugging it, you can do so with another utility named st-flash that writes binary files to the FLASH memory on the board. As for st-util there is a version of st-flash pre-built in the Windows hosted toolchain.

Note that st-flash requires that the executable produced by the toolchain be converted to a ‘binary’ format, which can be accomplished via the arm-eabi-objcopy command. For example, assuming we have a project that creates an executable named led_flasher in an obj subdirectory, we could build the project and then invoke the arm-eabi-objcopy command, as shown in the following:

$ gprbuild -P flasher.gpr
$ arm-eabi-objcopy -O binary obj\led_flasher obj\led_flasher.bin

The result is the file led_flasher.bin that can be specified to st-flash on the command line. If you just enter the name st-flash on the command line it will print usage information. Usage information is also widely available on the Internet.

Automatic Board Reset For Debugging

When debugging a bare ARM board, you may need to reset the board after initially connecting the debugger. A reset is suggested because the previously running application may have put the processor and peripherals in an inconvenient state for debugging. If, when running an application under the debugger, initial execution seems to have no effect, a reset is likely in order.

This frequently can be the case with the ST Microelectronics Discovery boards. You can use the dedicated on-board button to reset the board after GDB connects to the board. Alternatively, you can automate the reset action using a GDB command file script.

The command file below will cause a Cortex-M3 or -M4 board to automatically reset immediately after a GDB load command executes. Note that GPS issues that command as part of the Debug->Init menu invocation. Manual GDB load command invocations will also trigger the action.

define hookpost-load
echo Resetting the processor and peripherals...\n
set *0xE000ED0C := 0x05FA0004
echo Reset complete\n
end

To use the script, copy the contents below into a file named .gdbinit (note the leading period in the name). Create the file if you do not already have one defined.

You can put the file in the same directory as the project gpr file, but note that other locations are possible. In particular, other locations can be convenient, for example if you want to use the same initialization for every project, globally. See the GDB documentation for the possible locations and their effects.

If you do place the .gdbinit file in the same directory as the project file, you will also need to enable local instances of the file so that GDB will not ignore it. To do so, create another .gdbinit file in your HOME directory (either on Linux or on Windows) and place the following inside it:

set auto-load safe-path /

Of course, if you already had a .gdbinit in your HOME directory, just add that line to it. That line will enable local instances of .gdbinit and so GDB will execute their content.

The board reset is achieved by writing to the ‘Application Interrupt and Reset Control’ register located at address 0xE000ED0C. Both the processor and the peripherals can be reset by writing a value of 0x05FA0004. That value will write to the SYSRESETREQ bit. If you want to avoid resetting the peripherals, change the value to 0x05FA0001. That value will write to the VECTRESET bit. Do not use a value that sets both bits.

For further details see the book by by Joseph Yiu: The Definitive Guide to the ARM Cortex-M3 and Cortex-M4 Processors, 3rd edition, pp. 262-263.

In both cases, any on-board debug hardware is not reset. For boards with a JTAG interface, you can use the GDB command:

monitor jtag_reset

to reset it. This command can be issued interactively at the GDB prompt, or could be part of another GDB macro.

Troubleshooting

Here are some recommendations when you encounter unexpected behavior:

  • Sometimes, when first starting a debugging session, invoking the debugger’s Run or stepping commands may not have any effect. This can happen when prior execution leaves the board in an uncommunicative state. (Remember the board starts executing a valid loaded application whenever power is applied.) In such cases the board probably just needs to be reset. You can push the black button on the board to do so, and then try to load the executable (‘Debug’ => ‘Initialize demo’) again. If that doesn’t suffice, try disconnecting the board from the USB cable in order to cycle power, reconnect, and then try the full debugging steps again.

  • If st-util can communicate with the board but does not properly interact with it as described above, it may be that the firmware on the board has been updated. If you cannot reinstate the prior version of the firmware, you will need to invoke an additional utility each time you power-up the board. Download the STM32 ST-Link utility from the same ST Micro web page above by selecting the ‘STSW-LINK004’ part number. Selecting ‘STSW-LINK004’ will present a file named stsw-link004.zip for download. Unzip and install it. Put the binaries on the host system path. Then, before calling st-util, each time after first applying power to the board, invoke the following on the command line: st-link_cli -c

    If the firmware is the problem this can allow the board and st-util to work together properly.