痞子衡嵌入式:飞思卡尔Kinetis系列MCU启动那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是飞思卡尔Kinetis系列MCU的KBOOT形态。
痞子衡在前一篇文章里简介了 KBOOT架构,我们知道KBOOT是一个完善的Bootloader解决方案,这个解决方案主要设计用于Kinetis芯片上,目前Kinetis芯片起码有上百种型号,KBOOT在这上百种Kinetis芯片里存在的形式并不是完全一样的,KBOOT主要有三种存在形式(ROM Bootloader、Flashloader、Flash-Resident Bootloader),下面痞子衡为大家细说这三种形态:
一、KBOOT形态区别
KBOOT有三种形态,分别是如下图所示的ROM Bootloader、Flashloader、Flash-Resident Bootloader,三种形态共享大部分KBOOT源码,仅在一些细节上有差别,这些细节在KBOOT源码里是用条件编译加以区分的,对应的条件编译宏分别是BL_TARGET_ROM, BL_TARGET_RAM, BL_TARGET_FLASH。三种形态最大的区别其实是在链接文件上,经过汇编器后的read only section分别链接在了Kinetis芯片System memory空间里的ROM(起始地址0x1c000000)、RAM(区间地址0x20000000)、Flash(起始地址0x00000000)区域。
下表是KBOOT三种形态的对比,分别从use case、delivery mechanism、supported device、clock configuration、feature五大角度进行了对比:
总结来说,可以这么看KBOOT这三种存在的由来:
- 对于2014年初及以后问世的Kinetis芯片(比如MKL03、MKL27、MKL43、MKL80、MKE18F等),芯片内基本都是含ROM空间的,因此KBOOT是以ROM Bootloader的形式存在的;
- 对于2014年初及以后主推的Kinetis芯片(比如MK22、MK65、MKV31、MKS22等),芯片内虽然没有ROM空间,但飞思卡尔希望能给客户提供至少一次免编程器烧录Application(用于量产)的机会,因此KBOOT是以Flashloader的形式存在的;
- 对于在市场上主流又畅销的Kinetis芯片(比如MKL25、MK22、MK66、MKL28等),不管芯片内是否有ROM空间,飞思卡尔都希望能够给出Bootloader源码,以便让客户自由修改来满足其个性化需求,因此KBOOT是以Flash-Resident Bootloader的形式存在的;
二、KBOOT各形态实现
2.1 ROM Bootloader
KBOOT的ROM Bootloader形态是放在ROM空间里的,随着芯片一起Tape-out出厂,固化在芯片里面,所以该形态可以被当做硬件模块,可以被无限次使用。
因为有了ROM的存在,所以芯片上电启动便有了两种选择:从ROM启动、从内部Flash启动,这种启动选择是由芯片系统决定的。
如果是从ROM启动,那么我们可以借助ROM将Application烧写进Flash(内部/外部)的起始空间并跳转过去执行。跳转至Flash执行分为:从内部Flash执行、从外部QSPI NOR Flash执行,这种执行选择是由ROM代码决定的。
如果已经使用ROM将Application下载进内部Flash起始地址,并在系统设置里设置芯片从内部Flash启动,那么下次芯片复位启动完全可以绕开ROM直接从内部Flash起始地址执行Application。
2.2 Flash-Resident Bootloader
KBOOT的Flash-Resident Bootloader形态是放在内部Flash起始空间的,以源代码的形式提供给客户,客户需要自己编译KBOOT工程并使用编程器/调试器将编译生成的KBOOT binary下载进芯片内部Flash起始地址,除非使用调试器将其擦除,否则其也可以被无限次使用。
对于没有ROM的芯片,芯片上电只能从内部Flash起始地址处开始启动,因为Flash-Resident Bootloader已经占据了内部Flash的起始空间,所以芯片永远是先执行Flash-Resident Bootloader。借助Flash-Resident Bootloader只能将Application烧写进内部Flash一定偏移处(这个偏移地址由Flash-Resident Bootloader指定)并跳转过去执行。
2.3 Flashloader
KBOOT的Flashloader形态其实也是放在内部Flash起始空间的,不过与Flash-Resident Bootloader形态在Flash里执行不同之处在于Flashloader形态是在SRAM里执行的,众所周知,SRAM断电是不保存数据的,因此Flashloader需要一个放在内部Flash里的配套loader程序,在芯片上电时先运行Flash里的loader程序,由loader程序将Flashloader从Flash中搬运到SRAM中并跳转到SRAM中运行。
Flashloader是在芯片出厂之后由飞思卡尔产品工程师将其binary预先下载进内部Flash再售卖给客户,所以客户拿到芯片之后至少可以使用一次Flashloader,客户借助Flashloader可以将Application烧写进内部Flash起始空间(同时也覆盖了原Flashloader-loader),这就是Flashloader只能被使用一次的原因。
2.3.1 loader机制
关于loader机制与实现,有必要详细讲解一下,让我们结合代码分析,首先从官网下载NXP_Kinetis_Bootloader_2_0_0.zip包,就以KS22芯片为例(\targets\MKS22F25612\bootloader.eww):
使用IAR EWARM 7.80.x开发环境打开KS22的Bootloader工程,可以看到有如下三个工程,其中flashloader.ewp便是主角,其源码文件与ROM和Flash-Resident Bootloader是一样,只是工程链接文件有区别,其代码段链接在于SRAM里;maps_bootloader.ewp便是Flash-Resident Bootloader,不是此处讨论的重点;flashloader_loader.ewp就是Flashloader能在SRAM里执行的关键所在。
flashloader_loader.ewp工程里除了必要的芯片startup文件外,只有三个源文件:flashloader_image.c/h,bl_flashloader.c,其中bl_flashloader.c里包含了工程main函数,让我们试着分析这个文件以及main函数,下面是bl_flashloader.c的文件内容:
#include <string.h>
#include "bootloader_common.h"
#include "fsl_device_registers.h"
#include "bootloader/flashloader_image.h"
#if DEBUG
#include "debug/flashloader_image.c"
#else
#include "release/flashloader_image.c"
#endif
////////////////////////////////////////////////////////////////////////////////
// Code
////////////////////////////////////////////////////////////////////////////////
// @brief Run the bootloader.
void bootloader_run(void)
{
// Copy flashloader image to RAM.
// 关键拷贝,实现了flashloader binary从Flash到RAM的转移
memcpy((void *)g_flashloaderBase, g_flashloaderImage, g_flashloaderSize);
// Turn off interrupts.
__disable_irq();
// Set the VTOR to default.
SCB->VTOR = 0x0;
// Memory barriers for good measure.
__ISB();
__DSB();
// Set main stack pointer and process stack pointer.
__set_MSP(g_flashloaderStack);
__set_PSP(g_flashloaderStack);
// Jump to flashloader entry point, does not return.
// 关键跳转,执行位置从Flash切换到了RAM
void (*entry)(void) = (void (*)(void))g_flashloaderEntry;
entry();
}
// @brief Main bootloader entry point.
int main(void)
{
bootloader_run();
// Should never end up here.
while (1);
}
从上述bl_flashloader.c的文件里我们可以看到,其实loader工程的main函数特别简单,它就是将内部Flash里的g_flashloaderImage[]数据(即flashloader.ewp编译生成的binary)拷贝到g_flashloaderBase地址(即flashloader binary起始地址)处,并将SP和PC分别指向g_flashloaderStack(即flashloader初始SP)和g_flashloaderEntry(即flashloader的Reset Handler入口)。
那么g_flashloaderXX常量都放在哪里的呢?打开\targets\MKS22F25612\iar\flashloader\output\Release\flashloader_image.c可以找到答案:
const uint8_t g_flashloaderImage[] = {
0x70, 0x62, 0x00, 0x20, 0x11, 0xc4, 0xff, 0x1f, 0xeb, 0xc4, 0xff, 0x1f, 0x75, 0xef, 0xff, 0x1f,
// 此处省略41552 bytes
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const uint32_t g_flashloaderSize = 41584U;
const uint32_t g_flashloaderBase = 0x1fffc000;
const uint32_t g_flashloaderEntry = 0x1fffc411;
const uint32_t g_flashloaderStack = 0x20006270;
loader机制越来越清晰了,现在只剩最后一个问题了,flashloader_image.c文件是哪里来的?这个文件当然可以手动创建,文件里的信息都可以从flashloader.ewp工程生成的elf/map文件里中找到,但本着高效的原则,但凡能脚本自动生成的决不手动创建,是的这个flashloader_image.c文件就是脚本自动生成的,在flashloader.ewp的Option选项的Build Actions里可以看到调用脚本的命令,这个脚本名叫create_flashloader_image.bat。
在\bin目录下存放了所有脚本文件,当然也包括create_flashloader_image.bat,先打开这个脚本看一下:
cd /d %1
ielftool --bin output\%2\flashloader.elf flashloader.bin
python ..\..\..\..\bin\create_fl_image.py output\%2\flashloader.elf flashloader.bin output\%2\flashloader_image.c
ielftool.exe是IAR软件目录下的工具,可以将elf文件转换成bin文件。最核心的脚本其实是create_fl_image.py,这个python脚本根据elf文件和bin文件生成了flashloader_image.c文件。打开create_fl_image.py文件如下(作了一些异常判断的删减,为了突出脚本主逻辑):
import sys
import os
import elf
# usage: create_fl_image.py <elffile> <binfile> <cfile>
def main(argv):
# Collect arguments
elfFilename = argv[0]
binFilename = argv[1]
cFilename = argv[2]
# Open files
binFile = open(binFilename, 'rb')
cFile = open(cFilename, 'w')
# 创建了elfData对象,用于后续处理.elf格式文件
elfData = elf.ELFObject()
with open(elfFilename, 'rb') as elfFile:
# 开始处理输入的.elf文件
elfData.fromFile(elfFile)
if elfData.e_type != elf.ELFObject.ET_EXEC:
raise Exception("No executable")
# 开始从.elf里获取关键信息
resetHandler = elfData.getSymbol("Reset_Handler")
vectors = elfData.getSymbol("__Vectors")
stack = elfData.getSymbol("CSTACK$$Limit")
# Print header
print >> cFile, 'const uint8_t g_flashloaderImage[] = {'
# Print byte data
totalBytes = 0
while True:
data = binFile.read(16)
dataLen = len(data)
if dataLen == 0: break
totalBytes += dataLen;
cFile.write(' ')
for i in range(dataLen):
cFile.write('0x%02x, ' % ord(data[i]))
print >> cFile
print >> cFile, '};\n'
# Print size and other info
cFile.write('const uint32_t g_flashloaderSize = %dU;\n' % totalBytes)
cFile.write('const uint32_t g_flashloaderBase = 0x%x;\n' % vectors.st_value)
cFile.write('const uint32_t g_flashloaderEntry = 0x%x;\n' % resetHandler.st_value)
cFile.write('const uint32_t g_flashloaderStack = 0x%x;\n' % stack.st_value)
if __name__ == "__main__":
main(sys.argv[1:])
create_fl_image.py脚本里除了普通文件操作外,最关键的是这句elfData = elf.ELFObject(),调用了elf.py文件提供的elf格式文件操作接口,通过这些接口得到了flashloader里的关键信息(vectors、resetHandler、stack),感兴趣的可以自己去分析elf.py文件。
三、KBOOT各形态芯片支持
截止目前(2017年),KBOOT支持的Kinetis芯片全部列出在下表:
至此,飞思卡尔Kinetis系列MCU的KBOOT形态痞子衡便介绍完毕了,掌声在哪里~~~
欢迎订阅
文章会同时发布到我的 博客园主页、CSDN主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。
痞子衡嵌入式:飞思卡尔Kinetis系列MCU启动那些事(2)- KBOOT形态(ROM/Bootloader/Flashloader)的更多相关文章
- 痞子衡嵌入式:飞思卡尔Kinetis系列MCU启动那些事(1)- KBOOT架构
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔Kinetis系列MCU的KBOOT架构. Bootloader是嵌入式MCU开发里很常见的一种专用的应用程序,在一个没有Boo ...
- 痞子衡嵌入式:飞思卡尔Kinetis系列MCU启动那些事(3)- KBOOT配置(FOPT/BOOT Pin/BCA)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔Kinetis系列MCU的KBOOT配置. KBOOT是支持配置功能的,配置功能可分为两方面:一.芯片系统的启动配置:二.KBO ...
- 痞子衡嵌入式:飞思卡尔Kinetis系列MCU启动那些事(9)- KBOOT特性(IntegrityCheck)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔Kinetis系列MCU的KBOOT之完整性检测(Integrity Check)特性. Application完整性检测是非常 ...
- 痞子衡嵌入式:飞思卡尔Kinetis系列MCU启动那些事(11)- KBOOT特性(ROM API)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔Kinetis系列MCU的KBOOT之ROM API特性. KBOOT的ROM API特性主要存在于ROM Bootloader ...
- 痞子衡嵌入式:飞思卡尔Kinetis系列MCU启动那些事(10)- KBOOT特性(可靠升级)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔Kinetis系列MCU的KBOOT之可靠升级(Reliable Update)特性. 所谓可靠升级机制,即在更新Applica ...
- 痞子衡嵌入式:飞思卡尔Kinetis系列MCU开发那些事 - 索引
大家好,我是痞子衡,是正经搞技术的痞子.本系列痞子衡给大家介绍的是飞思卡尔Kinetis系列微控制器相关知识. 飞思卡尔半导体(现恩智浦半导体)于2010年开始推出的Kinetis系列昭示着ARM C ...
- 痞子衡嵌入式:恩智浦LPC系列MCU开发那些事 - 索引
大家好,我是痞子衡,是正经搞技术的痞子.本系列痞子衡给大家介绍的是恩智浦LPC系列微控制器相关知识. 恩智浦半导体最早于2003年便开始推出LPC系列MCU,但早期的产品LPC2000/3000系列属 ...
- 痞子衡嵌入式:ARM Cortex-M内核MCU开发那些事 - 索引
大家好,我是痞子衡,是正经搞技术的痞子.本系列痞子衡给大家介绍的是ARM Cortex-M内核微控制器相关知识. ARM公司从2004年开始推出Cortex-M系列内核,迄今Cortex-M家族已经包 ...
- 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(1)- Boot简介
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的BootROM功能简介. 截止目前为止i.MX RT系列已公布的芯片有三款i.MXRT105x, i. ...
随机推荐
- 菜鸡谈OO 第二单元总结
“欢迎来到(玄学)多线程的新世界” Homework1 单部傻瓜电梯调度 Part1 多线程设计策略 第一次学到了线程这个概念,与之前的编程体验大有不同.最大的区别在于从原本的线性发生程序变成了多个行 ...
- Vue 项目: npm run dev 报错 webpack-dev-server
从码云上下载vue项目,运行npm run dev 时报错: > webpack-dev-server --inline --progress --config build/webpack.de ...
- web测试实践——day01
一.任务进展情况 主要是找寻网站的bug,分析bug的严重程度.同时找了本专业的同学进行博客园系统的使用. 二.存在的问题 由于上线的网站做的比较完善,导致找寻bug比较困难. 三.解决方法 对此我们 ...
- js面向对象自定义MyString()的构造器函数,实现内建String()属性和方法:
js面向对象自定义MyString()的构造器函数,实现内建String()属性和方法: var s = new MyString('hello'); s.length; s[0]; // " ...
- HBuilder git使用-分工合作
1.初始项目的创建 创建好项目,在项目名上右键,Team->共享 完成后,就实现了本地仓库的建立,另外你要注意现在创建的项目所有文件变成了红色,Git Repositories视图列出了相应的本 ...
- MySql的编译安装
一 前期准备 1 cmake包,要求2.8以上版本 https://cmake.org/download/ 2 boost库包 boost Boost库是一个可移植.提供源代码的C++库,作为标准库的 ...
- 【RL-TCPnet网络教程】第29章 NTP网络时间协议基础知识
第29章 NTP网络时间协议基础知识 本章节为大家讲解NTP (Network Time Protocol,网络时间协议)和SNTP(简单网络时间协议,Simple Network Time ...
- emWin收音机,含uCOS-III和FreeRTOS两个版本
第11期:收音机配套例子:V6-919_STemWin提高篇实验_收音机(uCOS-III)V6-920_STemWin提高篇实验_收音机(FreeRTOS) 例程下载地址: http://forum ...
- [Swift]LeetCode329. 矩阵中的最长递增路径 | Longest Increasing Path in a Matrix
Given an integer matrix, find the length of the longest increasing path. From each cell, you can eit ...
- [Swift]LeetCode629. K个逆序对数组 | K Inverse Pairs Array
Given two integers n and k, find how many different arrays consist of numbers from 1 to n such that ...