跳转至

内存使用指南

内存使用指南*

第一章 概述*

1.1 芯片存储空间*

型号 GX8301A GX8301B GX8302A GX8302B
SRAM 128KB 128KB 144KB 144KB
PSRAM 2MB
Flash 512KB 512KB 1MB 外挂

1.2 总线映射图*

只读 16K 的 Rom 区域内部启动程序使用,不对外开放。用于启动保存在 Flash 上的空间,和与上位机通过 UART 或者 USB 做在线烧录。

第二章 内部布局*

2.1 链接脚本*

编译完成后,可以打开 apus\output\link.ld 链接脚本,了解方案的内存规划。

可以对照上面的 “总线映射图” 来了解各内存区域的布局。

temp_statck_dram:用于启动阶段,加载代码执行的临时栈空间。代码加载完成后,就不再使用,应用可以使用

em_dram:蓝牙库需要的内存空间

2.2 内存分布表*

第三章 低功耗下的内存管理*

3.1 retention 内存*

SRAM在是否低功耗下掉电上分(开启了低功耗功能才有这个区别):

  • RETENTION区域 80KB
  • NO RETENTION区域 64KB

RETENTION 表示里面的数据在任何情况下都一直保留。NO RETENTION 表示里面的数据,在低功耗的时候,会全部丢失,必须应用自己在低功耗起来后,做数据恢复。

SRAM按照16KB大小进行分片,使得不同模块访问不同分片的时候可以互不影响,提高带宽。

芯片全型号提供最多 80KB RETENTION 功能,即在 Deep Sleep Mode 下可以保证数据不丢失,分别对应SRAM0~SRAM4。

内存物理地址分布图:

3.2 开发中的注意事项*

我们在编译完成的时候,会有section使用量的输出,需要关注下:

link...
./output/blehr-rtthread-gx8302b_dev/apus.elf  :
section                      size        addr
.ram_text                    9878   268444160
.dummy_for_nocache_iram      9878   805315072
.ram_nocache_text              10   805324950
.dummy_for_dram              9888   536879616
.ram_rodata                  3116   536889504
.ram_data                    2452   536892620
.ram_bss                    29600   536895072
.ram_noretein_bss               0   536952832
.dummy_for_xip              15456   318775296
.xip_text                  148044   318790752
.xip_rodata                 12516   318938796
.temp_stack                  2048   537016320
.psram_buf                   1024   553648128
.em_data                     8704   805306368
.debug_info                490967           0
.debug_abbrev               79632           0
.debug_loc                 236279           0
.debug_aranges              14320           0
.debug_ranges               28712           0
.debug_line                380608           0
.debug_str                  82842           0
.comment                       69           0
.riscv.attributes              90           0
.debug_frame                45464           0
Total                     1611597

通过这些细节,可以了解我们代码里,使用的内存,都消耗了哪些区域。

因为 SRAM 只有 128KB 或者 144KB,而 RETENTION 的只有 80KB 可用。去除SDK本身需要内存后,剩余的需要仔细使用。确保需要RETENTION,或者对执行速度要求非常高的,才在 SRAM 里面分配内存。

80k RETENTION sram 涵盖了.ram_text + .ram_nocache_text + .ram_rodata + .ram_data + .ram_bss + .em_data, blehr这个app是一个基本的ble gatt应用,没有其他模块,默认不开打印也有57K(#define CONFIG_USING_CONSOLE (0)),开了打印有65K。(系统,蓝牙,应用都算进去了)

使用 const 定义的只读全局变量和只读全局数组,会放到 XIP 区域,保存在flash上,不使用 SRAM 空间。

不使用任何 __attribute __或者 const 修饰的全局变量和数,会使用 SRAM 的 RETENTION 区域。

使用 chip_os_task_create 创建的线程,使用的栈空间是 SRAM 的 RETENTION 区域。如果这个任务栈不需要使用 SRAM 的 RETENTION 区域,比如不在低功耗环境下用,那么可以使用 chip_os_task_init 方式创建线程,自己来指定使用的栈空间用哪块区域。

大家在移植第三方代码的时候,特别注意下,不要随意消耗 SRAM 空间,避免编译的时候提示内存不足错误。能放XIP或者PSRAM上的,就尽量放上去。

第四章 开发中的内存使用指南*

4.1 attrtube 相关说明*

#ifndef __ATTR_DEF_H__
#define __ATTR_DEF_H__

#include <mem_config.h>

#define ATTR_SECTION_RAM_TEXT    __attribute__((section(".ram_text")))
#define ATTR_SECTION_RAM_RODATA    __attribute__((section(".ram_rodata")))
#define ATTR_SECTION_RAM_DATA    __attribute__((section(".ram_data")))
#define ATTR_SECTION_RAM_BSS    __attribute__((section(".ram_bss")))

#define ATTR_SECTION_RAM_NOCACHE_TEXT __attribute__((section(".nocache_text")))

#if CONFIG_NORETEIN_RAM_SIZE > 0
#define ATTR_SECTION_RAM_NORETEIN_BSS __attribute__((section(".noretein_bss")))
#else
#define ATTR_SECTION_RAM_NORETEIN_BSS
#endif

#define ATTR_SECTION_XIP_TEXT    __attribute__((section(".xip_text")))
#define ATTR_SECTION_XIP_RODATA    __attribute__((section(".xip_rodata")))

#if CONFIG_PSRAM_DRAM_SIZE > 0
#define ATTR_SECTION_PSRAM_BUF    __attribute__((section(".psram_buf")))
#else
#define ATTR_SECTION_PSRAM_BUF
#endif

#define ATTR_ISR    __attribute__ ((interrupt ("machine")))

#define ATTR_WEAK    __attribute__((weak))
#define ATTR_UNUSED    __attribute__((unused))
#define ATTR_USED    __attribute__((used))
#define ATTR_PACKED    __attribute__((packed))
#define ATTR_ALIGN(n)    __attribute__((aligned(n)))

4.2 使用示例*

使用的时候,头文件包含:

#include <attr_def.h>
// Enable psram
#define CONFIG_USING_PSRAM    (1) // 需要在sdk_config.h中使用支持PSRAM

// 使用 PSRAM 内存
unsigned char psram_buf[1024] ATTR_SECTION_PSRAM_BUF; 
// 使用.ram_text定义函数,常驻RETEIN内存
ATTR_SECTION_RAM_TEXT static void _mod_clk_cfg(void) 
{
  // do something
}

函数常驻RETEIN内存有两个原因: 一是某些函数由于涉及到Flash XIP执行,在 flash 还没有上电和初始化完成时,是无法通过XIP执行的 二是放到ram中执行的函数每次被调用时不需要从flash重新读取,可以节省时间,所以对于一些执行时间有要求的函数可以放到常驻内存,提高执行效率

// 存储在flash上, 通过XIP读取
const char data[4] = {0x11,0x22,0x33,0x44}; 
// 存储在 RETEIN SRAM 上
char data[4] = {0x11,0x22,0x33,0x44}; 
// 4字节对齐,BSS段,使用 SRAM NORETEIN 区域
static uint8_t _audio_input_pcm_buf[64] ATTR_SECTION_RAM_NORETEIN_BSS ATTR_ALIGN(4); 

内存申请:

  • malloc 使用 SRAM RETENTION 的 80KB 区域,如果没有开启低功耗这个宏,就是使用剩余后全部的 SRAM 空间
  • tmp_malloc 使用 SRAM NO RETENTION 区域,apps 里面对应的 sdk_config.h 里,低功耗宏 CONFIG_USING_LOWPOWER 开启和 CONFIG_USING_TMP_HEAP 设置为 1 才能使用
  • psram_malloc 使用 PSRAM 的内存,CONFIG_USING_PSRAM 宏开启才能使用

使用 tmp_malloc 和 psram_malloc 申请的内存,在系统进入深度休眠再唤醒后,里面的内存数据会全部丢失。并且系统会重新初始化它们对应的栈。因此,应用要继续使用这些区域的内存,需要重新去申请。

下面的代码,可以看的对应的栈被重新初始化了:

void rt_system_resume(void)
{
#if defined(CONFIG_USING_TMP_HEAP) && (CONFIG_USING_TMP_HEAP)
    extern int _eram_noretein_bss;
    rt_tmp_heap_init((void *)(&_eram_noretein_bss), (void *)(CONFIG_NORETEIN_DRAM_BASE + CONFIG_NORETEIN_RAM_SIZE));
#endif

#if defined(CONFIG_USING_PSRAM) && (CONFIG_USING_PSRAM)
    extern int _psram_buf_end_;
    rt_psram_heap_init((void *)(&_psram_buf_end_), (void *)(CONFIG_PSRAM_DRAM_BASE + CONFIG_PSRAM_DRAM_SIZE));
#endif

    _systick_config();
}

void rt_system_suspend(void)
{
    gx_free_irq(IRQ_NUM_SYSTICK);
#if defined(CONFIG_USING_TMP_HEAP) && (CONFIG_USING_TMP_HEAP)
    rt_tmp_heap_deinit();
#endif
#if defined(CONFIG_USING_PSRAM) && (CONFIG_USING_PSRAM)
    rt_psram_heap_deinit();
#endif
}