Using xdc.runtime Memory

From RTSC-Pedia

Jump to: navigation, search
revision tip
—— LANDSCAPE orientation
[printable version]  [offline version]offline version generated on 24-Feb-2019 01:02 UTC

Using xdc.runtime Memory

Introduction to memory management

Contents

Introduction

The xdc.runtime package contains several modules that work together to provide modular and extensible memory management support. This support is centered around the Memory module that enables the application to allocate and free memory. All memory allocations are eventually "handled" by a module that implements the IHeap interface. These modules are responsible for managing a region of memory in a manner appropriate for the application.

Two such modules are provided in the xdc.runtime package:

  1. HeapMin - a module that simply maintains a "high water" mark for each managed memory block and never frees memory, and
  2. HeapStd - a module that uses the ANSI C standard library functions malloc() and free().

While it is possible to create new implementations of the IHeap interface that provide more sophisticated or even application-specific memory management policies, most users will rely on IHeap implementations from their preferred RTOS (such as TI's BIOS 6.0). For information about creating and deploying a custom heap see Extending xdc.runtime Memory.

Overview

Memory management within embedded systems often requires

  • explicit management of distinct memory "classes";
  • control over alignment of critical buffers;
  • deterministic memory allocation, support for variable length allocation; and in some cases
  • the ability to eliminate _all_ runtime allocation and freeing of memory

Explicit use of different "classes" of memory, such as high-speed on-chip memory versus bulk external DRAM, is critical for embedded real-time applications. For example, careful use and efficient management of precious high-speed on-chip memory often determines whether an application meets its real-time deadlines or not. Even with different classes of memory, to get full entitlement from modern CPU cores, it is important that critical buffers be allocated on appropriate address boundaries - either for improved cache performance or to take advantage of specialized "zero overhead" looping constructs.

Different applications have widely differing "policy" requirements. For example, traditional embedded applications do not need, nor can they afford, extra memory management code to allow the system to automatically free memory from a terminated thread. On the other hand, some embedded systems allow for the execution of untrusted (but not malicious) code. In this case, the robustness requirements of the system all but require a memory manager that can track the ownership of every allocated memory block. Similarly, because real-time applications provide "guaranteed" response times for certain events and variable length memory allocators often have execution times that vary as a function of the number and size of previous allocations, real-time applications generally take one of two approaches to memory management:

  1. use "fixed size" block allocation algorithms which have deterministic execution times but have poor memory utilization, or
  2. use a distinguished "low priority" thread to perform all functions that require memory allocation so the "real-time" threads never stall waiting to allocate or free memory.

From these examples, it's clear that embedded systems have unique memory management requirements and that no one memory allocation algorithm is appropriate for all embedded applications.

The xdc.runtime package provides core memory management functions suitable for virtually any embedded application. In particular, it includes a single module, Memory, that supports

  • multiple heaps and allows each heap to manage its heap in a memory-specific or application-specific manner;
  • aligned allocations on any boundary supported by the underlying hardware;
  • static placement of buffers required by other modules that create objects at configuration-time; and
  • zero-space-overhead allocations that do not require "allocation header" information to be retained on each allocation.

Architecture

The Memory module's methods take a first parameter that identifies the heap from which to allocate memory. This parameter may be NULL and, in this case, a "default heap" will be used. The "default heap" is specified by the developer by setting the Memory module's defaultHeapInstance configuration parameter. If this parameter is not explicitly set by the developer, an instance of xdc.runtime.HeapStd (of size Memory.defaultHeapSize) will be assigned to this parameter.

Memory Architecture

The Memory module itself does very little work: it simply delegates "real" memory management to the specified heap instance object. This makes clients of Memory portable, but where do the heap instances get created? If they are created by the application, the code that creates these instances is not portable to other applications; because it contains direct references to specific heap managers. Of course, applications can factor this "non portable" code into a single place and pass IHeap instance handles to other parts of the application that need memory allocation services. But, an even better solution is to entirely eliminate the runtime creation of IHeap instances in favor of static creation. In this case, not only do we eliminate unnecessary code, but we improve the portability of the code by eliminating direct references to application-specific heap managers.

Memory simply provides the "glue" between portable clients and underlying modules, heap managers, that actually manage the memory. But the xdc.runtime package only provides two heap managers, HeapMin and HeapStd, and these modules have limited usefulness; HeapMin does not reuse freed memory and HeapStd has no advantage over malloc() and free(). So where do you get "real" heap managers that realize the benefits promised by the xdc.runtime package's memory architecture? Heap managers are available from RTOS providers (such as TI's DSP/BIOS 6.x) and Extending xdc.runtime Memory describes how to create new heap managers.

Configuration

Users of libraries that reference the xdc.runtime.Memory module can configure their application to specify:

  • on a per-system basis, the default heap instance object to use when "managing" memory;
  • on a per-module basis, what IHeap service providers should be used to "manage" the memory allocated by the module for its instances; and
  • on a per-module basis, whether instance objects are only statically created, only created but never freed, or are both created and deleted at runtime.

The ability to configure a unique IHeap memory manager for each module that supports instances together with the use of NULL to represent the distinguished heap instance xdc.runtime.memory.defaultHeap, enables the integrator to separately manage memory from all parts of the code base independent of who produced the code.

These configuration options make it possible for the system integrator to strictly control which parts of the system get precious high-speed memory without making any changes to or even re-compiling the code containing embedded Memory_alloc statements. As a result, it possible to leverage these options and (re)configure even pre-built binary libraries that utilize xdc.runtime.Memory methods.

Examples

This section provides a number of code "fragments" illustrating how to leverage the xdc.runtime package's Memory module.

Using design-time heaps

Using design-time heaps.  To assist in creating portable modules, every module that supports instances has a heap instance that can be set at configuration time and referenced "anonymously" at runtime; see the instanceHeap field of IModule.common$. Modules access their heap instance via Mod_Object_heap(), where Mod is the name of the calling module. C code that is not part of a module can also use these heap instances. In addition, the Memory module accepts NULL as a heap handle; in this case, the Memory module will use the design-time heap specified by the Memory.defaultHeapInstance configuration parameter.

The following code fragment illustrates how these design-time heaps are used.

app.c (fragment)
 
 
 
 
 
 
 
 
 
#include <xdc/runtime/Memory.h>
#include <xdc/runtime/LoggerBuf.h>
 
    :
/* allocate from the LoggerBuf module's heap */
Ptr buf0 = Memory_alloc(LoggerBuf_Object_heap(), ...);
 
/* allocate from the default heap */
Ptr buf1 = Memory_alloc(NULL, ...);

The configuration code necessary to create instances for the default heap and the LoggerBuf module's heap now contains references to the specific IHeap implementations.

app.cfg (fragment)
 
 
 
 
 
 
 
 
 
 
var Memory = xdc.useModule("xdc.runtime.Memory");
var LoggerBuf = xdc.useModule("xdc.runtime.LoggerBuf");
var Heap1  = xdc.useModule("...");
var Heap2  = xdc.useModule("...");
 
/* define "default" heap for NULL instance allocations */
Memory.defaultHeapInstance = Heap1.create();
 
/* define a specific heap for the LoggerBuf module */
LoggerBuf.common$.heapInstance = Heap2.create();

If a module's heapInstance is not explicitly set, it defaults to NULL. Since calling the Memory methods with a NULL heap instance is equivalent to using the "default heap" and each module's default heap is NULL, setting Memory.defaultHeapInstance effectively sets the default heap for all modules in the system.

Static buffer allocation

Static buffer allocation.  In addition to the runtime memory allocation services offered, the Memory module also supports placement and alignment of static buffers via the meta-domain function Memory.staticPlace(). This method is typically used when implementing a RTSC module that requires the creation and placement of a static buffer.

For examples of its use see acme/filters/Fir.xs in RTSC Module Primer Lesson 9 and SysBuf.xs in Extending xdc.runtime System Example 1.

Runtime heap creation

Runtime heap creation.  There are times when it's necessary to create a heap at runtime. Suppose, for example, that you need to create a single ROM image that must support multiple platforms with differing memory maps. In this case, the size of the memory managed by the heap cannot be known until the application runs.

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <xdc/runtime/HeapMin.h>
#include <xdc/runtime/Memory.h>
    :
IHeapMin_Handle heap; /* dynamically sized heap used by app */
 
int main(int argc, char *argv[])
{
    HeapMin_Handle heapMin;
    HeapMin_Params params;
 
    /* create a heap from a specific heap manager */
    HeapMin_Params_init(&params);
    params.size = ...;
    heapMin = HeapMin_create(&params, NULL);
    /* convert HeapMin handle to abstract IHeap handle */
    heap = HeapMin_Handle_upCast(heapMin);
        :
}
    /* allocate from the new heap */
    Ptr buf1 = Memory_alloc(heap, ...);

The call to HeapMin_create() requires allocation of memory from a heap. In this case it will allocate memory from a heap associated with the HeapMin module by the application's configuration script. You can either create an initial HeapMin instance at configuration time large enough to bootstrap the application or you can use HeapMin to "construct" a heap instance from a statically allocated structure. Recall that all modules that support instances also support the initialization of a static structure as a way to create runtime instances (see Lesson 4 of the RTSC Module Primer).

The example above can be re-written to avoid all heap allocations prior to the first call to Memory_alloc() with the runtime created heap instance.

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
int main(int argc, char *argv[])
{
    HeapMin_Handle heapMin;
    HeapMin_Params params;
    HeapMin_Struct heapStruct;
 
    /* create a heap from a specific heap manager */
    HeapMin_Params_init(&params);
    params.size = ...;
    HeapMin_construct(&heapStruct, &params, NULL);
    /* convert HeapStruct to a HeapMin_Handle handle */
    heapMin = HeapMin_handle(&heapStruct);
    /* convert HeapMin handle to abstract IHeap handle */
    heap = HeapMin_Handle_upCast(heapMin);
        :
}

Built-in Heap Managers

The xdc.runtime package includes two built-in heap managers:

  1. HeapMin - a module that simply maintains a "high water" mark for each managed memory block and never frees memory, and
  2. HeapStd - a module that uses the ANSI C standard library functions malloc() and free().

The following sections briefly describe these heap managers and show examples of their use. Extending xdc.runtime Memory describes how to create additional heap managers.

The HeapMin Module

The HeapMin module provides a simple "growth-only" heap, meaning that it does not support Memory_free(). It is intended as a minimal footprint heap implementation.

HeapMin services any size allocation request. Attempting to free memory back to a HeapMin instance results in an error.

Examples

Examples.  The following configuration example statically creates a HeapMin instance named myHeap:

 
 
 
 
 
 
var HeapMin = xdc.useModule("xdc.runtime.HeapMin");
var params = new HeapMin.Params();
params.size = 1024;
 
/* Create heap as global variable so it can be used in C code */
Program.global.myHeap = HeapMin.create(params);

The following C code fragment dynamically creates a HeapMin instance named myHeap.

 
 
 
 
 
 
 
 
 
 
#include <xdc/runtime/HeapMin.h>
 
static char buf[1024];
HeapMin_Handle myHeap;
 
HeapMin_Params params;
HeapMin_Params_init(&params);
params.size = 1024;
params.buf = (Ptr)buf;
myHeap = HeapMin_create(&params, NULL);

The HeapStd Module

The HeapStd is a wrapper for the ANSI C Standard Library malloc() and free() functions. As a consequence, allocating from a HeapStd instance requires the "align" parameter passed to Memory_alloc() to be less than or equal to the value returned from Memory_getMaxDefaultTypeAlign.

Examples

Examples.  The following configuration example statically creates a HeapStd heap instance named myHeap.

 
 
 
 
var HeapStd = xdc.useModule("xdc.runtime.HeapStd");
 
/* Create heap as global variable so it can be used in C code */
Program.global.myHeap = HeapStd.create({size: 1024});

The following C code fragment dynamically creates a HeapStd instance named myHeap.

 
 
 
 
 
 
 
 
#include <xdc/runtime/HeapMin.h>
 
HeapStd_Params params;
HeapStd_Handle myHeap;
 
HeapStd_Params_init(&params);
params.size = 1024;
myHeap = HeapStd_create(&params, NULL);

Performance Considerations

This section provides an overview of the performance overhead you can expect when using the xdc.runtime memory facilities. Generally speaking, the configuration capabilities allow developers to minimize the code space footprint by eliminating unnecessary functionality as well as tightly control which memory is used by each part of the system and how each memory region is managed.

Execution Time

The execution time of the xdc.runtime.Memory methods is almost entirely a function of the underlying IHeap module. For example, the xdc.runtime.HeapMin memory manager is extremely fast and deterministic. However, it does not support freeing of memory. The xdc.runtime.HeapStd module, on the other hand, has execution times dominated by the underlying ANSI C Standard Library's malloc() and free() functions.

Code Space

As with execution time, the code space overhead is largely determined by the underlying IHeap manager(s) used in the application. However, there are additional configuration options that can further reduce an application's code space requirements. Provided that an application does not need to delete any created instances or, even better, never needs to create instances at runtime, it is possible to reduce the code space by setting the xdc.runtime.Defaults.common$.memoryPolicy configuration parameter appropriately. There are two options that can significantly decrease your code space requirements:

  1. STATIC_POLICY - for "static-only" systems, where all instance objects managed by the module must be created at configuration time, and
  2. CREATE_POLICY - for "create-only" systems, where instance objects managed by the module may be created at runtime but are never deleted.
Static-only systems

Static-only systems.  To absolutely minimize the memory management code-space overhead in an application, eliminate all runtime create calls (by replacing them with configuration-time create calls) and set the memoryPolicy for all modules to be STATIC_POLICY. For example, the following lines in a configuration script set the default memoryPolicy for all modules to STATIC_POLICY.

 
 
 
var Types = xdc.useModule("xdc.runtime.Types");
var Defaults = xdc.useModule("xdc.runtime.Defaults");
Defaults.common$.memoryPolicy = Types.STATIC_POLICY;

Any attempt to call a create operation from a module that has its memoryPolicy set to STATIC_POLICY will result in a fatal runtime error. For example, if you use the configuration statements above and any part of your application calls LoggerBuf_create(), your application with terminate with the following error messages.

 
 
xdc.runtime.LoggerBuf: generic error: create policy error
xdc.runtime.Error.raise: terminating execution

Create-only systems

Create-only systems.  There are times when it is simply more convenient to create instance at runtime during an application initialization phase and never delete the instances; objects are deleted by rebooting the application. In this case, code for the Memory manager APIs must be present - otherwise it would not be possible to link the application. However, since memory never needs to be freed, the underlying heap managers can be very simple (and small).

The following lines in a configuration script set the default memoryPolicy for all modules to CREATE_POLICY.

 
 
 
var Types = xdc.useModule("xdc.runtime.Types");
var Defaults = xdc.useModule("xdc.runtime.Defaults");
Defaults.common$.memoryPolicy = Types.CREATE_POLICY;

Any attempt to call a delete operation from a module that has its memoryPolicy set to CREATE_POLICY will result in a fatal runtime error. For example, if you use the configuration statements above and any part of your application calls LoggerBuf_delete(), your application with terminate with the following error messages.

 
 
xdc.runtime.LoggerBuf: generic error: delete policy error
xdc.runtime.Error.raise: terminating execution
[printable version]  [offline version]offline version generated on 24-Feb-2019 01:02 UTC
Copyright © 2008 The Eclipse Foundation. All Rights Reserved
Personal tools
package reference