赞
踩
上一篇文章用一张图的方式综述了smp系统的端到端启动流程[1],本文将详细介绍一下第一阶段的引导启动过程,这个阶段主要完成cpu,flash,ddr和外部管脚的初始化,为下一阶段的代码的执行环境做准备。通常情况下这部分是cpu内部实现的,通过汇编语言编写,不需要过多关注,但是有些需求,比如双区备份启动,就需要对该部分代码进行修改,因此有必要理一下这部分流程。本文先通过一张图的方式将关键流程节点进行了论述,然后通过举例xilinx芯片的引导过程加深对其的理解。
在嵌入式系统的启动中第一个运行的函数并不是我们通常所理解的main函数,实际上在运行main函数之前已经完成了诸如cpu,flash等的初始化工作,并且为c代码提供了基础运行环境,这一部分工作就是我们所谓的第一阶段的引导启动。
这一阶段涉及的硬件包括电源开关、cpu(核,寄存器,内部rom和内部ram)、外部的管脚、flash和ddr,系统对这些硬件由内而外,逐层初始化,核心流程是搬移代码然后执行。 编程语言涉及汇编语言和c语言,其中汇编语言主要实现cpu内部初始化过程,如向量表,中断等,代码初始是在cpu内部的rom中,被搬移到ram中执行,最后为c语言的执行做好准备。c语言部分的代码,通常是完成flash,ddr,外部管脚的初始化,代码初始是放到flash中,搬移到ddr中执行,直到引导完成交接给uboot,进行第二阶段的引导或者直接可以裸跑程序了。
如上图所示是第一个引导阶段的执行流程,系统上电后,cpu的0核跑起来,首先会在ram中执行向量表相关的操作,向量表是arm体系结构的核心,系统reset后进来执行的第一个异常,包含着不同的用户模式和中断模式,后续笔者会重点解读一下arm处理器的工作模式。如果是核0,对处理器的寄存器进行初始化,恢复为0,如果是其他的核则进入wfi状态,等待后面0核将其唤醒。再往下就是初始化中断,flash,使cpu可以直接访问flash,从flash中加载后续的引导代码到ram中。再接下来就是对外围管脚进行初始化,即MIO部分,将管脚配置为SPI/UART/I2C/等协议,这些协议都跟时钟紧密关联,因此需要对系统使用的时钟进行初始化。最后就是对即将使用的ddr进行初始化,后续代码的执行都是在其中进行,uboot的代码会从flash中找到并加载到ddr的对应位置,然后就可以交接给uboot执行。
如下图所示是笔者基于xilinx芯片的zynq 7000和zynqmp ultrascale总结的第一阶段的关键启动流程。从图中可以看出这个阶段分为两个关键的阶段,一个是cpu初始化阶段,一个是fsbl阶段。
1)对硬件进行初始化,使其可以访问flash,ddr,为后续从flash往ddr中搬移代码提供条件,同时也会外部管脚和总线进行了初始化,具备了一个系统运行的基本硬件驱动基础
2)配置和检查boot.bin的头部,在后续搬移代码到ddr时,首先要保证boot.bin文件是合法有效和完整的,其次还需要知道boot.bin文件的flash偏移地址,内部包含哪些组件,这些组件的大小、偏移地址、是否加密等信息,这些信息都被打包在boot.bin的头中,因此需要对头部进行解析和检查,缓存上述的偏移地址。
3)按照2)提供的偏移地址,依次将boot.bin中的各个组件加载到ddr中的对应区域,比如将uboot加载到ddr的任意位置;将逻辑的bit(pl bit)加载到ddr中专门为逻辑划分的内存区域,或者单独的ddr中;将pmu fw加载到cpu的pmu ram的位置。此处加载的目的位置也是可以实现规划好,比如为uboot专门划分固定的内存位置。
4)上述的代码被成功加载后,就可以从当前fsbl阶段退出,交接给uboot或者裸机代码进行操作系统或者应用程序的运行了。
本文较为详细的总结了嵌入式系统启动的第一阶段的引导过程,并且以xilinx芯片的第一个阶段的启动流程为例进行了论述,希望读者也可以根据自己手边的项目去总结出这样一张图出来,对于后续无论是进行诸如双区备份开发,还是问题的定位都会有所帮助。
参考文献:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。