目录
SBL(Second Boot Loader)由PBL下载到RAM种进行代码执行,理论上PBL与SBL的代码功能应几乎是完全一模一样的,差异体现在如下俩部分:
1 PBL有的功能SB却L没有的功能: PBL下载并激活SBL
2 SBL有的功能PBL却没有的功能: flash驱动
本文档总结的情况,SBL仅仅只有flash驱动功能,基于vector的SIP包进行功能开发。
Flash驱动程序,主要提供Flash内存的擦除及刷写。在线升级中,首先擦除特定flash内存中的数据,然后把APP的可执行文件刷写进去。
flash驱动 常见方式
项目开发种,需要首先把刷写/擦除程序下载到RAM,然后再进行应用程序的下载。SBL有下面三种方式:
第一种是把驱动程序固化在falsh中,定义一个const数组,使用时拷贝到RAM中。
第二种是驱动程序是放在PC端,通过总线下载到RAM中去。
第三种是芯片供应商已经固化flash刷写/擦除程序在Flash的空间里,这时候只需根据提供的函数指针调用函数进行内存操作。
着重介绍第二种方式:
SBL flash驱动代码在PC端,当需要升级时,首先把SBL通过总线下载到RAM中,然后根据指针调用相关驱动函数。
在bootloader代码中定义一个全局变量的数组,其数组长度为SBL可执行代码的长度。同时设定这个数组存放的内存位置。
/*fbl_flio.c*/
#define FLASH_SIZE (2304)
__attribute__ ((section ("FLASHCODE")))
uint8 flashCode[FLASH_SIZE];
/*定义一个长度2048字节的数组,并把这个数组放在FLASHCODE段中*/
FLASHCODE段在内存中的具体定义,见如下链接文件:
链接文件定义了flashcode这段的开始地址为0x20005E00,长度为2034(这段内存是上一步骤定义的flashcode数组的存储内存)
MEMORY {
VECTORS : ORIGIN = 0x00000000, LENGTH = 652
RESERVED : ORIGIN = 0x00000400, LENGTH = 0x0C
OPTBYTES : ORIGIN = 0x0000040C, LENGTH = 0x48
FLASH : ORIGIN = 0x00000500, LENGTH = 0xF000
RAMCODE_FLASH : ORIGIN = 0x0000F500, LENGTH = 0x8B00
SIMFS_REGION : ORIGIN = 0x00018000, LENGTH = 0x2000
TPCFG : ORIGIN = 0x0001A000, LENGTH = 0x5000
OTPREGION : ORIGIN = 0x0001F000, LENGTH = 0x1000
APPL_VECT : ORIGIN = 0x00020000, LENGTH = 0x08
flash_bld_app : ORIGIN = 0x00020100, LENGTH = 0x100
flash_bld_dev : ORIGIN = 0x00020200, LENGTH = 0x100
RAM : ORIGIN = 0x1FFF8000, LENGTH = 0xE700
HEAP : ORIGIN = 0x20006700, LENGTH = 0x100
STACK : ORIGIN = 0x20006800, LENGTH = 0x800
magicflag : ORIGIN = 0x20005DF0, LENGTH = 0x10
flashcode : ORIGIN = 0x20005E00, LENGTH = 0x900
BSS : ORIGIN = 0x1FFFF000, LENGTH = 0x6DF0
}
.flash_drv (NOLOAD) : {
*(FLASHCODE)
} >flashcode
打开SBL的可执行文件:可看到该文件对应的开始地址为0x20005E00,长度为0x900。
当通过总线接收到SBL的文件后,会直接根据文件定义的start address 以及 data length直接把数据写进RAM中(写入RAM不需要驱动程序,可直接通过地址操作)。
这时候flash_code指向的这段RAM内存中。
/*fbl_ramio.c*/
#RAM_DRV_BUFFER flash_code
IO_ErrorType V_API_NEAR RamDriver_RWriteSync( IO_MemPtrType writeBuffer, IO_SizeType writeLength, IO_PositionType writeAddress ) /* PRQA S 3673 */ /* MD_FblRamio_3673 */
{
IO_ErrorType result;
IO_SizeType offset;
IO_SizeType i;
result = RamDriver_GetOffset(writeLength, writeAddress, &offset);
if (IO_E_OK == result)
{
for (i = 0u; i < writeLength; i++)
{
if (0u == (i & (RAM_DRV_POLLING_INTERVAL - 1u)))
{
RAM_DRV_POLLING_FUNCTION();
}
#endif /* RAM_DRV_POLLING_INTERVAL */
RAM_DRV_BUFFER[offset] = writeBuffer[i];
offset++;
}
}
return result;
}
/*******************************************************************
*注:在auotsar中,对内存的操作都认为是操作文件,都为专门的接口函数 */
如操作RAM的接口模块fbl_ramio.c
如操作falsh的接口fbl_flio.c
SBL驱动接口与Bootloader
前面介绍SBL如何在运行bootloader程序过程中,下载到RAM空间中,当程序下载到空间后,我们如何调用并访问需要的接口呢?
autosar的做法是,定义如下结构体变量,并把这个结构体放在特定的内存中
typedef struct
{
/** Version Information */
tFlashUint8 version; /**< Version of downloaded flash driver */
tFlashUint8 reserved; /**< Reserved for future use */
tFlashUint8 maskType; /**< Flash cell mask type */
tFlashUint8 CPUType; /**< Microcontroller type */
/** Function pointer to routines */
tFlashFct flashInitFct; /**< Function pointer to init function */
tFlashFct flashDeinitFct; /**< Function pointer to de-init function */
tFlashFct flashEraseFct; /**< Function pointer to erase function */
tFlashFct flashWriteFct; /**< Function pointer to write function */
} tFlashHeader; /* PRQA S 3448 */ /* MD_FblDrvFlash_3448 */
# define FLASH_START_SEC_FLASH_DRIVER_HEADER
# include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_19.1 */
const tFlashHeader flashHeader= /* PRQA S 3408 */ /* MD_FblDrvFlash_3408 */
{
FLASH_DRIVER_VERSION_INTERFACE,
0x00,
FLASH_DRIVER_VERSION_MASKTYPE,
FLASH_DRIVER_VERSION_MCUTYPE,
&ExpFlashInit,
&ExpFlashDeinit,
&ExpFlashErase,
&ExpFlashWrite
};
# define FLASH_STOP_SEC_FLASH_DRIVER_HEADER
# include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_19.1 */
这个结构体包含了当前驱动的版本信息以及flash驱动的函数指针。这个flash_header指向的是0x20005E00的位置。
打开SBL的可执行文件如下:(内存范围 0x20005E00~0x20005E16)
同时打开打开map文件,查看接口函数的地址:
可以发现:
0x20005E004~0x20005E007 存放的是ExpFlashDeinit的函数的地址: 20 00 5f 25
0x20005E008~0x20005E00b 存放的是ExpFlashInit的函数的地址: 20 00 5e b9
0x20005E00c~0x20005E00f 存放的是ExpFlashErase的函数的地址:20 00 5f 6d
0x20005E010~0x20005E014 存放的是ExpFlashWrite的函数的地址:20 00 5f c9
在bootloader程序中:
通过以flash_code的地址为基地址加上偏移量,就可以找到存储函数地址的地址。
/*可以看到MAP文件里的函数的地址的数值与hex文件里的数据对比,hex里函数的地址是在MAP文件对应函数地址的值的基础上加1,而不是相等?*/
这是因为本芯片采用Thumb-2指令,如果指令读取20 00 5e b9的内容,实际上读取20 00 5e b8 的内容。
typedef void (* tFlashFct)( tFlashParam *fp );
/***********************************************************************************************************************
* MACROS
**********************************************************************************************************************/
/* macros to call flash driver functions in flashCode buffer ----------------*/
#define FLASH_DRIVER_WRITE(flashCode, fp) \
((tFlashFct)(*(tFlashUint32 *)&(flashCode)[16]))(fp) /* PRQA S 3453 */ /* MD_MSR_19.7 */
/* Flash erase function --------------------------------------------*/
#define FLASH_DRIVER_ERASE(flashCode, fp) \
((tFlashFct)(*(tFlashUint32 *)&(flashCode)[12]))(fp) /* PRQA S 3453 */ /* MD_MSR_19.7 */
/* Flash init function ---------------------------------------------*/
#define FLASH_DRIVER_INIT(flashCode, fp) \
((tFlashFct)(*(tFlashUint32 *)&(flashCode)[4]))(fp) /* PRQA S 3453 */ /* MD_MSR_19.7 */
/* Flash deinitialization function ---------------------------------*/
#define FLASH_DRIVER_DEINIT(flashCode, fp) \
((tFlashFct)(*(tFlashUint32 *)&(flashCode)[8]))(fp) /* PRQA S 3453 */ /* MD_MSR_19.7 */
根据不同的flash使用手册,有如下需要根据flash手册进行确认:
1 底层单次写入flash的数据长度,S32k144单次只能写入8字节。
/* Minimum number of bytes that have to be programmed at a time */
#define FLASH_SEGMENT_SIZE 0x08ul
flash接口函数ExpFlashWrite传递的刷写数据的大小必须是8的整数倍。
2 flash单次擦除falsh的数据长度是0x1000
# define FLASH_SECTOR_SIZE 0x1000u
flash接口ExpFlashErase传递擦除的数据大小是必须是0x1000的整数倍。
常用FLASH擦写规则
- 最小擦除单位:扇区
- 可选择擦除单位:扇区、块、全片
- 最小编程(写入)单位:页( 256 Byte),大于256 Byte则需要循环写入。
- 未写入时FLASH里面的数据为全1,即0xFF。
(reference:FLASH知识及读写擦除规则 - CodeAntenna)