VxWorks Topics

This appendix covers topics related to GNAT for various versions of the VxWorks RTOS.

Common VxWorks Topics

This section describes topics that are relevant to all GNAT for VxWorks configurations. Note that the GNAT tool prefix for Tornado 2.x / VxWorks 5.x or Workbench / VxWorks 6.x is powerpc-wrs-vxworks-, e500v2-wrs-vxworks- or i586-wrs-vxworks-; for Workbench / VxWorks 7, it is <CPU>-wrs-vxworks7- where <CPU> is the CPU being targeted (Eg: arm, i586, etc); for Workbench / VxWorks 653, it is powerpc-wrs-vxworksae-.

Executing a Program on VxWorks

Getting a program to execute involves loading it onto the target, running it, and then (if re-execution is needed) unloading it. These instructions apply mainly to Tornado 2.x / VxWorks 5.x, or Workbench / VxWorks 6.x and Workbench / VxWorks 7 when building an application for execution in the kernel space.

Loading and Running the Program

An Ada program is loaded and run in the same way as a C program. Details may be found in the Tornado User’s Guide.

In order to load and run our sample program, we assume that the target has access to the disk of the host containing the required object and that its working directory has been set to the directory containing this object. The commands are typed in Tornado’s Windshell. The windsh prompt is the -> sequence. In this example, the object is named hello, and when its execution results in the display of the Hello World string.

For VxWorks 5.x, VxWorks 6.x and VxWorks 7:

-> vf0=open("/vio/0",2,0)
new symbol "vf0" added to symbol table.
vf0 = 0x2cab48: value = 12 = 0xc
-> ioGlobalStdSet(1,vf0)
value = 1 = 0x1
-> ld < hello
value = 665408 = 0xa2740
-> hello
Hello World
value = 0 = 0x0
->

The first two commands redirect output to the shell window. They are only needed if the target server was started without the -C option. The third command loads the module, which is the file hello created previously by the gnatmake command.

The Hello World program comprises a procedure named hello, and this is the name entered for the procedure in the target server’s symbol table when the module is loaded. To execute the procedure, type the symbol name hello into windsh as shown in the last command above.

Note that by default the entry point of an Ada program is the name of the main Ada subprogram in a VxWorks environment. It is possible to use an alternative name; see the description of gnatbind options for details.

Unloading the Program

It is important to remember that you must unload a program once you have run it. You cannot load it once and run it several times. If you don’t follow this rule, your program’s behavior can be unpredictable, and will most probably crash.

This effect is due to the implementation of Ada’s elaboration semantics. The unit elaboration phase comprises a static elaboration and a dynamic elaboration. On a native platform they both take place when the program is run. Thus rerunning the program will repeat the complete elaboration phase, and the program will run correctly.

On VxWorks, the process is a bit different. The static elaboration phase is handled by the loader (typically when you type ld < program_name in windsh). The dynamic phase takes place when the program is run. If the program is run twice and has not been unloaded and then reloaded, the second time it is run, the static elaboration phase is skipped. Variables initialized during the static elaboration phase may have been modified during the first execution of the program. Thus the second execution isn’t performed on a completely initialized environment.

Note that in C programs, elaboration isn’t systematic. Multiple runs without reload might work, but, even with C programs, if there is an elaboration phase, you will have to unload your program before re-running it.

Mixed-Language Programming

The GNAT User’s Guide for Native Platforms, in the section “Building Mixed Ada & C++ Programs”, subsection “A Simple Example”, presents the compilation commands needed to build one of the GNAT examples.

Note that mixing Ada with C++ units compiled using diab is not supported.

The relevant commands for the GNAT VxWorks/PowerPC target are as follows, assuming WIND_BASE is correctly defined:

$ powerpc-wrs-vxworks-gnatmake -c simple_cpp_interface
$ powerpc-wrs-vxworks-gnatbind -n simple_cpp_interface
$ powerpc-wrs-vxworks-gnatlink simple_cpp_interface -o ada_part
$ c++ppc -c -DCPU=PPC604  -I$WIND_BASE/target/h -I$WIND_BASE/target/h/wrn/coreip cpp_main.C
$ c++ppc -c -DCPU=PPC604  -I$WIND_BASE/target/h -I$WIND_BASE/target/h/wrn/coreip ex7.C
$ ldppc -r -o my_main my_main.o ex7.o ada_part

Note that use of gprconfig/gprbuild will insert the relevant includes above automatically. By default, the first C/C++ compiler on the path will be selected; in order to use the Wind River compilers, either make sure that they appear before GNAT in the path or select them explicitly using gprconfig.

The gprconfig/gprbuild knowledge base contains the appropriate linker options for producing a module for the configuration that you select. In order to take advantage of that, WIND_BASE shall be properly defined before invoking these tools.

As a special exception, for RTPs on x86 vxWorks 6 targets, the user needs to link against different libraries if he targets vxsim instead of a real board. When using GNAT for the link, this can be done by using the linker option -vxsim. For instance, one may have the following entries in a project file:

Wind_Base := external ("WIND_BASE");

type Target_Machine_Type is ("vxsim", "real_target");
Target_Machine : Target_Machine_Type := external ("TARGET_MACHINE",
                                                 "real_target");
package Linker is
  case Target_Machine is
    when "vxsim" =>
      for Switches ("Ada") use ("-vxsim");
    when "real_target" =>
      null;
  end case;
end Linker;

When building a c-only x86 RTP, the path to libraries should however be passed explicitly:

Wind_Base := external ("WIND_BASE");

type Target_Machine_Type is ("vxsim", "real_target");
Target_Machine : Target_Machine_Type := external ("TARGET_MACHINE",
                                                 "real_target");
package Linker is
  case Target_Machine is
    when "vxsim" =>
      for Switches ("C") use
        ("-L" & Wind_Base
         & "/target/usr/lib/simpentium/SIMPENTIUM/common",
         "-L" & Wind_Base
         & "/target/lib/usr/lib/simpentium/SIMPENTIUM/common");
    when "real_target" =>
      for Switches ("C") use
        ("-L" & Wind_Base
         & "/target/usr/lib/pentium/PENTIUM/common",
         "-L" & Wind_Base
         & "/target/lib/usr/lib/pentium/PENTIUM/common");
  end case;
end Linker;

The GNU C and C++ compilers for VxWorks 653 are compatible with the full, cert and ravenscar-cert run-time libraries with respect to exception propagation. All of these compilers use the ‘setjmp/longjmp’ (SJLJ) exception propagation mechanism.

VxWorks 5 GNU C has the same compatibility with the default (SJLJ) library.

VxWorks 6.6 and VxWorks Cert 6.6 and lower GNU C and C++ compilers are also compatible with SJLJ. Note that as of GNAT 6.3.1, SJLJ is no longer the default run-time library, so must be selected explicitly with builder switch --RTS=kernel-sjlj.

VxWorks 6.7 and higher GNU C and C++ compilers are compatible with the ‘zero cost exception handling’ scheme that is now the default GNAT run-time library. As of this writing, there are incompatibilities in obtaining tracebacks across language boundaries.

‘Munching’, the process of calling C++ static initializers, is automatic when building applications under Workbench 3.2 and higher. The Workbench application project types build the necessary wrapper around the application generated by GNAT. For VxWorks 5 applications that include C++ sources, munching must be done manually. See the Wind River documentation.

Tip: If an illegal instruction is encountered, check that the GNAT-compiled and WindRiver-C/C++ compiled code is compatible with respect to processor.

Kernel Configuration for VxWorks

When configuring your VxWorks kernel we recommend including the target shell. If you omit it from the configuration, you may get undefined symbols at load time, e.g.

-> ld < hello.exe
Loading hello.exe
Undefined symbols:
mkdir

Generally, such undefined symbols are harmless since these are used by optional parts of the GNAT run time. However if running your application generates a VxWorks exception or illegal instruction, you should reconfigure your kernel to resolve these symbols.

Kernel Compilation Issues for VxWorks

This section describes the different ways to statically link an Ada module (built using the GNAT toolchain) with a VxWorks kernel. The main issue is that two distinct toolchains are used:

  • The Wind River toolchain (diab or gnu)
  • The GNAT toolchain

Both toolchains compile Ada or C modules using either of two libraries: GCC Library or Wind River Library. These two libraries share common symbols and are mostly compatible. However, there is a potential issue with the exception handling functions. If you tried to directly link your Ada module with the VxWorks kernel you would get “duplicate symbols” errors for the linker, as two versions of the exceptions management library are included. In order to resolve this issue you will need, depending on the context, to select only one of these libraries or else find a way to include both of them. This section covers two topics:

  • Which library to use
  • How to achieve the link

The library to select depends on the following issues:

  • Whether C++ is used
  • Whether the ZCX run-time is used for the Ada module

If you are using C++ in your project, then you will need the C++ exception handling functions. As the GCC Library from the GNAT toolchain implements the exception mechanisms differently from the Wind River Library, you need in this case to use the Wind River Library.

The ZCX run-time library packaged in the PowerPC GNAT toolchains uses the exception mechanism implemented in the GCC Library distributed with GNAT. If you are using this run-time library you must select the GCC Library and not the one from the Wind River toolchain.

The sjlj Ada run-time library in the GNAT toolchain does not rely on the mechanism present in the GCC Library, so if you are using that run-time in your Ada module, it’s better to use the Wind River Library as it allows you to use C++.

If you are using both C++ and the ZCX runtime, then the situation is more complex as you need the two libraries. A possible approach is described at the end of this section.

There are several different ways to perform the link:

  • Using the Wind River Library

    You need to compile your Ada module with the -nostdlib option, which excludes the GNAT GCC Library:

    $ gnatmake foo.adb -largs -nostdlib
    

    and then add the resulting executable file (foo or foo.exe) to your kernel. On VxWorks 5.x this can be achieved by adding the object in the EXTRA_MODULES macro of your kernel project.

  • Using the GNAT GCC Library

    You need to do the following:

    • Compile your Ada module:

      $ gnatmake foo.adb -largs -nostdlib
      
    • Add the resulting executable file to the kernel as described above.

    • Remove the Wind River Library from the kernel and then put in the GNAT version instead. On VxWorks 5.x, this step is achieved by modifying the LIBS macro. In order to obtain the location of the GNAT GCC Library do the following:

      $ powerpc-wrs-vxworks-gcc -print-libgcc-file-name <options>
      

      Even if this command does not perform any compilation it is important to specify to GCC the options used when the Ada module was compiled. Indeed, the GNAT toolchain provides several versions of the GCC Library; by specifying the GCC options in the previous command you will obtain the correct version.

    • In your kernel configuration, be sure to remove “Compiler Support Routines” components. Indeed this component adds a C file to your kernel that will create a dependence on all .o files present in the Wind River Library, and you will receive error messages from the linker about missing symbols. An inconvenient consequence is that if you plan to dynamically download modules to your kernel, then some symbols from the GCC Library might not be present.

  • Including both GCC and Wind River Libraries

    • Compile your Ada module

      $ powerpc-wrs-vxworks-gnatmake foo.adb
      
    • Make all the symbols of your Ada module local except the main entry point

      $ objcopyppc -G foo foo new-foo
      
    • Add the resulting executable file to your kernel.

If you need a bootable application, then refer to the section “Creating Bootable Application” in the “Projects” chapter of the Tornado User’s Guide. There is a detailed discussion on adding user initialization routines.

Main Task Attributes and the Application Stub

Several characteristics of the application main can only be controlled from the VxWorks environment: main stack size, main priority, and main task attributes. Often the default values are fine. In other cases one must explicitly set the desired values outside of the application source code.

The system call taskSpawn() can be used to provide explicit values for the task name string, its VxWorks priority, stack size and other options (such as the floating point type).

For applications requiring modification of these attributes that are invoked from the host or target shell, the system call can be used directly to spawn the application main task.

For applications started using a Workbench launcher, these attributes can be set in the launcher.

For applications being linked into a kernel image, one should use an application stub for this purpose. Although it can have any name, the default is usrAppInit() and is placed in a file in the project called usrAppInit.c. By defining the build macro USER_APPL_INIT one can use a routine with a different stub or go directly to the Ada main subroutine if no attribute modifications are needed.

Here is a sample usrAppInit.c suitable for starting an Ada application linked into a VxWorks kernel image:

#include <vxWorks.h>
#include <taskLib.h>

/* Ada binder-generated main - ADA_MAIN is passed as a macro from the makefile */

extern void ADA_MAIN(void);

void usrAppInit()

{

int stackSize = 0x80000;

/* For e500v2 processors, use VX_SPE_TASK instead */
int spawnFlags = VX_FP_TASK;

/* MAIN_TASK_NAME is passed as a macro from the makefile */
char * mainTaskName = (char *) MAIN_TASK_NAME;

/* VxWorks priority = 255 - Ada priority */
int priority = 100;

/* Spawn Ada environment task */

taskSpawn(mainTaskName, priority, spawnFlags, stackSize,
(FUNCPTR) ADA_MAIN, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}

ADA_MAIN is the name of the Ada main subprogram in lower case (i.e. the symbol for the entry point of the Ada application). MAIN_TASK_NAME is the name you would like associated with the Ada environment task for the application. These are typically passed using a -D<symbol>=<value> switch to the compiler in the makefile for the application component.

The stack size can be varied to suit the needs of the environment task. It must be spawned with at least option VX_FP_TASK or one of the other floating point type options, depending on your board.

Stack Overflow Checking on VxWorks

GNAT does not perform stack overflow checking by default. This means that if the main environment task or some other task exceeds the available stack space, then unpredictable behavior will occur.

To activate stack checking, compile all units with the gcc option -fstack-check. For example:

$ <target>-gcc -c -fstack-check package1.adb

Units compiled with this option will generate extra instructions to check that any use of the stack (for procedure calls or for declaring local variables in declare blocks) does not exceed the available stack space. If the space is exceeded, then a Storage_Error exception is raised.

For declared tasks, the stack size is always controlled by the size given in an applicable Storage_Size pragma (or is set to the default size if no pragma is used).

For the environment task, the stack size is defined by the stackSize argument when spawning the Ada program via the taskSpawn() routine. If no stack size is specified, the stack size is set to the VxWorks default value.

Use of stack overflow checking requires that a buffer be added to the application stack requirement for each task, including the environment task, in order to handle stack overflows gracefully. The size of this buffer varies between 8KB and 16KB depending on architecture, e.g. a 64-bit architecture requires a larger buffer.

For VxWorks 5, the GNAT run-time library retrieves stack boundary information from the VxWorks kernel using the taskInfoGet() routine provided by the taskShow library. Therefore, in order to have stack overflow checking in these environments, the taskShow library must be linked into the VxWorks kernel. This can be done using one of the following methods:

  • Defining INCLUDE_SHOW_ROUTINES in config.h when using configuration header files
  • Selecting INCLUDE_TASK_SHOW when using the Tornado project facility

For VxWorks 6 and the VxWorks 653 vThreads partition operating system (POS), -fstack-check is implemented using stack probing (more efficient).

For VxWorks 6 kernel applications, the kernel must be built with INCLUDE_PROTECT_TASK_STACK enabled. For VxWorks 653 vThreads, one or more guard pages must be specified in the POS configuration file.

Interrupt Handling for VxWorks

GNAT offers a range of options for hardware interrupt handling on VxWorks 5 and for VxWorks 6 downloadable kernel modules (DKM).

Interrupt handling is not supported for VxWorks 653 and for VxWorks 6 and VxWorks 7 real-time processes. These are operating system restrictions.

In rough order of latency and lack of restrictions:

  • Directly vectored interrupt procedure handlers
  • Directly vectored interrupt procedures that signal a task using a suspension object
  • Ada 95 / Ada 2005 / Ada 2012 protected procedure handlers for interrupts
  • Ada 83 style interrupt entry handlers for interrupts

In general, the range of possible solutions trades off latency versus restrictions in the handler code. Restrictions in direct vectored interrupt handlers are documented in the VxWorks Programmer’s Guide. Protected procedure handlers have only the restriction that no potentially blocking operations are performed within the handler. Interrupt entries have no restrictions. We recommend the use of the protected procedure mechanism as providing the best balance of these considerations for most applications.

All handler types must explicitly perform any required hardware cleanups, such as issuing an end-of-interrupt if necessary.

Note that in the examples below certain routines are specific to VME bus interrupt handling. These are only examples, and the appropriate routines for your processor and board should be used instead.

  • Direct Vectored Interrupt Routines

    This approach provides the lowest interrupt latency, but has the most restrictions on what VxWorks and Ada run-time calls can be made, as well as on what Ada entities are accessible to the handler code. Such handlers are most useful when there are stringent latency requirements, and very little processing is to be performed in the handler. Access to the necessary VxWorks routines for setting up such handlers is provided in the package Interfaces.VxWorks.

    VxWorks restrictions are described in the VxWorks Programmer’s Manual. Note in particular that floating point context is not automatically saved and restored when interrupts are vectored to the handler. If the handler is to execute floating point instructions, the statements involved must be bracketed by a pair of calls to fppSave and fppRestore defined in Interfaces.VxWorks. As the compiler may use floating point registers for non floating point operation (such as memory block copy) it is highly recommended to either save and restore floating point registers or to inspect code generated for the interrupt handler.

    As stack checking doesn’t work within an interrupt handler, be sure that units containing interrupt handlers are not compiled with -fstack-check.

    As there is no task identity when executing an interrupt handler, any Ada run time library code that depends on knowing the current task’s identity must not be used. This includes tasking constructs (except for most subprograms of Ada.Synchronous_Task_Control), concatenation and other functions with unconstrained results and exceptions propagation.

    In general, it is a good idea to save and restore the handler that was installed prior to application startup. On VxWorks 5.x for m68k, the routines intVecGet and intVecSet are used for this purpose. For other platforms, please refer to VxWorks manuals. The Ada handler code is installed into the vector table using routine intConnect, which generates wrapper code to save and restore registers.

    Example:

    with Interfaces.VxWorks; use Interfaces.VxWorks;
    with System;
    
    package P is
    
       Count : Natural := 0;
       pragma Atomic (Count);
    
       --  Interrupt level used by this example
       Level : constant := 1;
    
       --  Be sure to use a reasonable interrupt number for the target
       --  board!  Refer to the BSP for details.
       Interrupt : constant := 16#14#;
    
       procedure Handler (Parameter : System.Address);
    
    end P;
    
    package body P is
    
       procedure Handler (parameter : System.Address) is
          S : Status;
       begin
          Count := Count + 1;
          --  Acknowledge interrupt.  Not necessary for all interrupts.
          S := sysBusIntAck (intLevel => Level);
       end Handler;
    end P;
    
    with Interfaces.VxWorks; use Interfaces.VxWorks;
    with Ada.Text_IO; use Ada.Text_IO;
    
    with P; use P;
    procedure Useint is
       task T;
    
       S : Status;
    
       task body T is
       begin
          for I in 1 .. 10 loop
             Put_Line ("Generating an interrupt...");
             delay 1.0;
    
             --  Generate interrupt, using interrupt number
             S := sysBusIntGen (Level, Interrupt);
          end loop;
       end T;
    
       --  Save old handler
       Old_Handler : VOIDFUNCPTR := intVecGet (INUM_TO_IVEC (Interrupt));
    begin
       S := intConnect (INUM_TO_IVEC (Interrupt), Handler'Access);
       S := sysIntEnable (intLevel => Level);
    
       for I in 1 .. 10 loop
          delay 2.0;
          Put_Line ("value of count:" & P.Count'Img);
       end loop;
    
       --  Restore previous handler
       S := sysIntDisable (intLevel => Level);
       intVecSet (INUM_TO_IVEC (Interrupt), Old_Handler);
    end Useint;
    
  • Direct Vectored Interrupt Routines

    A variation on the direct vectored routine that allows for less restrictive handler code is to separate the interrupt processing into two levels.

    The first level is the same as in the previous section. Here we perform simple hardware actions and signal a task pending on a Suspension_Object (defined in Ada.Synchronous_Task_Control) to perform the more complex and time-consuming operations. The routine Set_True signals a task whose body loops and pends on the suspension object using Suspend_Until_True. The suspension object is declared in a scope global to both the handler and the task. This approach can be thought of as a slightly higher-level application of the C example using a binary semaphore given in the VxWorks Programmer’s Manual. In fact, the implementation of Ada.Synchronous_Task_Control is a very thin wrapper around a VxWorks binary semaphore.

    This approach has a latency between the direct vectored approach and the protected procedure approach. There are no restrictions in the Ada task code, while the handler code has the same restrictions as any other direct interrupt handler.

    Example:

    with System;
    package Sem_Handler is
    
       Count : Natural := 0;
       pragma Atomic (Count);
    
       --  Interrupt level used by this example
       Level : constant := 1;
       Interrupt : constant := 16#14#;
    
       --  Interrupt handler providing "immediate" handling
       procedure Handler (Param : System.Address);
    
       --  Task whose body provides "deferred" handling
       task Receiver is
           pragma Interrupt_Priority
              (System.Interrupt_Priority'First + Level + 1);
        end Receiver;
    
    end Sem_Handler;
    
    with Ada.Synchronous_Task_Control; use Ada.Synchronous_Task_Control;
    with Interfaces.VxWorks; use Interfaces.VxWorks;
    package body Sem_Handler is
    
       SO : Suspension_Object;
    
       task body Receiver is
       begin
          loop
             --  Wait for notification from immediate handler
             Suspend_Until_True (SO);
    
             --  Interrupt processing
             Count := Count + 1;
          end loop;
       end Receiver;
    
       procedure Handler (Param : System.Address) is
          S : STATUS;
       begin
          --  Hardware cleanup, if necessary
          S := sysBusIntAck (Level);
    
          --  Signal the task
          Set_True (SO);
       end Handler;
    
    end Sem_Handler;
    
    with Interfaces.VxWorks; use Interfaces.VxWorks;
    with Ada.Text_IO; use Ada.Text_IO;
    with Sem_Handler; use Sem_Handler;
    procedure Useint is
    
       S : STATUS;
    
       task T;
    
       task body T is
       begin
          for I in 1 .. 10 loop
             Put_Line ("Generating an interrupt...");
             delay 1.0;
    
             --  Generate interrupt, using interrupt number
             S := sysBusIntGen (Level, Interrupt);
          end loop;
       end T;
    
       --  Save old handler
       Old_Handler : VOIDFUNCPTR := intVecGet (INUM_TO_IVEC (Interrupt));
    begin
       S := intConnect (INUM_TO_IVEC (Interrupt), Handler'Access);
       S := sysIntEnable (intLevel => Level);
    
       for I in 1 .. 10 loop
          delay 2.0;
          Put_Line ("value of Count:" & Sem_Handler.Count'Img);
       end loop;
    
       --  Restore handler
       S := sysIntDisable (intLevel => Level);
       intVecSet (INUM_TO_IVEC (Interrupt), Old_Handler);
       abort Receiver;
    end Useint;
    
  • Protected Procedure Handlers for Interrupts

    This is the recommended default mechanism for interrupt handling. It essentially wraps the hybrid handler / task mechanism in a higher-level abstraction, and provides a good balance between latency and capability.

    Vectored interrupts are designated by their interrupt number, starting from 0 and ranging to the number of entries in the interrupt vector table - 1.

    In the GNAT VxWorks implementation, the following priority mappings are used:

    • Normal task priorities are in the range 0 .. 245.
    • Interrupt priority 246 is used by the GNAT Interrupt_Manager task.
    • Interrupt priority 247 is used for vectored interrupts that do not correspond to those generated via an interrupt controller.
    • Interrupt priorities 248 .. 255 correspond to PIC interrupt levels 0 .. 7.
    • Priority 256 is reserved to the VxWorks kernel.

    Except for reserved priorities, the above are recommendations for setting the ceiling priority of a protected object that handles interrupts, or the priority of a task with interrupt entries. It’s a very good idea to follow these recommendations for vectored interrupts that come in through the PIC as it will determine the priority of execution of the code in the protected procedure or interrupt entry.

    No vectored interrupt numbers are reserved in this implementation, because dedicated interrupts are determined by the board support package. Obviously, careful consideration of the hardware is necessary when handling interrupts. The VxWorks BSP for the board is the definitive reference for interrupt assignments.

    Example:

    package PO_Handler is
    
       --  Interrupt level used by this example
       Level : constant := 1;
    
       Interrupt : constant := 16#14#;
    
       protected Protected_Handler is
          procedure Handler;
          pragma Attach_Handler (Handler, Interrupt);
    
          function Count return Natural;
    
          pragma Interrupt_Priority (248);
       private
          The_Count : Natural := 0;
       end Protected_Handler;
    
    end PO_Handler;
    
    with Interfaces.VxWorks; use Interfaces.VxWorks;
    package body PO_Handler is
    
       protected body Protected_Handler is
    
          procedure Handler is
             S : Status;
          begin
             --  Hardware cleanup if necessary
             S := sysBusIntAck (Level);
    
             --  Interrupt processing
             The_Count := The_Count + 1;
          end Handler;
    
          function Count return Natural is
          begin
             return The_Count;
          end Count;
       end Protected_Handler;
    
    end PO_Handler;
    
    with Interfaces.VxWorks; use Interfaces.VxWorks;
    with Ada.Text_IO; use Ada.Text_IO;
    
    with PO_Handler; use PO_Handler;
    procedure Useint is
    
       task T;
    
       S : STATUS;
    
       task body T is
       begin
          for I in 1 .. 10 loop
             Put_Line ("Generating an interrupt...");
             delay 1.0;
    
             --  Generate interrupt, using interrupt number
             S := sysBusIntGen (Level, Interrupt);
          end loop;
       end T;
    
    begin
       S := sysIntEnable (intLevel => Level);
    
       for I in 1 .. 10 loop
          delay 2.0;
          Put_Line ("value of count:" & Protected_Handler.Count'Img);
       end loop;
    
       S := sysIntDisable (intLevel => Level);
    end Useint;
    

    This is obviously significantly higher-level and easier to write than the previous examples.

  • Use of Alternate Interrupt Connection Routines

    The default connection routine for handlers in VxWorks is intConnect(), defined in intArchLib.

    Some boards require special routines (e.g. for PCI). GNAT provides package Interfaces.VxWorks.Int_Connection to allow a user to dynamically substitute alternate connection routines prior to calling Ada.Interrupts.Attach_Handler. This facility is only available for unrestricted Ada runtime libraries, when using the protected procedure mechanism for interrupt handling. Use of pragma Attach_Handler is incompatible with the use of an alternate connection routine.

    Connection routines should have the same profile as intConnect():

    type Interrupt_Connector is access function
      (Vector    : Interrupt_Vector;
       Handler   : VOIDFUNCPTR;
       Parameter : System.Address := System.Null_Address) return STATUS;
    pragma Convention (C, Interrupt_Connector);
    

    They are registered for subsequent calls to Ada.Interrupts.Attach_Handler by passing an access to them into Interfaces.VxWorks.Int_Connection.Connect.

    The connection routine can be changed multiple times in an application.

  • Ada 83 Style Interrupt Entries

    GNAT provides a full implementation of the Ada 83 interrupt entry mechanism for vectored interrupts. However, due to latency issues, we only recommend using these for backward compatibility. The comments in the previous section regarding interrupt priorities and reserved interrupts apply here.

    In order to associate an interrupt with an entry, GNAT provides the standard Ada convenience routine Ada.Interrupts.Reference. It is used as follows:

    Interrupt_Address : constant System.Address :=
       Ada.Interrupts.Reference (Int_Num);
    
    task Handler_Task is
       pragma Interrupt_Priority (248);  -- For instance
       entry Handler;
       for Handler'Address use Interrupt_Address;
    end Handler_Task;
    

    Since there is no restriction within an interrupt entry on blocking operations, be sure to perform any hardware interrupt controller related operations before executing a call that could block within the entry’s accept statements. It is assumed that interrupt entries are always open alternatives when they appear within a selective wait statement. The presence of a guard gives undefined behavior.

    Example:

    with Ada.Interrupts;
    with System;
    package Task_Handler is
    
       --  Interrupt level used by this example
       Level : constant := 1;
    
       Interrupt : constant := 16#14#;
    
       Interrupt_Address : constant System.Address :=
          Ada.Interrupts.Reference (Interrupt);
    
       task Handler_Task is
          pragma Interrupt_Priority (248);  -- For instance
          entry Handler;
          for Handler'Address use Interrupt_Address;
    
          entry Count (Value : out Natural);
       end Handler_Task;
    end Task_Handler;
    
    with Interfaces.VxWorks; use Interfaces.VxWorks;
    package body Task_Handler is
    
       task body Handler_Task is
          The_Count : Natural := 0;
          S : STATUS;
       begin
          loop
             select
                accept Handler do
                   --  Hardware cleanup if necessary
                   S := sysBusIntAck (Level);
    
                   --  Interrupt processing
                   The_Count := The_Count + 1;
                end Handler;
             or
                accept Count (Value : out Natural) do
                   Value := The_Count;
                end Count;
             end select;
          end loop;
       end Handler_Task;
    
    end Task_Handler;
    
    with Interfaces.VxWorks; use Interfaces.VxWorks;
    with Ada.Text_IO; use Ada.Text_IO;
    
    with Task_Handler; use Task_Handler;
    procedure Useint is
    
       task T;
    
       S : STATUS;
       Current_Count : Natural := 0;
    
       task body T is
       begin
          for I in 1 .. 10 loop
             Put_Line ("Generating an interrupt...");
             delay 1.0;
    
             --  Generate interrupt, using interrupt number
             S := sysBusIntGen (Level, Interrupt);
          end loop;
       end T;
    
    begin
       S := sysIntEnable (intLevel => Level);
    
       for I in 1 .. 10 loop
          delay 2.0;
          Handler_Task.Count (Current_Count);
          Put_Line ("value of count:" & Current_Count'Img);
       end loop;
    
       S := sysIntDisable (intLevel => Level);
       abort Handler_Task;
    end Useint;
    

Handling Relocation Issues for PowerPc Targets

Under certain circumstances, loading a program onto a PowerPC board will fail with the message Relocation value does not fit in 24 bits. This section summarizes why and when such a problem will arise, and explains how it can be solved.

Background and Summary

Prior to release 3.15 of GNAT, the compiler’s default behavior was to use relative addressing mode for all subprogram calls, including those in the GNAT run-time library. This led to the ‘24 bits’ problem for many user applications. Starting with 3.15, the run-time library accompanying the compiler has been compiled with the -mlongcall command line argument, which directs the compiler to generate absolute addresses for subprogram calls. Furthermore, users who were encountering the problem started compiling their code with an explicit -mlongcall option, and the problem largely disappeared.

Starting with release 6.0.2 of GNAT, the effects of -mlongcall are turned on by default for all compilations, including the compilation of binder-generated files. Thus users no longer have to supply the -mlongcall option themselves.

In light of this change in the default behavior, a user requiring the relative addressing mode (for example for performance tuning) will now need to explicitly specify the -mno-longcall option.

Technical Details

VxWorks on the PowerPC follows the variation of the SVR4 ABI known as the Embedded ABI (EABI). In order to save space and time in embedded applications, the EABI specifies that the default for subprogram calls should be the branch instruction with relative addressing using an immediate operand. The immediate operand to this instruction (relative address) is 24 bits wide. It is sign extended and 2#00# is appended for the last 2 bits (all instructions must be on a 4 byte boundary).

The resulting 26 bit offset means that the target of the branch must be within +/- 32 Mbytes of the PC-relative branch instruction. When loading a program, VxWorks completes the linking phase by resolving all of the unresolved references in the object being loaded. When one of those references is a relative address in a branch instruction, and the linker determines that the target is more than 32 Mbytes away from the branch, the error occurs.

This only happens when the BSP is configured to use more than 32 MBytes of memory. The VxWorks kernel is loaded into low memory addresses, and the error usually occurs when the target loader is used (because it loads objects into high memory, and thus calls from the program to the VxWorks kernel can be too far).

One way to solve this problem is to use the Tornado / Workbench host loader; this will place programs in low memory, close to the kernel.

For versions of GNAT which precede 6.0.2, the -mlongcall option for gcc causes the compiler to construct the absolute address of a subprogram in a register and use a branch instruction with absolute addressing mode. Starting with 6.0.2, absolute addresses are the default, and -mno-longcall is needed to use relative addresses for subprogram calls.

For GNAT versions 3.15 through 6.0.1, the GNAT run-time libraries are compiled with -mlongcall. In many cases the use of these libraries is sufficient to avoid the relocation problem, since it is the run-time library that contains calls to the VxWorks kernel that need to span the address space gap. The run-time libraries for version 6.0.2 and later also contain only the absolute addressing mode.

When using the GNAT 3.15 through 6.0.1, you may need to compile your application code using -mlongcall if there are calls directly to the kernel, if the application is very large, or in some specialized linking/loading scenarios.

You can compile individual files with -mlongcall by placing this option on the gcc command line (for brevity we are omitting the powerpc-wrs-vxworks- prefix on the commands shown in this paragraph). If you provide -mlongcall as an option for gnatmake, it will be passed to all invocations of gcc that gnatmake directly performs. Note that one other compilation is made by gnatlink, on the file created by gnatbind for the elaboration package body (see “Binding Using gnatbind” in the GNAT User’s Guide for Native Platforms. Passing -mlongcall to gnatlink, either directly on the gnatlink command line or by including -mlongcall in the -largs list of gnatmake, will direct gnatlink to compile the binder file with the -mlongcall option.

To see the effect of -mlongcall, consider the following small example:

procedure Proc is
   procedure Imported_Proc;
   pragma Import (Ada, Imported_Proc);
begin
   Imported_Proc;
end;

If you compile Proc with the default options (no -mlongcall) with a GNAT release prior to 6.0.2, the following code is generated:

_ada_proc:
        ...
        bl imported_proc
        ...

In contrast, here is the result with the defaults for GNAT 6.0.2 and later, or with the -mlongcall option passed to earlier releases

_ada_proc:
        ...
        addis 9,0,imported_proc@ha
        addi 0,9,imported_proc@l
        mtlr 0
        blrl
        ...

For additional information on this issue, please refer to WRS’ SPRs 6040, 20257, and 22767.

Zero Cost Exceptions on PowerPC Targets

The Zero Cost eXceptions handling (ZCX) model is available for kernel mode on PowerPC processors.

On VxWorks 6 and VxWorks 7, ZCX is activated by default. In this mode, proper transformation of signal deliveries into Ada exceptions requires increasing the kernel exception stack to at least 10*1024 bytes, which may be achieved by modifying the taskKerExcStackSize global variable before tasks are spawned. This variable is defined in $WIND_BASE/target/h/private/taskLibP.h. This affects all the tasks spawned after the change, and is typically needed for user stack-checking purposes. On PowerPC 64-bit this global variable needs to be 32*1024 bytes for proper stack overflow handling. The setjmp/longjmp (SJLJ) exception model is still available, by switching to an alternative runtime library with –RTS=kernel-sjlj.

On VxWorks 5, the SJLJ model is activated by default. ZCX is available by switching to an alternative runtime library with –RTS=zcx.

Calling exported Ada procedures from the VxWorks shell

If you need to call Ada subprograms from outside your Ada application (for example, from the Windshell), you will in general need to make the executing task known to the Ada run-time library. A typical situation is the need to enable or disable traces in your Ada application from the shell by calling an Ada procedure that sets the value of a Boolean controlling the traces.

The problem is the following: when you call the Ada procedure, it may raise an exception on the target. This could be through normal execution, or it could be caused solely by the fact that the thread executing the procedure has not been made known to the Ada run-time library. There are a number of situations in full Ada applications where the run-time library must be able to determine what task is running, and must be able to access some Ada-specific data structures in order to do its job. If a VxWorks task has not been registered, the ‘id’ that is accessed by the run-time library will be invalid, and will reference invalid data (or invalid memory).

The solution is to use the GNAT.Threads.Register_Thread and GNAT.Threads.Unregister_Thread routines, so that the task in which the called subprogram is executing (typically the shell task) is known by the Ada run-time. This way, when the Ada task id is requested at some point in the procedure’s execution, a valid value will be fetched, and the necessary data structures will have been allocated and properly initialized.

The following small example shows how to use this feature:

package Traces is

   procedure Put_Trace (Info : String);
   --  Print Info on the standard output if the traces are on

   procedure Trace_On;
   pragma Export (C, Trace_On, "trace_on");
   --  Activate the traces

   procedure Trace_Off;
   pragma Export (C, Trace_Off, "trace_off");
   --  Deactivate the traces
end Traces;

with System;       use System;
with Ada.Text_IO;  use Ada.Text_IO;
with GNAT.Threads; use GNAT.Threads;
package body Traces is
   Trace : Boolean := False;

   ---------------
   -- Put_Trace --
   ---------------

   procedure Put_Trace (Info : String) is
   begin
      if Trace then
         Put_Line (Info);
      end if;
   end Put_Trace;

   --------------
   -- Trace_On --
   --------------

   procedure Trace_On is
      Id : System.Address;
   begin
      Id := Register_Thread;
      Trace := True;
      Unregister_Thread;
   end Trace_On;

   ---------------
   -- Trace_Off --
   ---------------

   procedure Trace_Off is
      Id : System.Address;
   begin
      Id := Register_Thread;
      Trace := False;
      Unregister_Thread;
   end Trace_Off;

end Traces;

with Traces; use Traces;
procedure Increment is
   Value : Integer := 1;
begin
   for J in 1 .. 60 loop
      Value := Value + 1;
      Put_Trace (Integer'Image (Value));
      delay 1.0;
   end loop;
end Increment;

After having compiled and loaded the application on your target, spawn it and control the traces by calling trace_on and trace_off from the shell. You should see numbers displayed in the VxWorks console when the traces are on.

-> sp increment
task spawned: id = 3b0dcd0, name = s1u0
value = 61922512 = 0x3b0dcd0
-> trace_on
value = 0 = 0x0
-> trace_off
value = 0 = 0x0

Simulating Command Line Arguments for VxWorks

The GNAT implementation of Ada.Command_Line relies on the standard C symbols argv and argc. The model for invoking kernel ‘programs’ under VxWorks does not provide these symbols. For kernel programs the typical method for invoking a program under VxWorks is to call the sp function in order to spawn a thread in which to execute a designated function (in GNAT, this is the implicit main generated by gnatbind). sp provides the capability of pushing a variable number of arguments onto the stack when the function is invoked. But this does not work for the implicit Ada main, because it has no way of knowing how many arguments might be required. This eliminates the possibility of using Ada.Command_Line for kernel. However there is no such restriction for either command line arguments nor for environment varables for RTP mode ‘programs’. For more information see the VxWorks documentation for rtpLib/rtpSpawn().

One way to solve this problem is to define symbols in the VxWorks environment, then import them into the Ada application. For example, we could define the following package that imports two symbols, one an int and the other a string:

with Interfaces.C.Strings;
use Interfaces.C.Strings;
package Args is
   --  Define and import a variable for each argument
   Int_Arg : Interfaces.C.Int;
   String_Arg : Chars_Ptr;
private
   pragma Import (C, Int_Arg, "intarg");
   pragma Import (C, String_Arg, "stringarg");
end Args;

An Ada unit could then use the two imported variables Int_Arg and String_Arg as follows:

with Args; use Args;
with Interfaces.C.Strings;
use Interfaces.C, Interfaces.C.Strings;
with Ada.Text_IO; use Ada.Text_IO;
procedure Argtest is
begin
   Put_Line (Int'Image (Int_Arg));
   Put_Line (Value (String_Arg));
end Argtest;

When invoking the application from the shell, one will then set the values to be imported, and spawn the application, as follows:

-> intarg=10
-> stringarg="Hello"
-> sp (argtest)

Using addr2line on VxWorks

For general information about addr2line, see “Getting Internal Debugging Information” in the GNAT User’s Guide for Native Platforms.

Differences between VxWorks and native platforms

Using addr2line on the VxWorks, where modules are often dynamically loaded, is a bit different than on native platforms. When dealing with dynamically loaded modules, one needs to determine the offset at which the module has been loaded. This allows addr2line to correlate the target addresses of the stack trace with the addresses in the load module.

On VxWorks, there are two ways a module can be located into target memory:

  • it is dynamically linked and loaded
  • it is statically linked with the kernel

In both cases, the addresses used in the module are different from the ones used within the target memory address space under VxWorks.

In contrast, on a native system, the executable has the same addresses in the object file and in memory, as virtual addresses are used. There is no need to collate the addresses on target with those in the object module.

As addr2line uses the addresses in the module, we need to adjust the addresses returned as a traceback at run-time so that they can be correctly interpreted by addr2line. To do this manually, we would follow a procedure like this:

A symbol that is always present in the module is determined in order to compute the offset between the addresses at run-time and the addresses in the module. Its address in the module is subtracted from its address in memory, and the computed delta is added to each address in the traceback.

Using <target>-vxaddr2line

Manually performing the computation just described is tedious, so a tool has been introduced to automate the process: <target>-vxaddr2line, where <target> is the name of target (e.g. powerpc-wrs-vxworks for VxWorks 5.x and 6.x; powerpc-wrs-vxworksae for VxWorks 653). All that needs to be done is to give the name of the module, the address of the symbol adainit in memory and the traceback values as parameters. The computation is then done automatically, and the result is transmitted to addr2line, which returns the symbolic traceback in the usual manner.

An example

The example session of this section is for powerpc-wrs-vxworks. Replace this target name by the target you are using. Consider the following simple source code :

 1
 2 procedure CE is
 3
 4    procedure Raise_Constraint_Error is
 5    begin
 6      raise Constraint_Error;
 7    end;
 8
 9    procedure Sub_2 (Should_Raise_CE : Boolean) is
10    begin
11      if Should_Raise_CE then
12        Raise_Constraint_Error;
13      end if;
14    end;
15
16    procedure Sub_1 is
17    begin
18      Sub_2 (Should_Raise_CE => True);
19    end;
20 begin
21    Sub_1;
22 end;
  • Build the example with gnatmake, providing the -E binder argument in this case so that a raw backtrace is returned for the unhandled exception. -g is required in any case because addr2line uses debug information to perform the symbolic transcription.

    $ powerpc-wrs-vxworks-gnatmake -g ce -bargs -E
    
  • Load and run the resulting module on the target board. It raises the expected unhandled exception and generates the associated raw backtrace:

    -> ld < ce.out
    Loading /ce.out |
    value = 591824 = 0x907d0
    -> sp ce
    task spawned: id = 1b8aae0, name = s2u0
    value = 28879584 = 0x1b8aae0
    ->
    Execution terminated by unhandled exception
    Exception name: CONSTRAINT_ERROR
    Message: ce.adb:6
    Call stack traceback locations:
    0x3b8394 0x3b83e0 0x3b8420 0x3b8458 0x3b82f0 0x19a184
    

    Now convert the backtrace into symbolic references...

  • Determine the address of a reference symbol of the module (we use the address of adainit), which we obtain by calling the VxWorks lkup function:

    -> lkup "adainit"
    adainit                   0x003b81d0 text     (ce.out)
    value = 0 = 0x0
    
  • We now have the information needed to run vxaddr2line:

    powerpc-wrs-vxworks-vxaddr2line ce.out 0x003b81d0 0x3b8394 ... 0x19a184
                                    ------ ---------- ---------------------
                                 exec file  Ref.addr.  Run-Time backtrace
    

    This gives the following:

    000001C0 at .../ce.adb:6
    0000020C at .../ce.adb:12
    0000024C at .../ce.adb:18
    00000284 at .../ce.adb:21
    0000011C at .../b~ce.adb:88
    

    [meaningless output for the last address]

    The last line is not shown here because it designates stub code within VxWorks and is not part of the user’s application.

    Note that on VxWorks 653, the program name argument to vxaddr2line is the .sm file for the partition in which the application is executing.

Removal of Unused Code and Data

As for all elf platforms using 2.16.1 GNU binutils, GNAT for VxWorks now supports unused code and data elimination. For a complete description of this functionality, please refer to the GNAT User’s Guide.

However, the use of this functionality need some extra care for VxWorks. In fact, GNAT for VxWorks performs by default partial linking on all VxWorks versions, except VxWorks 6 in RTP mode.

Because of this partial linking, the unused code and data elimination requires the use of -e / --entry ld option to correctly work. The usage of these options is also described in the GNAT User’s Guide.

For example, in order to compile my_program.adb, you can use the following command line:

powerpc-wrs-vxworks-gnatmake my_program.adb -cargs -ffunction-sections \
  -fdata-sections -largs -Wl,--gc-sections -Wl,--entry=my_program

To debug a program built with these options on gdb, the option -gdwarf-3 should be used.

Debugging

In general, two debuggers are available for GNAT applications targeting VxWorks: the Workbench debugger and gdb (via GPS).

The Workbench debugger can be used for all targets supported by Workbench, except possibly VxWorks 5, which has not been tested.

gdb can be only used on these versions of VxWorks:

  • VxWorks 5
  • VxWorks 6 uniprocessor DKMs
  • VxWorks 653 1.x and 2.x

In order to display the value of an Ada object in Workbench, right-click on one of its occurences in the source editor and select Add Watch Expression.... The encoded name of the entity will be displayed in a dialog window: for example, a variable named Alpha declared in package Ownship will be shown as ownship__alpha. After clicking on OK, you will see that the corresponding expression has been added to the Expression view. From there, you will be able to browse the content of your object.

Workbench also displays the value of local variables in Ada subprograms; these are shown by the view Variables.

For more information about the Workbench debugger, see Wind River WorkBench User’s Guide.

Frequently Asked Questions for VxWorks

  • When I run my program twice on the board, it does not work. Why not?

    Ada programs generally require elaboration and finalization, so the compiler creates a wrapper procedure whose name is the same as the Ada name of the main subprogram, which invokes the elaboration and finalization routines before and after your program executes. But the static part of the elaboration is handled while loading the program itself, so if you launch it twice this part of the elaboration will not be performed the second time. This affects the proper elaboration of the GNAT run-time, and thus it is mandatory to reload your program before relaunching it.

  • Can I load a collection of subprograms rather than a standalone program?

    It is possible to write Ada programs with multiple entry points that can be called from the VxWorks shell (or from a C or C++ application). To do this, generate an externally-callable Ada subsystem (see “Binding with Non-Ada Main Programs” in the GNAT User’s Guide for Native Platforms. If you use this method, you need to call adainit before calling any Ada entry point.

  • When I use the break exception command, I get the message "exception" is not a function, why?

    If you are using gdb from GNAT: you are not in the proper language mode. Issue the command:

    (gdb) set language ada
    

    If you are using the gdb interface of the Workbench: this debugger does not provide such a feature. You may emulate it by set breakpoints on the following functions: __gnat_debug_raise_exception for catching any exception, __gnat_unhandled_exception for catching only unhandled exceptions and __gnat_debug_raise_assert_failure to catch assert failures.

  • When I load a large application from the VxWorks shell using the ld command, the load hangs and never finishes. How can I load large executables?

    This is a classic VxWorks problem when using the default rsh communication method. Using NFS instead should work. Use the nfsShowMount command to verify that your program is in a NFS mounted directory.

  • When I load a large application from the debugger using the wtx target connection, the load never finishes, why?

    Make sure that the memory cache size parameter of the target server is large enough. (target -m big_enough_size, or Memory cache size box in GUI.) See Tornado 1.01 API Programming Guide, Section 3.6.2.

  • When I spawn my program under the VxWorks kernel shell, interactive input does not work, why?

    When a task is spawned from a kernel shell with the sp() or taskSpawn() command, it shares the same standard I/O file descriptors as the shell. Since the shell has a higher priority than the task, the shell receives the interactive input.

    The recommended method to pass input to such a task is via file redirection:

    ->    # here you can have interactive input
    -> main
    ->    # here you cannot
    -> sp main
    ->    # neither here
    -> taskSpawn("ess",100,0,8000000,main)
    ->    # but you can input from a file:
    -> taskSpawn("Bae",100,0,8000000,main) < input_file
    

    This approach is preferred because it removes the contention between the task and the shell over the interactive input.

    VxWorks 6.9 and later include the ability for the standard input of the shell to be redirected to allow a task to access the interactive input unimpeded, an approach that may be suitable for some applications. See Redirecting Shell IO in the VxWorks Kernel Shell Users Guide for more information.

  • The errno of the task(s) of my Ada application is not null even though my application is running correctly. Is that normal?

    Yes. As explained in the VxWorks OS Libraries API Reference in the errnoLib section: “most VxWorks functions return ERROR when they detect an error, or NULL in the case of functions returning pointers. In general, they set an error status that describes the nature of the error”.

    There are a large number of calls to VxWorks functions in the Ada run-time. Whenever a system call returns a value indicating an error, the error status is set to a non-zero value. So despite the checking of the return value is to determine an appropriate action, errno can still be non-null.

    Resetting the error status in the Ada run-time each time a VxWorks function is called would add unnecessary system call overhead and would not avoid masking error status information that may appear in user code. So this approach would not help.

    It is a good practice not to rely on the error status value to detect errors. It should be only used to get more information on errors that have already been detected by checking the code returned by the VxWorks function. To be sure to get the error status corresponding to the error detected in the code, the use of the debugger is recommended.

  • I cannot display Ada objects in the debugger, why?

    On certain versions of Workbench and GNATbench, Ada debugging is not enabled by default. To enable it, you must set the environment variable DFW_ENABLE_ADA_SYMBOL_SEARCH to 1. You need only set this environment variable once, as long as it does not become undefined later.

Workbench / VxWorks 6.x Topics

This section describes topics that are specific to VxWorks 6. It introduces the basic features provided by the GNAT toolchain for this target, and shows how to use them. It also describes which functionalities are implemented, and summarizes known issues.

Using GNAT for VxWorks 6

GNAT for VxWorks 6 comes with a choice of two development environments:

  • GNAT Programming Studio (GPS), which is the standard GNAT IDE,
  • GNATbench, the GNAT plug-in for Workbench (Wind River’s IDE based on Eclipse).

Both environments share a common core technology, the GPS engine, so the choice of environment is mostly a matter of taste. GPS is more GNAT-centric and Workbench with the GNATbench plug-in is more VxWorks-centric.

The debugger technology depends on the environment. The underlying debugger is gdb in GPS, and DFW in Workbench. Both debuggers are well integrated into their respective development environments. The debugging interface in Workbench has a more VxWorks-centric view, and has practical displays to interact with the target. GPS has, via gdb, additional knowledge about Ada-specific data structures and provides additional facilities to display Ada variables.

The choice between the GPS and the Workbench development environment will be determined by your development habits and your personal preferences. Note that, when using Workbench, you can easily switch to GPS to use some GPS-specific features such as the entity viewer or the dependency graph, and then switch back to Workbench. GPS is opened with the same files as Workbench, and file synchronization is assured between GPS and Workbench at each switch time.

Building a VxWorks 6 Application

GNAT for VxWorks 6 supports two build interfaces:

  • Workbench;
  • GPS;

for three types of modules:

  • downloadable kernel modules (DKM)
  • real time process (RTP) modules
  • statically linked kernel modules (SKM) for VxWorks Cert

Building from Workbench

The GNATbench Ada Development User Guide describes how to use GNATbench within Workbench. This document, like all Workbench user guides, is accessible via the “Help Contents” sub-menu entry under the top-level Workbench “Help” menu.

See the “Building” chapter for all the topics related to building Ada projects within Workbench. This chapter includes general material, such as an overview of the predefined and GNATbench-defined build commands, as well as specific information that is essential to building with GNATbench.

When building a downloadable kernel module (DKM) using GNATbench, make sure that the Linker.Switches (<language>) attribute includes "-noauto-register". This switch prevents conflicts with the OS-provided static constructors and destructors that are added into DKM projects by Workbench when it builds an application image.

You should also note that GNATbench provides a number of tutorials that provide detailed, step-by-step instructions and screen-shots showing how to create and build VxWorks projects. In addition to General Purpose Platform (GPP) projects, there are also tutorials on creating and building projects not yet directly supported by GNATbench. All these tutorials are located under the top-level node of the GNATbench Ada Development User Guide.

Building from GPS

Chapter “Working in a Cross Environment” in the GPS User’s Guide explains how to adapt the project and configure GPS to work in a cross environment.

RTPs and kernel modules

To support both RTPs and kernel modules, two different run-time libraries are provided. For example, you can build an Ada kernel module (DKM) from the demo1 example using the following command:

$ powerpc-wrs-vxworks-gnatmake -g --RTS=kernel demo1.adb

This will build a relocatable object that you can download in the kernel context. The -g switch adds debugging information to the module.

If you are building the same DKM within GNATbench, add the following into the Linker package in your root GPR file:

package Linker is
  for Switches ('Ada') use (..., "-noauto-register", '-r', ...);
end Linker;

The linker switch prevents conflicts with the munching step performed by Workbench on relocatable DKMS.

For DKMs that will be statically linked into the VxWorks image, use:

$ powerpc-wrs-vxworks-gnatmake -g --RTS=kernel demo1.adb -largs -nostdlib -r

In this case, the -nostdlib switch prevents conflicts between the GNAT Pro version of libgcc.a and that provided by VxWorks, by suppressing the inclusion of the former. The -r switch allows a partial link that will be resolved when linked into the image.

To build it as an Ada RTP module, you need to use the RTP run-time library. The compiler will automatically find the VxWorks 6 RTP libraries using the WIND_BASE environment variable. Note that if you want to build an application for Wind River Systems’ VxSim simulator you will need to pass -vxsim option to the linker. Otherwise the application will be built for the real x86 target. The previous example may be built as follows:

$ powerpc-wrs-vxworks-gnatmake -g --RTS=rtp -mrtp demo1.adb -largs -Wl,-L$WIND_BASE/target/usr/lib/ppc/PPC32/common

To do so in GPS, you need to update your project properties and add the options to the corresponding project attributes (e.g., –RTS=rtp, -mrtp for Ada make options).

Note that use of gprconfig/gprbuild will insert the necessary includes, library references and -mrtp switches for compilation and linking automatically.

If you are storing a RTP in a ROM filesystem (for example when using RTPs with the VxWorks Cert 6.x certified kernel), you must specify the base address for the RTP using a linker switch. This one generally works due to default memory layout:

package Linker is
  for Switches ('Ada') use (..., "-Wl,--defsym,__wrs_rtp_base=0x40000000", ...);
end Linker;

None of the above special switches need to be provided when using Workbench with the GNATbench plugin to perform builds, except for defining __wrs_rtp_base for VxWorks RTPs residing in the ROM filesystem.

SMP Support

Starting with VxWorks 6.6, the OS supports symmetric multi-processing (SMP) for specific target boards. GNAT includes run-time libraries supporting DKM and RTP applications running on a SMP kernel.

To use these libraries, use –RTS=kernel-smp or –RTS=rtp-smp, depending on the application type.

When using these libraries, the VxWorks kernel must be configured with __thread support.

These libraries can also be used for uniprocessor kernels starting with VxWorks 6.6.

Processor affinity is controlled by using pragma Task_Info, by declaring a discriminant of type System.Task_Info.Task_Info_Type for a task type and giving it a value on task creation, or by using Ada 2012 pragma CPU.

For the first two options, allowable values range from 0 to n-1, where n is the number of processors available. A value of zero sets the processor affinity to VxWorks logical processor 0, and so on. For pragma CPU, allowable values range from 1 to n, and the affinity in VxWorks terms is set to the logical processor with an ID one less than the specified value. Out of range values, or an attempt to use this mechanism on VxWorks versions prior to 6.6, will cause creation of the task to fail.

By default, when the above mechanisms are not used, tasks inherit the processor affinity of the task that creates them (but see the notes on RTPs in the VxWorks user manuals).

The Workbench debugger should be used to debug SMP applications, as multiprocessor synchronization is not provided in gdb.

Using the VxWorks 6 Simulator

If you have access to the build toolchain for the simulator, it should be invoked using the i586-wrs-vxworks prefix. This toolchain also supports both RTP and kernel modules. The toolchain for the simulator is used in the same way as toolchains for real targets. Please see Building a VxWorks 6 Application.

Debugging an Application on VxWorks 6

In VxWorks 6, the debugging interface used by the debuggers is a GDB/MI-based protocol named DFW. The component that offers debug services is called the DFW server.

Therefore, to be able to connect to the target board, you need first to have a DFW server running and your target board registered into this server. You can do that in Workbench using the panels Remotes Systems or Target Manager.

Note also that, to be able to run an RTP, you need access to the file system where the RTP module is located. This can be done in several ways, for example via FTP or NFS.

If the kernel has been loaded on the target board using an FTP server, the target board has access to the file system of the this server. Then, if your RTP module is accessible in the FTP server’s file system, the target board will be able to load it.

If not, you will have to use one of the other file system clients that VxWorks provides. Here is an example for NFS. Assuming that you want to execute /home/workspace/demo1.exe, located on a machine named rtphost whose IP address is 192.168.0.1 and which has a mountable NFS file system, you can execute it from the target shell with the following commands:

hostAdd ("rtphost", "192.168.0.1")
nfsMount ("rtphost", "/home/workspace", "/mnt")

In any case, in the target server properties it is important to set the Pathname Prefix Mapping (corresponding to the previous settings) when creating the target server (section 19.1.3 of the Wind River WorkBench User’s Guide):

Target Path       Host Path
rtphost:/mnt   /home/workspace
/mnt          /home/workspace

After this preliminary setup, you are ready to debug. A debugging session on VxWorks 6 can be divided into the following steps:

  • connect to the target board;
  • load the module on the target memory;
  • execute and debug your program.

The following sections explain how these three steps map into the two provided debugging solutions: Workbench and GPS.

Using Workbench DFW

GNATbench is integrated smoothly into Workbench’s debugging solution. It provides Ada support similar to the what is available for C (breakpoints, control of the debugger execution, etc.). See Debugging for more information.

Using GPS and GDB

As mentioned previously, the Ada debugger in GPS uses the DFW interface for controlling the execution on the program executing on the target. To be able to debug an Ada program, the DFW server has to be launched from Workbench and has to be connected to the target you are debugging.

Note that gdb can only debug uniprocessor kernel applications; the Workbench debugger should be used for RTPs and SMP applications. gdb is not supported with Workbench 4, so cannot be used to debug when VxWorks 6 Cert is run as a guest OS over VxWorks 653 3.x.

Several DFW servers may have been launched on your host; if so, you will need to tell the debugger which one it should pick. To do so:

  • In Workbench, open Windows => Preferences, then expand Wind River => Debug Server Setting. There, you should find the session name of the DFW server; e.g. dfw-wb3111-${user}.
  • Set the environment variable DFW_SERVER_NAME to this name; e.g., on Unix: DFW_SERVER_NAME=dfw-wb3111-me; export DFW_SERVER_NAME

A kernel module generated by the GNAT toolchain can be loaded and run in the same way as a VxWorks 5.* kernel module (Ada or C). An example may be found in Common VxWorks Topics.

Before starting a debugging session in GPS, you should ensure that several fields in your GNAT Project are filled in:

  • Program host: <name of your connection to the board>
  • Protocol: dfw to debug an kernel task
  • Debugger: powerpc-wrs-vxworks6-gdb

The name of your connection to the board is displayed in Workbench, in panel Remotes Systems or Target Manager, at the root of your target’s connection tree.

As was mentioned previously, the three steps to debugging on VxWorks 6 are connecting, loading, and executing.

To connect to your target and debug a kernel module, the debugger uses a target interface named dfw. To do so in GPS, you can specify the target protocol in your project properties, and use the menu Debug => Initialize => <no_main_file>. Alternatively, if you want to use the command line debugger, you should use the target command:

(gdb) target dfw <myboard>
Connecting to DFW server <myhost>:1603...Connecting to <myboard>@<myhost>...
done.

where <myboard> is the name of the target board you are attaching to.

You cam load your module either during initialization in GPS, with one of the choices given by the menu (e.g., Debug => Initialize => demo1), or from the console:

(gdb) load demo1

You can then start executing, using the menu Debug => Run or the Start/Continue button. As always, there is a corresponding call in the command line debugger, which is named start and takes the name of the main procedure as a parameter:

(gdb) start demo1
Breakpoint 1 at 0xad5768: file demo1.adb, line 4.
Starting program:  demo1
demo1 () at demo1.adb:4
4       procedure Demo1 is

Note that, if your module has already been loaded and executed from an other tool (e.g. Workbench or the host shell) you can also attach to the running process in the debugger. To do so, first get the list of kernel tasks and RTPs with the command info wtx threads:

(gdb) info wtx threads
0 task 0x604df868       (tShell0)
1 task 0x6037bc70       (tWdbTask)
2 task 0x6036f6d8       (ipnetd)
3 task 0x604159e0       (ipcom_syslogd)
4 task 0x60427af0       (tNet0)
5 task 0x60403a38       (tAioIoTask0)
6 task 0x60403740       (tAioIoTask1)
7 task 0x603e59a8       (tAioWait)
8 task 0x6037e290       (tNbioLog)
9 task 0x60387c38       (tLogTask)
10 task 0x60170774      (tExcTask)
11 task 0x6037a480      (tJobTask)
12 task 0x73acc0        (tMy_p)

This gives you the id of the different tasks. To debug a task, use the attach command. For example, to attach tMy_p:

(gdb) attach 0x73acc0

You can then set breakpoints, start and stop the execution, and display your variables... For more details, please refer to the GDB User’s Manual.

Workbench / VxWorks 7 Topics

This section describes topics that are specific to VxWorks 7. It introduces the basic features provided by the GNAT toolchain for this target, and shows how to use them. It also describes which functionalities are implemented, and summarizes known issues.

Using GNAT for VxWorks 7

GNAT for VxWorks 7 comes with a choice of two development environments:

  • GNAT Programming Studio (GPS), which is the standard GNAT IDE,
  • GNATbench, the GNAT plug-in for Workbench (Wind River’s IDE based on Eclipse).

Both environments share a common core technology, the GPS engine, so the choice of environment is mostly a matter of taste. GPS is more GNAT-centric and Workbench with the GNATbench plug-in is more VxWorks-centric.

Note that interfacing GNAT with Wind River C++ compilers is only supported when building from GNATbench. See section Interoperability with VxWorks C/C++ compilers for more details.

Building a VxWorks 7 Application

GNAT for VxWorks 7 supports two build interfaces:

  • Workbench;
  • GPS;

for three types of modules:

  • downloadable kernel modules (DKM)
  • real time process (RTP) modules

Building a VxWorks 7 Application from Workbench

The GNATbench Ada Development User Guide describes how to use GNATbench within Workbench. This document, like all Workbench user guides, is accessible via the “Help Contents” sub-menu entry under the top-level Workbench “Help” menu.

See the “Building” chapter for all the topics related to building Ada projects within Workbench. This chapter includes general material, such as an overview of the predefined and GNATbench-defined build commands, as well as specific information that is essential to building with GNATbench.

When building a downloadable kernel module (DKM) using GNATbench, make sure that the Linker.Switches (<language>) attribute includes "-noauto-register". This switch prevents conflicts with the OS-provided static constructors and destructors that are added into DKM projects by Workbench when it builds an application image.

You should also note that GNATbench provides a number of tutorials that provide detailed, step-by-step instructions and screen-shots showing how to create and build VxWorks projects. In addition to General Purpose Platform (GPP) projects, there are also tutorials on creating and building projects not yet directly supported by GNATbench. All these tutorials are located under the top-level node of the GNATbench Ada Development User Guide.

Building a VxWorks 7 Application from GPS

Chapter “Working in a Cross Environment” in the GPS User’s Guide explains how to adapt the project and configure GPS to work in a cross environment.

VxWorks 7 RTPs and kernel modules

To support both RTPs and kernel modules, two different run-time libraries are provided. For example, you can build an Ada kernel module (DKM) from the demo1 example using the following command:

$ powerpc-wrs-vxworks7-gnatmake -g --RTS=kernel demo1.adb

This will build a relocatable object that you can download in the kernel context. The -g switch adds debugging information to the module.

If you are building the same DKM within GNATbench, add the following into the Linker package in your root GPR file:

package Linker is
  for Switches ('Ada') use (..., "-noauto-register", '-r', ...);
end Linker;

The linker switch prevents conflicts with the munching step performed by Workbench on relocatable DKMS.

For DKMs that will be statically linked into the VxWorks image, use:

$ powerpc-wrs-vxworks7-gnatmake -g --RTS=kernel demo1.adb -largs -nostdlib -r

In this case, the -nostdlib switch prevents conflicts between the GNAT Pro version of libgcc.a and that provided by VxWorks, by suppressing the inclusion of the former. The -r switch allows a partial link that will be resolved when linked into the image.

To build it as an Ada RTP module, you need to use the RTP run-time library. The compiler will automatically find the VxWorks 7 RTP libraries using the VSB_DIR environment variable. This variable shall point to the root of the VxWorks Source Build for your target. The previous example may be built as follows:

$  powerpc-wrs-vxworks7-gnatmake -g --RTS=rtp -mrtp demo1.adb

To do so in GPS, you need to update your project properties and add the options to the corresponding project attributes (e.g., –RTS=rtp, -mrtp for Ada make options).

Note that use of gprconfig/gprbuild will insert the necessary includes, library references and -mrtp switches for compilation and linking automatically.

None of the above special switches need to be provided when using Workbench with the GNATbench plugin to perform builds.

VxWorks 7 SMP Support

VxWorks 7 supports symmetric multi-processing (SMP) and all GNAT runtimes are built with SMP support. But these runtimes can also be used with uniprocessor kernels.

When using these libraries, the VxWorks kernel must be configured with __thread support.

Processor affinity is controlled by using pragma Task_Info, by declaring a discriminant of type System.Task_Info.Task_Info_Type for a task type and giving it a value on task creation, or by using Ada 2012 pragma CPU.

For the first two options, allowable values range from 0 to n-1, where n is the number of processors available. A value of zero sets the processor affinity to VxWorks logical processor 0, and so on. For pragma CPU, allowable values range from 1 to n, and the affinity in VxWorks terms is set to the logical processor with an ID one less than the specified value. Out of range values will cause creation of the task to fail.

By default, when the above mechanisms are not used, tasks inherit the processor affinity of the task that creates them (but see the notes on RTPs in the VxWorks user manuals).

Debugging an Application on VxWorks 7

GNAT for VxWorks 7 generates debug informations that are following the dwarf standard; these can be interpreted by the Workbench debugger. In order to use this debug technology, please consult the Workbench manual.

Interoperability with VxWorks C/C++ compilers

VxWorks comes with a set of C/C++ compilers: GNU, DIAB, LLVM, ICC. This section documents how to use these compilers when building Ada with GNAT.

Interoperability issues have to be handled in two cases:

  • when mixing C/C++ with Ada, using GNAT with a different C++ toolchain;
  • when building an application from Workbench.

Note that these two cases are linked together, since mixing Ada and C++ is only supported when building from Workbench. But this is also relevant for Ada-only applications built from Workbench: in this case, the Workbench workflow is such that the final link is handled by VxWorks C/C++ toolchains, even when only Ada is used in the application.

On the contrary, there are no such interoperability issues if a Ada-only application is built outside of Workbench projects, e.g. directly from GPS or gprbuild, even if the VxWorks kernel is built by a VxWorks C/C++ toolchain.

exception schemes on mixed-language applications

The main issue when mixing two toolchains is making exceptions work. Ada and C++ exception schemes can have differences that limit what can usually be expected from a mixed Ada/C++ application. In this section, we will describe briefly the level of interoperability that GNAT can provide depending on the C/C++ toolchain.

3 different build options are provided by GNATbench, providing 3 different levels of interoperability with the third-party C/C++ toolchain:

  • EXCEPTIONS_GPRBUILD_ONLY: exception propagation only works for Ada-only applications.
  • EXCEPTIONS_SEPARATE_DOMAINS: exception propagation is supported on mixed language application; however, exceptions cannot be propagated through different languages. C++ exceptions only go through C++ frames, and if they hit an Ada frame then the application stops with an error. Same behavior for Ada exceptions.
  • EXCEPTIONS_FULL: exception support is complete in mixed-language applications: C++ exceptions can be handled in Ada, Ada exceptions can be caught in C++.

These 3 modes are not available on all configurations. The following sections give an executive summary of our support for each C/C++ toolchain.

Interfacing with DIAB

Exception handling is not supported when mixing Diab C++ and GNAT Ada.

Due to this limitation, and to avoid any surprise, GNATbench is set to support only pure-Ada applications by default (EXCEPTIONS_GPRBUILD_ONLY).

It is still possible to have mixed-language applications under the restriction of no exception propagation.

Interfacing with GNU

GNAT and Wind River G++ are based on the same underlying technology: they are both GNU-based. Because of that, two options are available to the user:

  • EXCEPTIONS_SEPARATE_DOMAINS: provides the most robust solution by using GNAT libraries for Ada and G++ libraries for C++.
  • EXCEPTIONS_FULL: being able to go through different languages requires using the same library for all languages, and in that case it has to be Wind River’s G++’s libraries. In our experience this option works properly on VxWorks 7, however Wind River does not guarantee a full compatibility of VxWorks library with the exception information that GNAT generates. Even if both are GNU-based, they rely on different versions of GCC and it is still possible to get incompatibilities. If you want to rely on EXCEPTION_FULL, you will need to validate it on your own context.
Interfacing with LLVM

LLVM support is similar to GNU:

  • EXCEPTIONS_SEPARATE_DOMAINS: provides the more robust solution.
  • EXCEPTIONS_FULL: a possible option at your own risk. Wind River provides a GNU-compatible layer with no guarantee: UnwindLevel1-gcc-ext.c. Contact Wind River support to know more about this option.
Interfacing with ICC

AdaCore does not support the interfacing of ICC with GNAT. If you are interested by such an option, please contact sales@adacore.com.

VxWorks 653 Topics

This section provides information specific to the GNAT cross-development system for the VxWorks 653 target. Supported versions are selected VxWorks 653 1.8.x, 2.2.x, 2.3.x, 2.4.x and 2.5 editions. For VxWorks 653 3.0.x, VxWorks 6 Cert is supported as a guest OS. See the release notes for details.

Introduction

VxWorks 653 is a time- and space-partitioned real-time operating system that conforms to the ARINC-653 standard. Its purpose is to support the implementation of Integrated Modular Avionics (IMA) system architectures and similar architectures in other industries. Because an application running in one partition cannot be affected by those in different partitions, applications can be developed with independently, and their safety certification can be unlinked from the safety certification of other subsystems.

The VxWorks 653 architecture and programming model is described in the VxWorks 653 Programmer’s Guide.

For programming purposes the operating system (OS) is divided into two parts:

  • The Module OS,

    which contains drivers and other privileged software. This part of the system presents an API that is described in the VxWorks 653 Module OS API Reference, VxWorks 653 Module OS Errno Code List and the VxWorks 653 Programmer’s Guide. GNAT Pro does not directly support building applications for the Module OS.

  • The Partition OS,

which is used within application partitions. This part of the system presents an API similar to that of VxWorks 5.x, or the VxWorks 6.x kernel, called vThreads, and is also enhanced with optional POSIX and APEX (ARINC-653) components. It is primarily described in the VxWorks 653 Partition OS API Reference, VxWorks 653 Partition OS Errno Code List and the VxWorks 653 Programmer’s Guide.

GNAT for VxWorks 653 lets you develop applications for the various VxWorks 653 partition types and operating modes:

  • Partition OS applications can be built with the full Ada run-time library (rts-full), the restricted Ravenscar run-time library (rts-ravenscar-cert), the restricted certifiable run-time library (rts-cert) or the zero footprint run-time library (rts-zfp). Application partitions may include either the full or partial APEX components, or just the vThreads components. Ada tasking applications must use only the APEX_MINIMAL subset of APEX.

It is expected that applications developed with GNAT will execute in application partitions under the Partition OS.

This section describes the mechanics of building and debugging Ada applications for VxWorks 653, and also treats other issues relevant to application development in the various contexts listed above.

Configuring a VxWorks 653 System

Typically, the platform will be jointly defined by specialists such as the platform developer and systems integrator. Application developers will then be provided with the platform and a description in XML configuration files of the resources available to them in the partitions their applications will execute. Definition of the platform is outside the scope of this manual, and is described in the VxWorks 653 Configuration and Build Guide.

In this section, we will focus on integration of applications into the platform. We suggest you first familiarize yourself with the overall process as described in the above reference.

Application Configuration Files

For each application, there are two sections of XML configuration info to be provided.

The first is in the partition description, which will be in either the module XML file, or in a partition-specific XML file. Refer to the VxWorks 653 Configuration and Build Guide for specific contents. An example fragment for a two partition system would be:

    <Applications>
         <Application Name="part1">
         <xi:include href="C:/testbench/first_app/config/application.xml"
/>
          </Application>
         <Application Name="part2">
<xi:include href="C:/testbench/second_app/config/application.xml"
/>
          </Application>
    </Applications>

...

    <Partitions>
          <Partition Name="part1" Id="1">
            <PartitionDescription>
                <Application NameRef="part1"/>
                <SharedLibraryRegion NameRef="ssl"/>
                <Settings
                          RequiredMemorySize="0x500000"
                          PartitionHMTable="part1Hm"
                          watchDogDuration="0"
                          allocDisable="false"
                          numStackGuardPages="0xffffffff"
                          numWorkerTasks="0"
                          isrStackSize="0xffffffff"
                          selSvrQSize="0xffffffff"
                          maxEventQStallDuration="INFINITE_TIME"
                          fpExcEnable="true"
                          syscallPermissions="0xffffffff"
                          numFiles="0xffffffff"
                          maxGlobalFDs="10"
                          numDrivers="0xffffffff"
                          numLogMsgs="0xffffffff"/>
            </PartitionDescription>
        </Partition>
           <Partition Name="part2" Id="2">
            <PartitionDescription>
                <Application NameRef="part2"/>
                <SharedLibraryRegion NameRef="ssl"/>
                <Settings
                          RequiredMemorySize="0x400000"
                          PartitionHMTable="part1Hm"
                          watchDogDuration="0"
                          allocDisable="false"
                          numStackGuardPages="0xffffffff"
                          numWorkerTasks="0"
                          isrStackSize="0xffffffff"
                          selSvrQSize="0xffffffff"
                          maxEventQStallDuration="INFINITE_TIME"
                          fpExcEnable="true"
                          syscallPermissions="0xffffffff"
                          numFiles="0xffffffff"
                          maxGlobalFDs="10"
                          numDrivers="0xffffffff"
                          numLogMsgs="0xffffffff"/>
            </PartitionDescription>
        </Partition>
   </Partitions>

The application part references external XML files used to define the application characteristics, and the applications defined in that fragment are then referred to in the partition fragments.

A typical application XML file will look like this:

<ApplicationDescription
     xmlns="http://www.windriver.com/vxWorks653/ConfigRecord"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.windriver.com/vxWorks653/ConfigRecord Application.xsd">
<MemorySize
                        MemorySizeHeap="0x100000"
                        MemorySizeBss="0x80000"
                        MemorySizeText="0x100000"
                        MemorySizeData="0x100000"
                        MemorySizeRoData="0x20000"
                        MemorySizePersistentData="0x10000"
                        MemorySizePersistentBss="0x10000">
                        <AdditionalSection
                             Name=".gcc_except_table"
                             Size="0x10000"
                             Type="DATA"/>
     </MemorySize>
     <Ports>
                       <QueuingPort
                                  Name="queuing_answer_dest"
                                  Direction="DESTINATION"
                                  MessageSize="4"
                                  QueueLength="100"
                                  Protocol="NOT_APPLICABLE"/>
                    <QueuingPort
                                  Name="queuing_sender"
                                  Direction="SOURCE"
                                  MessageSize="4"
                                  QueueLength="100"
                                  Protocol="SENDER_BLOCK"/>
                     <SamplingPort
                                  Name="sampling_sender"
                                  Direction="SOURCE"
                                  MessageSize="4"
                                  RefreshRate="0.001"/>
                    <SamplingPort
                                  Name="sampling_answer_dest"
                                  Direction="DESTINATION"
                                  MessageSize="4"
                                  RefreshRate="0.001"/>
</Ports>
</ApplicationDescription>

Its parts include a standard header that points to the corresponding schema, definition of the application entry point, the location of the generated object module and its name, memory area sizes, and an additional section to support C++ exception handling (if used). Finally, a section is provided that defines the APEX ports used by the applications. These are connected among partitions in the module XML file.

A final configuration file that you may need to create (it will usually be provided as part of the platform) defines the interface to the system shared library that contains the partition operating system. An example for the certified vThreads POS would contain something like the following, and would be used as part of the application partition build as described in the next section:

<Shared_Library_API
    xmlns="http://www.windriver.com/vxWorks653/SharedLibraryAPI"
    xmlns:xi="http://www.w3.org/2001/XInclude"
    Name="vThreads"
    >

    <Interface>
     <Version Name="Cert"/>
<xi:include
href="$(WIND_BASE)/target/vThreads/config/comps/xml/apex.xml" />
<xi:include
href="$(WIND_BASE)/target/vThreads/config/comps/xml/vthreads_cert.xml" />
    </Interface>
</Shared_Library_API>

Here we again see a standard header, followed by references to the interface description XML files for the components we need in the partition operating system.

If, as recommended, you compose your systems directly with Workbench 3.1+ using GNATbench for VxWorks 653 2.3+, you will need to provide full XML files rather than the fragments described here.

Partition Makefiles

Typical partition makefiles for C and C++ are described in the @cite{VxWorks 653 Configuration and Build Guide}. For Ada and mixed applications, we use the GPRbuild tool, rather than using .o files as targets as is shown in the guide. Here is an example makefile for a partition containing an Ada or mixed language application. It is invoked from Makefile.wr in the usual way for building partitions, from within the VxWorks 653 development shell or Workbench.

Note that if you are using GNATbench with VxWorks 653 2.3.x or more recent, GNATbench will automatically generate the partition Makefile, so this section can be skipped.

# Makefile.part1 - makefile for partition 1

########################
#
# Parameters
#

# INSTALL_DIR: root of build location
# FIRST_APP_DIR, FIRST_APP_PROJ, FIRST_APP_MAIN: location, GPR file, module name
# SYS_CFG_DIR: directory containing ssl interface file ssl.xml
# XML_FILE: module XML file
# POS: cert, full
# RTS: cert, full, ravenscar-cert, zfp
# BINDING: APEX binding to use - apex, apex_95, apex_minimal, apex_zfp, apex_zfp_95
# CPU_TYPE: PPC, SIMNT

########################
#
# Variables
#

include $(WIND_BASE)/target/vThreads/config/make/Makefile.vars

USER_OBJ    = $(INSTALL_DIR)/part1/$(FIRST_APP_MAIN).o
USER_MODULE = $(INSTALL_DIR)/first_app/$(FIRST_APP_MAIN).out

CFLAGS_EXTRA = "-DUSER_APPL_INIT=$(FIRST_APP_MAIN)()"

ifeq ($(POS), cert)
   CFLAGS_EXTRA      += -DCERT
endif

ifeq ($(POS),full)
   PART_OBJS    =  vThreadsCplusComponent.o \
                vThreadsCplusLibraryComponent.o \
                __ctype_tab.o
else
   PART_OBJS    =  vThreadsCplusComponent.o
endif

ifeq ($(CPU_TYPE), PPC)
   BUILD_SPEC = powerpc-wrs-vxworksae
else
   BUILD_SPEC = i586-wrs-vxworksae
endif

########################
#
# Rules
#
########################

include $(WIND_BASE)/target/vThreads/config/make/Makefile.rules

vpath %.gpr $(FIRST_APP_DIR)
vpath %.c   $(WIND_BASE)/target/vThreads/config/comps/src
vpath %.c   $(WIND_BASE)/target/vThreads/config/comps/src/templates

default: part1.sm part1.rpt

# ssl.xml is the interface definition, not the ssl definition:
ssl-stubs.c: $(SYS_CFG_DIR)/ssl.xml

FORCE_EXTERNAL_MAKE:

$(USER_MODULE): FORCE_EXTERNAL_MAKE
        gprconfig --batch --target=$(BUILD_SPEC) \
        --config=ada,,$(RTS) --config=c --config=c++
     gprbuild -P \
        $(INSTALL_DIR)/first_app/$(FIRST_APP_PROJ) \
        -gargs -XRuntime=$(RTS) -XCPU=$(CPU_TYPE) -XBinding=$(BINDING)

$(USER_OBJ): $(USER_MODULE)
     $(CP) $(USER_MODULE) $(USER_OBJ)

LDSFLAGS_EXTRA = -j $*
part1.lds: $(XML_FILE)

LDFLAGS_EXTRA = -T $*.lds

part1.sm: vxMain.o ssl-stubs.o $(USER_OBJ) $(PART_OBJS) part1.lds

clean:
     $(RM) *-stubs.c *.o *.lds *.sm *.rpt
     powerpc-wrs-vxworksae-gnatclean -P \
        $(INSTALL_DIR)/first_app/$(FIRST_APP_PROJ) \
        -XRuntime=$(RTS) -XCPU=$(CPU_TYPE) -XBinding=$(BINDING)

Running and Debugging Applications

Although VxWorks 653 1.8.x provides a C-oriented debugger that can be used in only a limited way with Ada applications, later versions (2.x, 3.x) provide a sophisticated debugger that can handle C, C++ and Ada. The “GNATbench for WRS Workbench User’s Guide” describes how to prepare for debugging VxWorks 653 systems using Ada in the Workbench IDE and debugger. Specifically, see the “Preparing To Debug VxWorks 653 Integration Projects” section under the more general “Debugging” chapter of the guide. That chapter also discusses general use of the Workbench debugger for Ada.

On 1.8.x and 2.x, GNAT provides a VxWorks 653-targeted version of gdb that has been enhanced to provide Ada-knowledgeable debugging (as well as supporting C and C++). This version of gdb is normally invoked from within the GNAT Programming System (GPS) development environment. For 3.x, use the Workbench debugger. This section describes the use of the GPS debugger on VxWorks 653.

VxWorks 653 System Setup

In order to debug on VxWorks 653 you need to attach a debugger at the correct time during OS initialization. The suggested approach is to define two schedules in the system in the module XML file.

The first (default) schedule does not allocate any time to application partitions that we might wish to debug; the second gives the application partitions their expected allotments. For example:

  <Schedules>
       <Schedule
                Id="0"
                Name="schedule0"
                MajorFrame="0"
                MinorFrame="0">
          <PartitionWindow
                           PartitionNameRef="part1"
                           Duration="0.0"
                           ReleasePoint="true"/>
          <PartitionWindow
                           PartitionNameRef="part2"
                           Duration="0.0"
                           ReleasePoint="true"/>
      </Schedule>
       <Schedule
                Id="1"
                Name="schedule1"
                MajorFrame="0"
                MinorFrame="0">
          <PartitionWindow
                           PartitionNameRef="part1"
                           Duration="0.0001"
                           ReleasePoint="true"/>
          <PartitionWindow
                           PartitionNameRef="part2"
                           Duration="0.0001"
                           ReleasePoint="true"/>
      </Schedule>
</Schedules>

This approach will ensure that none of the application partitions will start execution after the system boots. We can then attach a debugger to a partition before it starts, so we can debug it from its beginning.

Environment Setup

For PSC 1.8.x, it is necessary to set up the Tornado environment variables for the debugger before launching GPS. These variables are set by the torVars script. On UNIX machines, source the script that corresponds to the shell you are using – torvars.sh or torvars.csh. On a Windows machine, either make the system call the torVars.bat script on startup, or modify your environment variables manually so that you do not have to execute the script every time. For VxWorks 653 2.x execute GPS from within the VxWorks 653 Development Shell (wrenv) or from Workbench, as torVars is no longer used on these versions. Alternatively, you can debug using the Workbench debugger.

GPS Setup

In GPS, on the general properties page, designate the name of your target server in the Program Host field and select wtx as the protocol. Leave the Tools host field blank.

Debugging a Partition

The procedure to debug partition 1 is as follows:

  • Boot the target. It will eventually boot the partition OS and stop at a target shell prompt (assuming you’ve included a target shell). At this time, the Module OS has performed most of its initializations, and the partitions have been created, but not yet started.

  • Start your target server. This can be done from within Tornado, or from a command line (preferably using a shell script). It is recommended that you close the Tornado debugger if you start the target server from the Tornado GUI.

  • Within GPS, select Debug => Initialize => <No Main Program>

  • Within GPS, select Debug``=>`Attach`. A dialog will pop up with a list of all Module OS tasks.  Select ``tPartition1 from the list. The name given is constructed from the name of the partition as given in configRecord.c, prefixed by t.

  • At this point, you should be able to set breakpoints within the application partition, including the one to receive control whenever an exception occurs. You might also wish to display the call stack at this time by selecting Debug => Data => Call Stack.

  • Once you have set any desired breakpoints, hit the Continue button in GPS. The system is running, but since the first schedule is still being used by the core OS, the application partitions are not being scheduled.

  • Change to the schedule that allocates time to the application partitions by typing the following in the target shell:

    arincSchedSet (1, 0)
    

You can now debug the partition as you would any other program. When your debugging session is finished we recommend you detach from your target (Debug => Detach) before closing the debugger.

Debugging Multiple Partitions

There are two methods that can be used to debug a multi-partition VxWorks 653 system. In both methods the Tornado debugger should not be running.

The first method is to use one GPS instance per partition. Each GPS instance should be attached to a partition, and no two instances should be attached to the same partition. Also, when debugging a multi-partition system you should not do system mode debugging with any of the instances of GPS.

The second method is to use system-mode debugging. In this mode, the debugger takes control of the entire system, and allows you to debug all partitions at the same time. To use that method, you need to type “system” as the name of the task to attach to. Only one debugger can be attached to the system.

System Mode Debugging

Note that gdb supports ‘system mode’ debugging (see the Tornado documentation). To debug in system mode follow the steps provided above to start the debugger. Then open the task list dialog selecting Debug => Attach, type SYSTEM in the text entry at the bottom of the dialog, and click OK.

This will bring the target to system mode and attach the debugger to it. Breakpoints are preserved when switching between task mode and system mode.

Alternatively, you can start system mode debugging by typing the following in the gdb prompt:

(gdb) attach system

Note then that instead of using the Continue button to start your application, you will have to type the following in the gdb prompt:

(gdb) continue

To exit system mode you can use the Debug => Detach menu entry. This will detach the debugger from the target and resume the execution of your VxWorks 653 system.

Debugger Commands Specific to VxWorks 653

The debugger provides the following two commands related to the handling of partitions. These features are also available graphically in GPS, through the Debug => Data => Protection Domain menu entry.

  • info pds

    This command prints the list of partitions available on the target. For each partition, the debugger prints its ID and name. An asterisk at the beginning of a partition entry identifies it as the current partition.

    (gdb) info pds
        PD-ID        Name
    *  0x15f78c      vxKernel
       0x8007a8      vxSysLib
       0x800890      pdsAppDomain
    
  • pd <new-partition>

    This command switches the debugger to the new partition. The partition ID or the partition name can be used to identify the target partition.

    Using the list of partitions from the example above, the two commands in the following example demonstrate how to switch to the vxSysLib partition using its partition ID (0x8007a8), and then how to switch to the pdsAppDomain using its name.

    (gdb) pd 0x8007a8
    [Switching to PD 0x8007a8 (vxSysLib)]
    (gdb) pd pdsAppDomain
    [Switching to PD 0x800890 (pdsAppDomain)]
    

Application Design Considerations

As a general comment, one should never mix APEX processes, POSIX threads or Ada tasks in a single application partition. Each of these entities is part of a distinct high-level model for developing concurrent applications; mixing them will lead to confusion and unpredictable application behavior.

Before starting, one should know what ‘foreign threads’ are. ‘Foreign threads’ refer to threads that are not created by the Ada run-time environment, and therefore are not known to it without taking additional bookkeeping steps. In order to facilitate seamless operation of such ‘foreign threads’ when they execute Ada code, they need to be registered with the Ada run-time. On some native platforms, GNAT accomplishes this automatically; another alternative is to use GNAT.Threads (which can be used for raw vThreads on VxWorks 653).

Since VxWorks 653 applications are expected to comprise mainly APEX processes, the standard way to perform registration on VxWorks 653 is through the Apex_Processes.Create_Process routine in the Ada APEX binding provided with GNAT. By using this version of the binding, one can ensure that exception handling and other operations such as functions with unconstrained results and the process-level health-monitoring handler are executed correctly.

General Application Structure on VxWorks 653

For applications that are to execute in application partitions, there is a typical structure.

There is usually one ‘main’ application that will perform initializations: allocating data, creating and starting various APEX processes, and finally setting the partition into ‘normal’ mode. Once the partition has entered normal mode, the vThread executing the main is suspended, no more dynamic allocation is allowed, and the APEX processes defined within the partition are scheduled according to the characteristics defined for them within the main application.

For an Ada application, this means that the main subprogram is expected to perform initializations as described, and then to suspend until the partition is restarted. This suspension occurs when Apex_Processes.Set_Partition_Mode (Normal); is called. All of the work of the application is performed by the subsidiary processes. These processes communicate with each other and with processes in other partitions using the APEX interface. C, C++ and Ada code can be mixed within an application, and there are various approaches available to manage the code. GNAT project files are the best method for combining mixed-language code within a partition.

Note that more complex organization of applications within a partition (e.g. multiple initialization threads) are possible, but require more complex setup and coordination. Refer to VxWorks 653 Programmer’s Guide.

The ARINC-653 APEX Binding

The Ada binding to the ARINC-653 APEX (APplication EXecutive) comes with the compiler and is available for all run-time libraries. The bindings are precompiled for each valid combination of run-time library and binding variant. Sources are found under <gnat-root>/include/apex/apex[_variant]/src/.

Static libraries that can be linked to are in <gnat-root>/lib/apex/<PLATFORM>-<RUNTIME>-<BINDING>/libapex.a. For example, for VxWorks 653 for the PowerPC architecture, and the use of the cert run-time and Ada 83 version of the APEX binding, the location of the library to link is <gnat-root>/lib/apex/powerpc-wrs-vxworkae-cert-apex/libapex.a.

A GNAT project file to include in a project needing the APEX bindings is located at <gnat-root>/lib/gnat/apex.gpr. gprbuild and gnatmake know about this location, so to access this project file, add

with "apex";

to the top of your project file. This project file eliminates the need to explicitly include the desired APEX library into the gnatlink switches. Note that if you previously used environment.gpr in your project files for earlier versions of GNAT, it can still be used.

This project file takes three scenario variables:

  • PLATFORM - the target triplet for your platform, e.g. powerpc-wrs-vxworksae. The default value is ‘powerpc-wrs-vxworksae’.
  • RUNTIME - the run-time library to use: zfp, cert, ravenscar-cert or full. The default value is ‘full’.
  • BINDING - the APEX binding to use: apex, apex_95, apex_zfp apex_zfp_95 or apex_minimal. The default value is ‘apex’.

The bindings and valid combinations of RUNTIME and BINDING are described next.

Five bindings are provided:

  • The standard Ada 83 APEX binding, designated by BINDING=apex. This binding is compatible with the full and cert run-time libraries. Applications using this binding must not use the Ada tasking facilities.
  • The standard Ada 83 APEX binding, designated by BINDING=apex_zfp. This binding is compatible with the zfp, full and ravenscar-cert run-time libraries. In this case, applications using the full or ravenscar-cert libraries must not use any APEX processes.
  • A subset of the standard APEX binding that corresponds to VTHREADS component APEX_MINIMAL, designated by BINDING=apex_minimal. This binding, rooted in unit VxWorks_653.Apex_Minimal, is suitable for use full and ravenscar-cert run-time libraries, when only APEX_MINIMAL is included in the application partition.
  • The standard Ada 95/05 APEX binding, designated by BINDING=apex_95. This binding makes use of the hierarchical library facility of Ada 95 and Ada 2005. It also changes some definitions so that fewer explicit type conversions are needed in application code that uses the APEX binding. Wrapper packages are provided for backward compatibility with the Ada 83 binding. This binding is compatible with the cert and full run-time libraries. Applications must not use Ada tasking facilities.
  • The standard Ada 95/05 APEX binding, designated by BINDING=apex_zfp_95. This binding is compatible with the zfp, full and ravenscar-cert run-time libraries. In this case, applications using the full or ravenscar-cert libraries must not use any APEX processes.

The easiest way to set the binding is to add the project file <gnat-root>/lib/gnat/apex.gpr into the user project files via a context clause. GNAT will search and locate the path to this file if the environment variable GPR_PROJECT_PATH is defined to include <gnat-root>/lib/gnat/ (this should be the case by default).

It is strongly recommended that you use the bindings provided with the product rather than alternatives, as the provided bindings have been extensively tested for correctness.

To access and use the apex project in an user project file:

with 'apex';
project My_Project is
   ...
   for Source_Dirs use (<list of project source dirs>);
   -- To discriminate on eg variable Runtime:
   case apex.Runtime is
      when "cert" =>
      ...
   end case;
end My_Project;

Using the APEX Binding

The full bindings can be used if no Ada tasking constructs are used (note: ravenscar-cert applications should use apex_zfp, apex_zfp_95 or apex_minimal). The last is appropriate if only the APEX_MINIMAL component is included in the partition.

Alternatively, the zfp bindings can be used with the full and ravenscar-cert run-time libraries as long as no APEX processes are used.

Using Ada Processes

The standard way to define an APEX process in C is to create an attributes structure containing the process information (i.e. its name, entry point, priority, etc.). This record is then passed to the APEX CREATE_PROCESS routine.

It works the same way in Ada. One defines a record of type Apex_Processes.Process_Attribute_Type describing the process. This record is then given as a parameter to Apex_Processes.Create_Process.

The APEX / Ada binding provided with GNAT for VxWorks 653 adds the defaulted parameter Secondary_Stack_Size to the procedure Apex_Processes.Create_Process. In order to support the use of unconstrained function results, a data structure called the secondary stack is allocated to each APEX process that executes Ada code. (The secondary stack is actually a mark/release heap). This parameter allows the user to specify the size of this data structure (in bytes). If the parameter is defaulted, the APEX binding will allocate a secondary stack with a size equal to one-fourth of the requested stack size for the process. This data structure is allocated out of the primary stack, which will have had its allocation increased by one-fourth, so that the original requested stack allocation is honored.

Calls to Apex_Processes.Get_Process_Status will return the size of the primary stack minus the size of the secondary stack (i.e. the size originally requested by the application developer).

Note that an APEX process created via the APEX Ada binding can query its secondary stack ‘high-water mark’ using GNAT.Secondary_Stack_Info.SS_Get_Max. This package is described in the GNAT Reference Manual.

All APEX processes that execute Ada code, regardless of whether their bodies are largely in C or C++, must use the Ada binding routines to create the process or query its process status, or to create a process-level health monitoring handler. These routines and their parameters (for C) are described in apex_processes.ads and apex_health_monitoring.ads.

Selection of a Run-Time Profile

As mentioned GNAT High-Integrity Edition for VxWorks 653 provides several versions of the Ada Run-Time library suitable for differing certification and application needs.

These include:

  • A full Ada run-time for application partitions, designated by the keyword ‘full’.
  • A restricted Ada run-time for application partitions, certified to DO-178B, Level A, designated by the keyword ‘cert’.
  • A minimal Ada run-time that generates no object code (‘Zero FootPrint’), for use in either the Module OS or in application partitions, designated by the keyword ‘zfp’.
  • An implementation of the Ravenscar profile based on the cert profile for use in application partitions, designated by the keyword ‘ravenscar-cert’.

The desired run-time library is selected at build time by setting the gnatmake flag –RTS=<run-time keyword>, using one of the keywords given above. One can also import apex.gpr, as described in the section on the APEX bindings, and define project variable RUNTIME using the -X switch.

The VxWorks 653 certified partition operating system supports the use of the restricted, Ravenscar and zero footprint profiles. For more information about profiles, see The GNAT User’s Guide Supplement for GNAT Pro Safety-Critical.

Replacement of the Default Last Chance Handler

All Ada run-time libraries provided with the GNAT for VxWorks 653 include the concept of a last chance handler. This routine is called when an application terminates due to the occurrence of an unhandled Ada exception. All of the provided run-time libraries, except ZFP, provide a default implementation of the last chance handler. ZFP requires one to be written by the application developer.

The default handler can be overridden by the application developer in all cases, in order to provide such capabilities as raising an error to the health monitor. The following treatments apply:

  • For the cert, ravenscar-cert and full run-time libraries, the default handler prints a stack dump and exception message, calls APEX RAISE_APPLICATION_ERROR, and then terminates the process in which the exception occurred.
  • For the zfp run-time library, there is no default handler.

The profile of the last chance handler for all run-times except ZFP is:

procedure Ada.Exceptions.Last_Chance_Handler
  (Except :  Exception_Occurrence);
pragma Export (C,
               Last_Chance_Handler,
               "__gnat_last_chance_handler");
pragma No_Return (Last_Chance_Handler);

This handler may be replaced by any Ada or C routine that exports the symbol __gnat_last_chance_handler and that matches the given parameter profile.

The profile of the ZFP last chance handler is:

procedure Last_Chance_Handler
  (Source_Location : System.Address; Line : Integer);
pragma Export (C, Last_Chance_Handler,
               "__gnat_last_chance_handler");

The Source_Location parameter is a C null-terminated string representing the source location of the raise statement, as generated by the compiler, or a zero-length string if pragma Discard_Names is used.

The Line parameter (when nonzero) represents the line number in the source. When Line is zero, the line number information is provided in Source_Location itself.

Again, any Ada or C routine that exports __gnat_last_chance_handler and matches the designated profile may be used here.

The recommended mechanism for replacing the last chance handler is to execute powerpc-wrs-vxworksae-gnatmake or the corresponding C compiler on the file providing __gnat_last_chance_handler. The resulting .o file should then be included in the linker directives for the Ada main application.

Note that the cert, full and ravenscar-cert run-times support stack overflow checking using the gcc switch -fstack-check. It is expected that a user-written last chance handler will be compiled without this switch (or that it will be overridden with -fno-stack-check). Additionally, user-written last chance handlers must not require more than 4KB of stack space so that they can handle a stack overflow Storage_Error.

Process-Level Health Monitoring Handler

The APEX routine Apex_Health_Monitoring.Create_Handler allows an application developer to provide a process-level handler for health monitoring events. The vThread that executes this routine will receive the requested stack size, plus 1/4 of the requested size for a secondary stack. This allows such routines to be written in Ada, though they need not be.

In addition to the standard HM events defined in APEX (Apex_Health_Monitoring), VxWorks 653 defines a number of extended HM codes. These are defined in $WIND_BASE/target/vThreads/h/hmTypes.h.