s5pv210——SD卡启动的细节思考

来源:互联网 发布:大数据时代 txt 久久 编辑:程序博客网 时间:2024/06/08 12:18

以下内容源于朱友鹏《物联网大讲坛》的学习,以及博客http://www.cnblogs.com/biaohc/p/6361662.html的学习整理,如有侵权,请告知删除。


1、首先,通过write2sd.sh将BL1,BL2烧录至SD卡。

(1)脚本内容

#!/bin/shsudo dd iflag=dsync oflag=dsync if=./BL1/BL1.bin of=/dev/sdb seek=1sudo dd iflag=dsync oflag=dsync if=./BL2/BL2.bin of=/dev/sdb seek=45
(2)可知sd卡内容分布



2、其次,明确启动的一些细节。

由博客http://blog.csdn.net/oqqhutu12345678/article/details/70120598的地址映射可知:

(1)IROM是64KB的大小,范围为0x0000_0000~0x0000_FFFF,内部存储的是BL0的代码。

(2)IRAM是96KB的大小,范围为0xD000_2000~0xD000_3800,它细分不同用途的存储区。

  • BL1存储区(开始地址为0xD002_0010,这地址是BL1的真正地址);
  • BL1存储区之前有16字节用于BL1的校验头;
  • 外部拷贝函数区(开始地址为0xD003_7F80);
  • 中断向量表区(开始地址为0xD003_7400);

(3)SD启动的细节

  • 当用SD卡启动时,由于三星开发板用sd卡启动的机制的限定,需要对原来的bin文件(原生的led.bin文件)进行加工(经过mkv210_image.c相关的添加16字节的校验头),得到210.bin文件(包含led.bin文件+16字节校验头);
  • 再利用上述脚本进行烧录到SD卡中。
  • BL0读取SD卡中的BL1(即210.bin文件,包含led.bin文件+16字节校验头)至ISRAM中以0xD002_000开始的区域;(此时led.bin还是处于0xD002_0010开始的区域
  • BL0根据led.bin文件计算出一个校验checksum,与16字节校验头中的cheksum对比,如果相同则执行BL1,否则不再执行。
  • 当利用DWN工具下载时,摆脱了SD卡启动的限制,因此烧录的地址直接为0xD002_0010,烧录的文件是直接的文件bin文件。

(4)也就是说,SD卡启动时,BL0负责将SD卡中的BL1复制到ISRAM中以0xD002_0000开始的区域。

  • 换言之,凡是以SD卡作为载体的,都需要对原来的,bin文件进行加工;
  • 加工方法是mkv210_image.c相关的添加16字节的校验头操作。
  • 执行裸机程序,类似于执行BL1,一般不用到执行BL2(如果BL1的大小超过了16KB,则应该分割成BL1,BL2)
  • 执行裸机程序的过程,和执行uboot的过程(源文档描述的BL1,BL2过程)不一样。

(5)问题

  • 为什么将BL1,BL2分别烧录至SD卡的以第1,第45块为始的区域,其他的区域可以吗?(猜想,BL1必须烧录到第1块起始的区域,BL2可以适当位置)
  • BL0代码怎样知道BL1代码,BL2代码在SD卡中存储的位置的?BL0怎么知道自己应该复制多少个字节?(猜想,BL1必须烧录到块1起始的区域,因此BL0很容易读取BL1,而由于BL1中有内容提示BL2烧录到了哪个块 ,BL1将到那个区域把BL2复制到内存中。一般我们会规定复制多少个字节?)
  • BL0把复制的内容放置哪里?为什么复制到哪里?复制到别处可以吗?怎样体现复制到哪个地点的?(猜想,BL0把内容复制到ISRAM中以0xD002_0000开始的区域,因为BL1存储区里的会被自动执行,所以要复制到该区域,复制到别处就不能执行了。原生程序中BL1阶段的链接脚本地址为0xD002_0010,因此原生的bin会被链接到链接脚本指定的0xD002_0010)。
  • 如何将一个大于16kb的裸机程序进行分割成BL1,BL2?(猜想,BL1需要把SRAM给初始化,然后BL1把BL2的内容复制到相应的位置(上面提到的外部拷贝函数区与此有关),然后跳转执行)

3、程序太大,分成BL1,BL2。详见博客http://www.cnblogs.com/biaohc/p/6361662.html

(1)BL0首先从SD卡的第一扇区中复制16KB(即BL1,包括16字节的校验头)到ISRAM中去执行。

  • 第一扇区,16KB(默认BL1为16KB的大小);

(2)BL1程序所做的事情(BL1的链接地址为0xD002_0010,这样BL1的运行地址和链接地址是一致的,BL1不存在重定位的问题)

  • 开关置锁
  • 关看门狗
  • 开icache
  • 设置栈
  • 初始化dram
  • 初始化dram以后,再执行sd卡复制函数,把接下来要执行的代码复制到dram中;
  • 跳转到内存中去执行接下来的代码即可;

/* *        s5pv210裸机实验 *         *         * */ #define WTCON                0xE2700000#define PS_HOLD_CONTROL        0xE010E81C#define SVC_STACK            0xD0037D80     .global _start_start:        //关看门狗    ldr r0, =WTCON    ldr r1, =0    str r1, [r0]        //PS_HOLD置锁    ldr r0, =0xE010E81C    ldr r1, =0x301    ldr r2, [r0]    orr r2, r2, r1    str r2, [r0]     //开icache    mrc p15, 0, r0, c1, c0, 0    //bic r0, r0, #(1<<12)        //置0 关icache    orr r0, r0, #(1<<12)        //置1 开icache    mcr p15, 0, r0, c1, c0, 0            //设置栈    ldr sp, =SVC_STACK    bl sdram_asm_init//此函数见博文《s5pv210——SDARM的初始化》            bl cpysd_2_dram        b .
/ *        s5pv210裸机实验 *         *        sd卡中内容复制到内存中 * */#define CopySDMMCtoMem            0xD0037F98#define MEM_ADDRESS                0x23e00000#define bool                     intextern void led_blink(void);typedef bool (*CopyFunc_t)(int, unsigned int, unsigned short, unsigned int*, bool);void cpysd_2_dram(void) {    //初始化函数指针p1,将0xD0037F98中的值强制类型转换为CopyFunc_t函数类型    CopyFunc_t p1 = (CopyFunc_t)(*(unsigned int*)CopySDMMCtoMem);        //初始化函数指针p2,    void (*p2)(void) = (void (*)(void))MEM_ADDRESS;        // 通道号:0,或者2    // 开始扇区号:45    // 读取扇区个数:40    // 读取后放入内存地址:0x23E00000,注意BL2的链接地址是0x23E00000    // with_init:0    (*p1)(2, 45, 40, (unsigned int*)MEM_ADDRESS, 0);        //跳转到0x23e00000内存地址处执行函数            (*p2)();    }

由上面可知,BL1从SD卡哪个块开始拷贝BL2,拷贝多少,都是在拷贝函数中指定的,这很好回答了上面2中的问题。另外,注意BL2的链接地址应该设置为0x23E00000,因为BL1将BL2内容拷贝到了该地址。

疑惑:“将0xD0037F98中的值强制类型转换为CopyFunc_t函数类型”,即0xD0037F98中的值表示的拷贝函数的入口地址。这个拷贝函数是怎样的?


(3)BL2:

/* *        s5pv210裸机实验 *         *         * */ #define WTCON                0xE2700000#define PS_HOLD_CONTROL        0xE010E81C#define SVC_STACK            0xD0037D80     .global _start_start:    bl led_blink            b .
/* *        s5pv210裸机实验 *         *        LED流水灯 * */ #define _REG_GPJ0CON            *((unsigned int*)0xE0200240)#define _REG_GPJ0DAT            *((unsigned int*)0xE0200244)#define _REG_GPD0CON            *((unsigned int*)0xE02000A0)#define _REG_GPD0DAT            *((unsigned int*)0xE02000A4)void delay(void);void led_blink(void){    //GPJ03、4、5设置为output模式        _REG_GPJ0CON = 0x11111111;        //GPD0 1设置为输出模式    _REG_GPD0CON &= ~(0xF << 4);    _REG_GPD0CON |= (0x1 << 4);        while (1) {                //led4 暗        _REG_GPD0DAT |= (1<<1);        //暗                //GPJ03、4、5输出低电平为亮        //led1 亮        _REG_GPJ0DAT = ((0<<3) | (1<<4) | (1<<5));                                       //延时        delay();                //led2 亮        _REG_GPJ0DAT = ((1<<3) | (0<<4) | (1<<5));                //延时        delay();                //led3 亮        _REG_GPJ0DAT = ((1<<3) | (1<<4) | (0<<5));                //延时        delay();        //关 1 2 3        _REG_GPJ0DAT = ((1<<3) | (1<<4) | (1<<5));        //led4 亮        _REG_GPD0DAT &= ~(1<<1);        //亮                //延时        delay();            }    }void delay(void){    volatile unsigned int i = 900000;        // volatile 让编译器不要优化,这样才能真正的减    while (i--);                            // 才能消耗时间,实现delay}


总结:

(1)BL1,BL2不同的链接地址(如d0020010 ,23e00000);

(2)BL1运行时,将sd卡中的BL2拷贝到内存23e00000。

  • 这里涉及到内存的操作,因此BL1必须先初始化SDRAM;
  • 拷贝动作是在BL1中进行的,拷贝完后由函数指针的解引用,实现跳转。这里涉及重定位?
  • BL1有一系列初始化。如锁存、cache等。



0 0
原创粉丝点击