SD卡启动的好处
- 一般烧录的方式都是借助电脑进行,特别麻烦,有啦SD卡后,可以直接烧写在SD卡进行刷机,无论机子在哪儿,只要拿着SD卡即可
- 譬如在量产S5PV210时,可以用SD卡进行量产刷机,板子贴片好的时候,内部iNand是空的,此时直接启动无启动;板子出厂前官方刷机时是把事先做好的量产卡插入SD卡卡槽,然后打到iNand方式启动;因为此时iNand是空的所以第一启动失败,会转而第二启动,就从外部SD2通道的SD卡启动了。启动后会执行刷机操作对iNand进行刷机,刷机完成后自动重启(这回重启时iNand中已经有image了,所以可以启动了)。刷机完成后SD量产卡拔掉,一般烧机48小时,无死机即可装箱待发货。
SD卡启动的难点
- 对于SRAM、DDR来说,这两种都是总线访问,而SRAM不需要初始化即可使用,DDR需要初始化,总而言之,CPU可以直接和SRAM、DDR交互;
- SD卡不能通过总线来访问,而是需要时序,常用于SD的两种时序有–> SD协议、SPI协议;
回顾S5PV210的启动过程
如图,来自210官方手册
该图描述啦210的整个启动过程:
*. 首先板子上电,BL0开始运行,然后判断210从哪个设备启动(一般支持oneNand、SD、MMC、eMMC、NAND、eSSD、NOR等启动),如果是SD卡启动方式,则BL0会从SD卡读取16KB到SRAM去执行,也就是复制BL1到SRAM运行,然后BL1把BL2复制到SRAM,让BL2跑起来,接下来BL2去初始化DDR,并把OS复制到DDR,这时程序就运行起来啦。
SD卡启动流程(分为两部分)
- 整个镜像小于16KB:
这个时候相当于整个镜像作为BL1被steppingstone直接硬件加载执行 - 整个镜像大于16KB
只要大于16KB,就把整个镜像分为两部分,第一部分是16KB大小,第二部分是剩下的大小,对于UBOOT 会把第一部分作为BL1启动,然后去初始化DRAM,并且把第二部分(BL2)复制到DRAM去执行。
代码编写思路(大于16KB的SD卡启动)
首先分为两部分:BL1 BL2
BL1中需要完成:
1)关看门狗
2)设置SVC栈
3)开iCache
4)初始化DDR
5)在SD卡中复制BL2到特定位置
6)跳转到执行BL2
BL2中需要完成:
让LED闪烁
如图是SD卡的boot分区图
一般都是从第二个块开始,第一个块保留
BL1
.global _start //声明_start为外部变量(相当于C语言全局变量)
_start:
//第1步,关闭看门狗
ldr r0, =WATCH_DOG
ldr r1, =0x0
str r1, [r0]
//第2步,设置栈
ldr sp, =svc_stack
//第3步,开关cache
mrc p15,0,r0,c1,c0,0; //读出cp15的c1到r0中
//bic r0,r0,#(1<<12) //bit 12 置0 关icache
orr r0,r0,#(1<<12) //bit 12 置1 开icache
mcr p15,0,r0,c1,c0,0;
// 第4步:初始化ddr
bl sdram_asm_init
//第5步:重定位,从SD卡第45扇区开始,
//复制32个扇区内容到DDR的0x23E00000
//复制BL2到DDR
bl copy_bl2_2_ddr
//汇编最后的死循环
b .
#define SD_START_BLOCK 45
#define SD_BLOCK_CNT 32
#define DDR_START_ADDR 0x23E00000
typedef unsigned int bool;
//参数1:通道号:0或者2
//参数2:开始扇区号: 45
//参数3:读取扇区个数: 32
//参数4:读取后放入内存地址:0x23E00000
//参数5:with_init: 0
typedef bool(*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);
typedef void (*pBL2Type)(void);
//第5步,从SD卡第45扇区开始,
//复制32个扇区内容到DDR的0x23E00000,然后跳转到0x23E00000去执行
void copy_bl2_2_ddr(void)
{
//第一步,读取SD卡扇区到DDR中
pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)(*(unsigned int *)0xD0037F98);
p1(2, SD_START_BLOCK, SD_BLOCK_CNT, (unsigned int*)DDR_START_ADDR, 0); //读取SD卡扇区到DDR中
//跳转到DDR中的BL2运行
pBL2Type p2 = (pBL2Type)DDR_START_ADDR;
p2();
}
BL2
void main(void)
{
//led初始化
unsigned int *p = (unsigned int*)GPJ0CON;
*p = 0x11111111;
while(1)
{
unsigned int *p1 = (unsigned int*)GPJ0DAT;
//亮
*p1 = ((0<<3) | (0<<4) | (0<<5));
//延时
delay();
//灭
*p1 = ((1<<3) | (1<<4) | (1<<5));
//延时
delay();
}
}