内存管理

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,需注意以下两点:

    1. PSRAM 容量需 ≥8MB(用于加载代码和资源 );

    2. 代码(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 显示,配置路径menuconfigSiFli SDK configurationThird party packagesLittlevGL2RTTLVGL configurationSiFli extend

4

堆内存(Heap)

包括系统堆(system heap)和专用堆(memheap),配置路径menuconfigmemory config

5

场景动画切换 buffer

支持应用切换动画时所需缓冲区,配置路径menuconfigApp switch animation申请方式见 app_mem.c

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_SRAMCACHE_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程序,如果客户定义了多个产品,一些产品对块内存的配置不一样,则可以通过如下的步骤为某个工程单独配置块内存:

  1. 通过menuconfig (Top) -> Memory Config -> User-defined block memory size 定义APP_BMEM_SELF_DEFINED

  2. copy __template__\project\__applicaiton_private__目录到在project/hcpu下面,修改app_mem_custom.cBMEM_REGISTER中的参数block大小以及个数。

5. 内存头信息

内存的管理,app_mem.c在RTT的内核函数的基础上做了一层封装,内存申请/释放主要依赖两个RTT的文件mem.cmemheap.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_tbmem_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. 内存异常导致的死机及原因

常见内存异常场景如下(需重点规避 ):

  1. 内存越界:分配后使用范围超出申请大小。

  2. 野指针操作:未初始化/未申请的指针直接使用,覆盖其他内存或访问非法地址。

  3. 非保持区内存误用:睡眠唤醒后未重新初始化 non-retention 区域内存,继续使用。

  4. 退栈异常:野指针导致函数返回时栈错误。

  5. 回调函数指针异常:指针未赋值或指向非法地址。

  6. 释放后使用:内存已 rt_free或·app_cache_free 释放,仍继续访问。

  7. 多线程互斥缺失:同一片内存被多线程并发访问,数据异常。

  8. Cache 操作错误:未执行 mpu_dcache_cleanmpu_dcache_invalidate,导致数据不一致。