统一bootloader系统
统一bootloader范围
下图展示了PC通过适配器与ECU连接。高亮公布部分是ECU和PC共同的。所有平台都是一样的。
统一bootloader关键特征
- UDS(ISO14229)
- TP(ISO1765-2/CAN,ISO17987-2/LIN)
- Flash驱动包括共同的要求
- 从主机下载Flash驱动
- bootloader检测APP完整性
- APP和bootloader控制信号放在RAM
- 使用Reset(watchdog)切换APP或者bootloader
- CRC
- AES
- 随机数
- 硬件驱动HAL
UI关键特征
- 支持JSON配置文件控制固件下载过程
- 支持导入/导出JSON
- 支持配置JSON
- 支持打印日志(例如:发送/接收 消息和时间戳)
- 支持解析OBJ(S19/Bin/HEX)
- 支持两种选择窗口:选择Flash driver或者APP OBJ
- 支持配置功能和外设,回应CAN的ID,LIN能够配置NAD ID
- 支持CRC和AES。都可用通过用户改变
- 支持显示下载进程
- 下载固件的状态
- UDS和TP通过PEAK与 ECU通讯
统一bootloader 复位/供电过程
bootloader reset/power处理过程解释。在power/reset过程中,通过bootloader检查下述的执行。
- 检查updateAPP标识
- 检查APP有效性
下表展示bootloader需要依赖的flag值。
Update APP flag | APP 有效性标识 | 描述 |
---|---|---|
TRUE | N/A | 进入bootloader模式,更新APP固件 |
FALSE | TRUE | 跳转到APP |
FALSE | FALSE | 进入bootloader模式等待更新APP固件 |
从上述信息,bootloader和APP需要交换信息,该信息存储到保留RAM。警告复位可以保存信息,但在冷复位(开/关电源)期间,信息可能丢失。下图展示bootloader reset/power 过程。
划分bootloader、APP falsh和RAM存储空间
MCU有两个内部存储(Flash和RAM)。下述例子划分FLASH和RAM空间给bootloader和APP。
- FLASH:POWER ON或者reset都会进入bootloader。bootloader复位处理 存储着 默认复位处理地址。
- RAM:bootloader和app通过保留RAN交换信息。意味着,bootloader和APP从link文件细分一些内部RAM来交换信息。
下面是在S32K144划分闪存和RAM空间的示例
S32K144 p-flash(program flash)和RAM空间
- 存放程序flash
- bootloader RAM空间
- APP RAM空间
细分bootloader p-flash和flash驱动尺寸都基于bootloader和flash驱动尺寸。P-flash的bootloader使用flash尺寸都是N*扇。Bootloader和APP交换包括CRC信息。CRC算法在bootloader和APP都是相同的。
统一bootloader系统架构
bootloader有6个模块,即服务层、传输层、FIFO、硬件通讯驱动(CAN/LIN etc),flash驱动和flash操作。统一bootloader通过下述步骤通讯。
- Services layer
- 接收从TP层(services RX FIFO)发送消息
- 控制启动过程。例如操作 初始化flash、擦出,编程等等
- host回复消息(发送消息给Sevices TX FIFO)
- TP
- 接收来自TP RX FIFO消息(CAN/LIN etc)
- 从TP RX FIFO分析接收的数据。如果消息可用,发送一帧给服务RX FIFO
- 如果来自sevices层消息接受,发送消息给TP TX FIFO。
- TP发送一帧消息给sevice RX FIFO并删除帧头和checksum
- TP加帧、checksum和发送一帧消息给TP TX FIFO
- Hardware driver(CAN/LIN etc)通讯
- 从总线读取数据和发送数据给TP
- RX FIFO读取来自TP TX FIFO的数据和写到总线
如何将堆栈移植到新平台上
统一bootloader系统图
下图展示统一bootloader的系统图。统一bootloader需要一些硬件驱动,如CAN/LIN,watchdog,定时器,flash和debug(I/O,Uart)来移植到新的平台。需要准备所有的硬件驱动。
统一bootloader堆栈文件夹
序号 | 文件名 | 描述 | 移植是否要验真 |
---|---|---|---|
1 | auto_lib | 发布函数如memcpy,memset,rand等等 | NO |
2 | boot | 给其他模块提供启动服务,如boot信息,跳转APP | Yes |
3 | Debug | UART打印,debug I/O,定时器 | Yes/No |
4 | Demo | 使用bootloader堆栈的demo。用户只用添加时钟和CAN/LIN模块到文件中 | YES/No |
5 | FIFO | FIFO模块 | No |
6 | Flash_APP | 提供操作UDS的FLASH服务 | No |
7 | HAL | CRC,FLASH、定时器、UDS算法、看门口 HAL硬件抽象库。使用HAL库API提花底层API | Yes |
8 | public_inc | 公共头文件,如用户配置文件和重定义数据类型。默认是32位MCU。 | Yes |
9 | UDS_stack | TP和UDS堆栈 | Yes |
统一bootloader移植文件
下面子节中的阴影文本表示在移植引导加载程序堆栈时需要注意或修改它.
Public_inc文件夹
包含common_types.h, toolchain.h, autolibc.h and user_config.h
includes.h
为解决不同平台数据类型冲突(e.g. uint32/16/8).includes.h头文件应该包含其他的头文件。
user_config.h
用户关闭或者打开一些宏。下表描述可以配置的宏
序号 | 宏名 | 描述 |
---|---|---|
1 | CORE_NO | 保留 |
2 | EN_DEBUG_IO | 测试时间或者翻转LED |
3 | EN_DEBUG_TIMER | 保留 |
4 | EN_ASSERT | 是否使能断言 |
5 | EN_UDS_DEBUG、EN_TP_DEBUG、EN_APP_DEBUG、EN_DEBUG_FIFO | 通过串口打印信息 |
6 | DebugBootloader_NOTCRC | 是否使用CRC检测flash驱动和app镜像 |
7 | EN_ALG_SW、EN_ALG_HW、AES_SEED_LEN | 软硬件是否使能UDS算法,默认是使能软件 |
8 | EN_CAN_TP、EN_LIN_TP、EN_ETHERNET_TP、EN_OTHERS_TP | 使能TP类型。 |
9 | RX_FUN_ID、RX_PHY_ID、TX_ID | CAN RX功能ID、CAN RX物理ID、CAN TX消息ID。如果使能TP应该配置这些ID |
10 | RX_BOARD_ID、RX_FUN_ID、RX_PHY_ID、TX_ID | LIN通上 |
11 | EN_CRC_HARDWARE、EN_CRC_SOFTWARE | 软和硬件使能计算CRC。软件CRC必须使能 |
12 | RX_BUS_FIFO、RX_BUS_FIFO_LEN | RX BUS FIFO ID 和 长度 |
13 | TX_BUS_FIFO、TX_BUS_FIFO_LEN | TX 消息 BUS FIFO ID和长度 |
14 | EN_SUPPORT_APP_B、EN_NEWEST_APP_INVALID_JUMP_OLD_APP | 保留给OTA/FOTA |
15 | DisableAllInterrupts()、EnableAllInterrupts() | 使能/关闭所有中断。bootloader堆栈使用API关闭/使能所有中断。 |
16 | MCU_S12Z、MCU_S32K14x、MCU_TYPE | 设置当前MCU类型。使用擦除P-flash区域尺寸和计算擦除P-flash时间 |
17 | EN_DELAY _TIME、DELAY_MAX_TIME_MS | 使能进入bootloader模式最大时间。如果APP要求进入bootloader模式和不发送任何诊断命令,bootloader等待最大延时时间。如果APP有效,将会跳转到APP。 |
18 | EN_TX_CAN_FD | 使不使能TX CAN FD特性。如果使能该宏,发送CAN消息将会基于CAN FD。 |
command_types.h
这个文件都是一些基本数据类型定义。e.g. uint32/16/8.如果基本数据类型与typedef冲突,应该给编译器添加一些条件防止冲突。
toolchain.h
重定义一些关键词。e.g. inline,asm,interrupt etc。Bootloader 堆栈不使用这些文件的关键词。
HAL库
CRC HAL
- crc_hal:通过软硬件计算CRC。CRC模块必须使能(使能在Public_inc/usr_config.h中EN_CRC_SOFTWARE宏)。bootloader使用CRC模块之前,要初始化CRC硬件模块。可以修正和计算CRC模块和CRC表格。Bootloader和APP须有相同的CRC表格和计算方法。如果他们两个都不相同,APP和bootloader交换数据将会失败。
CRC API
API名字 | 描述 |
---|---|
gs_aCRC16Tab | CRC表格 |
boolean CRC_HAL_Init(void) | 初始化CRC模块。成功返回ture,失败返回false |
void CRC_HAL_CreatHarewareCrc(const uint8 *i_pucDataBuf,const uint32 i_ulDataLen,uint32 *m_CurCrc) | 硬件计算CRC值。i_pucDataBuf:计算CRC数据。i_ulDataLen:消息长度。m_CurCrc:计算出CRC值 |
void CRC_HAL_CreatsoftwareCrc(const uint8 *i_pucDataBuf,const uint32 i_ulDataLen,uint32 *m_CurCrc) | 软件计算CRC值。i_pucDataBuf:计算CRC数据。i_ulDataLen:消息长度。m_CurCrc:计算出CRC值 |
CRC_HAL_Deinit | 保留 |
FLASH HAL
flash_hal.c应该要移植。
API名字 | 描述 |
---|---|
#define SECTOR_LEN(XXX) #define MAX_ERASE_SECTOR_FLASH_MS(XX) | SECTOR_LEN索引是sector大小(BYTES)。擦错sector最大时间 |
#define APP_VECTOR_TABLE_OFFSET | gs_asrBlockNumA/B头文件的APP向量表偏移。该值配置目标 |
#define RESET_HANDLE_OFFSET | 来自vector表复位 handle/startup偏移 |
#define HANDLER_ADDR_LEN | 复位handle点长度。 |
boolen FLASH_HAL_Init(void) | 初始化flash.可以计算和存储在flash驱动表所有的flash驱动APIs地址。返回初始化flash状态。 |
boolen FLASH_HAL_EraseSector(const uint32 i_startAddr,const uint32 i_noEraseSectors) | 擦除flash扇区。擦除flash N*扇区。i_startAddr:擦出flash开始地址。i_noEraseSectors:擦出扇区的数量。返回擦出flash状态 |
boolen FLASH_HAL_WriteData(const uin32 i_startAddr,cosnt uint8 i_pDataBuf,const uint32 i_dataLen) | flash的program data有一些条件。如程序总是8N bytes data。如果程序数据不等于8N,应该数据长度加到8 *N。i_startAddr:程序数据开始地址。i_pDataBuf:程序数据buf。i_DataLen:程序数据长度 |
boolean FLASH_HAL_ReadData(const uin32 i_startAddr,const uint32 i_readLen,uint8 *o_pDataBuf) | 保留 |
void FLASH_HAL_Deinit(void) | 保留 |
boolean FLASH_HAL_RegisterFlashAPI(tFlashOperateAPI *o_pstFflashOperateAPI) | 寄存器flashAPI.API寄存器操作flash函数。目的为了应hi不需要校验 |
Flash驱动编译独立位置代码。意味着flash驱动能够复制到任何一个RAM地址和完成正常运行。Flash驱动需要表,该表包含了一些API。init/erase/program/read/verity 闪存空间。flash_hal.c 调用APIs可以计算在RAM 的init/erase/program/read/verity闪存地址。
如果将闪存驱动器复制到0x1000 (RAM),则闪存驱动器API表将重新计算API起始地址。api的起始地址可以在Flash_init中重新计算。
timer_hal
API名字 | 描述 |
---|---|
void TIMER_HAL_Init(void) | 初始化定时器。定时器设置tick定时1ms |
void TIMER_HAL_1msPeriod | 1ms调用一次。可以通过ISR或者主函数。 |
UDS诊断HAL
UDS诊断是基于AES。如果用户不使用诊断,可以加自己的诊断到该文件。
API名字 | 描述 |
---|---|
gs_aKey | AES key |
gs_UDS_SWTimerTickCnt | 存储软件定时器Tick计数。给AES使用。 |
watchdog_hal
在bootloader,watchdog模块常常用作检测系统是否工作或者触发bootloader复位。
API名字 | 描述 |
---|---|
void WATCHDOG_HAL_Init(void) | 初始化看门口模块 |
void WATCHDOG_HAL_Fed(void) | 喂狗 |
void WATCHDOG_HAL_SystemReset(void) | Trigger system reset |
void WATCHDOG_HAL_Deinit(void) | 保留 |
UDS堆栈
UDS堆栈包含UP和UDS层。为了移植bootloader堆栈,你需要验真xx_cfg.c.
- TP:当前版本,TP包含CAN和LIN。CAN TP是基于ISO 1576502和LIN TP基于ISO17987-2.
- TP_cfg.c:提供基础服务给TP和UDS层。如果使用CAN或者LIN TP,不需要验证这个文件。为了在bootloade加一些ISO标准,你需要加一些服务到文件,下表格展示服务TP_cfg.c描述.
TP_cfg.c
API名字 | 描述 |
---|---|
uint32 TP_GetConfigTxMsgID(void) | 获得配置TX消息ID |
uint32 TP_GetConfigRxMsgFUNID(void) | 获取配置RX功能消息ID |
uint32 TP_GetConfigRxMsgPHYID(void) | 获取配置RX物理消息ID |
uint32 TP_GetConfigRxMsgBoardcastID(void) | 获取配置RX抓板NAD |
uint32 TP_GetConfigRxMsgBoardcastID(void) | 获取配置RX抓板NAD |
void TP_RegisterTransmittedAFrmaeMsgCallBack(const tpfUDSTxMsgCallBack i_pfTxMsgCallBack) | 寄存器发送一帧等待回调 |
void TP_DoTransmittedAFrameMsgCallBack(const uint8 i_result) | 发送一帧调用 |
boolean DriverWriteDataInTP(const uint32 i_RxID,const uint32 i_RxDataLen,const uint8 *i_pRxDataBuf) | 当驱动接收到来自总线的消息,驱动调用这个API将消息发送给TP层。 i_RxID:RX消息ID。如CAN ID:0x7DE。i_RxDataLen:RX 消息长度。i_pRxDataBuf:RX 消息buf |
boolen TP_DriverReadDataFromTP(const uint32 i_readDataLen,uint8 *o_pReadDatabuf,uint32 *o_pTxMsgID,uint32 *o_pTxMsgLength) | 调用API获取TP发送驱动消息。i_readDataLen:TX消息长度,o_pReadDatabuf:消息buf o_pTxMsgID:TX消息ID,CANID。o_pTXMsgLength:TX消息长度 |
void TP_RegisterAbortTxMsg(const void (*i_pfAbortTxMsg)(void)) | 当TP发送消息超时,TP调用这个API中止发送。i_pfAbortTxMsg:中止TX消息指针 |
void TP_DoTxMsgSuccesfulCallback(void) | 当驱动发送消息,必须调用该API。作为,xx_TP_cfg.c发送API有一个参数显示TX消息成功,如果你有使用这个指针,这个API不再被调用。 |
TP层提供3个API给驱动。
当驱动接收一个消息,将发送消息给TP层调用TP_DrivierWriteDataInTP。
当消息发送时,需要调用TP_DoTxMsgSuccesfulCallback来通知TP层。
驱动需要检查TP层,如果消息需要在一次发送TP_DriverReadDataFromTP。
下图将显示TP_DriverWriteDataInTP,TP_DriverReadDataFromTP和TP_DoTxMsgSuccesfulCallback的例子。
CAN_TP_cfg.c
如果你使能CAN_TP,这个文件配置信息和验证CAN TP层一些APIs.
CAN TP有一些信息需要配置如STmin,N_As,N_Bs等待。大多数时候,你不需要验真这些参数。下图展示参数。
下表格描述这些API
API名字 | 描述 |
---|---|
uint8 CANTP_TxMsg(const tUdsId i_xTXId,const uint16 i_DataLen,const uint8 *i_pDataBuf.const tpfNetTxCallBack i_pfNetTxCallBack,const uint32 txBlockingMaxtime) | CAN TP TX消息。发送消息给硬件API。i_xTxId: TX message ID、i_DataLen: TX message length、i_pDataBuf: message buff、i_pfNetTxCallBack: TX message successful callback、txBlockingMaxtime: reserved |
uint8 CANTP_RxMsg(tUdsld *o_pxRxId,uint8 *o_pRxDataLen,uint8 *o_pRxBuf | CANTP RX接收来自FIFO消息。CAN驱动从总线接收消息和检查ID有效性和些写FIFO消息。o_pxRxI:RX 消息ID,o_pRxDataLen:RX消息数据长度,o_pRxBuf:RX消息buf |
tUdsId CANTP_GetConfigTxMsgID(void) | 获得CAN TP配置TX消息ID |
tUdsId CANTP_GetConfigRxMsgFUNID(void) | Get CAN TP config RX function ID |
boolean CANTP_IsReceivedMsgIDValid(const uint32 i_receiveMsgID) | 检查接收到消息是否有效 |
tUdsId CANTP_GetConfigRxMsgPHYID(void) | Get CAN TP config RX message physical ID |
void CANTP_AbortTxMsg(void) | 等待发送消息超时,TP层中止硬件发送消息。 通过硬件中止发送的消息。 |
void CANTP_RegisterAbortTxMsg(const tpfAbortTxMsg i_pfAbortTxMsg) | Register abort TX message call back |
boolean CANTP_DriverWriteDataInCANTP(const uint32 i_RxID, const uint32 i_dataLen, const uint8 *i_pDataBuf) | 这个API提供给TP_cfg.c.常用作驱动接收和发送消息给TP层 |
boolean CANTP_DriverReadDataFromCANTP(const uint32 i_readDataLen, uint8 *o_pReadDataBuf, tTPTxMsgHeader *o_pstTxMsgHeader) | 这个API提供给TP_cfg.c.常用作驱动读取TP层的消息。 |
void CANTP_DoTxMsgSuccessfulCallBack(void) | This API is provided for TP_cfg.c. It is used by driver to send message successfully and call callback the API |
static boolean CANTP_ClearTXBUSFIFO(void) | This API is used for clear TX BUS FIFO |
boot
boot模块包含了boot.c和boot_cfg.c。boot模块检查APP可用,跳转到APP和管理信息交换。移植时,有两个重点特征
- 跳转到APP复位处理地址
- 配置和交换信息地址和检查有效值。
Boot_cfg.c
- 两种场景APP和bootloader交换信息。
场景1 APP接收更新固件命令。APP按照下述步骤:
- 发送请求,进入bootloader模式。
- 更新交换信息CRC
- 通过UDS层发送请求
- 通过看门口触发MCU复位
进入bootloader模式,检查请求进入bootloader设置和CRC有效 - 清除请求进入bootloader模式
- 更新CRC交换信息
- 通过UI/host的UDS发送接入bootloader模式命令
常见2 成功成bootloader固件。bootloader按照下述步骤:
- 设置APP更新
- 更新CRC交换信息
- 通过UDS协议到UI/host
- 看门狗复位
进入APP检查下述: - 检查APP更新成功和CRC交换信息有效
- 清除APP更新标识、CRC更新交换信息成功
- 通过UI/主机的UDS层发送激活消息
Boot_cfg.c
给boot提供服务,比如 APP和bootloader交换信息地址配置和值,检查进入bootloader模式。
API | 描述 |
---|---|
不同平台配置不同地址(info StartAddr,requestEnterBootloaderAddr和downloadAppSuccessfulAddr) | infoDataLen // information length 、requestEnterBootloader// request enter bootloader valid value、downloadAPPSuccessful//download APP successful、valid valueinfoStartAddr//information start address、requestEnterBootloaderAddr//request enterbootloader address、downloadAppSuccessfulAddr//download APPsuccessful address |
void SetDownloadAppSuccessful(void) | Set download APP successful |
boolean IsRequestEnterBootloader(void) | Does request enter bootloader mode? Return TRUE/FALSE |
void ClearRequestEnterBootloaderFlag(void) | Clear request enter bootloader mode |
boolean Boot_IsPowerOnTriggerReset(void) | The function is used to check power on trigger reset or not? Return TRUE/FALSE. |
void Boot_PowerONClearAllFlag(void) | When Powered on, clears all exchange information for ECC. |
void Boot_RemapApplication(void) | Reserved |
void Boot_JumpToApp(const uint32 i_AppAddr) | Jump to APP. This function is used to jump to APP.i_AppAddr: jump to APP address |
boolean Boot_IsInfoValid(void) | Check information validity? Return TRUE/FALSE |
uint16 Boot_CalculateInfoCRC(void) | Calculate information CRC. Return CRC value |
Demo
Demo文件夹包含了bootloader_main.c 和 bootloader_main.h 。这些文件提供如何调用其他模块的信息。
API | 描述 |
---|---|
void BOOTLOADER_MAIN_Init(void (*pfBSP_Init)(void),void (*pfAbortTxMsg)(void)) | 初始化所有bootloader模块。这个函数可用加入时钟,CAN/LIN初始化。pfBSP_Init:指针函数,包含了BSP初始化。BSP应该包含:clock/BUS(CAN/LIN)/flash init。Flash初始化不太重要,因为flash驱动初始化有多种方法。 pfAboreTxMsg:指针函数常用作中止TX消息和清除一些全局变量。如果没有使用,将这个设置为NULL_PTR; |
void BOOTLOADER_MAIN_Demo(void) | Bootloader主函数。用户调用主函数。e.g. void main(void) |
Debug
Debug文件夹包含了bootloader_debug.c,debug_IO.c和debug_timer.c
bootloader_debug.c
包含了debugIO,debug timer和debug打印。
API | 描述 |
---|---|
void BOOTLOADER_DEBUG_Init(viod) | 初始化debugIO,定时器和打印。通常移植时,不需要验证 |
void Bottloader_DebugPrintInit(void) | UART硬件初始化 |
void Bootloader_DebugPrint(const char *fmt,…) | 通过串口打印信息。通常移植需要调换硬件发送消息函数 |
Debug_IO.c
API | 描述 |
---|---|
void DEBUG_IO_Init(viod) | 如果使能debug_IO,应该加debugIO初始化 |
void DEBUG_IO_Deinit(void) | 保留 |
void DEBUG_IO_SetDebugIOLow(void) | 设置低位debugIO |
void DEBUG_IO_SetDebugIOHigh(void) | 设置高位debugIO |
viod DEBUG_IO_ToggleDebugIO(void) | 翻转debugIO |
FIFO
FIFO通常被用作不同层交换信息。FIFO不能被修改。只需要配置FIFO长度或者应用支持最大的FIFO数量。意味着FIFO支持最大存储数据。如果应用长度超过数据长度,长度采用将会失败。
FLS_APP
FLS_APP模块使用CRC,UDS和看门口APIs.FLS_APP模块不能被修改。默认程序FLASH存储是128bytes.如果需要改变程序bytes或者配置指针,打印长度。可以在FLS_APP.h能修改。
bootloader代码大小
不同平台和编译器的bootloader堆栈测试代码大小。下图展示代码大小的细节信息。
APP需要
APP和bootloader有相似的特征,如CRC、定义交换信息和无效值。如果APP支持FOTA,需要额外验证APP信息。