What is the Memory Module?
The Memory module enforces static-only allocation for the entire framework. No malloc(), no free() — all memory is allocated at compile-time or initialization-time. This guarantees deterministic behavior and eliminates memory fragmentation.
Key idea: If you can't statically allocate it, you don't need it.
Why No Dynamic Allocation?
Safety-Critical Systems Need Guarantees
Dynamic allocation has unpredictable latency and may fail at runtime. Static allocation has O(1) time, fails at compile-time, no fragmentation.
Real-World Impact
Railway signaling system example:
if (cmd == NULL) {
}
#define RTE_SAFESTATE(level, reason)
Explicitly enters the given safe-state level with a reason code, capturing the call site automaticall...
Quick Start
1. Include Header
#include "rte/memory/rte_memory.h"
2. Allocate at Compile-Time or Initialization
static uint8_t buffer[256];
typedef struct {
rte_timer_t heartbeat_timer;
uint8_t work_buffer[1024];
} app_t;
int main(void) {
app_t app = {0};
}
rte_channel_storage_t rte_channel_t
Opaque handle to a vital channel instance.
3. Calculate Required Sizes Upfront
#define MAX_CHANNELS 16
#define MAX_TIMERS 32
#define MAX_TASKS 64
#define BUFFER_SIZE (MAX_CHANNELS * 256)
typedef struct {
rte_timer_t timers[MAX_TIMERS];
task_t task_queue[MAX_TASKS];
uint8_t buffer[BUFFER_SIZE];
} system_t;
Allocation Patterns
Pattern 1: Static Global Buffers
For singleton objects (one per program):
static uint8_t log_buffer[4096];
static uint32_t log_write_pos = 0;
void log_write(const char *msg, size_t len) {
if (log_write_pos + len > sizeof(log_buffer)) {
}
memcpy(&log_buffer[log_write_pos], msg, len);
log_write_pos += len;
}
Pattern 2: Application State Structure
For complex applications with multiple subsystems:
typedef struct {
rte_timer_t timers[MAX_TIMERS];
task_t tasks[MAX_TASKS];
uint8_t input_buffer[256];
uint8_t output_buffer[256];
statistics_t stats;
} application_t;
static application_t g_app = {0};
int main(void) {
init_application(&g_app);
while (1) {
execute_application(&g_app);
}
}
struct rte_ipc_impl_s * rte_ipc_handle_t
Opaque handle to a created IPC channel, returned by rte_ipc_create().
Pattern 3: Thread-Local State (Multi-Threaded)
__thread uint8_t thread_buffer[256];
void thread_worker(void *arg) {
process_data(thread_buffer, sizeof(thread_buffer));
}
Resource Sizing
Calculate Max Requirements
#define MAX_CHANNELS 16
#define MAX_TIMERS 32
#define MAX_PENDING_TASKS 64
#define MAX_LOG_ENTRIES 1000
#define MAX_NVM_RECORDS 100
Verify at Compile-Time
#define APP_STATIC_MEMORY_SIZE (65536)
_Static_assert(
sizeof(application_t) <= APP_STATIC_MEMORY_SIZE,
"Application state exceeds maximum memory"
);
Practical Examples
Example 1: Train Control System
typedef struct {
rte_timer_t main_loop_timer;
rte_timer_t watchdog_timer;
struct {
uint32_t speed_kmh;
int32_t acceleration;
bool brake_engaged;
uint8_t signal_aspect;
} state;
struct {
uint8_t input[256];
uint8_t output[256];
uint8_t temp[512];
} buffers;
struct {
uint32_t cycles_executed;
uint32_t errors_count;
uint64_t total_uptime_ms;
} stats;
} standby_unit_t;
static standby_unit_t g_standby = {0};
Example 2: Ring Buffer Without malloc
typedef struct {
uint8_t buffer[256];
uint16_t write_pos;
uint16_t read_pos;
uint16_t count;
} ring_buffer_t;
void ring_buffer_init(ring_buffer_t *rb) {
rb->write_pos = 0;
rb->read_pos = 0;
rb->count = 0;
}
rte_status_t ring_buffer_write(ring_buffer_t *rb, uint8_t
byte) {
if (rb->count >= 256) {
}
rb->buffer[rb->write_pos] = byte;
rb->write_pos = (rb->write_pos + 1) % 256;
rb->count++;
}
rte_status_t
Common result/status codes.
@ RTE_STATUS_RESOURCE_EXHAUSTED
Common Patterns
Pattern 1: Pool-Based Allocation
Pre-allocate a pool, hand out slots:
typedef struct {
uint8_t data[32];
bool in_use;
} message_t;
static message_t message_pool[16];
for (int i = 0; i < 16; i++) {
if (!message_pool[i].in_use) {
message_pool[i].in_use = true;
*msg_out = &message_pool[i];
}
}
}
void free_message(message_t *msg) {
msg->in_use = false;
}
Pattern 2: Fixed-Size Queues
#define QUEUE_SIZE 32
typedef struct {
item_t items[QUEUE_SIZE];
uint16_t head;
uint16_t tail;
uint16_t count;
} queue_t;
if (q->count >= QUEUE_SIZE) {
}
q->items[q->tail] = *item;
q->tail = (q->tail + 1) % QUEUE_SIZE;
q->count++;
}
Best Practices
- Allocate Everything at Initialization
- No malloc/free in real-time paths
- All memory allocation happens at startup
- Deterministic memory layout
- Know Your Maximum Sizes
- Max channels: fixed at design time
- Max concurrent operations: fixed at design time
- Max queue depths: fixed at design time
- Use Compile-Time Assertions
- _Static_assert catches oversized allocations
- Fail fast at build-time, not runtime
- Prevents deployment of broken systems
- Plan for Worst Case
- Don't assume light load
- Allocate for peak burst capacity
- Better to over-allocate than under-allocate
- Document Memory Usage
- Comment with actual sizes
- List worst-case memory need
- Track memory budget through integration
Out-of-Memory
When allocation fails (pool exhausted):
return rc;
}
#define RTE_SAFESTATE_REASON_UNSPECIFIED
@ RTE_SAFESTATE_LEVEL_SAFE
Never ignore allocation failure:
message_t *msg = get_from_pool();
msg->data = 42;
See Also