8.5.1任务:大于16KB的bin文件使用SD卡启动
(1)总体思路:将我们的代码分为2部分,第一部分BL1小于等于16KB,第二部分为任意大小,iROM代码执行完成后从SD卡启动会自动读取BL1到iRAM中执行;BL1执行时负责初始化DDR,然后手动将BL2从SD卡copy到DDR中正确位置,然后BL1远跳转到BL2中执行BL2。
(2)细节1:程序怎么安排?程序分为2个文件夹BL1和BL2,各自管理各自的项目。
(3)细节2:BL1中要完成:关看门狗、设置栈、开iCache、初始化DDR、从SD卡复制BL2到DDR中特定位置,跳转执行BL2。
(4)细节3:BL1在SD卡中必须从Block1开始(Block0不能用,这是三星官方规定的),长度为16KB内,我们就定位16KB(也就是32个block);BL2理论上可以从33扇区开始,但是实际上为了安全都会留一些空扇区作为隔离,譬如可以从45扇区开始,长度由自己定(实际根据自己的BL2大小来分配,我们实验时BL2非常小,因此我们定义BL2长度为16KB,也就是32扇区)。
(5)细节4:DDR初始化好后,整个DDR都可以使用了,这时在其中选择一段长度足够BL2的DDR空间即可。我们选0x23E00000(因为我们BL1中只初始化了DDR1,地址空间范围是0x20000000~0x2fffffff)。
8.5.2代码划分为2部分(BL1和BL2)
BL1中重定位
BL2远跳转
(1)因为我们BL1和BL2其实2个独立的程序,链接时也是独立分开链接的,所以不能像以前一样使用ldr pc, =main这种方式来通过链接地址实现远跳转到BL2。
(2)我们的解决方案是使用地址进行强制跳转,因为我们知道BL2在内存地址0x23E00000处,所以直接去执行这个地址即可。
总结:代码分为2部分,这种技术叫做分散加载。这种分散加载的方法可以解决问题,但是比较麻烦。它的缺陷有:第一,代码完全分2部分,完全独立,代码编写和组织上麻烦;第二,无法让工程兼容SD卡启动和Nand启动、NorFlash启动等各种启动方式。
8.5.3 uboot中的做法
(1)第二种思路:程序代码仍然包括BL1和BL2两部分,但是组织形式上部分为2部分,而是作为一个整体来组织。它的实现方式是:iROM启动,然后从SD卡的扇区1开始读取16KB的BL1然后去执行BL1,BL1负责初始化DDR,然后从SD卡中读取整个程序(BL1+BL2)到DDR中,然后从DDR中执行(利用ldr pc, =main这种方式以远跳转到iRAM中运行的BL1跳转到DDR中运行BL2)。
(2)细节1:uboot编译好后有200多KB,超出了16KB。uboot的组织方式就是前面16KB为BL1,剩下的部分为BL2。
(3)细节2:uboot在烧录到SD卡的时候,先截取uboot.bin的前16KB(实际脚本截取的是8KB)烧录到SD卡的block1~block32,然后将整个uboot烧录到SD卡的某个扇区中(譬如49扇区)
(4)细节3:实际uboot从SD卡启动时是这样的:iROM先执行,根据OMpin判断出启动设备是SD卡,然后从SD卡的block1开始读取16KB(8KB)到iRAM中执行BL1,BL1执行时负责初始化DDR,并且从SD卡的49扇区开始复制整个uboot到DDR中指定位置(0x23E00000)去备用;然后BL1继续执行直到ldr pc, =main时BL1跳转到DDR上的BL2中接着执行uboot的第二阶段。
总结:uboot中的这种启动方式比分散加载的好处在于:能够兼容各种启动方式。
我的总结:假设启动程序为100KB,分散加载将程序分为2部分,一部分BL1为16KB,另一部分BL2为84KB(100-16)。这两部分各有各的链接地址,其中BL1一般是链接在0xd0020010,而BL2链接在0x23E00000。BL1负责初始化DDR,程序运行中将BL2复制到DDR中的相应位置,然后跳转到0x23E00000位置继续执行BL2。
uboot方式假设启动程序为100KB,将程序分为2部分,一部分BL1为16KB,一部分为BL1+BL2 100KB。这两部分链接在同一个地址0xd0020010。BL1负责初始化DDR和将BL1+BL2 copy到DDR中,然后在执行过程中使用ldr pc, =main 语句跳转到DDR中继续执行BL2。
嵌入式物联网的学习之路非常漫长,不少人因为学习路线不对或者学习内容不够专业而错失高薪offer。不过别担心,我为大家整理了一份150多G的学习资源,基本上涵盖了嵌入式物联网学习的所有内容。在评论区输入“嵌入式”,即可0元领取学习资源,让你的学习之路更加顺畅!记得点赞、关注、收藏、转发哦!