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 RXF libraries take some space (less 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.

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. The RiCHeap class by IBM Rational is used by the RTOS extension of the RXF, and is also taken from IBM Rational.

All collection classes and RiCHeap use dynamic memory allocation. The functions RiCGetMemory(), RiCReturnMemory() and ricRealloc() 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 RXF 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. Is your RTOS configured in an optimal way and the use of its resources by your Rhapsody model?
    The size of various stacks, the maximum number of events etc. have all great influence on your footprint.
  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.




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.


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.



Task Switching


Some RTOSses come with an implementation of a malloc() and free() equivalent which is guarded against task switches. The file RiCOSDimMemoryAllocation.c implements the functions RiCOSMemoryAllocation_malloc(), RiCOSMemoryAllocation_calloc(), RiCOSMemoryAllocation_reaalloc() and RiCOSMemoryAllocation_free() which are used instead of malloc(), calloc(), realloc() and free() as a result of the profile <your product>.sbs

These functions use the appropiate native RTOS function when available, and adds protection agains task switches when needed.
 


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