Integrating RTSC Modules

From RTSC-Pedia

Jump to: navigation, search
revision tip
—— LANDSCAPE orientation
spacer [printable version]  [offline version]offline version generated on 13-May-2015 10:05 UTCspacer

Integrating RTSC Modules

Integrating RTSC modules into existing C/C++ environments

Contents

  • 1 Introduction
  • 2 Compiling
    • 2.1 Required -D Options
    • 2.2 Required -I Options
    • 2.3 A Complete GNU make Example
  • 3 Additional Compiler Options
    • 3.1 Portable Inclusion of Configuration Constants
    • 3.2 Eliminating Identifier Conflicts
      • 3.2.1 Disabling Short Names Defined By Modules
      • 3.2.2 Disabling Short Names Defined By xdc/std.h
      • 3.2.3 Disabling and Enabling Deprecated Definitions
    • 3.3 Controlling File Name Strings
  • 4 Linking
    • 4.1 Symbol Names
    • 4.2 Linker Command Files
  • 5 Debugging

Introduction

RTSC modules are designed to be portable to virtually any C/C++ environment - from host workstation applications to deeply embedded MCU or DSP applications. To enable this degree of portability, RTSC requires modules to conform to some simple physical design constraints and it defines an abstraction called a target whose attributes are used to compile sources. The physical design constraints enable modules from multiple suppliers to be installed without conflicts and the target enables the C headers from these suppliers to be compiled without conflicts.

As you'll see below, both the physical design constraints and the target, enables you to easily and uniformly build any code that references a RTSC module. In effect, once you've integrated one RTSC module into your code base, you can use any other RTSC module without making any changes to your build system.

Before using a RTSC module in an executable, the module must be configured; i.e., a RTSC configuration tool must be run and the output of this tool must be used in the application linking process. To simplify build integration, in addition to linker control files, the RTSC configuration tool generates a set of required compiler options that can be directly passed to the compiler when building the application's sources. However, if you are simply building a library that references a RTSC module, you only need to specify a few -D and -I compiler options "by hand".

Compiling

When compiling a C/C++ source file that references a RTSC module, there are two required -D options and a set of required -I options that correspond one-to-one with the repositories on the package path. All of these required options are independent of the module and the package containing the module.

In addition to these required options, RTSC headers support additional features that assist in integration with other code bases. These options are described in Additional Compiler Options below.

Required -D Options

Source files that reference a RTSC module must be compiled with pre-defined macros that identify the target being used to compile the source. Each target has a globally unique name (the module that implements the xdc.bld.ITarget interface) and is responsible for defining a set of standard types and constants that enables source code to be portable among all targets.

To compile sources that use a RTSC module, two symbols must be defined before including its header:

  • xdc_target_types__ - the package qualified path of the target's standard types header; e.g., ti/targets/std.h. This value is specified in the target's stdInclude config parameter; see xdc.bld.ITarget.stdInclude
  • xdc_target_name__ - the target's module name without the package prefix; e.g., C64 rather than ti.targets.C64.

If your source files use the xdc/cfg/global.h header to reference global objects created during configuration, you must also define the following symbol:

  • xdc_cfg__header__ - the package qualified name of the executable-specific C/C++ header generated by the program configuration tool. See Portable Inclusion of Configuration Constants for more information.

For example, to compile sources with the ti.targets.C64 target, the following command line is sufficient:

 
cl6x -Dxdc_target_types__=ti/targets/std.h -Dxdc_target_name__=C64 ...

Where, cl6x is the name of the TI compiler, ti/targets/std.h is the value of the stdInclude attribute of the ti.targets.C64 target, and C64 is the module name of the target within the ti.targets package.

To maximize portability, code should never directly include the stdInclude header specified by a target. Instead, the definitions from this header should always be included using the target-independent header xdc/std.h. See the xdc package reference documentation for more information about this header.

 
 
#include <xdc/std.h>   /* define portable types; e.g., String, Bool, ... */
    :

Required -I Options

RTSC packages can be installed in any directory, called a repository, and RTSC package names match the name of the sub-directory within the repository containing the package. For example, if the package xdc.runtime is installed in the repository /home/me/packages, the absolute path to the xdc.runtime package is /home/me/packages/xdc/runtime.

By convention, all code that references a module's interface, must #include the module's header using the module's package name as well as the module's name. For example, to use the xdc.runtime.Memory module, C files will contain the following statement.

 
#include <xdc/runtime/Memory.h>

Following this pattern implies that the compiler's command line need only include -I options for each repository named on the package path. Recall the package path is simply a list of all repositories containing packages that are required by your code. This has the advantage that the -I options are completely independent of the package names; adding a new package to your project does not require any change to your build system's files.

The following GNU make file converts a package path, defined by the PACKAGE_PATH variable, to the required -I options.

 
 
PACKAGE_PATH = $(XDCPATH);$(XDCROOT)/packages
CCINCS = -I$(strip $(subst ;, -I,$(PACKAGE_PATH))

A Complete GNU make Example

Suppose you want to build sources using the target defined by ti.targets.C64. You must first examine the ti.targets.C64.stdInclude attribute - either by reading the ti/targets/C64.xdc file or reading the generated reference documentation for this module (ti.targets.C64). Using just this one value, it is possible to build the required compiler command line options.

The following GNU makefile illustrates how this information might by used in a make-based build system.

 
 
 
 
 
 
 
 
 
 
PACKAGE_PATH = $(XDCPATH);$(XDCROOT)/packages
 
CC_INCS  = -I$(strip $(subst ;, -I,$(PACKAGE_PATH))
 
CC6x     = /home/tools/ti/c6x/6.1.0/bin/cl6x
 
C64_DEFS = -Dxdc_target_types__=ti/targets/std.h -Dxdc_target_name__=C64
 
%.o:%.c
    $(CC6x) -c $(CC_INCS) (C64_DEFS) $*.c -o $@

Notice that

  1. the package path is easily converted into the list of -I options required by the compiler, and
  2. the include path settings are independent of the target (so porting to a new target does not affect the -I options).

To illustrate how this pattern scales to other targets, suppose you want to support two different targets: one for native execution on, say, Linux running on a PC and one for an embedded C64P device. A simple GNU makefile for this is shown below. In this case, we need to choose different object file suffixes to allow side-by-side compilation of both the embedded target and the native target. But, as we noted above, the -I options don't change.

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
PACKAGE_PATH = $(XDCPATH);$(XDCROOT)/packages
 
CC_INCS  = -I$(strip $(subst ;, -I,$(PACKAGE_PATH))
 
CC6x     = /home/tools/ti/c6x/6.1.0/bin/cl6x
CCHOST   = /home/tools/gnu/gcc-4.1.0-glibc-2.3.2/i686-unknown-linux-gnu/bin/gcc
 
C64P_DEFS = -Dxdc_target_types__=ti/targets/std.h -Dxdc_target_name__=C64P
HOST_DEFS = -Dxdc_target_types__=gnu/targets/std.h -Dxdc_target_name__=Linux86
 
%.o86U:%.c
    $(CCHOST) -c $(CC_INCS) (HOST_DEFS) $*.c -o $@
 
%.o64P:%.c
    $(CC6x) -c $(CC_INCS) (C64_DEFS) $*.c -o $@

Additional Compiler Options

In addition to the required -D options, RTSC headers use two other -D options to facilitate the creation of portable code and minimize the chance of identifier conflicts with existing code bases.

Portable Inclusion of Configuration Constants

C/C++ source code that is dependent on values specified in a configuration script can portably reference these values by adding the following #include statement to the top of the referencing source file.

 
 
#include <xdc/cfg/global.h> /* include definitions appearing in an executable's config script */
    :

For more information about how to specify values that can be referenced using this header, see the reference documentation for the xdc.cfg.Program.global configuration parameter.

In order to support multiple side-by-side configurations, the xdc/cfg/global.h header requires the name of the header that is generated as part of configuration. So, to compile sources that include xdc/cfg/global.h, one symbol must be defined before including this header:

  • xdc_cfg__header__ - the package qualified name of the executable-specific C/C++ header generated by the program configuration tool; e.g., local/examples/package/cfg/mycfg_p62.h.

For example, to compile sources that reference the values declared in global for a TI C6x target with a generated configuration header named package/cfg/mycfg_p62.h in a package named local.examples the following command line is sufficient:

 
cl6x -Dxdc_cfg__header__=local/examples/package/cfg/mycfg_p62.h ...

The xdc_cfg__header__ symbol is automatically defined when you use the the RTSC Build Engine, xdc.bld, to create executables; see xdc.bld.Executable.addObjects. It is also defined in the compiler options file automatically generated by the xdc.tools.configuro tool.

Eliminating Identifier Conflicts

RTSC module headers are designed to enable developers to handle identifier conflicts that may arise when integrating RTSC modules or RTSC standard types with existing C source code bases. All RTSC headers allow you to control, to some extent, the names defined by the header via -D compiler options.

Every identifier ident defined by a RTSC module Mod (names of functions, constants, config params, typedefs, structs, etc.) contained in package pkg has at least two names:

  1. the fully qualified (or "long") name: pkg_Mod_ident, and
  2. short names: Mod_ident and, if @GlobalNames is specified in the pkg/Mod.xdc specification, ident.

Why are there multiple names for the same thing? Long names are guaranteed to be globally unique (assuming the package name is globally unique) and will not, therefore, conflict with any identifier from another part of the code base. On the other hand, the long name is ugly; it's onerous to type and makes sources difficult to read. Since conflicts are relatively rare, RTSC headers also define an optional short names for each identifier that is enabled by default. In those rare situations where short names conflict and it is not practical (or desirable) to modify existing code, it is possible to disable the definitions of these short names on a per module basis.

Disabling Short Names Defined By Modules

To disable the definition of short names for the module Mod in the package pkg, you must define the symbol pkg_Mod__nolocalnames before including the module's header. Suppose, for example, that you need to disable the short names for the xdc.runtime.Memory module. You can simply add a #define of the symbol xdc_runtime_Memory__nolocalnames prior to including xdc/runtime/Memory.h for the first in your source file.

 
 
#define xdc_runtime_Memory__nolocalnames  /* disable short name definitions of identifiers */
#include <xdc/runtime/Memory.h>

Alternatively, you can add this definition on the compile line used to build the file.

 
gcc -Dxdc_runtime_Memory__nolocalnames -Dxdc_target_types__=... -Dxdc_target_name__=...

Disabling Short Names Defined By xdc/std.h

As with module headers, each identifier defined by xdc/std.h - the header that defines the target's standard types - has both a "short name" and a "long name". The short name is often a single word, such as Bool, whereas the long name is the short name with the prefix "xdc_"; for example, the long name of Bool is just xdc_Bool.

To disable the definition of short names, you must define the symbol xdc__nolocalnames before including any header that includes xdc/std.h. You can add the definition directly to the files affected by the collision.

 
 
#define xdc__nolocalnames    /* disable short name definitions of standard types */
#include <xdc/std.h>

Alternatively, you can add this definition on the compile line used to build the file.

 
cl6x -Dxdc__nolocalnames -Dxdc_target_types__=ti/targets/std.h -Dxdc_target_name__=C64 ...

Of course, either way, you must reference the identifiers defined by the target using their (somewhat ugly but unique) long names.

Disabling and Enabling Deprecated Definitions

As modules and interfaces evolve over time it is common practice to keep definitions and declarations that should not be used in any new code; they exist solely to support backward compatibility with existing code bases. In order to verify that a code base does not leverage any of these deprecated definitions, you can disable them by defining the xdc__strict symbol prior to including any headers.

As above, you can do this either on the compiler's command line or within selected files.

 
 
#define xdc__strict    /* disable deprecated definitions */
#include <xdc/std.h>

Alternatively, you can add this definition on the compile line used to build the file.

 
cl6x -Dxdc__strict -Dxdc_target_types__=ti/targets/std.h -Dxdc_target_name__=C64 ...

Prior to completely removing deprecated definitions from an interface, the definitions will simply no longer be available by default; in order to use the deprecated definitions, you must explicitly define a new symbol, xdc__deprecated_types, prior to including any headers. In future releases, the deprecated types may be completely removed. So, it is best to migrate to the new definitions as soon as practical.

As above, you can do this either on the compiler's command line or within selected files.

 
 
#define xdc__deprecated_types    /* enable definitions of deprecated types */
#include <xdc/std.h>

Alternatively, you can add this definition on the compile line used to build the file.

 
cl6x -Dxdc__deprecated_types -Dxdc_target_types__=ti/targets/std.h -Dxdc_target_name__=C64 ...

Controlling File Name Strings

Both the xdc.runtime.Error and the xdc.runtime.Assert modules use the symbol xdc_FILE to obtain the current file name when raising errors. By default this symbol is defined to be __FILE__, a compiler generated string whose value is the name of the current translation unit. While these file names are valuable when tracking the source of errors, they also consume a considerable amount of data space.

It is possible to eliminate the string space overhead associated with errors by re-defining xdc_FILE to be NULL.

 
#define xdc_FILE NULL    /* eliminate use of __FILE__ in Assert and Error macros */

Alternatively, you can add the equivalent -D option to your compiler's command line.

 
cl -Dxdc_FILE=NULL ...

For more information see Eliminating File Name Strings in Using xdc.runtime Errors.

By default, assertions and errors from modules do not provide the file name. Since module sources must be organized in directories that match their package name, the module name should be sufficient to identify the source file responsible for the assertion or error.

If xdc_FILE is not previously set, a module's internal header (package/internal/«Mod».xdc.h) sets xdc_FILE to NULL. Of course, module producers can override this default by simply defining xdc_FILE to be __FILE__ in their C sources (before the inclusion of the module's internal header) or on the compiler's command line.

Linking

When linking an executable that references a RTSC module, you must include an object file and a linker command file produced by a separate "configuration step". This configuration step allows you to specify additional information about the executable that allows the RTSC modules to optionally supply alternative implementations that are "optimal" for your particular situation. For details about how to integrate configuration into your build system, start with Configuration Basics.

While this configuration step may seem unnecessary, it allows producers to completely change their underlying libraries without requiring you to make any changes to your build system's project files. In addition, since the configuration step is independent of any specific package, it is possible to add or remove modules from a project without changing the link steps required by your executable. In other words, the configuration step provides additional flexibility to the producer while at the same time insulating you from variations among different producers as well as future changes made by any of these producers. So, like the compilation requirements above, once you're integrated configuration into your build system for one module or package, you'll never have to change this to support new modules from any RTSC package.

Linking with RTSC modules has been designed to be modular and easy; new modules can be created at any time by third parties and added to a project without making any changes to linking process. To make this possible all symbols defined in the new modules must not clash with any existing symbols and the linking process must be independent of the ad hoc library names chosen by the module/package producer.

Symbol Names

To ensure symbol names do not clash, all RTSC module functions, types, and constants are given names that include both their package and module names. Assuming package names are globally unique, the C/C++ linker will never see a symbol conflict between two modules in different packages no matter who created the modules, when they were created, or what target you're using.

The table below describes the symbol references and definitions, as seen by the linker, that are created by a RTSC module named ModA delivered in a package whose name is pkg. See the C Language Binding for more information about what functions and variables are created for each module.

Kind Client Reference [Producer Ref/Def] External Symbol, Example Type
client-visible function REF (or DEF if @DirectCall) ModA_fxn() pkg_ModA_fxn__E, xdc_runtime_System_printf__E T
client-visible function DEF ModA_fxn() pkg_ModA_fxn__F, xdc_runtime_System_printf__F T
internal function DEF or REF (spec'd in internal: section) pkg_ModA_fxn() [ModA_fxn()] pkg_ModA_fxn__I, xdc_runtime_Core_createObject__I T
module-wide config param REF ModA_cfg pkg_ModA_cfg__C, xdc_runtime_System_A_cannotFitIntoArg__C D,A
per-instance config params default REF struct ModA_Params pkg_ModA_Object__PARAMS__C, xdc_runtime_HeapStd_Object__PARAMS__C D,A
static per-instance object array REF ModA_Object_get() pkg_ModA_Object__table__V, xdc_runtime_HeapStd_Object__table__V D,A
module-wide system function REF (spec’d in IModule) ModA_startupDone() pkg_ModA_Module__*__S, xdc_runtime_HeapStd_Module__startupDone__S T
per-instance system function REF (spec’d in IModule) HeapStd_create() pkg_ModA_Object__*__S, xdc_runtime_HeapStd_Object__create__S T
proxy module system function REF (spec’d in IModule) - pkg_ModA_pxy_Proxy__delegate__S, xdc_runtime_Memory_HeapProxy_Proxy__delegate__S T
module internal state structure REF [ModA_module] pkg_ModA_Module__state__V, xdc_runtime_HeapStd_Module__state__V B
inheriting module virtual function table REF ModA_Module_upCast() pkg_ModA_Module__FXNS__C, xdc_runtime_HeapStd_Module__FXNS__C D,A
interface run-time type identifier REF - pkg_IMod_Interface__BASE__C, xdc_runtime_IHeap_Interface__BASE__C D,A
internal module root structure DEF - pkg_ModA_Module__root__V, xdc_runtime_HeapStd_Module__root__V B

TODO:  review consumer/producer reference for both VFT and RTTI

The type codes in the table below are described in the following table.

Type Description
T read-only code section (often called Text)
B read-write data (sometimes called BSS)
D initialized read-only data, requires storage space
A absolute symbol, does not require storage space

Linker Command Files

To ensure that users of a package are insulated from having to make changes to their project's build files because of changes to physical library names provided by a package or the addition of a new package, the RTSC configuration step produces a linker command file that contains an ordered list of all libraries that need to be linked with to produce the application. The user never needs to consider the names or the order of libraries required to satisfy references to RTSC modules.

Debugging

Unless you are using a debugger that understands the symbol name mapping that occurs with RTSC modules and interfaces, you will need to use the linker symbol names, described above, to set breakpoints and view static data structures. Future releases of XDCtools will include Eclipse CDT support to make this easier.

The definitions of the key RTSC module data structures, shown below in pseudo-C, can help when navigating in-memory data structures.

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
struct pkg_ModA_Module__State {        /* typeof pkg_ModA_Module__state__V */
    fieldDecl;                         /* field of pkg.ModA.Module__State */
};
 
struct pkg_ModA_Object {
    pkg_ModA_Fxns__ *__fxns;           /* inheriting modules only */
    xdc_runtime_Types_Label __label;   /* 32-bit coded name, unless common$.namedInstance == false */
    fieldDecl;                         /* field of pkg.ModA.Instance__State */
};
 
struct pkg_ModA_Handle__;                   /* intentionally incomplete struct definition */
typedef pkg_ModA_Handle__ *pkg_ModA_Handle; /* convertible to/from pkg_ModA_Object* */
 
struct pkg_ModA_Params {                    /* typeof pkg_ModA_PARAMS */
    size_t      __size;                     /* sizeof this struct */
    const void *__self;                     /* pointer to self; used to check params are init'd */
    void       *__fxns;
    xdc_runtime_IInstance_Params *instance; /* common per-instance params */
    fieldDecl;                              /* per-instance param of pkg.ModA */
};
 
struct xdc_runtime_IInstance_Params {  /* common parameters for all instances */
    size_t     __size;                 /* sizeof this struct */
    xdc_String name;                   /* name of this instance */
};
 
struct pkg_ModA_Fxns__ {               /* typeof pkg_ModA_Module__FXNS__C */
    xdc_runtime_Types_Base *__base;    /* inherited interface’s id */
    (*__create)();                     /* system create function (or NULL) */
    (*__delete)();                     /* system delete function (or NULL) */
    (*fxnDecl)();                      /* inherited function of pkg.ModA */
};
 
struct xdc_runtime_Types_Base {
    xdc_runtime_Types_Base *base;      /* inherited interface’s id (or NULL) */
};

In addition to the structures shown above, the configuration tool generates a data structure for each module to hold runtime state that is not explicitly managed by the module; e.g., a module's diagnostics mask. These data structures vary depending on whether the module supports instance creation, logging is enabled for the module, and whether the creation policy is allows runtime creation.

non-instance module with logging enabled
 
 
 
 
struct pkg_ModA_Module__ {        /* typeof pkg_ModA_Module__root__V */
    diagsMask;                         /* module's diagnostics mask */
};
struct pkg_ModA_Module__ pkg_ModA_Module__root__V = { ...};

instance module with logging enabled
 
 
 
 
 
 
struct pkg_ModA_Module__ {        /* typeof pkg_ModA_Module__root__V */
    xdc_runtime_Types_Link link;       /* link to module's runtime created instances */
       :
    diagsMask;                         /* module's diagnostics mask */
};
struct pkg_ModA_Module__ pkg_ModA_Module__root__V = { ...};

These structures are generated as part of the configuration process and may change from release to release of XDCtools. So, to maintain compatibility with future releases, code should never directly reference fields within these structures nor should any code even assume the existence of this structure (this structure may not exist for modules that do not require runtime state).

By design, all direct accesses to the "extra" module state fields in the Module__root__V structure are limited to the configuration generated .c file itself; this enables changes in representation without impacting (binary) compatibility. Access to (say) the link field is via special getters/setters declared in the module's .h file and the implementation of these special functions is generated as part of the generated .c file.

spacer [printable version]  [offline version]offline version generated on 13-May-2015 10:05 UTCspacer
Copyright © 2008 The Eclipse Foundation. All Rights Reserved
Retrieved from "rtsc.eclipse.org/docs-tip/Integrating_RTSC_Modules"
Views
  • Article
  • Discussion
  • View source
  • History<">
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.