According to some coding guidelines such as MISRA, it is “common wisdom” that embedded firmware should not use dynamic memory, e.g. malloc/free, as it introduces non-deterministic behaviors. Many firmware engineers have taken this to heart.
So is it surprising that REXIS (our forthcoming RTOS) uses dynamic memory allocation? Have we gone mad? Surely not ;-). REXIS uses dynamic memory for three main reasons:
- It simplifies the user experience. As a user, you do not have to allocate task control block storage or task stacks individually, or use confusing configuration tables etc.
- Our target device is the “just right” class of MCUs, not the severely memory-constrained 8-bit MCU with 2K to 10Kbytes of SRAM, or the smartphone class of Application processors (e.g. Cortex-A series), but a larger MCU with 32K to 100Kbytes+ of SRAM
- IoT and Internet stacks. If you are going to use TCP/IP or other stacks, then it’s almost certain that those libraries will use dynamic memory to support their features and to provide optimal performance. So, using dynamic memory is practically inevitable in any case.
As an example of how it simplifies the user experience, the only memory-related REXIS API that the user must call is this:
/** \brief Initialize the system. Must be called with a memory block for REXIS memory allocation. e.g.
unsigned char memory_pool[1024*8]; // 8 KBytes
\param start address of the beginning of the memory pool.
\param size number of bytes in the memory pool.
\return 0 if success, negative value otherwise.
int REXIS_SysInit(unsigned char *start, unsigned size);
Before calling other REXIS APIs, you supply a memory block to REXIS_SysInit. The memory block is typically declared as a static or global byte array. All REXIS internal memory usage, from allocating memory for the task control structures, to IPC mailboxes etc., is allocated from this block of memory.
If you use the REXIS memory allocation routines for your firmware’s dynamic memory usage (e.g. using REXIS_MemoryAlloc vs. the C library malloc), then those blocks are chained to your task, and you can free them all at once by using a single REXIS API. Also, if your task exits, they will be freed automatically on your behalf.
Finally, one of the difficult parts of using an RTOS is to determine the size of the stack for each task: if the task stack is too small, the system will fail, often mysteriously, and if the task stack is too large, then you are wasting memory.
To address this, our plan is to release a post-processing “stack optimizer” utility with REXIS (which would be transparent to the user, invoked by a checkbox in the JumpStart C++ for Cortex IDE). If you call REXIS_TaskCreate with a stack size of zero, it will modify the firmware image to use the correct size for the task stack. With our comprehensive knowledge of compiler tools, we can create an excellent user experience through integration and paying attention to what could make life simpler for embedded developers.