#if 0 /* try doing this stuff after the relocation */ ldr r0, =pWTCON mov r1, #0x0 str r1, [r0] /* * mask all IRQs by setting all bits in the INTMR - default */ mov r1, #0xffffffff ldr r0, =INTMR str r1, [r0] /* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0] /* END stuff after relocation */ #endif ldr pc, _start_armboot _start_armboot: .word start_armboot
此处忽略已经注释掉的代码 |
|
最后的那两行,意思也很简单,那就是将地址为_start_armboot中的内容,即 start_armboot,赋值给PC,即调用start_armboot函数。 至此,汇编语言的start.S的整个工作,就完成了。 而start_armboot函数,在C文件中:
中: void start_armboot (void) { ...... } 这就是传说中的,调用第二层次,即C语言级别的初始化了,去初始化各个设备了。 其中包括了CPU,内存等,以及串口,正常初始化后,就可以从串口看到uboot的打印信息了。 |
/* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu_init_crit: /* * flush v4 I/D caches */ mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
关于mcr的来龙去脉:
一些要说明的内容,见下::
CP15有很多个寄存器,分别叫做寄存器0(Register 0),到寄存器15(Register 15), 每个寄存器分别控制不同的功能,而且有的是只读,有的是只写,有的是可读写。 而且这些寄存器的含义,随着版本ARM内核版本变化而不断扩展,详情请参考:Processor setup via co-processor 15 and about co-processors 其中,根据我们此处关心的内容,摘录部分内容如下:
而MCR的详细的语法为:
对照上面的那行代码: mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ 可以看出,其中 rd为r0=0 CRn为C7 CRm为C7 对于这行代码的作用,以此按照语法,来一点点解释如下: 首先,mcr做的事情,其实很简单,就是“ARM处理器的寄存器中的数据传送到协处理器寄存器中”, 此处即是,将ARM的寄存器r0中的数据,此时r0=0,所以就是把0这个数据,传送到协处理器CP15中。 而对应就是写入到“<CRn>”这个“目标寄存器的协处理器寄存器”,此处CRn为C7,即将0写入到寄存器7(Register 7)中去。 而上面关于Register 7的含义中也说了,“Any data written to this location will cause the selected cache to be flushed”,即你往这个寄存器7中写入任何数据,都会导致对应的缓存被清空。而到底那个缓存被清空呢,即我们这行指令 mcr p15, 0, r0, c7, c7, 0 起了什么作用呢 那是由“<CRm>和<opcode_2>两者组合决定”的。 而此处CRm为C7,opcode_2为0,而对于C7和0的组合的作用,参见上面的那个表中Register 7中的Flash I+D那一行, 当opcode_2为0,CRm为0111=7,就是我们要找的,其作用是“Flush I + D”,即清空指令缓存I Cache和数据缓存D Cache。 根据该表,同理,如果是opcode_2=0,而CRm=0101b=5,那么对应的就是去“Flush I”,即只清除指令缓存I Cache了。 而对应的指令也就是 mcr p15, 0, r0, c7, c5, 0 了。 |
||||||||||||||||
此注释说此行代码的作用是,清理v3或v4的缓存 其中v4,我们很好理解,因为我们此处的CPU是ARM920T的核心,是属于ARM V4的,而为何又说,也可以清除v3的cache呢? 那是因为,本身这些寄存器位域的定义,都是向下兼容的,参见上面引用的内容,也写到了:
即,对于ARM7的话,你写同样的这行代码 mcr p15, 0, r0, c7, c7, 0 也还是向register 7中写入了数据0,这也同样满足了其所说的“Any data written to this location”,也会产生同样的效果“cause the IDC (Instruction/Data cache) to be flushed”。 |
||||||||||||||||
同理,可以看出此行是去操作寄存器8,而对应的各个参数为: rd为r0=0 CRn为C8 CRm为C7 opcode_2为0 对照寄存器8的表:
其含义为: 向寄存器8中写入数据,会导致对应的TLB被清空。具体是哪个TLB,由opcode_2和CRm组合决定, 此处opcode_2为0,CRm为7=0111b,所以对应的作用是“Flush I + D”,即清空指令和数据的TLB。
|
/* * disable MMU stuff and caches */ mrc p15, 0, r0, c1, c0, 0
此处,对应的值为: rd为r0=0 CRn为C1 CRm为C0 opcode_2为0 即,此行代码是将r0的值,即0,写入到CP15的寄存器1中。 寄存器1的相关的定义为:
所以,对应内容就是,向bit[CRm]中写入opcode_2,即向bit[0]写入0,对应的作用为“On-chip MMU turned off”,即关闭MMU。 |
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) orr r0, r0, #0x00000002 @ set bit 2 (A) Align orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache mcr p15, 0, r0, c1, c0, 0
此处几行代码,注释中写的也很清楚了,就是去清楚对应的位和设置对应的位,具体位域的含义见下:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
此行作用是:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
此行作用是:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
此行作用是:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
此行作用是:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mcr指令,将刚才设置的r0的值,再写入到寄存器1中。 |
/* * before relocating, we have to setup RAM timing * because memory timing is board-dependend, you will * find a lowlevel_init.S in your board directory. */ mov ip, lr bl lowlevel_init mov lr, ip mov pc, lr #endif /* CONFIG_SKIP_LOWLEVEL_INIT */
将lr的值给ip,即指令指针r12,此处之所以要保存一下lr是因为此处是在子函数cpu_init_crit中,lr已经保存了待会用于返回主函数的地址,即上次调用时候的pc的值,而此处如果在子函数cpu_init_crit中继续调用其他子函数lowlevel_init,而不保存lr的话,那么调用完lowlevel_init返回来时候,就丢失了cpu_init_crit要返回的位置。 说白了就是,每次你要调用函数之前,你自己要确保是否已经正确保存了lr的值,要保证函数调用完毕后,也能正常返回。当然,如果你此处根本不需要返回,那么就不用去保存lr的值了。 |
|
典型的子函数调用,通过将lr的值赋值给pc,实现函数调用完成后而返回的。 |
|
这里,其是和前面的代码: #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif 是对应的。 |