Bareboard and Custom Kernel Topics

This appendix describes how to use the GNAT cross tools to build programs that do not require support from any kernel running on the target hardware.

Introduction

Since GNAT version 7.3, bareboard run-times for powerpc-elf, p55-elf, leon-elf, leon3-elf, arm-elf and aarch64-elf are provided as customized run-times. They target only specific boards so unless you are using the same board they must be ported to your target. This simplifies the build of a user program as explained in this appendix. However this requires (as before) an initial porting effort. Note that any such changes must be revisited (and likely re-ported) if you update the compiler. See Customized Run-Time Libraries for adapting a run-time to your board.

Bareboard Run-Time Library Names

The pre-built run-time libraries have names that reflect their characteristics. For example, those that include ‘ravenscar’ in their names provide a subset of the Ada tasking facilities defined by the Ravenscar profile. Similarly, those libraries with ‘zfp’ in the name implement the ‘Zero Foot Print’ profile.

The pre-built run-time libraries also include the name of the target board they support. The names therefore vary with the target and corresponding toolchain.

For example, the name ‘ravenscar-full-tms570’ denotes a run-time library targeted to the Texas Instruments TM570 board. The library will provide the Ravenscar Profile tasking subset. The ‘full’ in the name indicates that the full sequential (i.e., non-tasking) part of Ada is supported. This library could be specified as follows:

project Demo is
   ...
   for Runtime ("Ada") use "ravenscar-full-tms570";
   ...
end Demo;

See Selecting the Run-Time Library for instructions on how to specify your selection of a run-time library.

Minimizing Object Code and Data Sizes

Bareboard targets typically have stringent constraints on available storage, both for data as well as object code. In addition to careful coding practices, such as sharing generic package instantiations as much as possible, you can request the removal of unused code and data at build time. The technique uses standard GCC switches.

To achieve this reduction, you must specify switches for both the compiler and the linker.

In the compiler switches, request removal of unused code by applying the "-ffunction-sections" switch. In the same way, request removal of unused data by applying the "-fdata-sections" switch. Specifying both switches is allowed.

In the linker switches, specify "-Wl,--gc-sections" to have the linker remove (“garbage collect”) unused code and/or data. Both this linker switch and the compiler switch(s) are essential for any reduction to take effect.

You can get a list of what is removed by including the "-Wl,--print-gc-sections" linker switch.

For example:

package Compiler is
   for Default_Switches ("Ada") use ("-O2", "-ffunction-sections", "-fdata-sections");
end Compiler;

...

package Linker is
   for Default_Switches ("Ada") use (
      "-Wl,--gc-sections",
      "-Wl,--print-gc-sections");
end Linker;

Viewing Object Code and Data Sizes

You can have the linker display the amounts of storage used for both code and data, as well as the total amounts available and the resulting usage percentages.

To get this information specify the "-Wl,--print-memory-usage" linker switch.

For example:

package Linker is
   for Default_Switches ("Ada") use ("-Wl,--print-memory-usage");
end Linker;

Of course, you can combine this switch with the switch requesting removal of unused sections. The Linker package in the project file would then appear as follows:

package Linker is
   for Default_Switches ("Ada") use (
      "-Wl,--gc-sections",
      "-Wl,--print-memory-usage");
end Linker;

With this switch applied the linker will print the information to standard output. The results will thus appear in the GPS Messages window.

For example:

Memory region         Used Size  Region Size  %age Used
           flash:       44268 B         2 MB      2.11%
            sram:       48784 B       256 KB     18.61%

Character Input/Output

The bareboard run-time libraries provide a customized version of package Ada.Text_IO that exports a minimal set of facilities. The package declaration of this customized version is as follows, for all the bareboard run-time libraries:

package Ada.Text_IO is

   procedure Get (C : out Character);
   --  Read from console

   procedure Put (Item : Character);
   --  Output character to the console

   procedure Put (Item : String);
   --  Output string to the console

   procedure Put_Line (Item : String);
   --  Output string followed by new line to the console

   procedure New_Line;
   --  Output new line character to the console

end Ada.Text_IO;

The cut-down package is provided for the sake of initial board check-out and development and is not intended to be a production I/O capability.

The implementations of the package body are target dependent.

For example, on some targets the implementation drives a USART for this purpose. You can use a USB-to-Serial conversion cable to connect those pins (and a ground pin) to a USB port on the host computer, and run an application on the host to monitor that port in order to interact with the board.

In contrast, on the ARM STM32F4x Discovery boards the implementation uses “semihosting”, in which the output goes to the screen showing the st-util outputs. No USB-to-Serial conversion cable is required in that case.

The package GNAT.IO is also provided by the bareboard run-time libraries. The interface is slightly different, as shown below:

package GNAT.IO is
   pragma Preelaborate;

   procedure Put (X : Integer);
   --  Output integer to specified file, or to current output file, same
   --  output as if Ada.Text_IO.Integer_IO had been instantiated for Integer.

   procedure Put (C : Character);
   --  Output character to specified file, or to current output file

   procedure Put (S : String);
   --  Output string to specified file, or to current output file

   procedure Put_Line (S : String);
   --  Output string followed by new line to specified file, or to
   --  current output file.

   procedure New_Line (Spacing : Positive := 1);
   --  Output new line character to specified file, or to current output file

end GNAT.IO;

Note, in particular, the ability to output an integer value, and the absence of any input routine.

In both the GNAT.IO and Ada.Text_IO cases, the underlying board-specific implementation part is in an internal System.Text_IO package provided by the run-time library. See s-textio.adb in particular. You can change that package to reflect your target’s hardware I/O capabilities. However, since the exported interfaces are minimal, you may want to consider using a more target-specific facility as an alternative to GNAT.IO and Ada.Text_IO.

Examples

Given the many variations on the host and target environment that you may be using, it is not possible to formulate a universally applicable sequence of commands for building, loading, and running a program. Instead, this section presents a representative scenario. For illustration purposes the example assumes a Freescale QorIQ P2020 board. Note that there is a full tutorial for building, running, and debugging using one of the ARM boards supported: ARM-ELF Topics and Tutorial.

Let’s start with a simple “hello world” program:

with Ada.Text_IO; use Ada.Text_IO;

procedure Hello is
begin
   Put_Line ("Hello zfp world");
end Hello;

Let’s also have a project file hello.gpr for this program:

project hello is
   for Main use ("hello.adb");
end hello;

Use gprbuild to build the application. You need to specify both the target and the run-time to use.

$ gprbuild --target=powerpc-eabispe --RTS=zfp-p2020 -P hello.gpr

This creates an ELF executable hello that can be loaded in RAM on the p2020 target. This particular run-time assumes:

  • the target hardware has a monitor program in ROM that runs at powerup
  • a command line interface controls the target through a serial port connected to the host
  • a load command in the monitor program uses tftp to load a specified ELF file from the host (there is an Ethernet interface, and the monitor handles TCP/IP protocols)

In other words, the monitor on the target board is relatively ‘smart’. To run a program that you’ve built with GNAT Cross (an executable ELF file) you just need to load the program and ‘go’ – the monitor program keeps track of the start address recorded in the ELF file.

Other targets may have other mechanisms for loading and invoking applications. These mechanisms are described in the target-specific sections within this document.

Bareboard Debugging

Debugging applications on bareboard targets involves connecting gdb to the board. You can then either use gdb directly, on the command line, or use GPS to drive it. For some targets, you can also use an emulator (or simulator) instead of an actual target board. Typically a JTAG probe or something similar is involved. The specific mechanisms for connecting, and whether an emulator and/or simulator is available, are target-specific and are discussed in the corresponding sections within this document.