这一篇写得有点慢,期间为了弄清楚一些细节的问题耽搁了,不过写得也会更详细。
1 /* 2 ************************************************************************* 3 * 4 * CPU_init_critical registers 5 * 6 * setup important registers 7 * setup memory timing 8 * 9 ************************************************************************* 10 */ 11 /* 12 * we do sys-critical inits only at reboot, 13 * not when booting from ram! 14 */ 15 cpu_init_crit: 16 /* 17 * When booting from NAND - it has definitely been a reset, so, no need 18 * to flush caches and disable the MMU 19 */ 20 #ifndef CONFIG_NAND_SPL 21 /* 22 * flush v4 I/D caches 23 */ 24 mov r0, #0 //清零 r0 寄存器,以下的3个协处理器操作稍后详解 25 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ 26 mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ 27 28 /* 29 * disable MMU stuff and caches 30 */ 31 mrc p15, 0, r0, c1, c0, 0 32 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) 33 bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) 34 orr r0, r0, #0x00000002 @ set bit 2 (A) Align 35 orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache /* ------------------------------------------------------------------------ */ 36 /* Prepare to disable the MMU */ 37 adr r1, mmu_disable_phys //取 mmu_... 的地址放入,即55行的地址 38 /* We presume we're within the first 1024 bytes */ 39 and r1, r1, #0x3fc //仅保留 [9:2] 位 40 ldr r2, _TEXT_PHY_BASE //_TEXT_PHY_BASE --> CONFIG_SYS_UBOOT_BASE //上一篇已经分析过它可能的两种取址了 //这里出现了一个问题:SDRAM是在lowlevel_init中初始化的,那此时的程序运行在哪呢? //稍后解答~、~ 41 ldr r3, =0xfff00000 42 and r2, r2, r3 //仅保留 [32:20] 位,即保存下不同取取址的首地址 43 orr r2, r2, r1 //取或之后,寄存器已经包含两部分内容了: 44 b mmu_disable //r2中为 首地址 + 偏移地址(mmu_diable_phys) //从最终实现上看,这段代码是无意义的操作 /* ------------------------------------------------------------------------- */ 45 46 .align 5 //这里的 .align 是按 2^5 = 32 对齐,此处即 4个字节 //有别于.lds文件中的ALIGN(4), lds文件中的 4 即为 4个字节的意思 47 /* Run in a single cache-line */ 48 mmu_disable: 49 mcr p15, 0, r0, c1, c0, 0 //协处理器,稍后一起讲 50 nop 51 nop 52 mov pc, r2 53 #endif 54 55 mmu_disable_phys: 56 /* Peri port setup */ 57 ldr r0, =0x70000000 58 orr r0, r0, #0x13 //下面还是协处理器,下面依次讲完他们的功能 59 mcr p15,0,r0,c15,c2,4 @ 256M (0x70000000 - 0x7fffffff) 60 61 /* 62 * Go setup Memory and board specific bits prior to relocation. 63 */ 64 bl lowlevel_init /* go setup pll,mux,memory */
1、首先,协处理器的简介内容,可以参阅如下网站
http://blog.csdn.net/genglei1022/article/details/5712843
接着回到我们的代码段,要了解协处理器,先了解两个指令
MCR{cond} coproc, opcode1, Rd, CRn, CRm{, opcode2} MRC{cond} coproc, opcode1, Rd, CRn, CRm{, opcode2} //其中 //coproc(essor) 为协处理器,标准名为 pn, n = 1 ~ 15 对应 CPn //opcode1 为协处理器行为操作码,永远为 0, 否则协处理器状态不确定
//Rd ARM的寄存器, CRn 目标寄存器, CRm 附加寄存器,不使用则设为 c0
//提供附加信息比如寄存器的版本号或者访问类型,用于区分同一个编号的不同物理寄存器
//可以省略 <opcode_2> 或者将其 设置为 0,否则结果未知
接着是协处理器。
CP15 是系统控制协处理器寄存器,用于连接在内存中的页表描述符,此外还用于决定对MMU的操作。
此处最先遇到的是两个MCR:
24 mov r0, #0 //清零 r0 寄存器
//Move to Coprocessor frem Register , MCR的含义,下面一句相仿
25 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ 26 mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
此处出现了两个寄存器 c7, c8
寄存器编号 基本作用 MMU中的作用
c7 控制cache和写缓存 同左
c8 存储保护和控制 TLB 控制
这里贡献一个强大的网站,详细地介绍了芯片的各个部分
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0301h/ch03s02s01.html
然后说一下怎么查阅到相关寄存的信息:
1、找到芯片,这里我用的是 ARM1176-JZF-S
2、现在要找的是协处理器单元 System Control Coprocessor
3、当前要找的是协处理器的寄存器 System control processor registers
4、最后可以在 Register allocation 中查阅寄存器总表
5、若想了解某一个寄存器的实际功用,比如此处我们的寄存器操作位
25 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
那么我们找的就应该是
CRn Op1 CRm Op2 Register or operation c7 0 c7 0 Invalidate Both Caches
在此行中其实就有我们想了解的协处理器操作的信息,若想进一步了解,点击行末链接。
在此摘录一段 c7 的功能
The purpose of c7 is to: control these operations: clean and invalidate instruction and data caches, including range operations prefetch instruction cache line Flush Prefetch Buffer flush branch target address cache virtual to physical address translation. implement the Data Synchronization Barrier (DSB) operation implement the Data Memory Barrier (DMB) operation implement the Wait For Interrupt clock control function.
看得懂英文的童鞋应该明白了
25行的意思是清空和禁用 指令和数据高速缓存,和后面的注释意思相同。
26行的意思是禁用TLB。
31 mrc p15, 0, r0, c1, c0, 0 //Read Control Register configuration data
49 mcr p15, 0, r0, c1, c0, 0 //Write Control Register configuration data
59 mcr p15,0,r0,c15,c2,4 //Peripheral Port Memory Remap
此后,在代码段中,不详细解释每个汇编指令的实现了,而是按模块进行讲解。
但是一些难点的地方还是会仔细分析的。
2、SDRAM初始化前的代码运行
要了解代码的运行情况,首先得了解ARM的片上存储系统。
在此以XIP(eXecute In Place, 内执行)的标准来区分:
支持XIP:NOR flash, mDDR(mobile DDR);
不支持XIP:NAND flash。
而速度快慢来说 NOR flash > mDDR > NAND flash。
了解了这些,再联系开发板来细分有:
NOR flash作为内执行运存,负责系统最开始的初始化代码执行(例如 start.o);
mDDR作为常规运存,即SDRAM,是代码的主要运行环境;
NAND flash作为存储设备,可以视为我们PC上的硬盘。
在此就很容易理解了,SDRAM是在lowlevel_init中初始化的,在SDRAM初始化之前,代码执行在NOR flash中。
接着来解决刚才留下的初始化顺序的问题:
40 ldr r2, _TEXT_PHY_BASE
在这一句中,_TEXT_PHY_BASE究竟存的是什么。
我们先来反汇编看一下
arm-linux-objdump -D -S -t start.o //加入 -t 可以看到代码段的段标的位置
00000044 <_TEXT_PHY_BASE>: 44: 57e00000 .word 0x57e00000 ... ldr r2, _TEXT_PHY_BASE 8c: e51f2050 ldr r2, [pc, #-80] ; 44 <_TEXT_PHY_BASE>
很显然,这里的_TEXT_PHY_BASE就是0x57e00000了!
但是咱们细想一下,lowlevel_init还在后面,SDRAM还没有初始化!
所以我们是无法调用这个地址的,可是这里明明看到了。
眼睛看到的、不一定是真实的。
这里引入一个新概念来解释这个问题:运行地址无关性。
首先是参详地:
http://blog.sina.com.cn/s/blog_4a9fb5cf01008d8u.html~type=v5_one&label=rela_nextarticle
只需要看最后面的内容就行了。
作为运行代码,无论是 bootloader 还是 kernel 的初始化代码,总会有一段乏力期:代码太长、空间太短。
在 uboot 中表现为 SDRAM 初始化之前,在 kernel 中表现为 MMU 使能前。
在这个乏力期,代码的寻址空间是很有限的,而在编译过程中预定的位置就可能导致寻址出错。
所以地址无关性的需求就出现了,集中表现为需要这段代码寻址范围限于我们的 NOR flash,且不破坏代码执行。
此时,我们从本质上理解一下这里的 0x57e00000
00000044 <_TEXT_PHY_BASE>: 44: 57e00000 .word 0x57e00000 ... ldr r2, _TEXT_PHY_BASE 8c: e51f2050 ldr r2, [pc, #-80] ; 44 <_TEXT_PHY_BASE>
倘若,我们的SDRAM已经使能,那么这个地址实际上是 uboot.bin 安放的起始地址。
但是,此时的 uboot.bin 安放在了 NOR flash 中, 那么此时的起始地应该为 0x00000000。
按此逻辑往下推导,结果是正确地,那一段代码的执行是 “可有可无” 的,这也是为什么很多人在修改这段代码的时候选择删去。
当然,这个 0x00000000 只是我的理解,欢迎讨论。
lowlevel_init还是留在下一篇讨论了。