RTEMS Topics

This appendix presents information specific to GNAT Pro for RTEMS 6.

Getting Started

To use GNAT Pro with RTEMS you will need to set two environment variables before using the toolchain:

  • ENV_PREFIX: This is the path to the folder containing the RTEMS kernel and its headers. If you installed the RTEMS Tool Suite in $PREFIX the kernel path will be $PREFIX/<target-rtems6>.

  • RTEMS_BSP: The name of the BSP you are using for your RTEMS kernel. There will be a folder with this name located in the ENV_PREFIX directory.

For example: if you installed the RTEMS 6 Tool Suite for AArch64 in $HOME/my_rtems_project and are using the xilinx_zynqmp_lp64_qemu BSP you would set the environment variables as follows on Linux:

export ENV_PREFIX=$HOME/my_rtems_project/aarch64-rtems6
export RTEMS_BSP=xilinx_zynqmp_lp64_qemu

Developing for RTEMS is similar to native GNAT development, with two important differences. First, when building for RTEMS you need to specify the target in the 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 for your project, see Introduction to GNAT for Cross Platforms.

The second difference is that RTEMS requires a configuration and initialization file to configure the kernel and to call the application main. Due to the nature of RTEMS, this file is implemented in C. For Ada applications, it is recommended to launch the Ada application from the POSIX initialization thread, POSIX_Init, as the runtime uses the RTEMS POSIX API. For an Ada main, the signature of the Ada main to call is:

int main (int argc, const char *argv[]);

A sample configuration file, rtems_init.c, is shown below:

#include <stdlib.h>
#include <sys/stat.h>

extern int main (int argc, const char *argv[]);

void *
POSIX_Init (void *argument)
{
  int exit_code;

  const char *argv[] = { "main" };
  const int argc = sizeof (argv) / sizeof (const char *);

  /* Pass the environment task stack size to the runtime as an environment
   * variable */
  setenv ("GNAT_STACK_LIMIT", "8192", 1);

  exit_code = main (argc, argv);

  exit (exit_code);

  return 0;
}

/* configuration */

#include <bsp.h>

#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER

#define CONFIGURE_EXCEPTION_TO_SIGNAL_MAPPING

#define CONFIGURE_MICROSECONDS_PER_TICK RTEMS_MILLISECONDS_TO_MICROSECONDS (10)

#define CONFIGURE_POSIX_INIT_THREAD_TABLE
#define CONFIGURE_POSIX_INIT_THREAD_STACK_SIZE (8 * 1024 * 1024)

#define CONFIGURE_UNLIMITED_OBJECTS
#define CONFIGURE_UNIFIED_WORK_AREAS

#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 20

#define CONFIGURE_ZERO_WORKSPACE_AUTOMATICALLY 1
#define CONFIGURE_INIT

#include <rtems/confdefs.h>

rtems_init.c can be included in your Ada or mixed-language project by adding it to your project sources, and ensuring that the C language is selected in the project properties dialog in GNAT Studio or listed as a language in your project file. For example, the following project file is set up for an AArch64 RTEMS project:

project Demo is

   for Languages use ("Ada", "C");
   for Target use "aarch64-rtems6";

   for Source_Dirs use ("src");
   for Object_Dir use "obj";

   for Main use ("demo.adb");

end Demo;

Kernel Configuration

To build Ada applications, your RTEMS kernel needs to be configured with POSIX API support. This is done by adding RTEMS_POSIX_API = POSIX to your kernel configuration file.

GNAT Pro does not use RTEMS’s Ada support, so the __RTEMS_ADA__ option should not be enabled.

The GNAT Pro run-time requires the following configuration options to be defined:

  • CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER: if tasks or delays are used.

  • CONFIGURE_EXCEPTION_TO_SIGNAL_MAPPING: if hardware exceptions are to be handled as Ada exceptions.

Interrupts

GNAT Pro for RTEMS supports attaching protected procedure handlers to hardware interrupts. To do so, provide the hardware vector number of the hardware interrupt to the Attach_Handler aspect or attachment routines in the Ada.Interrupts package.

Using LibBSD with GNAT Pro

LibBSD for RTEMS provides RTEMS applications drivers and tools from the FreeBSD project. It is most notable for providing the networking stack for RTEMS. More information about LibBSD is available on the RTEMS project website.

To use LibBSD with GNAT Pro, you need to link your application with the LibBSD and LibM libraries in addition to having the linker remove unused linker sections. This can be done by adding the following Linker section to your project file:

package Linker is
   for Switches ("ada") use ("-lbsd", "-Wl,--gc-sections");
end Linker;

For example:

project Demo is

   for Languages use ("Ada", "C");
   for Target use "aarch64-rtems6";

   for Source_Dirs use ("src");
   for Object_Dir use "obj";

   for Main use ("demo.adb");

   package Linker is
      for Switches ("ada") use ("-lbsd", "-Wl,--gc-sections");
   end Linker;

end Demo;

A sample configuration file for the Xilinx Zynq UltraScale+ using LibBSD for network and SD card support, and optionally enabling libdebugger, is shown below:

#include <assert.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <rtems/bdbuf.h>
#include <rtems/bsd/bsd.h>
#include <rtems/media.h>

extern int main (int argc, const char *argv[]);

#ifdef GDB_SERVER

/* This is some additional initialization for the network stack.  */
#include <sys/unistd.h>
#include <rtems/rtems-debugger.h>
#include <rtems/rtems-debugger-remote-tcp.h>

#define NET_CFG_INTERFACE_0 "cgem0"
#define NET_CFG_GATEWAY_IP "192.168.1.2"
#define NET_CFG_NETMASK "255.255.255.0"
#define NET_CFG_SELF_IP "192.168.1.15"

static void configure_network(void)
{
  rtems_bsd_ifconfig_lo0();
  rtems_bsd_ifconfig(NET_CFG_INTERFACE_0,
                     NET_CFG_SELF_IP,
                     NET_CFG_NETMASK,
                     NET_CFG_GATEWAY_IP);
}

static void start_gdb_server(void)
{
  rtems_printer printer;
  int r;

  rtems_print_printer_fprintf(&printer, stdout);
  r = rtems_debugger_register_tcp_remote();
  if (r < 0) {
    perror("TCP remote register");
    abort();
  }

  r = rtems_debugger_start("tcp", "1122", 3, 1, &printer);
  if (r < 0) {
    perror("can't start debugger");
    abort();
  }
}

#else

static void configure_network(void) {};
static void start_gdb_server(void) {};

#endif /* GDB_SERVER */

/* The media server automatically mounts removable media (like an SD Card) for
   us. */
static void
setup_media_server(void)
{
  rtems_status_code sc;

  sc = rtems_bdbuf_init();
  assert(sc == RTEMS_SUCCESSFUL);

  sc = rtems_media_initialize();
  assert(sc == RTEMS_SUCCESSFUL);

  sc = rtems_media_server_initialize(
    200,
    32 * 1024,
    RTEMS_DEFAULT_MODES,
    RTEMS_DEFAULT_ATTRIBUTES
  );
  assert(sc == RTEMS_SUCCESSFUL);
}

void *
POSIX_Init (void *argument)
{
  int exit_code;

  const char *argv[] = { "main" };
  const int argc = sizeof (argv) / sizeof (const char *);

  setup_media_server();

  /* The SD Card and network drivers are part of libbsd */
  rtems_bsd_initialize();

  /* Configure the network if GDB_SERVER is enabled.  */
  configure_network();

  /* Pass the environment task stack size to the runtime as an environment
   * variable */
  setenv ("GNAT_STACK_LIMIT", "8192", 1);

  /* Start libdebugger if GDB_SERVER is enabled.  */
  start_gdb_server();

  exit_code = main (argc, argv);
  exit (exit_code);

  return 0;
}

/* configuration */

#define RTEMS_BSD_CONFIG_BSP_CONFIG
#define RTEMS_BSD_CONFIG_INIT

#include <machine/rtems-bsd-config.h>

#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK

#define CONFIGURE_EXCEPTION_TO_SIGNAL_MAPPING

#define CONFIGURE_MICROSECONDS_PER_TICK RTEMS_MILLISECONDS_TO_MICROSECONDS (10)

#define CONFIGURE_POSIX_INIT_THREAD_TABLE
#define CONFIGURE_POSIX_INIT_THREAD_STACK_SIZE (8 * 1024 * 1024)

#define CONFIGURE_UNLIMITED_OBJECTS
#define CONFIGURE_UNIFIED_WORK_AREAS

#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
#define CONFIGURE_FILESYSTEM_DOSFS
#define CONFIGURE_MAXIMUM_USER_EXTENSIONS 10
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 60

#define CONFIGURE_ZERO_WORKSPACE_AUTOMATICALLY 1
#define CONFIGURE_INIT

#include <rtems/confdefs.h>

Debugging

An RTEMS application can be debugged using the GDB Server built into RTEMS (libdebugger) or with a hardware probe. When using GDB with a hardware probe (or the GDB Server built into QEMU), task-level debugging is not available.

When using libdebugger, be aware that:

  • libdebugger does not have the ability to catch software signals

  • libdebugger only allows attaching GDB to a running application.

If you need to debug the initialization of your application with libdebugger, you will need to add a global variable to your application and have it loop on that variable until its state is changed by the debugger. For example, the code below will stop the application just before the call to the Ada main. The application will wait here until the debugger connects to the libdebugger and the user issues set release_debugger = 1.

volatile int release_debugger = 0;

...

void *
POSIX_Init (void *argument)
{
   ...

   while (!release_debugger) {
     sleep(0.1);
   }

   exit_code = main (argc, argv);
   exit (exit_code);
}