Customized Run-Time Libraries

This appendix describes how to customize, build, and install run-time libraries for use on bareboard targets. The issues involved in supporting debugging on a new target are also covered.

Note: When making permanent changes to a run-time library, for any reason, ensure that you can repeat the process because you may need to do it more than once. You may need to do it again, in particular, when you upgrade to a newer version of the compiler, for example to get new functionality or to get a bug fixed. The compiler and the run-time library are tightly coupled so the run-time may have changed as well. If so, you will need to reapply your changes to this new run-time library instance. Therefore, we suggest you manage the differences, in addition to the resulting files, under configuration control. For example, you could generate and keep “patch” files that can be reapplied to the files of the run-time library.

Adapting and Porting the Run-Time for Bareboard Platforms

The run-time library consists of files that are dependent on the target board and code that is target-independent. The latter implements language-defined functionality whereas the former is responsible for configuring and interacting with the hardware. The target-specific part is known as a Board Support Package (BSP) and contains files that may need to be modified when adapting a run-time library for an existing target or porting the run-time library to a new one.

To adapt or port a run-time library:

  1. Modify an existing BSP or create a new one to handle the specific target board configuration.
  2. Build the run-time library.
  3. Install the run-time library for use by the compiler.

While the steps involve in modifying or creating a new BSP depends on the new target, important aspects of the BSP to consider are:

  • Linker scripts: in particular the memory mapping and sizes are correct.
  • Startup code: in particular the clock speeds. Depending on the BSP, various clock options may be possible, for example: clock source, clock speed, bus clock multipliers and dividers, etc.
  • I/O (in the file s-textio.adb): verify that the proper UART is selected and appropriately configured. Note that semihosting is used on some targets.

To illustrate the steps involved the following sections document the structure of the BSPs and illustrates adapting the STM32F4 run-time library shipped with GNAT to the STM32F429 Discovery kit.

Board Support Packages

The GNAT bareboard run-times are designed to be easily adapted and ported by the user through the Board Support Packages (BSPs). GNAT provides a number of preconfigured run-times, with their BSPs sources located in:


while platform-independent run-time sources are located in:


where <GNAT_install_directory> is the installation directory for GNAT and target reflects the target instruction set and application binary interface of the compiler. For example, when GNAT is installed in the the default installation directory on Linux, the BSPs that come with the arm bareboard target (arm-eabi) can be found in:


The BSPs folder has a consistent schema across all targets, utilizing a hierarchy organized by processor architecture, processor family, board, and run-time profile. Each level in this schema contains sources that are common across its children. At the top level of the BSPs directory are folders containing the BSP sources, READMEs for each supported target, and project files (.gpr files) for each board supported by GNAT. A support directory is also present and may be ignored as it contains files that support run-time installation.

For example, the BSPs folder for the arm-eabi target has the following structure:

 \--> BSPs
       |--> cortex-ar:
       |--> cortex-m:
       |--> support:
       |--> ravenscar_full_stm32f4.gpr
       |--> ravenscar_full_tms570.gpr
       |--> ravenscar_full_zynq7000.gpr
       |--> ravenscar_sfp_stm32f4.gpr
       |--> ravenscar_sfp_tms570.gpr
       |--> ravenscar_sfp_zynq7000.gpr
       |--> zfp_lm3s.gpr
       |--> zfp_stm32f4.gpr
       |--> zfp_tms570.gpr
       |--> zfp_zynq7000.gpr
       |--> README-lm3s.txt
       |--> README-stm32f4.txt
       |--> README-tms570.txt
       \--> README-zynq7000.txt

The project files in this directory are used to build the run-time from the BSP and common run-time code, and are named after the run-time profile and the target processor/board. Typically the three bareboard run-time profiles (ZFP, Ravenscar-SFP, and Ravenscar-Full) are supported on each target and thus a target will have a separate project corresponding to each run-time profile. However, some targets (like the LM3S) may only support a particular profile and consequently will only offer a project file that corresponds to the that supported profile.

In addition to the project and README files, the BSPs folder for the arm-eabi target contains three subdirectories. The support folder contains files required to build and install the run-time.

The first two folders on the arm-eabi target are processor architecture folders. For example the arm-eabi target supports the Cortex-A, Cortex-R, and Cortex-M architectures through the cortex-ar folder for the Cortex-A and Cortex-R, and the cortex-m folder for Cortex-M.

Within the processor architecture folders are subdirectories containing source files common to that architecture together with files specific to the supported processor families. For example, the cortex-m architecture family would have the following directory layout:

 \--> BSPs:
       |--> cortex-ar:
       |--> cortex-m:
       |     |--> lm3s
       |     |--> stm32
       |     \--> src:
       |           |--> arch:
       |           \--> gnarl:
       \--> support:

Within the cortex-m directory files common to the architecture are located in the src directory, which is in turn divided into an arch directory containing processor-specific architecture files and a gnarl directory containing run-time files whose code is common amongst that processor architecture. By contrast, processor family specific source files are located in subfolders bearing their names. For example, the cortex-m directory contains lm3s and stm32 directories that support the TI Stellaris LM3S and STMicroelectronics STM32 processor families respectively.

The processor family folders are themselves further divided to support different boards and processors within that processor family. The lm3s directory is a simple example supporting a single board (the Stellaris LM3S811) and is made up of the following folders:

 |--> internal:
 |--> link:
 |--> src:
 |     \--> crt0:
 \--> zfp:
       |--> adalib:
       |--> arch:
       \--> obj:
  • internal contains files used for installing the run-times. You can safely ignore this directory in the BSP directory tree.

  • link contains linker scripts used by GNAT to link object files into the resulting binary. These scripts may need to be modified when porting a run-time to a new board.

  • src contains source files common to the run-time profiles supported for the LM3S target. For the LM3S target this folder contains a crt0 directory holding the processor startup source code and the package body for implementing Text_IO (s-textio.adb). Files in this directory may need to be modified if they do not meet the requirements of your board.

  • zfp directory contains source files and build directories specific to the Zero Footprint Profile (ZFP) run-time. If a board supports other run-time profiles, corresponding directories will exist that contain BSP source files specific to those profiles.

    In each of the run-time profile directories there is an arch directory that contains the source file for the package System. Generally, this package does not need modifying when porting to a new board using a similar processor. The profile directories also house the build directories adalib and obj.

The organization of processor family folders is more complex if multiple boards are supported, as is the case for the STM32 processor family in arm-eabi/BSPs/cortex-m/stm32.

 |--> link:
 |--> src:
 |     \--> crt0:
 \--> stm32f4:
       |--> internal:
       |--> link:
       |--> ravenscar-full:
       |--> ravenscar-sfp:
       |--> src:
       |     |--> crt0:
       |     \--> gnarl:
       \--> zfp:

Like the lm3s directory, the stm32 directory includes link and src directories that respectively contain linker scripts and startup files common to the STM32 family of microcontrollers. However, because aspects of the linker scripts and startup files are board specific, these board-specific parts are contained in separate files in the board-specific directories. Consequently, while the contents of the link and src should be reviewed for their suitability to the target platform, their sources should be general enough not to require any changes when adapting a run-time to a new board within an existing processor family.

The remaining run-time sources and the build directories for the STM32 family of microcontrollers are contained within board-specific folders. In this example, GNAT provides a generic STM32F4 BSP in the stm32f4 directory that will produce a GNAT run-time suitable to run on a range of STM32F4 microcontrollers that are setup similarly to the STM32F4 Discovery Kit (but will not take advantage of the capabilities of a specific STM32F4 microcontroller).

Consequently, the stm32f4 directory has link and src directories that contain the board-specific linker and startup sources for the STM32F407 in the STM32F4 Discovery kit (and which will need to be updated when tailoring the run-time to other STM32F4 processors to take advantage of the different memory (linker scripts) or clock setups (startup files)). The src directory also contains run-time profile independent sources in the gnarl directory. For the STM32F4 BSP, this directory provides the interrupt handler support for the Ravenscar Profiles and may need updating for a new microcontroller. An internal support directory also exists at this level and can safely be ignored.

The last directories within stm32 are profile specific and are organized in the same manner to the zfp folder in the LM3S BSP. As the STM32F4 BSP supports the ZFP, Ravenscar-Full and Ravenscar-SMP run-time profiles, directories exist for each. While the contents of these directories are used to build and install the run-times, take note of the arch directory as it contains a board and run-time specific file for package System that you may need to tailor.

Building and Installing a Run-Time Library

Run-time libraries are built and installed using the gprbuild and gprinstall tools using the run-time library projects files found the in the BSPs directory. These project files can also be used as the starting point for new run-time libraries that support different features or boards.

Important: you must use the GPR tools that come with the cross compiler for the target specified by the run-time project file. If you have multiple GNAT compilers installed, ensure that your environment path is setup so the path to the cross compiler for your target takes precedence over other paths.


To build a run-time library, invoke gprbuild with the desired run-time project file. For example, to build the Ravenscar-SFP run-time for the stm32f4 target (STM32F4 Discovery kit):

$ gprbuild -P ravenscar_sfp_stm32f4.gpr

If building the run-time fails, ensure the path to the compiler is at the head of the environment path.

You may apply additional gprbuild command-line switches if needed. For example, to have gprbuild utilize all processors on your computer to build the run-time specify the -j0 switch:

$ gprbuild -j0 -P ravenscar_sfp_stm32f4.gpr

Refer to the gprbuild documentation for all the switches available.

In addition, various project build options are defined within the project files. These options are specified via the BUILD project scenario variable and are useful when debugging or profiling run-times:

  • Production: (default) no debug information, optimized, no assertions
  • Debug: debug information, no optimization, no assertions
  • Assert: assertions within the run-time library code are enabled
  • Gnatcov: sets the proper flags for use with the gnatcov tool

To specify the scenario variable include -XBUILD=<value> on the command line when invoking gprbuild. For example, to build the run-time with debugging information use:

$ gprbuild -j0 -P ravenscar_sfp_stm32f4.gpr -XBUILD=Debug

Be advised enabling assertions within the run-time will significantly increase the execution and memory overhead. We recommend you only enable run-time assertions when requested by AdaCore support.


Once built, the run-time library needs to be installed before it can be used, including when an existing run-time is rebuilt. Installation completes the setup required to use the run-time with the GNAT toolchain and makes the newly-built run-time library accessible to the compiler.

Installation is carried out by invoking gprinstall with the same project file used to build. For example:

$ gprinstall -P ravenscar_sfp_stm32f4.gpr -p

The -p switch allows GPRinstall to create the required installation directories if they are not already present.

The installation places the run-time in a directory specified by the gpr file. By default, the parent directory for this run-time directory is shared with the prebuilt run-times provided with the GNAT compiler. For example, the above STM32F4 run-time would be installed by default to:


To install the run-time library to a different directory, you may edit the install path in the project file or specify the full installation path when invoking GPRinstall through the PREFIX scenario variable. The path should include the name of the run-time. For example:

$ gprinstall -P ravenscar_sfp_stm32f4.gpr -p -XPREFIX=/my/path/to/ravenscar_sfp_stm32f4

There is no requirement on where the run-time library should be installed to. However, if installed into the same location as the run-times provided with the GNAT compiler, the installed run-time can be referenced by the GNAT tools by name only. If installed outside this folder, these tools will require the full path of the run-time.

Finally, if a run-time is already installed in the target directory (when updating or modifying an existing run-time), it must be uninstall before the new run-time can be installed. This is achieved by using gprinstall with the --uninstall switch:

$ gprinstall --uninstall -P ravenscar_sfp_stm32f4.gpr

Once the run-time is installed, the compiler is able to use it via the standard approaches documented in Selecting the Run-Time Library.

Configuring for a New Board

The pre-built run-time libraries tested and shipped with GNAT target specific board and setup configurations. Because microcontrollers have a range of on-chip configuration options, for example memory and clock configurations, a pre-built run-time library supporting your microcontroller may not be configured to your specific hardware setup.

For example, the STM32F4 run-times shipped with GNAT are configured to support the STM32F4 Discovery kit and its STM32F407 microcontroller. Even if you use the STM32F407 in your own project you may need to customize the STM32F4 run-time to support your particular setup. For instance, the STM32F4 run-times are configured to use the 8MHz external clock provided by the STM32F4 Discovery kit while your setup may use a different clock source, like the internal RC oscillator.

However, the provided run-times are not necessarily tied to a particular microcontroller. For example, the STM32F4 run-times support both the STM32F4 Discovery and STM32F429 Discovery kits, which share similar hardware setups. But without tailoring the STM32F4 run-time you cannot take advantage of the higher frequencies and larger memory available on the STM32F429 microcontroller.

Additionally, creating a new run-time permits the creation of new run-time profiles to tailor the contents of the run-time to your solution. For example, a math library may be added to the ZFP run-time.

When tailoring an existing run-time to your board it is recommended to start from a copy of an existing Board Support Package (BSP). The BSP contains architecture, microcontroller and board-specific information of the run-time library and is separate to the platform-independent run-time files. Starting from a copy of an existing BSP enables you to use or refer to the original run-time without reinstalling GNAT. To illustrate the process of tailoring a run-time to a specific board this section will illustrate tailoring the GNAT provided STM32F4 run-time for the STM32F429 Discovery kit.

Creating a New Run-Time

To create a new run-time you first need to determine the run-time profile you wish to base your run-time on. This will determine the Ada features available to your application. For any target you have the choice of three run-time profiles: ZFP, Ravenscar-SFP, or Ravenscar-Full (note GNAT does not always include all three profiles for given supported target). Note that all profiles are customizable: you are able to add and remove certain features as needed. As a general guidance, choose:

  • ZFP if you need a minimal run-time without tasking support;
  • Ravenscar-SFP if you need to start with a minimal run-time with Ravenscar tasking;
  • Ravenscar-Full if you need to start with a full Ada run-time with Ravenscar tasking.

With a profile chosen, you can create your new run-time by:

  1. Make a copy of an existing run-time library project file in the BSPs folder that corresponds to your chosen run-time profile. It is recommended that the project name reflect the chosen profile, target, and purpose. It is also recommended to locate the new project in the BSPs folder.
  2. Make a copy of an existing processor family or board folder that is close to your desired target. If you are adapting an existing run-time to work on a new board within an existing processor family, copy an existing board folder. If you are porting an existing run-time to a new processor family copy an existing processor family folder. Ensure the name of the copied folder reflects its target.
  3. Update the contents of the new project file to reflect the new run-time name and target, and then update the Base_BSP_Source_Dir variable to reflect the new BSP source location.

Note: if you choose to locate your BSP sources outside of GNAT’s BSPs folder, you will need to ensure you have a copy of the BSPs/support folder in the same directory as the project file.

Once you have created the project files and directories from an existing BSP you can tailor the run-time for your target.

Creating a STM32F429 Run-Time

For our example, the STM32F429 Discovery kit is similar to the STM32F4 Discovery kit but contains more memory and supports a faster clock. Therefore, the new run-time will be created from the existing STM32F4 run-time library. We will also base our run-time off the Ravenscar-SFP run-time, so the new run-time library will be called ravenscar-sfp-stm32f429 to reflect the target and run-time profile.

Thus, our new run-time library will be made up of:

  • the project file ravenscar_sfp_stm32f429.gpr
  • board-specific directory at <GNAT_install_directory>/arm-eabi/BSPs/cortex-m/stm32/stm32f429/

and have the following layout:

 \--> BSPs
       |--> cortex-m
       |     |--> lm3s:
       |     |--> src:
       |     \--> stm32:
       |           |--> link:
       |           |--> src:
       |           |--> stm32f4:
       |           \--> stm32f429
       \--> ravenscar_sfp_stm32f429.gpr

Using a Unix shell for example, we can create this layout:

$ cd <GNAT_install_directory>/arm-eabi/BSPs
$ cp ravenscar_sfp_stm32f4.gpr ravenscar_sfp_stm32f429.gpr
$ cp -r cortex-m/stm32/stm32f4 cortex-m/stm32/stm32f429

After creating the new BSP sources, the contents of the new project file ravenscar_sfp_stm32f429.gpr will still reflect the target and name of the original target:

aggregate project Ravenscar_Sfp_Stm32F4 is

   Base_BSP_Source_Dir   := Project'Project_Dir & "cortex-m/stm32/stm32f4/";
   Base_Installation_Dir := "arm-eabi/lib/gnat/";
   Board                 := "stm32f4";
   Default_Prefix        := Base_Installation_Dir & "ravenscar-sfp-" & Board;
   Install_Dir           := external ("PREFIX", Default_Prefix);


end Ravenscar_Sfp_Stm32F4;

To tailor the new project file for our run-time:

  1. Change the project unit name from Ravenscar_Sfp_Stm32F4 to Ravenscar_Sfp_Stm32F429 at the start and end of the project file.
  2. Update the Base_BSP_Source_Dir variable on line three to reference the new stm32f429 board-specific directory.
  3. Change the Board variable on line five to "stm32f429" to reflect the board name of the new run-time.

The tailored project for our new board will look like:

aggregate project Ravenscar_Sfp_Stm32F429 is

   Base_BSP_Source_Dir   := Project'Project_Dir & "cortex-m/stm32/stm32f429/";
   Base_Installation_Dir := "arm-eabi/lib/gnat/";
   Board                 := "stm32f429";
   Default_Prefix        := Base_Installation_Dir & "ravenscar-sfp-" & Board;
   Install_Dir           := external ("PREFIX", Default_Prefix);


end Ravenscar_Sfp_Stm32F429;

These changes update the project to point to the correct BSP sources and setup the installation directory path so the new run-time will be installed alongside the run-times pre-installed by GNAT (with the installation directory given by the Install_Dir variable in line seven). If you desire the run-time to be installed in a different directory, you may either specify the PREFIX scenario variable when installing the run-time or by changing the Base_Installation_Dir variable on line four.

You can now build and install the new run-time library as described in the Building and Installing a Run-Time Library. While you can start using this run-time, the new run-time is currently functionality equivalent to the original STM32F4 run-time. It is at this point we can start tailoring the BSP source files and run-time project for our new run-time.

Tailoring the Run-Time Project

The starting point to tailoring a new run-time is to review the project file of the newly created run-time. In addition to defining the sources and installation directory of the run-time, the project file specifies the values for a number of scenario variables that control what common run-time sources are included within a run-time. This can be the inclusion or exclusion of a feature (like floating point support) or the selection of a particular version of a feature (like RTS_Profile).

For example, here is a sample of these scenario variable settings for the Ravenscar-SFP STM32F429 run-time:

for external ("Add_C_Support") use "no";
for external ("Memory_Profile") use "small";
for external ("Add_Memory_Operations") use "yes";
for external ("RTS_Profile") use "ravenscar-sfp";
for external ("Timer") use "timer32";
for external ("Use_Semihosting_IO") use "yes";
for external ("Add_Math_Lib") use "no";
for external ("CPU_Family") use "arm";
for external ("Has_FPU") use "yes";

Many of these scenario variable, like RTS_Profile, CPU_Family and Has_FPU, already reflect choices made when the new run-time was created from an existing project file and do not need to be reviewed when creating a new run-time. On the other hand, you may wish to review and customize the Use_Semihosting_IO and Add_Math_Lib variables to better tailor the features provided by the run-time to your application.

Note: while it is possible to change other scenario variable outside of ``Use_Semihosting_IO`` and ``Add_Math_Lib``, we strongly suggest you do so only with our direct support.

As an example of tailoring the run-time project, the Ravenscar-SFP STM32F429 run-time currently uses the semihosting feature to provide Ada.Text_IO facilities. If it is desired to use a UART peripheral instead, the value of Use_Semihosting_IO would be set to no and the body of the file s-textio.adb changed to initialize and drive a board-specific UART.

Similarly, you may wish to include or exclude support for the math packages defined in Annex A. The Ravenscar-Full profile already provides support for these packages while the ZFP and Ravenscar-SFP run-times do not (to minimize the complexity of these provided run-times). To support the differing floating point capabilities of different processors, the Add_Math_Lib scenario variable offers the following values:

  • no : no math support (the default for zfp and ravenscar-sfp)
  • softfloat : no FPU support (software computed only)
  • hardfloat_sp : the hardware only supports single-precision floating point.
  • hardfloat_dp : the hardware only supports double-precision floating point.
  • hardfloat : full FPU support (both single-precision and double-precision floating point)

All values other than “no” will result in the inclusion of the following math packages tailored to the specified FPU support:

  • Ada.Numerics
  • Ada.Numerics.Aux
  • Ada.Numerics.Complex_Elementary_Functions
  • Ada.Numerics.Complex_Types
  • Ada.Numerics.Elementary_Functions
  • Ada.Numerics.Generic_Complex_Elementary_Functions
  • Ada.Numerics.Generic_Complex_Types
  • Ada.Numerics.Generic_Elementary_Functions
  • Ada.Numerics.Long_Complex_Elementary_Functions
  • Ada.Numerics.Long_Complex_Types
  • Ada.Numerics.Long_Elementary_Functions
  • Ada.Numerics.Long_Long_Complex_Elementary_Functions
  • Ada.Numerics.Long_Long_Complex_Types
  • Ada.Numerics.Long_Long_Elementary_Functions
  • Ada.Numerics.Short_Complex_Elementary_Functions
  • Ada.Numerics.Short_Complex_Types
  • Ada.Numerics.Short_Elementary_Functions

Thus to include the math library in the new Ravenscar-SFP STM32F429 run-time, the Add_Math_Lib scenario variable would be changed from no to hardfloat_sp since the STM32F429 board includes a single-precision floating point co-processor (FPU):

for external ("Add_Math_Lib") use "hardfloat_sp";

Arbitrary Additional Source Files

In addition to the tailoring mechanism defined by the aggregate project files, users can tailor a run-time by adding arbitrary source files to the BSP.

Specifically, within the board-specific folder of a given BSP there is a user_srcs directory. This directory is initially empty but is referenced by the libgnat project file so any sources placed there will be compiled during the build and included in the resulting run-time library.

For example, for the STM32F4 ARM boards we saw that there are directories for each of the three profiles: zfp, ravenscar-sfp, and ravenscar-full (along with other ancillary directories not shown here):

 |--> link:
 |--> src:
 \--> stm32f4:
       |--> ravenscar-full:
       |--> ravenscar-sfp:
       |--> zfp:

These profile-specific directories are where the user_srcs directories are located. For example, in the ravenscar-full run-time directory:

 |--> link:
 |--> src:
 \--> stm32f4:
       |--> link:
       |--> ravenscar-full:
             |--> adalib:
             |--> arch:
             |--> obj:
             |--> user_srcs:

Once the additional sources are added to that directory, you simply rebuild the BSP to include the additional functionality in the run-time library.

Tailoring Clock Frequency

The BSP package System.BB.Parameters ( contains numerous board parameters used by the run-time library to setup the microcontroller and to describe the features of the microcontroller available to the run-time. This includes the clock source and frequency of the microcontroller. As most of the parameters are common to a processor family, System.BB.Parameters is defined at the processor-family level and consequently can be found in the crt0 directory of the processor family of the BSP.

When a run-time has board-specific folders to support multiple different boards under the one processor family, many System.BB.Parameters parameters will reference board specific parameters in the System.BB.Board_Parameters package ( . This package is located in the crt0 directory of the board-specific component of the BSP.

The STM32F429 run-time is one such case. Since the BSP descends from the STM32 processor-family, it automatically uses the common STM32 System.BB.Parameters package located at BSPs/cortex-m/stm32/src/crt0/ In this file, the clock frequency of our processor is defined via the Main_Clock_Frequency named number in the System.BB.Board_Parameters package:

package System.BB.Parameters is

   Clock_Frequency : constant := Board_Parameters.Main_Clock_Frequency;

The System.BB.Board_Parameters package for the STM32F429 microcontroller is located within the board-specific directory: stm32f429/src/crt0/ Since this file was copied from the stm32 run-time, the current value of the Main_Clock_Frequency is that of the maximum frequency of the STM32F4 Discovery kit:

package System.BB.Board_Parameters is

   Main_Clock_Frequency : constant := 168_000_000;

However, we would like our STM32F4 run-time to run at the maximum speed of the STM32F429 Discovery boards: 180MHz. We can tailor Main_Clock_Frequency to reflect this:

package System.BB.Board_Parameters is

   Main_Clock_Frequency : constant := 180_000_000;

Once the run-time is built and installed the new run-time will run applications on STM32F429 Discovery boards at the higher clock frequency.

Tailoring the Memory Layout

Changing the physical memory configuration for a board requires modification to linker scripts located in one or more BSP directories. These scripts can be specific to a board or common to a processor family. For the former, these scripts are located in the board-specific directories. For the latter, common linker scripts are contained in the processor family directories and board-specific linker constants defined at the board-specific level.

At each BSP level the link directories contain the linker scripts (if present). The names of the scripts in these folders will vary based on their purpose. For example, the LM3S target provides board specific linker scripts that are contained in the BSPs/cortex-m/lm3s/link directory:

  • lm3s-rom.ld
  • lm3s-ram.ld

The ROM variant of the LM3S linker script is designed to run programs from the Flash memory of the LM3S while the RAM variant runs programs from the RAM.

By contrast, the STM32 processor family supports different STM32 boards and thus common linker scripts are provided in the stm32/link that facilitate support for different boards:

  • common-RAM.ld
  • common-ROM.ld

Supporting these common scripts are target specific linker scripts defined at the board-specific level. The target-specific scripts define the sizes and locations of the memory regions defined in the common linker scripts. For example, the STM32F4 target contains memory-map.ld in the stm32f4/link directory that describes the memory sizes and locations of the RAM and Flash on the STM32F407.

For our new STM32F429 Discovery BSP, the common linker script suites our requirements as its memory layout is suitable to support the STM32F429. However, the STM32F429 has more Flash and RAM than the STM32F407 that we wish to take advantage of in our new BSP and thus we will need to update the board-specific linker script to take advantage of the larger memory. To do so, we update the linker script stm32f429/link/memory-map.ld to reflect the amount of memory available on the STM32F429 from:

  flash (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
  sram (rwx) : ORIGIN = 0x20000000, LENGTH = 192K


  flash (rx) : ORIGIN = 0x08000000, LENGTH = 2048K
  sram (rwx) : ORIGIN = 0x20000000, LENGTH = 256K

With this single change, our applications can now take advantage of the increased memory available on the STM32F429. Note that we do not need to modify the origin of each memory region as they are common between the STM32F407 and STM32F429.

Selecting linker scripts

Multiple linker scripts may be included with a BSP to support different memory layout and loading scenarios. For example, the ARM Cortex-M BSPs include scripts to support loading a program to Flash (common-ROM.ld) or to RAM (common-RAM.ld).

The LOADER scenario variable selects which linker script is used when building an application project with gprbuild. The variable is defined within an XML file named run-time.xml: a file that provides target-specific project settings to gprbuild and can be found at the run-time level of a target’s BSP.

For example, run-time.xml for our Ravenscar-Full STM32F429 run-time can be found in BSPs/cortex-m/stm32/stm32f429/ravenscar-full. As can be seen in the abridged version of this file, the scenario variable LOADER controls the choice of linker script:

   type Loaders is ("ROM", "RAM", "USER");
   Loader : Loaders := external("LOADER", "ROM");


   package Linker is
      for Required_Switches use Linker'Required_Switches &
         "-nostartfiles", "-nolibc",
         "-L${RUNTIME_DIR(ada)}/ld") &

      case Loader is
         when "ROM" =>
            for Required_Switches use Linker'Required_Switches &
              ("-T", "memory-map.ld",
               "-T", "common-ROM.ld");
         when "RAM" =>
            for Required_Switches use Linker'Required_Switches &
              ("-T", "memory-map.ld",
               "-T", "common-RAM.ld");
         when "USER" =>
      end case;
   end Linker;

You can specify the value of this scenario variable on the command line when invoking gprbuild, thereby controlling which script is applied. The default value (ROM) is used if no explicit argument is specified, while the value USER allows a project specific linker script to be specified in the gpr file of the project.

For example, given a project file app.gpr describing the application, we can invoke gprbuild and supply one or more scenario variable values:

$ gprbuild -P app.gpr -XLOADER=RAM

Or to take the default:

$ gprbuild -P app.gpr

Dynamic Memory Allocation

All three run-time profiles support dynamic memory allocation using standard Ada syntax when using the default storage pool (more commonly known as the “heap”). The run-time package System.Memory (s-memory.adb) provides run-time support for this feature and its implementation is profile dependent. As a consequent, it may be necessary to customize System.Memory to suit your application, particular if you need to control how memory is allocated and deallocated, or in some cases set the size of the heap.

The Ravenscar-Full profile implements System.Memory on top of calls to the C standard library Newlib, which is included with Ravenscar-Full run-time. This implementation is generally sufficient for most users, with the memory allocated from the start of the _end symbol in the linker script (note: Newlib does not appear to bound the amount of memory that can be allocated by malloc).

For Ravenscar-SFP, System.Memory implements dynamic memory allocation directly within the package and exports the procedures for use by C code. The location and size of the heap for this implementation is defined by the __heap_start and __heap_end symbols in the linker script, with the implementation using these bounds to ensure the RAM is not over-allocated. The BSP linker scripts included with GNAT assign RAM not allocated to static objects and stacks to the heap. Consequently, manual sizing of the heap is generally not required when using this implementation.

By contrast, the ZFP run-time implements dynamic memory allocation using a statically allocated storage array to represent the heap. All allocated objects come from this single array. The size of this array object is hard-coded in s-memory.adb and does not size or correspond to the physical memory available. By default it is set to an arbitrary 20K storage elements. Since this size may not be suitable for the memory requirement of your application you are free to change the size of the array, or implement a different allocation strategy. To change the array size modify the value of the Default_Size named number declared in the System.Memory package body.

Note the compiler creates calls to procedures defined within System.Memory and that the package defines a number of global symbols. Consequently, maintain the symbols it exports and its package specification.

Configuring Interrupts

When tailoring a BSP to a new processor the interrupt configuration of the BSP will need reviewing as the type and number of interrupts may have changed. This is important even when porting a BSP within the same microcontroller family as the number of interrupts will vary based on the peripherals offered by an individual family member. The files that need reviewing are located in the src/gnarl folder in the BSP’s board-specific directory. For example, on the STM32F429 they would be located in:


At a minimum the interrupt trap vectors and interrupt names will require reviewing. Typically interrupt trap vectors are defined in a interrupt vector table located in an assembly file: handler.s in the case of STM32F4 targets. When porting within a microcontroller family you will only need to ensure the correct number of trap vectors are defined. When porting to a different processor family you will need to refer to that family’s reference manual to determine how to define the interrupt vector table.

Interrupt names and their corresponding Interrupt_ID values are declared the package Ada.Interrupts.Names ( This file will need updating if the interrupt names or the number of interrupts differ on the new target.

The STM32F429 Discovery kit used in this chapter has the same interrupts as the STM32F4 Discovery kit supported by the existing STM32F4 run-time, so the STM32F429 Ravenscar-SFP run-time does not need any modification.

Interrupt Priorities

While less common, the new target hardware may support a different number of hardware interrupt priorities than the processor in the original BSP. In this case the Interrupt_Priority subtype defined in the System package will need updating. Take note of the guidelines for modifying priority ranges and their language-defined requirements as documented in this section.

The package System is located within the run-time specific directory of the BSP as it is highly dependent on the run-time profile (with each run-time profile requiring its own version of System). For example, System for the STM32F429 Ravenscar-SFP run-time is located here:


Within the System package are a series of priority subtypes and constants defined in accordance with the Ada Reference Manual. The STM32F429 Ravenscar-SFP run-time for example contains the following definitions:

Max_Interrupt_Priority   : constant Positive := 255;
Max_Priority             : constant Positive := 240;

subtype Any_Priority       is Integer range         0 .. 255;
subtype Priority           is Any_Priority range    0 .. 240;
subtype Interrupt_Priority is Any_Priority range  241 .. 255;

Default_Priority : constant Priority := 120;

Note: you should not modify anything else in the package.

The Ada Reference Manual defines the subtype Any_Priority to represent all priority values, with higher numbers indicating higher priorities. This subtype is divided into to two subranges: Priority and Interrupt_Priority. Interrupt_Priority corresponds to the priorities used by interrupt handlers, while tasks, including the environment task, typically use the priorities from the Priority subtype. This assignment of priorities ensures interrupts can interrupt the normal execution of tasks

Note: while Ada permits assigning tasks priorities from the ``Interrupt_Priority`` this is not common and may be prevented on some targets (like ARM).

The range 0 .. 255 is recommended for the Any_Priority subtype because, in practice, 256 levels of priority is sufficient for any application and a smaller range does not reduce the memory requirements of the run-time library. However, you may use whatever bounds correspond to the hardware while noting that the Ada requires that System.Priority include at least 30 values.

The range of System.Interrupt_Priority should map to the range of interrupt hardware priority levels so that there is one-to-one correspondence between them. Thus, if your new target supports a different number of hardware interrupt priority levels, update Interrupt_Priority to reflect this number and adjust the range of Priority accordingly. For example, if your new target only supports a single hardware interrupt priority, update System to:

Max_Interrupt_Priority   : constant Positive := 255;
Max_Priority             : constant Positive := 254;

subtype Any_Priority       is Integer range         0 .. 255;
subtype Priority           is Any_Priority range    0 .. 254;
subtype Interrupt_Priority is Any_Priority range  255 .. 255;

Default_Priority : constant Priority := 127;

Debug Support


You will need to determine how gdb running on your host will interact with code on your target. Most likely, it will use gdb‘s serial protocol to interact with some agent that can control execution of your code. One example is the use of a hardware probe that controls the processor through a JTAG (BDM, COPS) port. GNAT supports the Abatron BDI 2000, which is a hardware device that connects to the target through JTAG, implements the gdb serial protocol in firmware, and communicates using that protocol over its Ethernet port. For example, a BDI could be known on your local network by the hostname bdi, and when running powerpc-elf-gdb you would use the gdb command target remote bdi:2001. This tells gdb that it will control the execution of your program by using the serial protocol running on port 2001 of the host bdi. Another possibility is that there is a kernel running on your target hardware that controls program execution (your application) and which implements the gdb serial protocol over some connection – often a serial port.

Porting the GDB stub

The GDB stub provides remote loading and debugging facilities. The GDB stub is a tiny application that uses a serial line to communicate with a GDB hosted on the development machine.

On powerpc-elf the sources of the GDB stubs are in <gnat-root>/share/examples/gnat-cross/gdbstub/powerpc-elf. Two files have to be customized: the project file stub.gpr must be modified to extend the zfp-mpc8641 run-time library project for your board and the input/output file gdbstub_io.adb which must be ported to your board.

Once the GDB stub is working, you also need to write a simplified BSP for the stub that doesn’t initialize the board (as it is already done by the stub) and that maps the application in RAM.