内存使用指南
内存使用指南*
第一章 概述*
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
}