Realtime Memory Management


This section deals with two issues:

Memory Resources

Programs generated with Rhapsody use memory, both RAM as well as ROM. The generated code itself uses memory and the Framework that is automatically linked to the generated code uses memory.

The Real-Time eXecution FrameWork is set up with low memory resources in mind. It uses as less RAM and ROM as possible to allow the usage on very small systems. ROM usage is caused by the framework, the OO RTX libraries take some space (less then 4k if the Container classes are not used) and of course, the generated source code. This is not easy to influence, there are some parameters that allow tweaking but there is not much to gain.

The most valuable resource is usually RAM. And this is a point where we can win a lot of space.

In the OO RTX based Frameworks, NO_MALLOC can be set via the property CppCompileSwitches and by using the properties C_CG::Class::AllocateMemory and C_CG::Class::FreeMemory for example via a profile, see to it that only the static buffer pool is used.
If you want your objects allocated on the heap via malloc(), you must:

Objects

You can have your objects either static allocated or dynamic allocated: the way you allocate your object influences the amount of ROM respectively RAM required:
In most systems, objects can be created statically rather than dynamically. In this case there is no need to generate the Create and Destroy operations. How do we do that?

Dynamic Memory:
Property

As you can see, Rhapsody generates code to call malloc() on creation of an object, unless you uncheck the EnableDynamicAllocation property.

In many cases, objects are created but rarely destroyed. In this case, there is no need to generate a Destructor. This can be done by unticking the property C_CG:Class:GenerateDestructor. When the object is destroyed, the memory is returned to the heap using free(), depending on the EnableDynamicAllocation property.



Collection Classes

Rhapsody also supports so-called collection classes by IBM Rational. For links between objects, Rhapsody generates code for:
RiCString is also considered a collection class. All collection classes use dynamic memory allocation. The functions RiCGetMemory() and RiCReturnMemory() operate on the heap which size you can influence the footprint of your application.


There are some constants in the source files by IBM Rational in <your Rhapsody>\Share\WST_RXF_V5\<your product>\Sources  which you can change:


NOTE If you modify a source file, you must recreate the OO RTX libraries, see Create RXF Library.


Footprint

There are several details you must inspect when you want to decrease the size of your project. Usually, during development, one does not really wants to save every Kbyte or byte, but when the code reaches its final state you are ready for finetuning the configuration of your project.

Here are some things you may try:
  1. Please consult the documentation of your compiler manufactorer, in particular information on initialized data and data- and code sections which build up your memory map. Inspect the map file created by the linker/locator and try to understand how the memory map is build up, and why. Modifying the startup source and linker options or linker script file may help you gain room, pleasse refer to your toolchain documentation.

  2. The OO RTX contains a number of buffers like event queue, which can be dimensioned via properties. Using the Debug buildset, you can inspect the variable Highwater in RiCHighwater.h with a debugger to see how much space within these buffers are actually used. In the Debug buildset, the compiler constant WST_HIGHWATERMARKS is defined via the property CPPCompileDebug. You can downsize the OO RTX dimensions after inspecting this constant.

    There are some properties which define various sizes:
Property
Default value
WSTSmallBufferSize
16
WSTMediumBufferSize
32
WSTLargeBufferSize
96
WSTInitialSmallBuffers
12
WSTInitialMediumBuffers
6
WSTInitialLargeBuffers
1

The variable highwater is allocated in RiCHighwater.[ch] and is actually a structure with some members:
  1. is the memory model you are using the best suitable?  Depending on the architecture of your target you can optimize the code by arranging data and code. Please consult the documentation of your compiler manufactorer with respect to any memory models. The data type you use can influence the addressing modes involved; for example the M16C uses 16 bits pointers to non-constant data, while it uses 20 bits pointers for constant data. So even without using keywords like 'near' and 'far' which make your code less portable, you can influence the size of your project just by using specific data types.
    Although the memory model is something which is defined in your build environment, you may need to specify details in the configuration files you add to your model including RxfConstants.h
  2. are the stack and heap not too large? Again, you must consult the documentation of your toolchain and environment to see if you and how you can change their size and check if your project still functions correctly.
  3. do you need UML Ports? You can disable Ports in the OO RTX which saves code overhead.



Interrupt Handling

Using interrupts is the base of all embedded systems.  It is however an area where often errors are introduced. The fact that something can be interrupted and something else is done using the same data has caused numerous hours of work for thousands of programmers around the world.

Using malloc in an interrupt is a bad idea. Malloc uses global variables to manage the free memory and calling malloc from within an interrupt  when at the same time malloc was called by the program that was interrupted will cause your memory pointers to be messed up.

Sending events from interrupts is a good idea, but unfortunately not without problems. Sending a "normal" event will cause a malloc when the event is created. This is not always the case in the RXF, as it also supports working with statically allocated events.

Static Event Allocation and CGEN_ISR

The Realtine eXecution Framework offers a way to send events from your interrupt service routines (hardware driver layer) to the UML level without using non deterministic dynamically allocated events. The macro CGEN_ISR almost behaves like CGEN, but expects to work with statically allocated events that are marked to stay allocated after they got consumed.

The syntax of CGEN_ISR is not equal but very similar to that of the CGEN macro. The event parameter must be a pointer to a static event.


RiCGEN_PORT_ISR

The macro RiCGEN_PORT_ISR( Port, Event ) uses a dynamically allocated event and thus should not be used.




Example Interrupt to UML Event Implementation


Define a constant value named MAX_ISR_EVENTS representing the maxinum number of events generated by an interrupt service routine, your system should be able to handle at once (having them in the queue while finishing an action). If this should be just one, you can write an easier implementation of the following by leaving the array and iterations away.

Create an attribute "events" in an object "InterruptHandler" with following type:

struct evDetect %s[MAX_ISR_EVENTS]

The following code must be in the init function of the object. It will initialize all statically allocated events and mark them to stay allocated after they got consumed:

int i;

for (  i = 0; i < MAX_ISR_EVENTS; i++ )
{
  evDetect_Init( &InterruptHandler.events[ i ] );
  RiCEvent_setDeleteAfterConsume( (RiCEvent *) &InterruptHandler.events[ i ], FALSE );
}

This will create a static array with events. The can be used using the following macro:

InterruptHandler.index = (InterruptHandler.index + 1 ) % MAX_ISR_EVENTS;
CGEN_ISR( InterruptHandler.itsSensor, &(InterruptHandler.events[ InterruptHandler.index ] ) );

The event will be used, but it will not be destroyed. The array must be large enough to hold the maximum number of events created by interrupt handlers that can be in the system at any specific time.


Critical Regions

When sending an event from a specific interrupt using the OO RTX, this IRQ must be disabled in the WSTRTOS_enterCriticalRegion() function and enabled again in the WSTRTOS_exitCriticalRegion(). These functions are implemented in the RTOS interface of the OO RTX.

Please check if the interrupts you use to generate events in your model, and disable/enable these in the critical region handlers. The RXF libraries must be recompiled after changing the WSTRTOS_enterCriticalRegion() and / or WSTRTOS_exitCriticalRegion() functions. You can rebuild your libraries using the CreateRXFLibrary project which comes with your product.




Copyright (c) Willert Software Tools GmbH. All rights reserved.