存储架构
Flash
Flash 以 4KB 为一个 Page 进行擦除/写入操作(如果在一个 Page 需要写入一个字节,需要将整个 Page 擦除),Flash 组成部分及其它们关联的链接器文件如下。
对于协议栈以库文件方式链接的工程( _lirary ):
- Application Image Code Space (应用程序镜像代码区):应用程序和协议栈分为两个工程来管理,协议栈编译生成库文件供应用程序调用。应用程序会生成一个镜像文件,该镜像文件在应用程序的链接器配置文件中被配置为
cc26xx_app.icf
(IAR)和cc26xx_app.cmd
(CCS)。
对于采用两个 FLash 镜像方式编译的工程( Split Image ):
-
Application Image Code Space (应用程序镜像代码区):独立的应用程序 Flash 镜像,该镜像在应用程序的链接器配置文件中被配置为
cc26xx_app.icf
(IAR)和cc26xx_app.cmd
(CCS)。 -
Stack Image Code Space (协议栈镜像代码区):独立的协议栈Flash镜像。该镜像在协议栈的链接器配置文件中被配置:
cc26xx_stack.icf
(IAR)和cc26xx_ stack.cmd
(CCS)。 -
Simple NV (SNV)Area :非易失性存储器使用,可供应用程序和协议栈(GAP Bond Manager)使用。有关 SNV 的配置请参阅本文后面的使用 Simple NV 进行 Flash 存储。配置时,SNV Flash 存储区域是协议栈镜像的一部分。
- Customer Configuration Area(CCA):Flash 的最后一个 Page 用于存储客户特定芯片配置(CCFG)参数。CCA 扇区未使用的空间被分配给应用程序项目,详情请参阅本文后面的用户配置区。
Flash 内存映射
本节介绍用于两个拆分镜像项目配置的 Flash 内存映射。如下图1所示,应用程序链接文件存储在用实线箭头指向的内存位置,协议栈链接文件存储在用虚线箭头指向的内存位置。
注意:将协议栈作为库的工程,协议栈以库的形式被应用程序调用。协议栈和应用程序合并放在应用程序镜像代码空间中,不需要与拆分镜像
相关的任何边界要求。
下表总结了图 1 中的 Flash System Map
定义, 并提供了可在各个 IDE 链接器文件中找到的关联链接器的定义或符号。
表1. Flash 系统映射定义
符号/区域 | 意义 | 工程 | CCS定义 | IAR定义 |
---|---|---|---|---|
APP_FLASH_START | flash的起始位置/应用程序镜像的起始位置 | App | FLASH_APP_BASE | FLASH_START |
APP_FLASH_END | 应用程序镜像的结束位置(ICALL_STACK0_ADDR) | App | ADJ_ICALL_STACK0_START_FLASH_APP_BASE | FLASH_END |
STACK_FLASH_START | 协议栈镜像的起始位置(ICALL_STACK0_ADDR) | Stack | FLASH_START | FLASH_START |
STACK_FLASH_END | 协议栈镜像的结束位置,包括SNV | Stack | FLASH_SIZE_RESERVED_SIZE | FLASH_END |
CCA sector | flash最后的扇区,包括CCFG | App | FLASH_LAST_PAGE | FLASH_LAST_PAGE |
CCFG region | 位于CCA中,用来存储本地配置参数 | App | CCA的最后88个字节 | CCA的最后88个字节 |
应用程序和协议栈的 Flash 边界
应用程序和协议栈代码镜像区域是基于预定义符号 ICALL_STACK0_ADDR 和 ICALL_STACK0_START 的,这些值定义协议栈镜像入口函数的硬编码 Flash 地址,它本质上是应用程序和协议栈项目边界字对齐的 Flash 地址。为了确保正确链接,应用程序和协议栈项目都必须使用相同的定义符号。默认情况下,链接器配置会将未使用的 Flash 分配给应用程序项目,但可以通过边界工具手动或自动修改。使用边界工具配置 Flash 边界地址的有关信息,请参阅本文后面的边界工具操作。
使用 Simple NV 进行 Flash 存储
Flash 的 Simple NV (SNV)区域用于存储不易变动的数据,例如用于绑定的加密密钥或需要存储的自定义参数。协议栈可以配置成为 SNV 预留最多两个 4kB Flash page ,有效数据仅存储在一个可用的 Flash page 中。为了最小化 Flash 上的擦除周期数,当扇区具有 80% 的无效数据时,SNV 管理器对 Flash扇区(可能是多个扇区)进行压缩。压缩是将有效数据复制到临时区域,然后擦除先前存储数据的扇区。再根据 OSAL_SNV Values
中描述的 OSAL_SNV 值,该有效数据接着会被放回新擦除的扇区中或着保存在新扇区中。可以通过在协议栈项目中设置 OSAL_SNV 预处理器符号的值来配置分配给 SNV 的 Flash 扇区数目。
表 2 列出了可配置的有效值以及相关描述。
表2. OSAL_SNV 值
OSAL_SNV值 | 描述 |
---|---|
0 | SNV 未使能,不能在 NV 中存储绑定密钥。这时应用程序和协议栈工程代码的存储区域最大,GAP Bond Manager 也不能使能。在协议栈工程中需要设置预处理符号 NO_OSAL_SNV 关闭 GAP Bond Manager 功能。关于配置低功耗蓝牙协议栈特征可以查看 Stack Configurations |
1 (默认值) | 分配一个 Flash 扇区给 SNV ,绑定信息存储在 NV 中。Flash 压缩时使用 cache RAM 作为中间存储,因此压缩期间的功率损耗将导致 SNV 数据丢失。由于临时禁用 cache ,在压缩期间可能会导致系统性能的降低。在协议栈工程中设置预处理符号 OSAL_SNV = 1 |
2 | 分配两个 Flash 扇区给 SNV ,绑定信息存储在 NV 区。在压缩期间功率损耗时 SNV 数据会受到保护 |
将 OSAL_SNV 设置成其他值是无效的。设置的值越低,给应用程序或协议栈工程分配的代码空间就越大。可以使用以下 API 读取或写入 SNV 。
uint8 osal_snv_read( osalSnvId_t id, osalSnvLen_t len, void *pBuf)
||从 NV 读取数据|
|形参|id -有效的 NV 条目
len -读取数据的长度
pBuf -存储读取数据的缓冲区指针|
|返回值|SUCCESS:NV 条目读取成功
NV_OPER_FAILED:读取 NV 条目失败|
uint8 osal_snv_write(osalSnvId_t id,osalSnvLen_t len,void * pBuf)
||写数据到 NV |
|形参|id -有效的 NV 条目
len-写入数据的长度
pBuf -包含了需要写入数据的缓冲区指针。所有数据都会被立即更新|
|返回值|SUCCESS:写入 NV 条目成功
NV_OPER_FAILED:写入 NV 条目失败|
由于 SNV 与 BLE5-Stack 中的其他模块(如 GAP Bond Manager)是共享的,所以必须小心管理 NV 条目 ID。默认情况下,客户可用的 ID 在 bcomdef.h 中有定义,如下代码所示。
1 // Device NV Items - Range 0 - 0x1F
2 #define BLE_NVID_IRK 0x02 //!< The Device's IRK
3 #define BLE_NVID_CSRK 0x03 //!< The Device's CSRK
4 #define BLE_NVID_SIGNCOUNTER 0x04 //!< The Device's Sign Counter
5 #define BLE_LRU_BOND_LIST 0x05 //!< The Device's order of bond indexes in least recently used order
6 // Bonding NV Items - Range 0x20 - 0x5F - This allows for 10 bondings
7 #define BLE_NVID_GAP_BOND_START 0x20 //!< Start of the GAP Bond Manager's NV IDs
8 #define BLE_NVID_GAP_BOND_END 0x5f //!< End of the GAP Bond Manager's NV IDs Range
9 // GATT Configuration NV Items - Range 0x70 - 0x79 - This must match the number of Bonding entries
10 #define BLE_NVID_GATT_CFG_START 0x70 //!< Start of the GATT Configuration NV IDs
11 #define BLE_NVID_GATT_CFG_END 0x79 //!< End of the GATT Configuration NV IDs
12 // Customer NV Items - Range 0x80 - 0x8F - This must match the number of Bonding entries
13 #define BLE_NVID_CUST_START 0x80 //!< Start of the Customer's NV IDs
14 #define BLE_NVID_CUST_END 0x8F //!< End of the Customer's NV IDs
15
16
17
如下代码显示了如何从 SNV flash 中读取和写入一个字节数组:
/*********************************************************************
* GLOBAL VARIABLES
*/
#define BUF_LEN 4
#define SNV_ID_APP 0x80
uint8 buf[BUF_LEN] ={0,};
static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1)
{
// Initialize application
SimpleBLEPeripheral_init();
uint8 status = SUCCESS;
status = osal_snv_read(SNV_ID_APP, BUF_LEN, (uint8 *)buf);
if(status != SUCCESS)
{
Display_print1(dispHandle, 0, 0, "SNV READ FAIL: %d", status);
//Write first time to initialize SNV ID
osal_snv_write(SNV_ID_APP, BUF_LEN, (uint8 *)buf);
}
//Increment first element of array and write to SNV flash
buf[0]++;
status = osal_snv_write(SNV_ID_APP, BUF_LEN, (uint8 *)buf);
if(status != SUCCESS)
{
Display_print1(dispHandle, 0, 0, "SNV WRITE FAIL: %d", status);
}
else
{
Display_print1(dispHandle, 0, 0, "Num of Resets: %d", buf[0]);
}
// Application main loop
for (;;)
{
//...
}
NV 条目 ID 不需要事先初始化,OSAL SNV 管理器在首次调用 osal_snv_write()成功访问 SNV 时会初始化 NV ID。
当向 SNV 读取或写入大量数据时,TI 建议将读/写数据放置在静态(链接器)分配的数组或从堆中分配的缓冲区中。在本地数组中放置大量数据可能会导致任务堆栈溢出。
默认情况下,osalSnvId_t
和 osalSnvLen_t
被定义为 uint8
类型。如果定义为 uint16
类型,需要在应用程序和协议栈项目中去修改预处理符号 OSAL_SNV_UINT16_ID
的定义。
osal_snv_read
和 osal_snv_write
只允许在任务上下文中使用,在 Swis 或 Hwis 内无法调用此 API。
用户配置区
用户配置区(CCA)占用 Flash 的最后一页,用户可以在用户配置(CCFG)表中配置各种芯片和系统的参数。 ccfg_app_ble.c 中定义了 CCFG 表,你可以在应用程序项目的 Startup
文件夹中找到 ccfg_app_ble.c 文件。CCA 扇区的最后 88 个(sizeof(ccfg_t))字节是系统为 CCFG 表保留的,默认情况下,链接器将 CCA 扇区未使用的 Flash 分配给应用程序镜像来存储代码和数据,也可以修改链接器以保留整个扇区来存储用户参数(例如,板序列号和其他标识参数)。
CCA 区域由应用程序链接器文件中的 FLASH_LAST_PAGE
定义。在 IDE 中展示如下:
CCS 中:
MEMORY
{
...
// CCFG Page, contains .ccfg code section and some application code.
FLASH_LAST_PAGE (RX) : origin = FLASH_LAST_PAGE_START, length = FLASH_PAGE_LEN
...
}
SECTIONS
{
...
.ccfg : > FLASH_LAST_PAGE (HIGH)
...
}
IAR 中:
// Memory Regions
...
define region FLASH_LAST_PAGE = mem:[from(FLASH_SIZE - PAGE_SIZE) to FLASH_SIZE-1];
...
...
// CCFG
place at end of FLASH_LAST_PAGE { readonly section .ccfg };
keep { section .ccfg }
有关 CCFG 区域和相关配置选项的详细信息,以及如何设置 CCFG 来禁止对内部 Flash 内容的访问,请参阅 CC26XX 技术参考手册。
RAM
与 Flash 类似,RAM 在应用程序和协议栈工程之间也是共享。RAM 部分在其各自的链接器文件中被配置。
- 应用程序镜像:应用程序和共享堆的 RAM 空间。此镜像在应用程序的链接器配置文件中被配置:
cc26xx_app.icf
(IAR)和cc26xx_app.cmd
(CCS)。 - 堆栈镜像:协议栈的
.bss
和.data
部分的 RAM 空间。此镜像在协议栈的链接器配置文件中被配置:cc26xx_stack.icf
(IAR)和cc26xx_stack.cmd
(CCS)。
RAM 内存映射
下面的图 2 System Memory Map 展示了 simple_peripheral 工程默认的系统内存映射。这个图只是一个概要,你可以在 IAR 中的输出文件夹或 CCS 中的 FlashROM 文件夹中的 simple_peripheral_app.map 和 simple_peripheral_stack.map 文件中找到给定编译的确切内存位置。在利用 map 文件查看系统 flash 和 RAM 使用情况时,应用程序链接器文件包含实心箭头指向的符号,协议栈链接器文件包含虚线箭头指向的符号。
应用程序和协议栈的 RAM 边界
应用程序和协议栈的 RAM 内存映射基于通用的宏定义符号: ICALL_RAM0_START
。该值定义了应用程序的 RAM 空间结束以及协议栈 .BSS
和 .DATA
段镜像开始的硬编码 RAM 边界位置。与 Flash 边界不同,协议栈工程的元素(如任务堆栈和堆)在应用程序项目中分配。为确保正确链接,应用程序和协议栈工程都必须使用相同的 ICALL_RAM0_START
值。默认情况下,边界工具通过配置 ICALL_RAM0_START
将未使用的 RAM 空间分配给应用程序工程。使用边界工具配置 RAM 边界地址的有关信息,请参阅本文后面的边界工具操作。
系统堆栈
除了 RTOS 和 ICall 堆,考虑一下内存的其他部分。如 TI-RTOS 概述中的任务所述,每个任务都有自己用于上下文切换的运行时栈。运行时栈由 RTOS用于 main()、HWi 和 SWi 。该系统堆栈在应用程序链接器文件中分配,放置在应用程序 RAM 空间的末尾。
IAR 的 RTOS 系统堆栈由 CSTACK
符号定义:
// Stack
define symbol STACK_SIZE = 0x400;
define symbol STACK_START = RAM_END + 1;
define symbol STACK_END = STACK_START - STACK_SIZE;
//
define symbol STACK_TOP = RAM_END + 1;
export symbol STACK_TOP;
// Runtime Stack
define block CSTACK with alignment = 8, size = STACK_SIZE { section .stack };
place at end of RAM { block CSTACK };
在 IAR 中,更改 CSTACK
的大小需要调整应用程序链接器文件中的 STACK_SIZE
的值。
CCS 的 RTOS 系统堆栈由 RTOS 配置文件 appBLE.cfg 中的 Program.stack 参数定义:
/* main() and Hwi, Swi stack size */
Program.stack = 1024;
并由链接器放置在应用程序的 RAM 空间中:
/* Create global constant that points to top of stack */
/* CCS: Change stack size under Project Properties */
__STACK_TOP = __stack + __STACK_SIZE;
动态内存分配
系统使用两个堆进行动态内存分配。应用程序设计人员必须了解每个堆的使用情况,以便最大限度地利用可用内存。
在 RTOS 配置文件 app_ble.cfg 中,RTOS 配置了一个小堆:
var HeapMem = xdc.useModule('xdc.runtime.HeapMem');
BIOS.heapSize = 1668;
该堆( HeapMem )用于初始化 RTOS 对象、分配低功耗蓝牙协议栈的任务运行时堆栈。TI 选择使用此大小的堆来满足系统初始化要求。由于此堆的体积本来就小,所以不建议从 RTOS 堆分配内存以供一般应用程序使用。有关 TI-RTOS 堆配置的更多信息,请参阅 TI-RTOS SYS/BIOS Kernel User’s Guide 中的 Heap Implementations 部分。
应用程序必须使用单独的堆。ICall 模块使用应用程序 RAM 区域的一部分,这部分也可以由各种任务使用。ICALL 堆的大小由应用程序工程中的 HEAPMGR_SIZE
预处理符号定义,可以用非零值将 ICall 堆设定为指定大小。如果 HEAPMGR_SIZE
值为 0 ,系统会自动将堆的大小设置为链接器未分配而可用的空闲 RAM 的大小。默认情况下,如 simple_peripheral
项目使用的是自动大小分配功能。虽然 ICall 堆在应用程序工程中定义,但该堆由低功耗蓝牙协议栈使用。分配内存的 API(如 GATT_bm_alloc())是从 ICall 堆分配内存。
分析 ICALL 堆使用的情况需要在应用程序工程的预处理器符号中定义 HEAPMGR_METRICS
。启用自动堆大小功能后要确定 ICall 堆的大小的有关信息,请参阅 Profiling the ICall Heap Manager ( heapmgr.h )。
注意:自动堆大小功能不能确定和知晓应用程序所需的堆大小,系统设计人员必须确保堆具有应用程序运行时需要的足够内存空间。
以下是使用 ICALL 堆动态分配可变长度(n)数组的示例:
//define pointer
uint8_t *pArray;
// Create dynamic pointer to array.
if (pArray = (uint8_t*)ICall_malloc(n*sizeof(uint8_t)))
{
//fill up array
}
else
{
//not able to allocate
}
以下是释放上一个数组空间的示例:
ICall_free(pMsg->payload);
缓存( Cache )/ GPRAM
缓存是为处理器在 RAM 上保留的一个 8KB 空间。缓存模块临时存储从 Flash 读取的数据,以便经常使用的数据不需要每次访问 Flash 来获取。这样可以减少 CPU 等待状态并节省电量,当不使用缓存时就不对它供电,当缓存没有工作时就让它处于待机和空闲的状态。
把缓存作为 RAM 使用
如果应用程序需要更多内存或者 SRAM 空间,则缓存可以当作 RAM 使用。这样链接器就可以将编译应用程序的一部分存储在这一部分的 RAM 中,这部分 RAM 将被称为通用 RAM( GPRAM )。但这样做会稍微降低程序的运行速度,并且会增加休眠状态下的设备功耗。主要是因为与缓存相反的 GPRAM 即使是在设备休眠时也必须供电。CC2640R2F 数据表中列出了具有和不具有缓存的待机模式下的当前消耗,您可以去查看验证。
上面已经提到将缓存作为 GPRAM 使用会稍微降低运行速度以及增加功耗,如何影响设备的功耗还是取决于应用。对于某些应用增加的功耗会非常小,但是对于处理密集型应用增加的功耗会略高一些。您可以使用 Measuring Bluetooth Low Energy Power Consumption Application Report (SWRA478)中描述的方法来验证您应用程序的电流消耗。
把缓存作为 RAM 使用需要做两件事情。首先,必须在使用这块内存的时候告诉程序是将它作为 Cache 还是 GPRAM 。其次,链接器必须被告知将要用作缓存的内存区域分配给 GPRAM ,以及哪一部分代码要存储在 GPRAM 中,这是在链接器的命令/配置文件中完成。CCS 和 IAR 链接器命令/配置文件的语法略有不同。要了解有关 CCS 链接器命令文件的更多信息,请参阅 wiki 文章Linker Command File Primer。要了解有关 IAR 链接器的更多信息,请参阅 IAR C/C++ Development Guide 。
BLE5-Stack中有一些示例项目会存在一个编译配置,允许将缓存用作 RAM 。对于多角色工程来说是很好的,在这种情况下,选择该编译配置就可以将缓存作为 RAM 使用了。
在 CCS 中:Project -> Build Configurations -> Set Active -> FlashROM-CacheAsRAM
。
在 IAR 中:Project -> Edit Configurations -> FlashROM - CacheAsRAM
。
警告:更改项目的编译配置时,工程属性/选项可能会重置。更改编译配置后,需要检查更改一些工程预定义等 。
如果要想在没有 CacheAsRAM 编译配置的工程中将缓存作为 RAM 使用,请按照下列步骤操作:
注意:CCS 用户和 IAR 用户的步骤会不同。根据您的工程所基于的示例项目不同操作步骤也会不同。对于在 Ble5stack 文件夹中的示例项目,只需要 1-5 步。
- 在CCFG 文件(
app_ble_ccfg.c
或ccfg.c
)中#include <startup_files/ccfg.c>
之前添加下面的代码:
#ifdef CACHE_AS_RAM
#define SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM 0x0 /* Enable GPRAM */
#endif //CACHE_AS_RAM
#include <startup_files/ccfg.c>
- main() 中添加以下代码:
清单1. 在休眠时作为缓存使用。
#ifdef CACHE_AS_RAM
// retain cache during standby
Power_setConstraint(PowerCC26XX_SB_VIMS_CACHE_RETAIN);
Power_setConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
#else
// Enable iCache pre-fetching
VIMSConfigure(VIMS_BASE, TRUE, TRUE);
// Enable cache
VIMSModeSet(VIMS_BASE, VIMS_MODE_ENABLED);
#endif //CACHE_AS_RAM
警告:请确保您的程序在将缓存用作 RAM 时没有使用 VIMS 。
在同一个文件中,包括以下文件:(Ble5stack 项目中这些文件已经包含在 main.c 中)
/* Power Driver */
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>
/* Header files required to enable instruction fetch cache */
#include <ti/devices/cc26x0r2/inc/hw_memmap.h>
#include <ti/devices/cc26x0r2/driverlib/vims.h>
3.编译器预定义选项中添加 CACHE_AS_RAM
。对于来自 Ble5stack 文件夹的示例项目,此定义将在以下文件中对执行的代码进行更改:
- ble_user_config.c
- main.c
- ble_user_config.h
4.链接器预定义选项中添加 CACHE_AS_RAM=1
。这个定义会对 cc26xx_app.cmd/cc26xx_app.icf 中的执行代码进行更改。
注意:有关如何编辑编译器和链接器符号设置的更多信息,请参阅以下部分:
5.如果您的工程是基于 BLE5-Stack 的工程,这将使 .bss (ll.o 除外)从 SRAM 移动到 GPRAM ,LL.o 需要由 RF 驱动器放置在 SRAM 中。其他对象也可以根据需要移动到 .bss
中。您可以在本文后面的使用 AUX RAM 作为 RAM 中找到示例。
重新编译并更新您的应用程序工程,查看 .map
文件确定设备内存的哪些部分被占用。(或者在 CCS 中可以通过:View -> Memory Allocation
)
6.如果您的项目不是基于 BLE5-Stack 的项目,要将 Cache 用作 GPRAM 还需要进行相关修改。如果您的项目正在使用无线电,在无线电覆盖中添加 0x00018063
。
7.GPRAM 内存区域必须在链接器命令文件中定义。定义语法在 CCS 和 IA R链接器中是不同的。下面分别说明一下:
在 CCS 中,链接器命令文件的尾缀是 .cmd
(如:CC2640R2_LAUNCHXL_TIRTOS.cmd
)。
清单2. 在 Memory Sizes
下,添加 GPRAM 开始地址和长度的定义。
/*******************************************************************************
* Memory Sizes
*/
#define FLASH_BASE 0x00000000
#define GPRAM_BASE 0x11000000
#define RAM_BASE 0x20000000
#define ROM_BASE 0x10000000
#ifdef CC26X0ROM
#define FLASH_SIZE 0x00020000
#define GPRAM_SIZE 0x00002000
#define RAM_SIZE 0x00005000
#define ROM_SIZE 0x0001C000
#endif /* CC26X0ROM */
清单3. 在 Memory Definitions
下添加 GPRAM
/*******************************************************************************
* GPRAM
*/
#ifdef CACHE_AS_RAM
#define GPRAM_START GPRAM_BASE
#define GPRAM_END (GPRAM_START + GPRAM_SIZE - 1)
#endif /* CACHE_AS_RAM */
清单4. 在 MEMORY{}
中为 GPRAM 分配空间
#ifdef CACHE_AS_RAM
GPRAM(RWX) : origin = GPRAM_APP_BASE, length = GPRAM_SIZE
#endif /* CACHE_AS_RAM */
清单5. 在SECTIONS{}
中将.bss
从SRAM移动到GPRAM。
GROUP > SRAM
{
.data
#ifndef CACHE_AS_RAM
.bss
#endif /* CACHE_AS_RAM */
.vtable
.vtable_ram
vtable_ram
.sysmem
.nonretenvar
#ifdef CACHE_AS_RAM
}
#else // !CACHE_AS_RAM
} LOAD_END(heapStart)
#endif //CACHE_AS_RAM
.stack : > SRAM (HIGH) LOAD_START(heapEnd)
#ifdef CACHE_AS_RAM
ll_bss > SRAM
{
--library=cc2640_ll_*.a<ll.o> (.bss)
}LOAD_END(heapStart)
.bss :
{
*(.bss)
} > GPRAM
#endif /* CACHE_AS_RAM */
重新编译你的应用程序会将 .bss
从 SRAM 移动到 GPRAM ,放在自动堆大小开始的后面,其他对象也可以移动。请参阅本文后面的使用 AUX RAM 作为 RAM 查看例程。
- 在 IAR 中,链接器配置文件的后缀是
.icf
(例如CC2640R2_LAUNCHXL_TIRTOS.icf
)。
清单6. 在 Memory Definitions
下面添加对 GPRAM 起始地址和长度的定义。
//
// GPRAM
//
if ( isdefinedsymbol(CACHE_AS_RAM) )
{
define symbol GPRAM_START = 0x11000000;
define symbol GPRAM_SIZE = 8096;
define symbol GPRAM_END = GPRAM_START + GPRAM_SIZE;
}
清单7. 在 Memory Regions
下面为 GPRAM 分配空间。
if ( isdefinedsymbol(CACHE_AS_RAM) )
{
define region GPRAM = mem:[from GPRAM_START to GPRAM_END];
}
清单8. 在 Memory Placement
下将 .bss
从 SRAM 移动到 GPRAM。
if ( isdefinedsymbol(CACHE_AS_RAM) )
{
// GPRAM
define block GPDATA { section .bss };
place in GPRAM { block GPDATA } except { module ll.o };
}
重新编译应用程序就会将 .bss
从 SRAM 移动到 GPRAM,其他对象也可以移动。可以在本文后面的使用 AUX RAM 作为 RAM 查看例程。
AUX RAM
AUX RAM 是属于传感器控制器中的一个 2KB 的内存区域。如果应用程序不使用传感器控制器,则可以将此内存用作应用程序的 RAM 。但访问该存储器比访问 SRAM 要慢得多,这可能导致功耗增加以及程序执行速度变慢。
使用 AUX RAM 作为 RAM
要在应用程序中将 AUX RAM 作为 RAM 使用,请按照以下步骤操作(先描述 CCS,再描述 IAR)。
1.在链接器命令文件中添加一个新的定义:
在 CCS 中:Project -> Properties -> ARM Linker->Advanced Options -> Command File Preprocessing
。
在 IAR 中:Options -> Linker -> Config
,添加 AUX_AS_RAM=1
。
2.链接器命令/配置文件 CCS 和 IAR 有些不同。这里先介绍在 CCS 中更改链接器配置文件,再介绍 IAR。
- 在链接器命令文件
cc26xx_app.cmd
中,定义 AUX_RAM 的内存区域:
#ifdef AUX_AS_RAM
#define AUX_RAM_BASE 0x400E0000
#define AUX_RAM_SIZE 0x800
#endif /* AUX_AS_RAM */
- 在
MEMORY{}
中创建一个 AUX_RAM 内存区域
#ifdef AUX_AS_RAM
AUX_RAM (RWX) : origin = AUX_RAM_BASE, length = AUX_RAM_SIZE
#endif /* AUX_AS_RAM */
- 在
SECTIONS{}
中将内存或代码的部分添加到 AUX_RAM 中
清单9. 将目标文件移动到 AUX_RAM 中。示例来自 simple_peripheral( cc26xx_app.cmd
)
#ifdef AUX_AS_RAM
reorganized_into_auxram
{
simple_peripheral.obj(.data)
devinfoservice.obj(.data)
simple_gatt_profile.obj(.data)
icall.obj(.data)
board.obj(.bss)
} > AUX_RAM
#endif/* AUX_AS_RAM */
.obj 文件放在应用程序项目的 FlashROM 文件夹中,它们也在 .map 文件中列出了大小。链接器命令文件和内存部分的详细描述在 WiKi 文章 Linker Command File Primer 中给出。
如果你想要更多地控制 AUX_RAM 中存储的内容,你可以使用命令 #pragma DATA_SECTION
将指定的变量存储里面。但请注意,这仅适用于全局变量。
清单10.将全局显示句柄的变量移动到 AUX RAM 中一个叫做 my_section
的新区域。
// Display Interface
#pragma DATA_SECTION(dispHandle, "my_section")
Display_Handle dispHandle = NULL;
清单11. 在链接器命令文件( cc26xx_app.cmd )中,将这部分( my_section
)添加到 AUX_RAM。
#ifdef AUX_AS_RAM
reorganized_into_auxram
{
simple_peripheral.obj(my_section)
} > AUX_RAM
#endif /* AUX_AS_RAM */
警告:仅对链接器命令文件进行更改时请确保要重新编译(
Rebuild
),而不仅仅是编译(Build
)。( CCS 在您重新编译之前,不会认识到您对项>目进行了更改。)
- 对于 IAR ,打开链接器配置文件
cc26xx_app.icf
,在Memory Definitions
下添加
// AUX_RAM
//
if ( isdefinedsymbol(AUX_AS_RAM) )
{
define symbol AUX_RAM_START = 0x400E0000;
define symbol AUX_RAM_SIZE = 0x800;
define symbol AUX_RAM_END = AUX_RAM_START + AUX_RAM_SIZE;
}
- 在
Memory Regions
中添加
if ( isdefinedsymbol(AUX_AS_RAM) )
{
define region AUX_RAM = mem:[from AUX_RAM_START to AUX_RAM_END];
}
- 在
Memory Placement
下添加
清单12. 将目标文件移动到 AUX_RAM 中。来自 simple_peripheral( cc26xx_app.icf
)的示例
if ( isdefinedsymbol(AUX_AS_RAM) )
{
// AUX_RAM
define block AUXDATA { section .data object simple_peripheral.o,
section .data object devinfoservice.o,
section .data object simple_gatt_profile,
section .data object icall.o,
section .data object board.o};
place in AUX_RAM { block AUXDATA };
}
.o 文件是在 .map 文件中列出的。有关链接器配置文件的更多信息,请参阅 IAR C/C++ Development Guide 。
如果你想要更多操控 AUX_RAM 中存储的内容,你可以使用命令 #pragma DATA_SECTION
将指定的变量存储在里面。但请注意,这仅适用于全局变量。
清单13.将全局显示句柄的变量移动到 AUX RAM 中一个叫做 my_section
的新区域
// Display Interface
#pragma location="my_section"
Display_Handle dispHandle = NULL;
清单14. 在链接器配置文件( cc26xx_app.cmd
)中,将下面这部分代码添加到 AUX_RAM
if ( isdefinedsymbol(AUX_AS_RAM) )
{
// AUX_RAM
define block AUXDATA { section my_section object simple_peripheral.o };
place in AUX_RAM { block AUXDATA };
}
边界工具
在将应用程序和协议栈工程编译为两个镜像文件时,边界工具用来自动调整两个镜像之间共享的各个 RAM 和 Flash 的边界地址符号。应用程序将协议栈作为库使用的工程不需要使用 Frontier Tool 。
边界工具作为在协议栈工程编译后的一个步骤来运行,它基于对协议栈链接器和映射文件的分析来调整相应的 RAM 和 Flash 边界。边界工具不会修改任何项目文件和源代码,不运行任何编译器,也不执行链接器优化,只用于调整和更新位于编译器和链接器配置文件中的各个 Flash 和 RAM 的边界地址。应用程序以及协议栈工程都会使用这些配置文件。
边界工具安装到 SDK 中的以下路径:<SDK_INSTALL_DIR>\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\tools\frontier\frontier.exe
(< SDK_INSTALL_DIR >是蓝牙协议栈 SDK 的安装路径)
此工具的 python 源代码也包括在其中。
表3显示了边界工具更新的边界地址符号。
表3.边界地址和符号
边界地址符号 | 描述 |
---|---|
ICALL_STACK0_START | 应用程序和协议栈之间的 Flash 边界地址。代表应用程序镜像的结束和协议栈镜像的开始 |
ICALL_STACK0_ADDR | 协议栈实体的地址(Flash) |
ICALL_RAM0_START | 应用程序和协议栈之间的 RAM 边界地址。代表应用程序 RAM 的结束以及协议栈 RAM 的开始 |
默认情况下所有的示例应用程序项目都配置成使用边界工具,不需要进行边界工具的用户配置。当协议栈配置更改时,或者在协议栈工程中一些文件的更新导致协议栈镜像大小发生变化,边界文件可能会更新。因此在每次协议栈工程编译后都必须重新编译应用程序工程。
注意:
frontier tool 替代了早期 SDK 中使用的边界工具。
边界工具操作
边界工具( frontier.exe )是协议栈工程在 CCS 或 IAR 集成开发环境中编译后进行调用的。如果需要对 RAM 或 Flash 边界进行调整,则边界工具将更新以下列出的边界链接器配置和 C 定义文件。要并入更新后的配置值,请在应用程序工程上执行Project -> Rebuild All
。在重新编译应用程序之前,协议栈工程必须正确编译和链接。
SDK 中的每个工程都有一组配置文件,IDE 的链接器和编译器利用它们设置或调整相应的 Flash 和 RAM 值。这些配置文件在应用程序和协议栈工作区之间共享,并存储在以下位置:
<SDK_INSTALL_DIR>\examples\rtos\<EVAL_BOARD>\ble5stack\<PROJECT>\tirtos\<IDE>\config
其中 <EVAL_BOARD>
是评估平台,<PROJECT>
是示例应用程序(例如 simple_peripheral
),<IDE>
是 IAR 或 CCS 。
例如,在 CC2640R2F LaunchPad 上运行的 simple_peripheral
示例应用程序,边界配置文件位于以下路径:
CCS: <SDK_INSTALL_DIR>\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\tirtos\ccs\config
IAR: <SDK_INSTALL_DIR>\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\tirtos\iar\config
以下是边界配置文件:
- 边界链接器配置文件:
iar_boundary.xcl
[IAR] 或ccs_linker_defines.cmd
[CCS],为链接器定义边界地址。该文件位于 TOOLS IDE 文件夹中,并在需要进行调整时由边界工具进行更新。 - 边界 C 定义文件:
iar_boundary.bdef
[IAR] 或ccs_compiler_defines.bcfg
[CCS]。定义编译器的边界地址。该文件位于 TOOLS IDE 文件夹中,并在需要进行调整时由边界工具进行更新。
注意:边界链接器配置文件和边界 C 定义文件中的值必须匹配。
加入我们
文章所有代码、工具、文档开源。加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5.0。
转自:http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:memroy_map:flash_map