TriCore微控制器:CBL代码从Flash重定位至SRAM运行及自更新方案咨询
问题解答
你的理解是否正确?
完全正确。绝大多数微控制器的Flash存储单元在被CPU读取执行代码时,无法同时进行擦除或编程操作——这是Flash硬件的固有特性:要么当前存储块被用于指令读取,要么被用于擦写操作,二者无法并行;部分MCU甚至会直接锁定当前执行的Flash块,禁止任何修改操作。
解决方案
1. 将Flash擦写核心逻辑重定位到SRAM运行
这是最直接的解决方式,把需要操作Flash的代码移到SRAM中执行,避免占用当前运行的Flash块:
- 修改链接脚本:新增一个代码段,指定其加载地址在Flash,但运行地址在SRAM。示例GCC链接脚本片段:
MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K /* CBL所在Flash区域 */ SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K /* MCU的SRAM区域 */ } SECTIONS { /* 定义SRAM执行的代码段 */ .sram_code : { . = ALIGN(4); __sram_code_start = .; *(.sram_code) /* 匹配所有标记为.sram_code的函数 */ __sram_code_end = .; } >SRAM AT>FLASH /* 加载在Flash,运行在SRAM */ }
- 标记需要重定位的函数:给Flash擦写相关函数添加段属性,让编译器把它们放到
.sram_code段:
// GCC编译器属性,指定函数放到.sram_code段 __attribute__((section(".sram_code"))) void cbl_self_update_flash(uint32_t new_cbl_addr, uint32_t new_cbl_len) { // 解锁Flash HAL_FLASH_Unlock(); // 擦除原CBL所在的Flash块 FLASH_EraseInitTypeDef erase_init = {0}; uint32_t page_error = 0; erase_init.TypeErase = FLASH_TYPEERASE_PAGES; erase_init.PageAddress = 0x08000000; // 原CBL起始地址 erase_init.NbPages = (new_cbl_len + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE; HAL_FLASHEx_Erase(&erase_init, &page_error); // 写入新的CBL镜像 uint8_t *new_cbl_data = (uint8_t*)new_cbl_addr; for(uint32_t i=0; i<new_cbl_len; i+=4) { uint32_t word_data = *(uint32_t*)(new_cbl_data + i); HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x08000000 + i, word_data); } // 锁定Flash HAL_FLASH_Lock(); }
- 启动时复制代码到SRAM:如果链接脚本没有自动处理,需要在CBL初始化阶段(还未操作Flash前),手动把Flash中的
.sram_code段复制到SRAM:
// 从链接脚本中导入的符号 extern uint32_t __sram_code_start; extern uint32_t __sram_code_end; extern uint32_t LOADADDR(.sram_code); void copy_sram_code(void) { uint8_t *src = (uint8_t*)&LOADADDR(.sram_code); // Flash中的起始地址 uint8_t *dest = (uint8_t*)&__sram_code_start; // SRAM中的运行地址 uint32_t len = (uint32_t)&__sram_code_end - (uint32_t)&__sram_code_start; // 逐字节复制 while(len--) { *dest++ = *src++; } } // 在CBL的main函数开头调用 int main(void) { copy_sram_code(); // 先把SRAM代码复制好 // ... 其他初始化操作 ... // 需要更新CBL时,调用cbl_self_update_flash() }
2. 借助应用程序完成CBL更新
利用你已有的跳转逻辑,让应用程序承担CBL的擦写工作:
- 流程:
- CBL收到自身更新指令后,先将新的CBL镜像写入Flash的临时区域(比如应用程序的空闲块,或者专门预留的更新缓冲区)。
- CBL修改跳转标记,然后跳转到应用程序。
- 应用程序启动时检测到CBL更新标记,执行擦除原CBL区域、写入新CBL镜像的操作。
- 应用程序完成后,跳转回新的CBL。
- 优势:不需要修改CBL的内存布局,复用现有跳转逻辑,实现成本低。
3. 双Bank Flash硬件方案(如果MCU支持)
若你的MCU支持双Bank Flash(如STM32H7、STM32L4系列),可以采用双Bank交替运行的方式:
- 初始时CBL运行在Bank1,更新时将新CBL写入Bank2。
- 修改MCU的启动配置(比如通过Boot引脚设置,或者修改向量表偏移),让下一次启动从Bank2运行。
- 新CBL启动后,可选择擦除Bank1并同步镜像(保持双Bank一致性)。
- 注意:需要MCU硬件支持双Bank,且需适配启动流程和向量表配置。
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

