简介
本博客使用的开发板是友善之臂提供的tiny4412 SDK开发板。它的bootloader是由E4412_N.bl1.bin(三星提供的),uboot-spl.bin,uboot.bin和E4412_tzsw.bin组合而成,它可以通过sd卡启动也可以通过emmc启动,但是我这里只讲解emmc启动的方式,因为这样操作比较简洁,不用去拔插sd卡到电脑进行烧录。
先来看下芯片内部的memory map,0x0202_0000到0x0206_0000是4412的内部RAM,简称iRAM。由于iRAM是只读存储,所以4412把0x0202_1000到0x0202_1400的地址空间分配给iROM使用,iRAM的特性就是一上电就能使用,不需要初始化。0x0202_1400到0x0202_3400这段地址空间被划分为BL1,0x0202_3400到0x0202_7400则被分为BL2。如果我把程序E4412_N.bl1.bin(后面简称BL1)、uboot-spl.bin(等同于BL2)、uboot.bin、E4412_tzsw.bin烧录到emmc的指定位置,那么开发板上电之后,它的内部引导程序就会把E4412_N.bl1.bin加载到BL1的起始地方,在0x0202_1400执行bootloader的第一行代码,随后BL1会把emmc的uboot-spl.bin拷贝到0x0202_3400处,开始执行BL2的代码。由于BL2是由uboot-spl.bin产生的,所以BL2属于可控代码段,它会负责初始化时钟树,外部ram,串口等,然后将uboot.bin拷贝到对应的地址(我在uboot中定义为0x43e00000,这地址属于外部ram)并运行它。
知识点
下面列出可能会用到的知识点,供后面移植参考。
一、概念
1、uboot是一种通用性比较强的bootloader
SRAM: static randowm accecc memory,静态随机访问存储器。只要不掉电,存储的数据就不会丢失
上电后就可以直接读写SRAM中的数据,无需初始化操作
SDRAM:synchronous Dynamic Random Access Memory,同步动态随机存取器。需要不断的刷新,才能保存数据。
需要初始化ddr 控制器才能读写SDRAM里面的数据
2、根据“ARM-thumb 过程调用标准”:
r0-r3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。
被调用函数在返回之前不必恢复 r0-r3。如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
r4-r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。
r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。
在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。
R13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复
r15 是程序计数器 PC。它不能用于任何其它用途。
3、地址
1)运行地址<—>链接地址:他们两个是等价的,只是两种不同的说法。
我们在移植的时候会定义到uboot的链接地址,CONFIG_SYS_TEXT_BASE=0x43e00000。
uboot-spl在拷贝uboot到外部ram的时候,会把uboot拷贝到这个地址上面去,这样就能跳过去执行了。
这个地址不是随便定义的:
比如我 BL1运行后会负责把uboot-spl拷贝到0x0202_3400上面,那这样我们在编译uboot-spl的时候就需要指定链接地址为0x0202_3400,不然你拷贝之后的uboot-spl就运行有问题。
比如我uboot-spl运行后会负责把uboot拷贝到0x43e00000上面,那这样我们就需要在编译uboot 的时候指定链接地址为0x43e00000。
关于这个链接地址你使用工具链反汇编(类似arm-linux-objdump -S u-boot > u-boot.S )(类似arm-linux-objdump -S uboot-spl > uboot-spl.S )去反汇编文件,就可以看出链接地址
2)加载地址<—>存储地址:他们两个是等价的,也是两种不同的说法
加载地址其实就是你的存储的地址,这个没啥好说的。
二、指令
1、ldr
用来从存储器中将一个32位的字数据传送到目的寄存器中,LDR:通常都是作加载指令的,但是它也可以作伪指令,通常有两种不同的表示,一种是有等号,一种是没等号
格式: LSL:logic shift left 逻辑左移
ldr 目的寄存器,<存储器地址>
LDR R0,[R1,R2,LSL#2]!;//将存储器地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
1)LDR pc, =MyHandleIRQ 表示将MyHandleIRQ地址放入pc寄存器中,相当于PC=MyHandleIRQ 。
例如:
1. LDR r0,=label //用于加载立即数或一个地址值到指定寄存器中
//如果label是立即数: LDR r0,=0X123 ;将0X123存入r0中
//如果name是个标识符: LDR r0,=label_1 ;将label_1所指向的地址值存入r0中
2)LDR PC,MyHandleIRQ 表示将 MyHandleIRQ地址中的值放入pc寄存器中,类似于C语言中的指针形式,相当于PC=*(MyHandleIRQ )。
例如: