ROPI
The term ROPI (Read-Only Position-Independent) is a synonym for the Renesas term “PIC/PID”, and refers to RX ABI compliant position independence, where the PID base register is the static base pointer for accessing constant data as described by the RX ABI. In IAR Embedded Workbench for RX, this base register is the constant data and code base CB. This means that, even though the linker places the code and data at fixed locations, the application can still be executed correctly when the linked image is placed at a different address than where it was linked.
In a system with ROPI applications, there might be a small amount of non-ROPI code that handles startup, dynamic loading of applications, shared firmware functions, etc. Such code must be compiled and linked separately from the ROPI applications.
Note
Only functions and read-only data are affected by ROPI—variables in RAM are only position-independent when RWPI is enabled, see RWPI.
Drawbacks and limitations
There are some drawbacks and limitations to bear in mind when using ROPI:
The code generated for function pointer calls and accesses to read-only data will be somewhat larger
Function pointers passed as arguments have some limitations, see Function pointers as arguments
Data initialization at startup might be somewhat slower, and the initialization data might be somewhat larger
Interrupt handlers that use the
#pragma vectordirective are not handled automaticallyThe object attribute
__ramfuncis not supportedData pointer constants cannot be initialized with the address of constant data, or a string literal. Writable pointers will be initialized automatically.
Some C library functions will work differently, mainly because they use RAM instead of ROM for storage (for example the functions for locale support). The C99 functions
erfandgammaare not supported.
Note
In some cases, there is an alternative to ROPI that avoids these limitations and drawbacks: If there is only a small number of possible placements for the application, you can compile the application without ROPI and link it multiple times, once for each possible placement. Then you can choose the correct image at load time. When applicable, this approach makes better use of the available hardware resources than using ROPI.
Creating a static startup module
To execute a ROPI application there must also be a static part, a startup program, because the reset vector must always be static. This program should contain:
This basic sample program, using the normal C startup code, can be used as a starting point:
section .text:CODE:NOROOT(2)
public _main
extern __DebugBreak
require __DebugBreak
code
_main:
mov.l #my_address,R13
jmp R13 ; start the ropi application
endThis will include __DebugBreak and initialization code for any library parts that are located in the static part of the application. Add a require clause to the program for every additional C library function that should be static, for example _printf.
You can compile and link this startup program like a normal application. If you include runtime attributes, you can control which library that is used by the linker. If parts of the C runtime library are included, remember to include the data initialization routines.
If a static function should be visible to the ROPI application (such as the __DebugBreak function), you must export it from the linked application using the tool isymexport. Do not export symbols that exist in the ROPI module as well, such as __iar_program_start or _main. Which symbols that are made visible is determined by show clauses in the edit file (show.icf), for example show _printf. The syntax for the export is:
isymexport --edit show.icf static.out static.tab
If you need to call a static function indirectly from a ROPI module, you must declare it extern __absolute for the pointer to be initialized correctly.
Creating the ROPI module
To compile an application with position-independent code and read-only data, use the compiler command line option --ropi. The source code cannot contain any initializations that violate the ROPI model, that is, it cannot contain any constant data pointers to constant data, as in this example:
const char * const msg = "error string"; // cannot be initialized
// in ROPI modeCaution
To specify ROPI in the IDE, choose Project>Options>General Options>Target> Code and read-only data.
When you link the application, all code and constant data must be placed in a common block, tagged movable. For example:
define movable block PIC with static base CB, alignment = 4
{ first block PIC16 with maximum size = 256k, fixed order { ro code object cstartup.o*, ro section .data16* },
ro };This example places constant data with the memory attribute __data16 in the lowest 32 Kbytes of data memory, to allow efficient access to these objects. At the very beginning of the block, the C initialization code is placed. This is only to simplify calling the ROPI module, and is not required.
Interrupt handling
The interrupt vector table and the INTB register could be the responsibility of either module, but if the interrupt table is placed in the ROPI module, it must be placed in RAM and initialized at runtime.
Function pointers as arguments
When a ROPI module passes a function pointer as an argument, only the offset of the function pointer address is used, and the function pointer is not resolved until it is dereferenced.
To get the ROPI base address, use the intrinsic function __c_base, like this:
#define ROPI_TO_ABS_FPTR(x) ((f_type)((int)(x) + __c_base()))
This takes care of the address arithmetic and creates an absolute function pointer that can be used in a function call.
Building and debugging the application
Include the symbol table generated by isymexport when you link the ROPI module, so that it can call functions in the static module. If the static module uses its own RAM objects, the two applications must be linked with disjoint RAM spaces.
To run the application in C-SPY, use the static program as the main project, and add a C-SPY macro file to this project, that loads the ROPI application image:
execUserSetup()
{
__loadImage("ropi_module_path", offset, 0);
}The offset parameter to __loadImage is the difference between the linked base address of the ROPI module and its final address (my_address in the static assembler program, see Creating a static startup module). For more information about the __loadImage macro, see __loadImage.
Caution
You can also load the ROPI application image using the options on the Project>Options>Debugger>Images page in the IDE.