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 and provides both pre-built generic and microcontroller/processor specific runtimes.
Generic core runtimes¶
Generic Light run-times are provided for the following ARM Cortex cores:
Cortex M0: light-cortex-m0
Cortex M0+: light-cortex-m0p
Cortex M3: light-cortex-m3
Cortex M4: light-cortex-m4
Cortex M4 with single precision floating point: light-cortex-m4f
Cortex M7 with single precision floating point: light-cortex-m7f
Cortex M7 with double precision floating point: light-cortex-m7d
To use these generic runtimes you are required to provide your own microcontroller specific startup code and linker scripts. To help create these files you can use the Startup-gen tool as documented in Startup-gen User’s Guide
These generic Light run-times provide a minimal Ada.Text_IO implementation using ARM semihosting. See to your debug probe documentation on enabling semihosting output.
Run-time support is provided for the following processor families:
ST Microelectronics STM32F4
Texas Instruments TMS570LC
Texas Instruments TMS570LS
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 ST Microelectronics STM32F4, the run-time libraries are targeted at the STM32F4-Discovery and STM32F429I-Discovery kits. The following pre-built run-time libraries are available:
For the Texas Instruments TMS570LS, the following pre-built run-time libraries are available:
For the Texas Instruments TMS570LC, the following pre-built run-time libraries are available:
For the Xilinx Zynq-7000, the following pre-built run-time libraries are available:
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.
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 GNAT Studio) or on the command line. For details on how to specify the target and run-time library for your project see Introduction to GNAT for Cross Platforms. 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 "light-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, GNAT Studio 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), GNAT Studio integrates support that allows you to download programs to STM32 microcontrollers and debug them direct from GNAT Studio. 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 independent of the application and the target hardware itself.
The example used by this guide,
led_flasher-stm32f4, can be found in
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
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
embedded-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.
To program and debug the STM32F429-Discovery Kit you will need to install
stlink project and add it to your
PATH. You can obtain
the latest version of
stlink from its project page at
https://github.com/stlink-org/stlink. On Windows, you will also need to install
the ST-LINK debug probe USB driver from https://www.st.com/en/development-tools/stsw-link009.html.
Opening the Led_Flasher Project¶
led_flasher-stm32f4 example can be found in the directory:
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
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 GNAT Studio by double clicking on the project file
Windows Explorer. Otherwise you can launch GNAT Studio from the
and open the project from within GNAT Studio. On Linux you can open the project with
the following shell command from the
$ gnatstudio -P flasher.gpr
Note if this is your first time running GNAT Studio, GNAT Studio 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
Welcome to GNAT Studio page will appear in the center of the GNAT Studio work area.
Use the ‘x’ that will appear to the right of the title in the tab to close that
You can now explore the project in the
Project View on the left side of GNAT Studio.
Within GNAT Studio, build the project using either the
Build Main button on
the toolbar or the
Build/Project->led_flasher.adb menu entry. In
the latter case, if a pop-up window appears, press the
button in order to build with the default settings.
As GNAT Studio builds the project it will display the
Messages view at the bottom of
the GNAT Studio window to show information about the build. On a successful build,
Memory Usage view will graphically display the memory usage of the
led_flasher program, unless the preference is disabled. (See the preference
Build Targets/Project/Build Main page, among others, containing the
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
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
User button will cause the LEDs to flash at twice their rate.
User button again will cause the LEDs to flash at their
Debugging the Program¶
With the board connected, click on the
Debug on Board button on the toolbar.
This will start a GNAT Studio debug session and launch the necessary tools required to
debug the program on the board from GNAT Studio. The program will also be automatically
loaded onto the microcontroller. Debugging a program on a board through GNAT Studio is
similar to debugging a native program:
The program can continue to the next break point by pressing the
Continuebutton on the toolbar or by using the
The program can be stepped through statements using the corresponding menu items in
Debugmenu and buttons on the toolbar.
The program can be interrupted by pressing
<ctrl>-\or though the
Breakpoints can be set via the
Breakpointsview, 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
Variables can be queried from the
Variablesview or by hovering the mouse over a variable with the program interrupted.
GNAT Studio 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:
The debugging session can be exited via the
Debug/Terminate menu or
quit command in the
If you do not have an active GNAT Studio debug session, click on the
Debug on Board
button on the toolbar. (Remember that you can hover the mouse cursor over the
buttons to see their names, and that there is a preference in
General/Custom Styles to have the button names displayed along with
the button icons on the toolbar.)
When the GNAT Studio 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, GNAT Studio issues the
command to load the program onto the board and follows with the
reset halt command to reset the microcontroller to a debuggable state. If
Debugging Console does not display the above, there is a communication
problem with the board. If this occurs, quit GNAT Studio and reconnect the Developer Kit
to your computer.
Staying within the
Debugger View, at the GDB command prompt
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.
<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
Program received signal SIGTRAP, Trace/breakpoint trap. 0x080035ec in __gnat_sys_tick_trap ()
GNAT Studio 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 GNAT Studio will not switch to the
run-time source code.
We will now stop the debugger in a line in the example’s source code.
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 GNAT Studio 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
Breakpoints view deselect the
Enable checkbox next to the newly created
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 GNAT Studio 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
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
You can step through the statements of the program by pressing the step or next
statement buttons on the GNAT Studio toolbar or by entering the
Step (s) cause the debugger to stop at a
new line of source code, while
Next (n) will cause 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,
lights.ads and go to line 45. This task object declaration creates
a task that flashes the user LED
PG13. Change the
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:
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
GNAT Studio’s debugging session by either typing
quit in the
or by using the
Debug/Terminate menu item.
Changing Time Resolution¶
On the ARM Cortex-M boards, the Ravenscar run-time libraries use the ARM SysTick interrupt to implement Ada semantics for time, e.g., delay statements and package Ada.Real_Time. These clock tick interrupts occur at a given, regular rate determined by the run-time. As a result, the maximum resolution for time (e.g., in delay statements) is set by this interval because that is how often the run-time is invoked to examine the time and react as necessary.
The interval is set in the System.BB.Board_Support package body,
located within the run-time in the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
with ... package body System.BB.Board_Support is ... ----------------------- -- Sys_Tick Handling -- ----------------------- -- We use the Sys_Tick timer as a periodic timer with 1 kHz rate. This -- is a trade-off between accurate delays, limited overhead and maximum -- time that interrupts may be disabled. Tick_Period : constant Time.Timer_Interval := Clock_Frequency / 1000;
As you can see on line 15, the resolution for delays etc. is one millisecond. Applications can request smaller values but won’t actually get them because the hardware is not configured to do so. That one millisecond resolution is a good default, representing the trade-off described in the comments, but it may not be good enough for your application.
The System.BB.Board_Support package files are located in the
gnarl subdirectory. Therefore, we copy the body (not the spec)
for the package to the
gnarl_user subdirectory and make the
modification there. We can change the divisor to whatever is required,
keeping in mind that shorter intervals entail more frequent interrupt
handling overhead. Then we rebuild the run-time. See the
Customized Bareboard Run-Time Libraries annex for how to modify
and build run-time libraries.
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.
The means for debugging applications on bareboard targets vary with the target board and debugging probe used. This section addresses both general GNAT Studio debugging topics and the use of the debugging probe embedded on the ST Microelectronics Discovery Kits.
Debugging with GNAT Studio¶
GNAT Studio offers a similar debugging experience when targeting an embedded ARM board
as a native target, with GNAT Studio 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 GNAT Studio in the
Getting Started tutorial.
GNAT Studio can be set to automatically connect to your probe’s GDB server when you
launch a debugging session in GNAT Studio on a per-project basis. To do this open
the properties for your project in GNAT Studio by clicking on the pencil icon in
Project view and navigate to the
Embedded properties sheet. Under
Communication Settings enter the host address and port of your GDB server
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:
Protocol field choose
remote from the drop down menu.
If you are using the
st-util GDB server, GNAT Studio provides built-in support for
st-util when the user initializes a debug session. You can tell GNAT Studio
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 GNAT Studio.
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 GNAT Studio) over USB. Normally, GNAT Studio will handle the details of launching and connecting to the GDB server and thus this section is useful if you are not using GNAT Studio or run into problems while using GNAT Studio.
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 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
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
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
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 GNAT Studio
issues that command as part of the
Debug/Initialize menu invocation.
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
(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
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 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:
to reset it. This command can be issued interactively at the GDB prompt, or could be part of another GDB macro.
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.
st-utilcan 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.zipfor 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-utilto work together properly.