uboot之深入理解启动流程

标:用uboot来启动单位设计的ARMv7的处理器。

      问题:如何启动? 启动流程是什么?

 

      首先,我先来描述一下需要移植uboot的这块板子上使用到的存储器(处理器架构可以暂时先不考虑,后面我会根据arm公司官方提供的ARMv7-A体系结构文档去分析uboot初始化处理器的汇编代码。无论是arm9 arm11 armv7 armv8都是相同的套路,首先需要根据存储器资源,去定制自己的启动流程)。在我手上的这块板子上使用存储器资源:Norflash(16MB)  SRAM(512KB) SDRAM(512MB)。

         看到这里应该想到两个问题:

1. Norflash  SRAM SDRAM 作为存储器它们之间有何异同?

2. 如何通过它们去启动arm处理器?(重要的事情说三遍:先一定要理解启动流程  然后再去看uboot源码  要不然看了也无法理解  嵌入式的代码都是与硬件息息相关  了解硬件结构再去读代码 事半功倍)

 

下面我将对以上问题依次做出解析:

一. Norflash  SRAM  SDRAM 异同

       norflash:norfalsh是非易失存储器(失去电源供电后norflash里存储的数据依然存在),NOR flash带有SRAM接口,有足够的地址引脚来寻址,可以很容易地读取其内部的每一个字节(注意是读取!对于flash不是随意可以写入,一般写入NOR flash流程是:解保护->擦除->写入数据。由于flash特性只能从1翻转到0,无法从0翻转到1。擦除过程就是将flash中的某一个扇区全写0xFFFFFFF,再写入数据),代码指令可以直接在norflash上运行。(重要! 上电后可以读取norfalsh中的数据 写操作无法直接进行 前面我已经说得很清楚了)

       SRAM:静态随机访问存储器(Static Random Access Memory)它是一种类型的半导体存储器。“静态”是指只要不掉电,存储在SRAM中的数据就不会丢失。这一点与动态RAM(DRAM)不同,DRAM需要进行周期性的刷新操作。然后,我们不应将SRAM与只读存储器(ROM)和Flash Memory相混淆,因为SRAM是一种易失性存储器,它只有在电源保持连续供应的情况下才能够保持数据。“随机访问”是指存储器的内容可以以任何顺序访问,而不管前一次访问的是哪一个位置。 (重要!上电后就可以读写SRAM中的数据,无需初始化操作)

       SDRAM:同步动态随机存取存储器(Synchronous Dynamic Random Access Memory)需要不断的刷新,才能保存数据。而且是行列地址复用的,许多都有页模式。(重要! 需要对ddr控制器进行初始化<配置寄存器>,才能去读写SDRAM中的数据)

最后用表格来归纳总结下:

 

 

存储器 上电后存储器访问状态 掉电后存储器中数据状态
NORFLASH 可以读取数据,无法直接写人 数据存在
SRAM 可以直接读取写入数据 数据不存在
SDRAM 上电后没有初始化DDR控制器
         无法进行数据读写
数据不存在

 

 

 

       前面介绍了Norflash  SRAM  SDRAM存储特性,下面就来聊聊他们各自的功能和完成的任务(重要!!Norflash  SRAM  SDRAM在arm处理器启动阶段完成的任务,都是根据他们各自的存储特性来的。如果对于我前面介绍的Norflash  SRAM  SDRAM存储特性还不太理解的同学可以自行百度再深入理解。)

       Norflash特性:掉电非易失(失去电源供电后norflash里存储的数据依然存在),可以直接<读取>数据,无法直接<写入>数据。

        Norflash作为arm处理器程序存储器。可以试想一下,如果程序存储器掉电以后里面的数据没有了。那么你的电脑如何自启动,难道每次开机前都要重新烧写一次代码。在此处可以思考一个问题,在上电后norflash可以看作一个可以随机读取的只读存储器。但是我们运行的程序,一般情况下.text段(代码段)是只读(ok),.rodata(只读数据段)是只读(也ok)。那么问题来了,对于.data段(数据段)和.bss段(未初始化的全局变量和静态变量)在程序运行的过程中变量的值是需要改变的(改变一个变量的值在底层硬件完成操作<在相应的地址(变量在物理地址上存储地址)上写入数据>),很可惜Norflash只能直接读取无法直接进行写操作。(重要!  怎么解决这个问题?  这时就需要SRAM  因为SRAM上电后就可以直接去读写,下面我就解释下SRAM的功能和作用)

 

       SRAM特性:掉电易失(失去电源供电后SRAM里存储的数据不存在了),可以随意<读写>数据。(重要!容量小:512KB  程序运行速度快  价格贵!)。

       在实际运行时,SRAM可以作为c语言运行时的堆栈空间。把arm处理器的sp(堆栈指针寄存器)设置在sram中,为c语言运行提供环境。关于全局变量的问题,我单独提一下,uboot在重定位前(将uboot镜像从flash搬运到ddr中继续运行前),无论是汇编还是c程序中没有定义全局变量。只是定义了一个结构体指针gd_t *gd_ptr用于存储uboot镜像的长度,重定位地址等信息,将gd_ptr的地址存储在r9中,r9中存储的地址值为sram顶端减去一个sizeof(gd_t )。(存储在sram里就可以随意读写了嘛    后面分析uboot代码时我会详细讲解)

 

       SDRAM特性:掉电易失(失去电源供电后SDRAM里存储的数据不存在了),上电后没有初始化DDR控制器,无法进行数据读写。(重要!容量大:512MB  程序运行速度快  价格自己猜一下<必须价格便宜>)

       既然需要使用大容量的SDRAM,必须配置ddr时钟和ddr控制器的寄存器。这一步在哪完成呢?(思考一下)

       没错就是在norflash和SRAM搭建的程序运行环境中完成。完成什么呢? 

       1.完成对处理器时钟的初始化 

       2. DDR的初始化  

       3.给gd_t *gd_ptr赋值 (用于存储uboot镜像的长度,重定位地址,重定位偏移量等信息)     

    

       在uboot搬运到DDR中运行前进行最小系统的初始化,之后就将uboot搬运到ddr中进行运行。

       (重要!此时Norfalsh和SRAM的任务就完成了(这俩就没用了),现在uboot就在ddr中运行了)

 

二. arm处理器启动流程分析

        前面讲解了各个存储器在处理器启动阶段的作用,下面就归纳总结一下启动过程。

 

        有时候描述说不清楚,直接上图。下图为手上这块ARM处理器的存储器地址空间(包括各存储器在arm处理器上的地址空间 以及容量大小)

 

        

那么启动流程可以总结为:

       step1.uboot镜像(也就是bin文件 重要!不是elf文件  bin文件 和 elf文件异同 自行百度)存储在norflash中,上电后ARM默认从地址0x00000000处取出第一条指令。当运行C程时,形参和局部变量需要入栈,栈顶设置SRAM的顶端,为C程序提供运行环境。完成对gd_t *gd_ptr赋值(存储uboot镜像的长度,重定位地址等信息)。

 

       step2.在第一阶段会完成arm处理器的时钟和DDR的配置以及对gd_t *gd_ptr赋值(用于存储uboot镜像的长度,重定位地址等信息)。此时SDRAM就可以正常进行数据读写。将uboot镜像复制到SDRAM中(重定位地址由gd_t *gd_ptr提供),根据重定位偏移量得到新的PC指针值,在DDR中继续运行uboot。

 

       这里我想多提一下,uboot在ddr中运行后的地址跟其链接地址不一致(原来norflash中text_base = 0x0  在ddr中运行后text_base = 0x70453670 ),编译器会在链接时确定了其中变量以及函数的绝对地址,链接地址 加载地址 运行地址应该一致的(只是写裸机程序时大多数情况下)。

         (1)如何对函数进行寻址调用(执行函数的过程就是跳转到某个地址上去执行一段代码)

         (2)如何对全局变量进行寻址操作(全局变量的地址变了如何找到进行读写)

         (3)对于全局指针变量中存储的其他变量或函数地址在重定位到DDR中之后如何操作?    

     问题先放在这  后面我会专门写一篇博客   将一个可执行程序静态链接过程  动态链接过程  与uboot重定位过程放在一起比较  感兴趣的可以直接去阅读这篇博客。

 

       step3.在DDR中继续运行的uboot完成对ARM处理器各外围硬件的初始化,如SPI、I2C、网卡等。最终uboot进入main_loop,阻塞等待串口终端输入命令或者去启动内核。始终要清楚一点uboot的主要作用是通过 Flash和Sram初始化时钟和DDR(arm最小系统),最终uboot是需要在ddr中运行进行裸机调试或者去引导操作系统内核启动。

(如何编写uboot命令进行裸机调试 以及uboot如何引导操作系统 后面我会依次介绍)

最后上还是上图<启动流程图>:

 

 

 

      

       上电后arm默认从地址0x00000000处取出第一条指令。SP指向SRAM的顶端,当运行C程序时,形参和局部变量需要入栈,SP依次减4入栈。在这部分运行的程序完成对时钟和ddr的配置(arm最小系统)以及对gd_t *gd_ptr赋值(用于存储uboot镜像的长度,重定位地址,重定位偏移量等信息)。此时ddr已经可以成功进行数据读写,将uboot镜像从flash复制到DDR,根据重定位偏移量获得新的PC位置 继续运行uboot。

 

 

 

 

step3

     uboot已经在ddr中运行了,也就不存在任何硬件上限制了。step3完成对ARM处理器各外围硬件的初始化,如SPI、I2C、网卡等。最终uboot进入main_loop,阻塞等待串口终端输入命令或者去启动内核。

 

 参考--->:

https://www.cnblogs.com/lgslearn/p/14207072.html

 

同时下载好这几个技术文档

 (笔者在学习uboot时阅读的几个文档  可以根据需求去查找阅读  完全没必要重头读到尾)

1.arm公司官方提供的ARMv7-A体系结构文档 

《ARM® Architecture Reference Manual ARM®v7-A and ARM®v7-R 》

( 官方 品质保证! 如果觉得英文看着费劲 可以去看  杜春雷  《ARM体系结构与编程》 )
2.《Uboot中start.S源码的指令级的详尽解析_v1.6.pdf》(好吧  中文滴)

(对理解gnu arm汇编极其有帮助)

3.《ARM指令集快速查询手册.pdf》(中文滴)

(工具手册,快速查找各种arm指令用法)

4.《ARM指令详解[ARM标准].pdf》(还是中文滴)

(arm汇编的一些规范和常用形式,很多例子非常有用:子程序调用、散转、数据块复制等。对写裸机程序很有帮助,同样对理解uboot和内核启动代码有很大帮助)

5.《程序员的自我修养—链接、装载与库.pdf》(全是中文滴)

(里面会介绍ELF文件  程序链接的过程  最重要的部分就是帮助理解uboot的核心重定位!这个真的很重要!!)

 

posted @ 2021-05-27 15:32  DMCF  阅读(453)  评论(0编辑  收藏  举报