MS-RTOS bootloader
本章将介绍 MS-RTOS bootloader —— MS-BOOT 开发知识。
MS-BOOT 工作流程
- 判断升级请求文件是否存在,如果不存在,则启动 MS-RTOS
- 从升级请求文件读出升级动作文件的路径
- 判断升级动作文件是否存在,如果不存在,则启动 MS-RTOS,如果存在,则加载升级动作文件到内存
- 分析所有升级动作,如果有升级 OS 的动作(
action id
为MS_FLASHFS_ACTION_UPDATE_OS
),则升级 MS-RTOS 镜像 - 启动 MS-RTOS
如果在升级过程中发生意外断电,由于升级请求文件还存在,将在重新上电时再次进行升级。
内部 FLASH 分区
下面以 STM32F413 芯片为例说明内部 FLASH 如何分区:STM32F413 内部 FLASH 有 16 个扇区。前四个扇区 16K,第五个扇区 64K,后十一个扇区 128K。其中 MS-BOOT 必须放置在开始位置,因此占用 0 - 1 两个扇区共 32K 空间。MS-FLASHFS 的主文件系统表和从文件系统表分别占用 2 和 3 两个扇区。MS-RTOS 占用 4 - 5 两个扇区共 192K 空间。剩余 6 - 15 扇区分配给 MS-FLASHFS,如下表所示:
Name | Block base addresses | Size | Content |
---|---|---|---|
Sector 0 | 0x0800 0000 - 0x0800 3FFF | 16Kbyte | MS-BOOT |
Sector 1 | 0x0800 4000 - 0x0800 7FFF | 16Kbyte | MS-BOOT |
Sector 2 | 0x0800 8000 - 0x0800 BFFF | 16Kbyte | MS-FLASHFS MAIN TABLE |
Sector 3 | 0x0800 C000 - 0x0800 FFFF | 16Kbyte | MS-FLASHFS SEC TABLE |
Sector 4 | 0x0801 0000 - 0x0801 FFFF | 64Kbyte | MS-RTOS |
Sector 5 | 0x0802 0000 - 0x0803 FFFF | 128Kbyte | MS-RTOS |
Sector 6 | 0x0804 0000 - 0x0805 FFFF | 128Kbyte | MS-FLASHFS DATA |
Sector 7 | 0x0806 0000 - 0x0807 FFFF | 128Kbyte | MS-FLASHFS DATA |
Sector 8 | 0x0808 0000 - 0x0809 FFFF | 128Kbyte | MS-FLASHFS DATA |
Sector 9 | 0x080A 0000 - 0x080B FFFF | 128Kbyte | MS-FLASHFS DATA |
Sector 10 | 0x080C 0000 - 0x080D FFFF | 128Kbyte | MS-FLASHFS DATA |
Sector 11 | 0x080E 0000 - 0x080F FFFF | 128Kbyte | MS-FLASHFS DATA |
Sector 12 | 0x0810 0000 - 0x0811 FFFF | 128Kbyte | MS-FLASHFS DATA |
Sector 13 | 0x0812 0000 - 0x0813 FFFF | 128Kbyte | MS-FLASHFS DATA |
Sector 14 | 0x0814 0000 - 0x0815 FFFF | 128Kbyte | MS-FLASHFS DATA |
Sector 15 | 0x0816 0000 - 0x0817 FFFF | 128Kbyte | MS-FLASHFS DATA |
其他硬件平台需根据硬件扇区分布情况合理划分。
MS-BOOT 开发
MS-BOOT 开发需要实现如下功能接口:
- 时钟初始化
- 外部文件系统读接口实现
- 内部 FLASH 读写接口实现
- MS-RTOS 引导
1、时钟初始化
初始化硬件时钟,与 Tick 定时器。
2、外部文件系统接口实现
外部文件系统接口主要用于读取升级请求文件与升级动作文件以及更新 OS 时读取镜像文件,外部文件系统接口定义如下:
// 文件状态
typedef struct {
ms_uint32_t size;
} ms_boot_extfs_stat_t;
// 外部文件系统读接口
typedef struct {
// 打开文件
ms_err_t (*open)(const char *source, ms_ptr_t *handle);
// 关闭文件
ms_err_t (*close)(ms_ptr_t handle);
// 读文件
ms_err_t (*read)(ms_ptr_t handle, ms_ptr_t buf, ms_uint32_t len, ms_uint32_t *read_len);
// 调整读指针
ms_err_t (*seek)(ms_ptr_t handle, ms_uint32_t offset);
// 获得文件状态
ms_err_t (*stat)(const char *source, ms_boot_extfs_stat_t *stat);
} const ms_boot_extfs_if_t;
以 littlefs
为例:
// 打开文件
static ms_err_t __ms_littlefs_open(const char *source, ms_ptr_t *handle)
{
lfs_file_t *lfs_file;
int ret;
ms_err_t err;
lfs_file = ms_kzalloc(sizeof(lfs_file_t));
if (lfs_file != MS_NULL) {
ret = lfs_file_open(ms_lfs_partition, lfs_file, source + __MNT_PREFIX_LEN, LFS_O_RDONLY);
if (ret != LFS_ERR_OK) {
ms_kfree(lfs_file);
err = -__ms_littlefs_err_to_errno(ret);
} else {
*handle = lfs_file;
err = MS_ERR_NONE;
}
} else {
err = MS_ERR_KERN_HEAP_NO_MEM;
}
return err;
}
// 关闭文件
static ms_err_t __ms_littlefs_close(ms_ptr_t handle)
{
int ret;
ms_err_t err;
ret = lfs_file_close(ms_lfs_partition, handle);
if (ret == LFS_ERR_OK) {
ms_kfree(handle);
err = MS_ERR_NONE;
} else {
err = -__ms_littlefs_err_to_errno(ret);
}
return err;
}
// 读文件
static ms_err_t __ms_littlefs_read(ms_ptr_t handle, ms_ptr_t buf, ms_uint32_t len, ms_uint32_t *read_len)
{
ms_ssize_t ret;
ms_err_t err;
ret = lfs_file_read(ms_lfs_partition, handle, buf, len);
if (ret >= 0) {
*read_len = ret;
err = MS_ERR_NONE;
} else {
err = -__ms_littlefs_err_to_errno(ret);
}
return err;
}
// 调整读指针
static ms_err_t __ms_littlefs_seek(ms_ptr_t handle, ms_uint32_t offset)
{
lfs_soff_t ret;
ms_err_t err;
ret = lfs_file_seek(ms_lfs_partition, handle, offset, LFS_SEEK_SET);
if (ret >= 0) {
err = MS_ERR_NONE;
} else {
err = -__ms_littlefs_err_to_errno(ret);
}
return err;
}
// 获得文件状态
static ms_err_t __ms_littlefs_stat(const char *source, ms_boot_extfs_stat_t *stat)
{
struct lfs_info linfo;
int ret;
ms_err_t err;
ret = lfs_stat(ms_lfs_partition, source + __MNT_PREFIX_LEN, &linfo);
if (ret == LFS_ERR_OK) {
stat->size = linfo.size;
err = MS_ERR_NONE;
} else {
err = -__ms_littlefs_err_to_errno(ret);
}
return err;
}
// littlefs 外部文件系统接口
ms_boot_extfs_if_t ms_littlefs_if = {
.open = __ms_littlefs_open,
.close = __ms_littlefs_close,
.read = __ms_littlefs_read,
.seek = __ms_littlefs_seek,
.stat = __ms_littlefs_stat,
};
MS-BOOT 初始化时需要填充 struct lfs_config
结构体,并调用 lfs_mount
函数挂载 littlefs:
struct lfs_config {
...
// 读接口
int (*read)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// 写接口
int (*prog)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// 擦除扇区
int (*erase)(const struct lfs_config *c, lfs_block_t block);
// 同步设备状态
int (*sync)(const struct lfs_config *c);
// 最小的读数据长度
lfs_size_t read_size;
// 最小的写数据长度
lfs_size_t prog_size;
// 扇区大小
lfs_size_t block_size;
// 扇区数量
lfs_size_t block_count;
// 擦除周期
int32_t block_cycles;
// CACHE 大小
lfs_size_t cache_size;
// 预测长度
lfs_size_t lookahead_size;
...
};
// 挂载 littlefs
int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg);
示例:
// littlefs 分区
lfs_t *ms_lfs_partition = MS_NULL;
// 读块
static int __lfs_block_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size)
{
int ret;
if (BSP_QSPI_Read((uint8_t *)buffer, (block * c->block_size + off), size) == QSPI_OK) {
ret = LFS_ERR_OK;
} else {
ret = LFS_ERR_CORRUPT;
}
return ret;
}
// 编程块
static int __lfs_block_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size)
{
int ret;
if (BSP_QSPI_Write((uint8_t *)buffer, (block * c->block_size + off), size) == QSPI_OK) {
ret = LFS_ERR_OK;
} else {
ret = LFS_ERR_CORRUPT;
}
return ret;
}
// 擦除块
static int __lfs_block_erase(const struct lfs_config *c, lfs_block_t block)
{
int ret;
if (BSP_QSPI_Erase_Block(block * c->block_size) == QSPI_OK) {
ret = LFS_ERR_OK;
} else {
ret = LFS_ERR_CORRUPT;
}
return ret;
}
// 同步
static int __lfs_block_sync(const struct lfs_config *c)
{
return 0;
}
// littlefs 配置
static struct lfs_config __lfs_config = {
.read = __lfs_block_read,
.prog = __lfs_block_prog,
.erase = __lfs_block_erase,
.sync = __lfs_block_sync,
};
// littlefs 初始化
ms_err_t ms_littlefs_init(lfs_t *lfs)
{
ms_err_t err;
// 初始化 QSPI 接口
if (BSP_QSPI_Init() == QSPI_OK) {
QSPI_Info info;
// 获得 QSPI NOR FLASH 信息
if (BSP_QSPI_GetInfo(&info) == QSPI_OK) {
int ret;
// 填充 littlefs 配置
__lfs_config.read_size = 1U;
__lfs_config.prog_size = info.ProgPageSize;
__lfs_config.block_size = info.EraseSectorSize;
__lfs_config.block_count = info.EraseSectorsNumber;
__lfs_config.cache_size = info.ProgPageSize;
__lfs_config.block_cycles = 500U;
__lfs_config.lookahead_size = 8U * ((__lfs_config.block_count + 63U) / 64U);
// 挂载 littlefs 文件系统
ret = lfs_mount(lfs, &__lfs_config);
if (ret == LFS_ERR_OK) {
ms_lfs_partition = lfs;
err = MS_ERR_NONE;
} else {
err = -__ms_littlefs_err_to_errno(ret);
}
} else {
err = MS_ERR;
}
} else {
err = MS_ERR;
}
return err;
}
3、内部 FLASH 读写接口实现
MS-BOOT 需要实现以下的内部 FLASH 读写接口:
// FLASH 解锁
ms_err_t ms_flash_unlock(void);
// FLASH 锁定
ms_err_t ms_flash_lock(void);
// 擦除 OS 分区扇区空间
ms_err_t ms_flash_erase_os(void);
// FLASH 编程
ms_err_t ms_flash_program(ms_addr_t addr, ms_const_ptr_t buf, ms_uint32_t len);
示例:
// FLASH 解锁
ms_err_t ms_flash_unlock(void)
{
return (HAL_FLASH_Unlock() == HAL_OK) ? MS_ERR_NONE : MS_ERR;
}
// FLASH 锁定
ms_err_t ms_flash_lock(void)
{
return (HAL_FLASH_Lock() == HAL_OK) ? MS_ERR_NONE : MS_ERR;
}
// 擦除 OS 分区扇区空间
ms_err_t ms_flash_erase_os(void)
{
FLASH_EraseInitTypeDef EraseInitStruct;
uint32_t sector_err;
ms_err_t err;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
EraseInitStruct.Sector = MS_CFG_BOOT_OS_SECTOR_BASE;
EraseInitStruct.NbSectors = MS_CFG_BOOT_OS_SECTOR_COUNT;
err = (HAL_FLASHEx_Erase(&EraseInitStruct, §or_err) == HAL_OK) ? MS_ERR_NONE : MS_ERR;
return err;
}
// FLASH 编程
ms_err_t ms_flash_program(ms_addr_t addr, ms_const_ptr_t buf, ms_uint32_t len)
{
ms_ptr_t addr_bak = (ms_ptr_t)addr;
const ms_uint32_t *wbuf = buf;
ms_size_t wlen = len >> 2U;
const ms_uint8_t *cbuf;
ms_err_t err = MS_ERR;
while (wlen > 0) {
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, *wbuf++) != HAL_OK) {
break;
}
wlen--;
addr += 4U;
}
if (wlen == 0) {
wlen = len & 0x3U;
cbuf = (const ms_uint8_t *)wbuf;
while (wlen > 0) {
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, addr, *cbuf++) != HAL_OK) {
break;
}
wlen--;
addr += 1U;
}
}
if (wlen == 0) {
if (memcmp(addr_bak, buf, len) == 0) {
err = MS_ERR_NONE;
}
}
return err;
}
4、MS-RTOS 引导
MS-RTOS 通过跳转到 MS-RTOS 分区起始地址,实现启动 MS-RTOS。
示例:
static void ms_boot_os(ms_addr_t *addr)
{
register ms_func_t func = (ms_func_t)addr[1U];
#if MS_CFG_BOOT_OS_RUN_IN != MS_BOOT_FLASH
__disable_irq();
SCB_CleanDCache();
SCB_InvalidateICache();
__enable_irq();
#endif
ms_printk(MS_PK_NOTICE, "Start MS-RTOS...\n");
/*
* Disable system tick
*/
SysTick->CTRL = 0U;
__asm__ __volatile__ (
"MOV R13, %[sp]\n"
:
: [sp] "r" (addr[0])
: "cc");
func();
}
int main(void)
{
// do something
while (MS_TRUE) {
ms_boot_os((ms_addr_t *)MS_CFG_BOOT_OS_BASE);
}
}