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 theENV_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);
}