一. ARM 启动流程
ARM 开发板启动方式 : 可以选择从 NorFlash , NandFlash , SD 卡 启动 三种方式 , 这里我们着重介绍 NandFlash 启动的情况 ;
1. 各种类型开发板启动流程
( 1 ) 2440 开发板启动流程简介 ( ① Nand Flash 拷贝 4 KB -> SRAM 垫脚石 | ② PC 指向 0 地址 即 SRAM 起始地址执行 | ③ 初始化内存 拷贝 后续指令到内存执行 )
2440 NandFlash 启动 :
1.垫脚石 ( SRAM ) 简介 : 2440 Nand Flash 启动 , 需要依赖于 很重要的片内部件 -> SRAM , 这个部件 又叫 垫脚石 , 其 位于 ARM 地址空间 的 0 地址处 , 其容量大小是 4KB ;
2.拷贝最前面 4KB : Nand Flash -> SRAM : 处理器上电之后 , 2440 会自动 从 Nand Flash 中拷贝 出 最前端 4 KB 的内容 , 复制进 SRAM 垫脚石 中 ;
3.执行 拷贝的 SRAM 中的 4KB 指令 : PC 指针 指向 0 地址 , 即 指向 垫脚石 SRAM 中的第一条指令 , 然后开始执行 ;
4.SRAM 大小 4KB 局限性 : 这里 注意 , 只能 拷贝 4KB 的指令 , 对于小的 bootloader 程序足够用 , 但对于 u-boot 这种重量级的程序 编译出来 有 100多KB , 显然 4KB 就不够用了 ;
5.代码搬移策略 : 先拷贝 4KB 到 SRAM 中 , 执行这 4KB 指令 , 在这些指令中 , 先 把内存初始化好 , 然后 将后续指令从 Nand Flash 拷贝到内存中执行 ;
( 2 ) 6410 开发板启动流程简介 ( ① 上电 运行 SROM 的 BL0 程序 | ② NandFlash -> SRAM 拷贝 8KB 指令 | ③ 执行 SRAM 指令 拷贝其余 BL 指令到内存中执行 )
6410 NandFlash 启动 :
1.BL0 程序 : 6410 上电之后 , 首先去运行 SROM 中的 Bootloader 0 , BL 0 是 芯片出厂就已经 烧写到 SROM 中的程序 ;
2.BL 0 的 作用 : 从 Nand Flash 中拷贝 8KB 的指令 到 SRAM ( 垫脚石 ) 中 运行 , 显然 8KB 无法满足 大型 Bootloader 程序的要求 ;
3.拷贝代码 : 利用 这 8KB 的程序 , 初始化内存 , 将剩下的 Bootloader 拷贝到 内存中 运行 ;
( 3 ) 210 开发板启动流程简介 ( ① 上电 运行 SROM 的 BL0 程序 | ② NandFlash -> IRAM 垫脚石 拷贝 96KB 指令 | ③ 执行 IRAM 指令 拷贝其余 BL 指令到内存中执行 )
210 NandFlash 启动 :
1.BL0 程序 : 210 上电之后 , 首先去运行 SROM 中的 Bootloader 0 , BL 0 是 芯片出厂就已经 烧写到 SROM 中的程序 ;
2.BL 0 的 作用 : 从 Nand Flash 中拷贝 96KB 的指令 到 IRAM ( 垫脚石 ) 中 运行 , 如果 96 KB 大小不够用 , 就要将剩余的 BL 拷贝到内存中 ;
二. 代码搬移 简介
1. 从 SRAM -> 内存
( 1 ) 代码搬移 简介 ( ① 代码搬移起点 | ② 从 SRAM 搬移 的 原因 | ③ 不搬移也可正常运行 )
代码搬移 简介 :
1.代码搬移起点 : 代码搬移是 将 Nand Flash 中的 BL 搬移到内存中 , 其 起点 应该是 Nand Flash , 本博客 讲解的 代码拷贝的起点 改成 SRAM ( 垫脚石 ) ;
2.从 SRAM 搬移 的 原因 : 从 Nand Flash 读取数据 , 需要对 Nand Flash 进行初始化 ; 代码 搬移 的重要原因是 BL 大小 大于 其 垫脚石 大小 , 当前的指令编译完成后 仅有 不到 1KB 大小 , 其在上电后 , 会将整个的 BL 拷贝到 SRAM 垫脚石 中 ;
3.不搬移也可正常运行 : 代码在 SRAM 中 可以运行完毕 , 不拷贝到内存中也可以正常运行 , 此处只是最代码搬移进行介绍 ;
( 2 ) 代码搬移 起点 ( SRAM 首地址 文档中查询 | 6410 开发板 : 0x0C00_0000 )
各个开发板 代码搬移 的 起点 :
1.2440 中 , SRAM ( 垫脚石 ) 起始地址 是 0x0 ;
2.6410 SRAM ( 垫脚石 ) 起始地址是 0x0C00_0000 ; 6410 手册 Page 116 ;
3.210 开发板 IRAM ( 垫脚石 ) 地址起始地址 0xD002_0000 ; 210 手册 Page 30 ;
( 3 ) 链接地址 简介 ( 链接起始地址 | 反汇编 | _start 入口函数 | 指令汇编地址 )
链接地址 :
1.链接起始地址 : 在之前写的 链接器脚本中 写的 链接器 起始地址 . = 0x50008000; ;
2.反汇编程序 : 对程序进行反汇编 , 在代码编译目录中 , 执行 arm-linux-objdump -D -S u-boot.elf > dump 命令 , 将反汇编内容输出到 dump 文件中 ( 前提是 有 编译好的 可执行 文件 ) ;
3.查看汇编文件 : 打开 汇编 文件 ;
① _start 入口函数 : 汇编代码的 入口 是 _start 标号 , 查看反汇编之后的代码 , 可以看到 在 _start 标号前 看到地址 0x50008000 , 该地址 是 整个程序的起始地址 , 即 SRAM 的起始地址 ;
② 每行指令都有相应地址 : 每行代码都有一个链接地址 , 可以看到 反汇编 文件中 每行 前面都有一个 链接地址 ;
( 4 ) 链接地址 作用 ( C 语言 函数调用 | 汇编 ldr 修改 PC 指针 )
链接地址 作用 :
1.C 语言程序 : 调用 reset() 函数 , 调用之后 , PC 指针 会被 重新赋值 , 去执行 reset() 函数 , 这个 PC 指针 被赋予的 值 就是 reset 标号 前 的 链接地址 ; 如 PC 指针 被赋值成 0x50008058 , 该地址就是 reset() 函数的链接地址 ;
2.汇编指令 : 使用 ldr 伪指令 修改 PC 指针 , 如 ldr PC , reset , 让 PC 指针 执行 reset 函数 , 此时 PC 指针会被赋值成 0x50008058 地址 ;
( 5 ) 指令 跳转 ( 相对跳转 | 绝对跳转 )
指令 跳转 :
1.PC 指针跳转 : 开发板上电后 , PC 指针 首先 指向 0 , 但是 汇编程序的入口 _start 标号的 地址 是 0x50008058 , PC 指针 被赋值为 0x50008058 ;
2.相对跳转 :
① 相对跳转指令 : 使用 b , bl 等指令 产生的跳转 , 就是 相对跳转 ;
② 相对跳转过程 : 在跳转过程中 , 不是将对应 标号的 链接地址 , 直接赋值给 PC 指针 , 而是采用 跳转前的 PC 指针值 + 当前指针 与 要跳转的标号 位置之间的 差值 ;
③ 相对跳转举例 : 如 PC 指针在入口处 _start 地址为 0x50008000 , 如果要执行 reset 标号处的代码 , 需要 跳转到 0x50008058 中 , PC 指针 从 0x50008000 跳转到 0x50008058 中 , 这里只需要 相对跳转 0x58 地址增量 即可 ;
3.绝对跳转 : C 语言中 调用函数 , 或者 修改 PC 指针值 , 的情况 是 绝对跳转 ;
( 6 ) 代码搬移终点 ( 链接器脚本首地址 | 6410 代码搬移终点首地址 0x50008000)
代码搬移 终点 :
1.内存首地址 : 链接起始地址 决定了 程序第一行代码的链接地址 , 即 第一行代码 在 内存中出现的位置 , 如 6410 的第一行代码 的 内存 地址是 0x50008000 ;
2.拷贝终点 : 代码 从 SRAM 拷贝到 内存中 , 这个内存的位置 0x50008000 就是 第一行 代码 被拷贝到的位置 ;
3.拷贝过程 : 代码拷贝的时候 , 需要从 代码的起始地址开始拷贝 , 之后的代码 以此类推 , 拷贝到后续指定标号地址处 , 都要拷贝到对应的位置中 ;
4.6410开发板 拷贝 终点 : 0x50008000 是 6410 开发板 代码拷贝终点 的 第一行 指令的地址 ;