Hi!早,今天来和你一起聊聊芯片启动过程发生了什么。
内容基本来自文末推荐视频
当你按下电源开关的那一瞬间,第一行代码如何在芯片上运行起来的呢?
嵌入式软件代码需要一定的方式烧录到芯片中才能运行,除了物理刻蚀,无论是通讯端口的传输或者调试端口的烧录,都需要驱动程序的支持。
所以说是程序烧录了程序,软件启动了软件。
这就像自己提着自己的鞋带,把自己拎起来。
靴子(Boot),鞋带(Strap),提鞋带(Loader)。
这就是Boot Strap Loader的命名来源。通常称BootLoader,中文翻译为自举。
BootLoader是芯片最初运行的代码吗?
当然不是,其实每一块芯片在出厂时都在其内部的ROM中,烧录了它最基础的软件。
CPU 搬运并运行的第一条代码的默认位置,就在ROM的地址空间。所以一切的起始都在硬件上。
以 X86 架构的鼻祖 8086 芯片为例,按下开关的一瞬间,芯片 Reset 引脚接收到了电平跳变,在一连串电路的作用下,代码段寄存器CS恢复成0XFFFF,指令指针寄存器IP恢复成0X0000,他们组合成 20 位的地址正好等于 ROM 中存放第一条代码的位置。
所以,一个Soc真正的启动过程,在BootRom之前,其实逻辑进行了很多的动作,如果开了安全模式,那干的工作就更多额。但是作为嵌入式工程师,对于这部分涉及的机会实在是太少了。
之后取出这里的指令在跳转到别处。
ARM 架构的芯片也是类似的过程,对于 32 位的芯片,通电后,PC指针寄存器复位至零地址,随后从中断向量表表头的 reset 向量处获取下一个跳转的地址。
这时候的代码已经以二进制形式存储,处理器可以直接搬到自身缓存中运行。
有了这部分代码,就能跳转到存放有更多更复杂的代码的地址。
执行硬件自检,基本的初始化操作,提供基础的输入输出支持。
之后可以将操作系统从外部的存储空间加载到内部。
代码就这样接力式的流转起来。
所以我们把出厂就写在ROM里,负责启动后续用户软件的软件,称为Boot ROM或者ROM Code。
现在不一定是用只读存储器(Read Only Memory),但是至少是一块掉电不易失的存储器,现在主要用EEPROM,NOR Flash。
我们一般没有权限修改它,但是它也不完全是黑盒,大部分芯片都会有外部启动配置引脚,通常是以拨码开关的形式。
对于 PC 机来说,Boot ROM就是我们常说的BIOS,它也有启动配置途径。而且提供了交互界面,用于配置部分功能和选择后续的引导设备。
那是不是BootRom是绝对安全的呢?
答案是NO,详情参见认识Glitch到攻击BootROM
除了芯片自带的Boot ROM,还需要再给自己实际的应用程序,写一个二次引导代码或者 N 次引导代码,用作操作系统,文件系统加载等等。
我们所说的Bootloader时,其实大多数就是这样的二次引导代码。
这些事其实Boot ROM它也能做,但是Boot ROM实现的功能和配置方法不灵活,但是Bootloader是开发人员可以而完全控制的引导代码。
还有两点:
- 1、BootRom的存储介质成本很高,一般降成本会考虑缩小BootRom的大小,因此BootRom的代码尽量缩小
- 2、BootRom我们刚刚知道是不能修改的,所以代码越大,出问题的风险越大。因此一般BootRom都是放的必要内容。
在设计Bootloader时,MCU的引导步骤就开始和嵌入式 Linux 或者 PC 有所不同。
这一定程度与芯片架构所采用的的存储方案有关。
先来说MCU,与SOC相比MCU的主要特征是:
- 单核和或多核同构的微处理器,
- 单核或多核同构,
- 主频 < 1GHz,
- 没有MMU内存管理单元,
- 只能运行实时操作系统。
常见MCU内核:
程序的主要运行介质为NOR Flash,因为和RAM一样有分离的地址线和数据线。
并且可以以字节长度精确寻址,所以程序不需要拷贝到RAM中运行的。
以英飞凌家的 TC27x 系列 MCU 为例,上电后的默认取址位置是0x8FFF 8000,这就是他的Boot ROM在NorFlash中的地址。并且这块Boot Rom分为SSW,BSL,TF。
SSW 每次上电必须运行,他会根据写在program flash,PFO地址的前 32byte 中的配置字,来决定SSW执行完的跳转地址。
我们可以选择一个合适的跳转地址,比如0x80000020,放上自己写的Bootloader。
也可以选择不跳转,运行厂家提供的Bootloader(BSL)。
MCU下的Bootloader主要完成的事情有以下:
- 关闭看门狗,初始化中断和 trap 向量表,进行时钟和外设初始化,让芯片正常运行起来。
- 提供CAN,UART, ETH等用于通讯功能的驱动,能够接收外部数据传输请求。
- 提供FLASH的读写与擦除驱动,设计服务来对通讯端口接收到的更新代码进行校验、存储,以及跳转操作系统或后续应用程序代码。
- 如有必要,还会开发一些基础诊断服务,串口交互程序等等。
那么运行 Linux 的SOC和 PC 的这一过程有何不同呢。
还是先看存储方案,运行嵌入式 Linux 的 SoC。
一般将它的操作系统,文件系统和他的应用程序放在nand flash中。
运行代码前,需要将代码搬运到SRAM中,相比MCU多了一道步骤。
对于SOC的Boot ROM 和 PC 的BIOS而言,他们结束运行前的最终任务,是将某些代码从nand flash搬运到SRAM中,其中最重要的内容就是Boot Loader。
而一般SOC的Bootloader,又分为SPL(Secondary Program Loader)和uBOOT两个阶段。
SPL的 Secondary 就是相对于BootROM而言,他就像是接力赛中的第二棒选手。
SPL会初始化更大空间的外部DRAM,再把uBoot搬运到外部DRAM中去运行。
uBoot作为第三棒选手,开始运行它的初始化程序。
之后再根据系统环境变量,将 OS 内核搬运到外部DRAM中去运行。
OS 再完成根文件系统的加载等等等等。
相关阅读推荐:
- 【系统启动】uboot启动流程源码分析
- 【芯片启动】Kernel跳转到Android-linux与安卓交界
- 【系统启动】ARMv8架构u-boot启动流程详细分析
- 【系统启动】最全最完整的Uboot资料,看完你就懂啦!
参考资料
- 【系统启动】如云泊
- 【系统启动】uboot启动流程源码分析
- 【芯片启动】Kernel跳转到Android-linux与安卓交界
- 【系统启动】ARMv8架构u-boot启动流程详细分析
- 【系统启动】最全最完整的Uboot资料,看完你就懂啦!