Realtime
Memory Management
This section deals with two issues:
- memory resources or dynamic versus static memory and how to decrease your footprint
- interrupt handling;
you should not use malloc() or free() when interrupted or during a task switch, and you should
allocate static memory for any event which is sent from within an
Interrupt Service Routine.
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:
- enable the property
C_CG:Class:EnableDynamicAllocation.
- set the properties C_CG::Class::AllocateMemory and
C_CG::Class::FreeMemory to malloc() and free().
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?
- Untick the property
C_CG:Class:EnableDynamicAllocation
- If you create instances of
ObjectTypes in the main, then you will also need to tick
property
C_CG:Configuration:InitializeEmbeddableObjectsByValue
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:
- unordered links using the RiCCollection class, implemented
in RiCCollection.c
- ordered links using the RiCList class, implemented in
RiCList.c
- qualified links (one-to-many where a qualifier is
specified) using the RiCMap class, implemented in RiCMap.c
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:
- in RiCCollection.h the constant RiC_COLLECTION_INCREMENT_SIZE
defines the number of bytes extra requested when adding a collection
item, so a
stepwise allocation is used in larger chunks to prevent too many calls
to ricRealloc().
- in RiCString.h the constant RiC_STRING_INCREMENT_SIZE
defines the number of bytes extra requested when allocating, so a
stepwise allocation is used in larger chunks to prevent too many calls
to ricRealloc().
- in RiCSHeap.h the constant HEAP_INCREMENT_SIZE
defines the number of bytes extra requested when allocating, so a
stepwise allocation is used in larger chunks to prevent too many calls
to ricRealloc().
| 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:
- 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.
- 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.
- 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
- 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.