内存管理¶
1. 芯片内存配置基础¶
内存配置需先明确芯片硬件特性,核心关注 核结构、内存类型 及 存储支持,具体如下:
1.1 核结构与内存分配¶
核类型:
芯片分为 单核(如 52x 系列)和 双核(含低功耗小核 LCPU + 高性能大核 HCPU )。双核场景:需根据功能对性能/功耗的需求分配核资源(如低功耗任务放在 LCPU,高性能任务放在 HCPU )。
单核场景(52x 系列):硬件为单核,但软件架构虚拟“LCPU”(实际运行在 HCPU 上),确保跨平台代码兼容性。
内存类型:
包括 SRAM(高速、小容量)和 PSRAM(低速、大容量)。LCPU 通常仅挂载 SRAM;部分 SF32LB5x 系列 LCPU 可能挂载 NOR Flash(需根据功能需求选择芯片型号 )。
1.2 存储方案差异¶
SF32LB5x 系列支持两种存储方案,内存配置需适配:
1.2.1 NOR Flash 方案¶
特性:支持 XIP(直接执行),内存需求较低。
说明:代码和资源可直接从 NOR Flash 运行,无需额外搬运。
1.2.1 NAND/eMMC 方案¶
特性:不支持 XIP,需注意以下两点:
PSRAM 容量需 ≥8MB(用于加载代码和资源 );
代码(
code.bin)和图片资源需从 NAND/eMMC 搬运到 PSRAM 后才能执行/访问。
2. 内存分配与配置¶
内存主要用于 9 类场景,其存储位置(SRAM/PSRAM)通过链接脚本(solution\framework\__template__\config\link_scipt\.sct)配置,具体如下:
No |
内存用途 |
说明与配置方式 |
|---|---|---|
1 |
Code/RO |
必须常驻内存的代码及只读数据,由链接脚本指定存储位置 |
2 |
RW/ZI |
可读写数据(RW)和初始化为 0 的数据(ZI),默认随代码分配 |
3 |
帧缓冲区(framebuffer) |
用于 GUI 显示,配置路径 |
4 |
堆内存(Heap) |
包括系统堆(system heap)和专用堆(memheap),配置路径 |
5 |
场景动画切换 buffer |
支持应用切换动画时所需缓冲区,配置路径 |
6 |
线程栈(Thread stack) |
建议静态分配,频繁使用栈放SRAM(减少Cache换入换出提升性能) |
7 |
图形加速资源 |
图片旋转缓存从 SRAM 堆分配,绘图 canvas 从 PSRAM 堆分配 |
8 |
NAND/eMMC 的 code.bin |
因不支持 XIP,开机时从 NAND/eMMC 搬运到 PSRAM 执行 |
9 |
NAND/eMMC 的图片资源 |
需先搬运到 PSRAM 才能被 ePIC 硬件访问,需使用 PSRAM 堆 |
3. 堆内存(Heap)详解¶
Solution 在 app_mem.c 中提供多种堆内存管理接口,包括块内存、系统堆及专用堆,特性如下:
3.1 块内存(可选)¶
配置:通过menuconfig中
(Top) → App memory configuration → Using block memory开启。特性:
优点:分配速度快,无内存碎片;
缺点:需预先定义块大小和数量,复用性低,易浪费内存。
适用场景:映射
lv_mem(LVGL 琐碎内存分配 ),通过app_malloc申请(申请失败时自动转用系统堆 ),建议应用小内存优先使用。
3.2 系统堆(system heap)¶
分配接口:
rt_malloc。存储位置:可配置在 SRAM 或 PSRAM(通过
menuconfig中(Top) → Memory Config → Sysheap config in PSRAM指定 )。管理方式:
RT_USING_MEMHEAP_AS_HEAP未被定义: 小内存管理,此种模式下内存利用率高,但分配速度慢(适合 SRAM,因容量有限 );RT_USING_MEMHEAP_AS_HEAP被定义: 大内存管理,此种模式下分配速度快,但易产生碎片(适合 PSRAM,因容量较大 )。
配置系统堆Size:
SYS_HEAP_IN_PSRAM未被定义: 自动计算,系统堆Size = SRAM总Size - 已经分配的SRAM大小(静态变量,包括栈)SYS_HEAP_IN_PSRAM被定义: 通过 menuconfig中(Top) → Memory Config → Sys heap size in PSRAM配置
3.3 SRAM memheap / PSRAM memheap¶
分配接口:
app_cache_alloc(需指定CACHE_SRAM或CACHE_PSRAM);特性:采用大内存管理(分配快,易碎片 );
适用场景:
SRAM 堆用于高速度需求的内存(受容量限制 );
PSRAM 堆用于消息存储、图片缓存、canvas 等。
注:PSRAM 堆大小自动计算,无需手动配置。
配置:
SRAM HEAP: 通过 menuconfig中
(Top) → Memory Config → Cache in sram size(bytes)配置PSRAM HEAP: 在
app_mem.c中#define PSRAM_HEAP_SIZE (PSRAM_BASE + PSRAM_SIZE - PSRAMHEAP_BASE)自动计算。计算原理:PSRAM总的大小 - 已经分配的PSRAM大小(静态变量和动态的专用heap)
3.4 Freetype memheap¶
分配接口:
ft_malloc用途:专用于字体渲染(支持 Freetype 时需配置 )。
容量建议:
NAND/eMMC 方案:建议 ≥500KB;
NOR 方案:建议 ≥200KB。
配置:通过 menuconfig中
(Top) → Memory Config → FREETYPE_CACHE size(bytes)指定堆大小。
4. 为某个工程单独定义块内存大小¶
块内存默认定义在app_mem.c中,
#ifndef APP_BMEM_SELF_DEFINED
#if defined (USING_BLOCK_MEM) && defined (BSP_USING_PSRAM)
#if defined (SOLUTION_RES_USING_NAND)
#define _X 200
#else
#define _X 0
#endif
PSRAM_RET_SECT_BEG;
ALIGN_4
BMEM_REGISTER(16, (400 + _X));
BMEM_REGISTER(32, (200 + _X));
BMEM_REGISTER(48, (200 + _X));
BMEM_REGISTER(64, (200 + _X));
BMEM_REGISTER(100, (200 + _X));
PSRAM_RET_SECT_END
#endif
#endif
app_mem.c属于框架类c程序,如果客户定义了多个产品,一些产品对块内存的配置不一样,则可以通过如下的步骤为某个工程单独配置块内存:
通过menuconfig
(Top) -> Memory Config -> User-defined block memory size定义APP_BMEM_SELF_DEFINEDcopy
__template__\project\__applicaiton_private__目录到在project/hcpu下面,修改app_mem_custom.c中BMEM_REGISTER中的参数block大小以及个数。
5. 内存头信息¶
内存的管理,app_mem.c在RTT的内核函数的基础上做了一层封装,内存申请/释放主要依赖两个RTT的文件mem.c和memheap.c。
5.1 rt_xxx (malloc/free/calloc/realloc)¶
依赖于是否打开宏RT_USING_MEMHEAP_AS_HEAP,
如果
RT_USING_MEMHEAP_AS_HEAP定义,则rt_malloc/rt_free/rt_calloc/rt_realloc定义在memheap.c,此时内存头为struct rt_memheap_item类型
struct rt_memheap_item
{
rt_uint32_t magic; /**< magic number for memheap */
struct rt_memheap *pool_ptr; /**< point of pool */
rt_uint32_t size; /**< requested memory size excluding header*/
#ifdef RT_USING_MEMTRACE
//rt_uint8_t thread[4]; /* thread name */
rt_uint32_t ret_addr;
rt_tick_t tick;
//rt_uint32_t dbg_magic;
#endif
#ifdef MEM_ASYN_FREE
rt_uint16_t ref_count_magic;
rt_uint16_t ref_count;
#endif
struct rt_memheap_item *next; /**< next memheap item */
struct rt_memheap_item *prev; /**< prev memheap item */
struct rt_memheap_item *next_free; /**< next free memheap item */
struct rt_memheap_item *prev_free; /**< prev free memheap item */
};
Solution默认是打开RT_USING_MEMTRACE,板子上默认是打开MEM_ASYN_FREE的。因此,此时头信息总共有40个字节(未打开MEM_ASYN_FREE则为36个字节)。
如果未被定义,则
rt_malloc/rt_free/rt_calloc/rt_realloc定义在mem.c中,此时内存头为struct heap_mem *类型
struct heap_mem
{
/* magic and used flag */
rt_uint16_t magic;
rt_uint16_t used;
struct heap_mem *next, *prev;
rt_uint32_t size; /**< requested memory size excluding header*/
#ifdef RT_USING_MEMTRACE
#ifdef RT_MEM_RECORD_THREAD_NAME
rt_uint8_t thread[4]; /**< thread name */
#endif /* RT_MEM_RECORD_THREAD_NAME */
rt_uint32_t ret_addr;
rt_tick_t tick;
#endif
#ifdef MEM_ASYN_FREE
rt_uint16_t ref_count_magic;
rt_uint16_t ref_count;
#endif
};
Solution默认是打开RT_USING_MEMTRACE(但RT_MEM_RECORD_THREAD_NAME未打开),板子上默认是打开MEM_ASYN_FREE的。因此,此时头信息总共有28个字节(未打开MEM_ASYN_FREE则为24个字节)。
5.2 rt_memheap_xxx (alloc/free/calloc/realloc)¶
定义在memheap.c,此时内存头为 struct rt_memheap_item类型。
5.3 app_xxx (malloc/free/calloc/realloc)中申请的块内存¶
定义在app_bmem.h,此时内存头为struct struct_bmem_t或 bmem_item_t类型。
6. 内存信息查看¶
在恢复现场后,在trace32中装入:
sdk\tools\crash_dump_analyser\script\show_heap.cmm: 显示通过mem.c中函数申请的内存sdk\tools\crash_dump_analyser\script\show_rt_memheap.cmm:显示通过memheap.c中函数申请的内存sdk\tools\crash_dump_analyser\script\show_bmem.cmm:显示已经申请但未释放的块内存
装入路径为:trace32 -> File -> Run Script -> sdk\tools\crash_dump_analyser\script\xxx.cmm
7. 内存异常导致的死机及原因¶
常见内存异常场景如下(需重点规避 ):
内存越界:分配后使用范围超出申请大小。
野指针操作:未初始化/未申请的指针直接使用,覆盖其他内存或访问非法地址。
非保持区内存误用:睡眠唤醒后未重新初始化 non-retention 区域内存,继续使用。
退栈异常:野指针导致函数返回时栈错误。
回调函数指针异常:指针未赋值或指向非法地址。
释放后使用:内存已
rt_free或·app_cache_free释放,仍继续访问。多线程互斥缺失:同一片内存被多线程并发访问,数据异常。
Cache 操作错误:未执行
mpu_dcache_clean或mpu_dcache_invalidate,导致数据不一致。