最近在做一款车载产品的bootloader 设计,考虑可靠性,所以采用了Swap A/B方案
1、memmory 分配方案。
考虑安全性,同时充分利用MCU的片内资源,故将bootloader 放到了flexNVM,也就是另外一块的flash中。
(1 ) flexNVM 应是64KB,其中32KB 给bootloader,另外的当做EEPROM,用来存储一些需要长久保存的信息,比如车辆VIN,以及一些security KEY,OTA用到的秘钥,蓝牙车钥匙的 私钥等。
(2)考虑到对于固件信息做检验,所以拿出4KB ,用于存放固件信息,以及固件是否有效标志等。
2、固件A的链接文件配置
3、固件B的链接文件 配置
链接文件的配置,是对照memory分配表来的。
这里面很关键的就是中断向量表的位置。
4、分析下如何 这套机制如何工作的。
(1)startup.c 中的 一个初始化函数
startup.c 文件中的 section 定义
void init_data_bss(void)
{
uint32_t n;
uint8_t coreId;
volatile uint32_t * vectors[NUMBER_OF_CORES] = FEATURE_INTERRUPT_INT_VECTORS;
/* ARM Compiler 4/5 or ARM Compiler 6 (armclang) */
#if !((defined (__CC_ARM ) ) || \
(defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)) )
/* Declare pointers for various data sections. These pointers
* are initialized using values pulled in from the linker file */
uint8_t * data_ram;
uint8_t * code_ram;
uint8_t * bss_start;
const uint8_t * data_rom, * data_rom_end;
const uint8_t * code_rom, * code_rom_end;
const uint8_t * bss_end;
#endif
/* Addresses for VECTOR_TABLE and VECTOR_RAM come from the linker file */
extern uint32_t __RAM_VECTOR_TABLE_SIZE[];
extern uint32_t __VECTOR_TABLE[];
extern uint32_t __VECTOR_RAM[];
/* Get section information from linker files */
#if defined(__ICCARM__)
/* Data */
data_ram = __section_begin(".data");
data_rom = __section_begin(".data_init");
data_rom_end = __section_end(".data_init");
/* CODE RAM */
#pragma section = "__CODE_ROM"
#pragma section = "__CODE_RAM"
code_ram = __section_begin("__CODE_RAM");
code_rom = __section_begin("__CODE_ROM");
code_rom_end = __section_end("__CODE_ROM");
/* BSS */
bss_start = __section_begin(".bss");
bss_end = __section_end(".bss");
/* ARM Compiler 4/5 or ARM Compiler 6 (armclang) */
#elif ((defined (__CC_ARM ) ) || \
(defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)) )
extern uint32_t Image$$VECTOR_ROM$$Base[];
extern uint32_t Image$$VECTOR_RAM$$Base[];
extern uint32_t Image$$RW_m_data$$Base[];
// #define __VECTOR_TABLE Image$$VECTOR_ROM$$Base
#define __VECTOR_RAM Image$$VECTOR_RAM$$Base
#define __RAM_VECTOR_TABLE_SIZE (((uint32_t)Image$$RW_m_data$$Base - (uint32_t)Image$$VECTOR_RAM$$Base))
#else
extern uint32_t __DATA_ROM[];
extern uint32_t __DATA_RAM[];
extern uint32_t __DATA_END[];
extern uint32_t __CODE_RAM[];
extern uint32_t __CODE_ROM[];
extern uint32_t __CODE_END[];
extern uint32_t __BSS_START[];
extern uint32_t __BSS_END[];
data_ram = (uint8_t *)__DATA_RAM;
data_rom = (uint8_t *)__DATA_ROM;
data_rom_end = (uint8_t *)__DATA_END;
/* CODE RAM */
code_ram = (uint8_t *)__CODE_RAM;
code_rom = (uint8_t *)__CODE_ROM;
code_rom_end = (uint8_t *)__CODE_END;
/* BSS */
bss_start = (uint8_t *)__BSS_START;
bss_end = (uint8_t *)__BSS_END;
#endif
coreId = (uint8_t)GET_CORE_ID();
/* Check if VECTOR_TABLE copy is needed */
if (__VECTOR_RAM != __VECTOR_TABLE)
{
/* Copy the vector table from ROM to RAM */
for (n = 0; n < (((uint32_t)__RAM_VECTOR_TABLE_SIZE)/sizeof(uint32_t)); n++)
{
__VECTOR_RAM[n] = __VECTOR_TABLE[n];
}
/* Point the VTOR to the position of vector table */
*vectors[coreId] = (uint32_t)__VECTOR_RAM;
}
else
{
/* Point the VTOR to the position of vector table */
*vectors[coreId] = (uint32_t)__VECTOR_TABLE;
}
/* ARM Compiler 4/5 or ARM Compiler 6 (armclang) */
#if !((defined (__CC_ARM ) ) || \
(defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)) )
/* Copy initialized data from ROM to RAM */
while (data_rom_end != data_rom)
{
*data_ram = *data_rom;
data_ram++;
data_rom++;
}
/* Copy functions from ROM to RAM */
while (code_rom_end != code_rom)
{
*code_ram = *code_rom;
code_ram++;
code_rom++;
}
/* Clear the zero-initialized data section */
while(bss_end != bss_start)
{
*bss_start = 0;
bss_start++;
}
#endif
}
编译工程,查看map信息,看看生产的文件和配置的是否一致。
![](https://img-blog.csdnimg.cn/20200711235346408.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzEzOTc5NA==,size_16,color_FFFFFF,t_70)
5、跳转验证,
(1) 从bootloader --》固件A [测试OK]
(2) 从bootloader -》固件B [目前测试失败,固件B的APP不能启动]
(3)从固件A---->boot ,明天测试
(4)从固件B—》boot 明天测试
(5) 从固件A—固件B 测试。当然这个貌似没有这个场景的需求。
bootloader 方案的健壮可靠和合理,对于车载的OTA 来说,十分重要。
可以通过远程留解决很多软件bug。
防止变砖 是最基本,也是最核心的最求。
马上12点了,赶紧睡,写的很匆忙,条理性也不好,过几天有时间再完善吧!
2020-7-11
23:58
//========================================================//
**
2020年7月12更新
**
今天是周末,昨晚的跳转问题没有找到原因,今天心里堵的慌,下午刚好有时间,就赶紧坐下来 研究这个问题,终于在天黑的时候,搞定了。
目前:
要点如下:
1、从boot调准至APP的入口地址,必须是中断向量:m_interrupt_start 的地址,而不是固件存放的地址。
比如APP-A的配置如下:
因为是采用的IDE刷写,所以在下载程序到MCU时,这个地方的配置也很重要。(需要多验证,貌似这个地方也不影响,应为在hex文件中,都定义了数据对应到flash中的位置)
boot 工程中,main的代码如下:
while(1)
{
// LPUART0_send_char('>');
PTD->PTOR |= (1<<13);
sys_delay(500);
cnt++;
Uart_Printf("waiting for jump...%d\r\n",cnt);
if(cnt == 20)
{
Uart_Printf("P_flash progamme test!\r\n");
// P_Flash_test();
Uart_Printf("I will be jump to the app-A=%d\r\n",cnt);
sys_delay(1000);
INT_SYS_DisableIRQGlobal();
boot_jump_to_app(APP_BLOCK_B);
}
}
}
跳转 的函数如下,其实就直接跳转至地址;
void boot_jump_to_app( uint8_t app_block )
{
if(app_block == APP_BLOCK_A)
{
(*(void (*)(void))(APP_StartAddr_A))();/*run the function via function pointer convert with a certain address*/
while(1);
}
else if(app_block == APP_BLOCK_B)
{
(*(void (*)(void))(APP_StartAddr_B))();/*run the function via function pointer convert with a certain address*/
while(1);
}
}
其中 APP_StartAddr_B,这里要注意,要在其实地址后加+4,也就是一个PC寄存器的长度。
MCU上电后,要从PC寄存器抓起 数据,然后才是进入应用。
接下来的工作:
就是要搞定从APP应用程序向bootloader跳转,这也是我们的OTA 的流程,
所以这个必须要解决。
明天继续更新吧。