基于ARM裸机的知识点总结(10)-------SD卡启动详解

一、各种存储设备的介绍及差异

1.主流的外存设备介绍

内存和外存的区别:
一般是把这种RAM(random access memory,随机访问存储器,特点是任意字节读写,掉电丢失)叫内存把ROM(read only memory,只读存储器,类似于Flash SD卡之类的,用来存储东西,掉电不丢失,不能随机地址访问,只能以块为单位来访问)叫外存。

1、软盘、硬盘、光盘、CD、磁带
(1)存储原理大部分为磁存储缺点是读写速度、可靠性等优点是技术成熟、价格便宜。广泛使用在桌面电脑中,在嵌入式设备中几乎无使用。
(2)现代存储的发展方向是Flash存储,闪存技术是利用电学原理来存储1和0从而制成存储设备。所以闪存设备没有物理运动(硬盘中的磁头),所以读写速度可以很快,且无物理损耗。

闪存技术
2、纯粹的Flash:NandFlash、NorFlash
(1)这些是最早出现的、最原始的Flash颗粒组成芯片。也就是说NandFlash、NorFlash芯片中只是对存储单元做了最基本的读写接口,然后要求外部的SoC来提供Flash读写的控制器以和Flash进行读写时序。
(2)缺陷:1、读写接口时序比较复杂。2、内部无坏块处理机制,需要SoC自己来管理Flash的坏块;3、各家厂家的Flash接口不一致,甚至同一个厂家的不同型号、系列的Flash接口都不一致,这就造成产品升级时很麻烦。
(3)NandFlash分MLC和SLC两种。SLC技术比较早,可靠性高,缺点是容量做不大(或者说容量大了太贵,一般SLC Nand都是512MB以下);MLC技术比较新,不成熟,可靠性差,优点是容量可以做很大很便宜,现在基本都在发展MLC技术。

3、SD卡、MMC卡、MicroSD、TF卡
(1)这些卡其实内部就是Flash存储颗粒,比直接的Nand芯片多了统一的外部封装和接口。
(2)卡都有统一的标准,譬如SD卡都是遵照SD规范来发布的。这些规范规定了SD卡的读写速度、读写接口时序、读写命令集、卡大小尺寸、引脚个数及定义。这样做的好处就是不同厂家的SD卡可以通用。

4、iNand、MoviNand、eSSD
(1)电子产品如手机、相机等,前些年趋势是用SD卡/TF卡等扩展存储容量;但是近年来的趋势是直接内置大容量Flash芯片而不是外部扩展卡。
(2)外部扩展卡时间长了卡槽可能会接触不良导致不可靠。
(3)现在主流的发展方向是使用iNand、MoviNand、eSSD(还有别的一些名字)来做电子产品的存储芯片。这些东西的本质还是NandFlash,内部由Nand的存储颗粒构成,再集成了块设备管理单元,综合了SD卡为代表的各种卡的优势和原始的NandFlash芯片的优势。
(4)优势:1、向SD卡学习,有统一的接口标准(包括引脚定义、物理封装、接口时序)。2、向原始的Nand学习,以芯片的方式来发布而不是以卡的方式;3、内部内置了Flash管理模块,提供了诸如坏块管理等功能,让Nand的管理容易了起来。

5、SSD(固态硬盘):本质来说还是flash,闪存技术。

二、SD卡的一些基础知识与编程接口介绍

1、SD卡的特点和背景知识

1、SD卡和MMC卡的关系
(1)MMC标准比SD标准早,SD标准兼容MMC标准。
(2)MMC卡可以被SD读卡器读写,而SD卡不可以被MMC读卡器读写。

2、SD卡和Nand、Nor等Flash芯片差异
(1)SD卡/MMC卡等卡类有统一的接口标准,而Nand芯片没有统一的标准(各家产品会有差异)

3、SD卡与MicroSD的区别
(1)体积大小区别而已,传输与原理完全相同。

4、SD卡与TF卡的区别
(1)外观上,SD卡大而TF卡小;用途上,SD卡用于数码相机等而TF卡广泛用于手机、GPS等;
(2)时间上,SD卡1999年推出,TF卡于2004年推出;SD卡由日本松下、东芝与美国SanDisk共同推出,而TF卡由Motorola与SanDisk共同推出。
(3)SD卡有写保护而TF卡没有,TF卡可以通过卡套转成SD卡使用。

2、SD卡的编程接口

1、SD卡的物理接口
在这里插入图片描述

(1)SD卡由9个针脚与外界进行物理连接,这9个脚中有2个地,1个电源,6个信号线。

2、SD协议与SPI协议
(1)SD卡与SRAM/DDR/SROM之类的东西的不同:SRAM/DDR/SROM之类的存储芯片是总线式的,只要连接上初始化好之后就可以由SoC直接以地址方式来访问但是SD卡不能直接通过接口给地址来访问,它的访问需要按照一定的接口协议(时序)来访问。
(2)SD卡虽然只有一种物理接口(即:9个针脚),但是却支持两种读写协议:SD协议和SPI协议。

3、SPI协议特点(低速、接口操作时序简单、适合单片机)
(1)SPI协议是单片机中广泛使用的一种通信协议,并不是为SD卡专门发明的。
(2)SPI协议相对SD协议来说速度比较低。
(3)SD卡支持SPI协议,就是为了单片机方便使用。

4、SD协议特点(高速、接口时序复杂,适合有SDIO接口的SoC)
(1)SD协议是专门用来和SD卡通信的。
(2)SD协议要求SoC中有SD控制器,运行在高速率下,要求SoC的主频不能太低。

5、S5PV210的SD/MMC控制器
(1)数据手册Section8.7,为SD/MMC控制器介绍。
(2)SD卡内部除了存储单元Flash外,还有SD卡管理模块,**我们SoC和SD卡通信时,通过9针引脚以SD协议/SPI协议向SD卡管理模块发送命令、时钟、数据等信息,然后从SD卡返回信息给SoC来交互。**工作时每一个任务(譬如初始化SD卡、譬如读一个块、譬如写、譬如擦除····)都需要一定的时序来完成(所谓时序就是先向SD卡发送xx命令,SD卡回xx消息,然后再向SD卡发送xx命令····)

三、S5PV210的SD卡启动详解

1、SoC为何要支持SD卡启动
(1)一个普遍性的原则就是:SoC支持的启动方式越多,将来使用时就越方便,用户的可选择性就越大,SoC的适用面就越广。
(2)SD卡有一些好处:譬如可以在不借用专用烧录工具(类似Jlink)的情况下对SD卡进行刷机,然后刷机后的SD卡插入卡槽,SoC既可启动;譬如可以用SD卡启动进行量产刷机(量产卡)。像我们X210开发板,板子贴片好的时候,内部iNand是空的,此时直接启动无启动;板子出厂前官方刷机时是把事先做好的量产卡插入SD卡卡槽,然后打到iNand方式启动;因为此时iNand是空的所以第一启动失败,会转而第二启动,就从外部SD2通道的SD卡启动了。启动后会执行刷机操作对iNand进行刷机,刷机完成后自动重启(这回重启时iNand中已经有image了,所以可以启动了)。刷机完成后SD量产卡拔掉,烧机48小时,无死机即可装箱待发货。

2、SD卡启动的难点在哪里(SRAM、DDR、SDCard)
(1)SRAM、DDR都是总线式访问的,SRAM不需初始化既可直接使用而DDR需要初始化后才能使用,但是总之CPU可以直接和SRAM/DRAM打交道而SD卡需要时序访问,CPU不能直接和SD卡打交道;NorFlash读取时可以总线式访问,所以Norflash启动非常简单,可以直接启动,但是SD/NandFlash不行。
(2)以前只有Norflash可以作为启动介质,台式机笔记本的BIOS就是Norflash做的。后来三星在2440中使用了SteppingStone的技术,让Nandflash也可以作为启动介质。SteppingStone(翻译为启动基石)技术就是在SoC内部内置4KB的SRAM,然后开机时SoC根据OMpin判断用户设置的启动方式,如果是NandFlash启动,则SoC的启动部分的硬件直接从外部NandFlash中读取开头的4KB到内部SRAM作为启动内容。
(3)启动基石技术进一步发展,在6410芯片中得到完善,在210芯片时已经完全成熟。210中有96KB的SRAM,并且有一段iROM代码作为BL0,BL0再去启动BL1(210中的BL0做的事情在2440中也有,只不过那时候是硬件自动完成的,而且体系没有210中这么详细)。

3、S5PV210的启动过程回顾
(1)210启动首先执行内部的iROM(也就是BL0),BL0会判断OMpin来决定从哪个设备启动,如果启动设备是SD卡,则BL0会从SD卡读取前16KB(不一定是16,反正16是工作的)到SRAM中去启动执行(这部分就是BL1,这就是steppingstone技术
(2)BL1执行之后剩下的就是软件的事情了,SoC就不用再去操心了。

210的启动过程大致是:
第一步:CPU上电后先从内部IROM中读取预先设置的代码(BL0),执行。这一段IROM代码首先做了一些基本的初始化(CPU时钟、关看门狗···)(这一段IROM代码是三星出厂前设置的,三星也不知道我们板子上将来接的是什么样的DRAM,因此这一段IROM是不能负责初始化外接的DRAM的,因此这一段代码只能初始化SoC内部的东西);然后这一段代码会判断我们选择的启动模式(我们通过硬件跳线可以更改板子的启动模式),然后从相应的外部存储器去读取第一部分启动代码(BL1,大小为16KB)到内部SRAM。
第二步:从IRAM去运行刚上一步读取来的BL1(16KB),然后执行BL1负责初始化NandFlash,然后将BL2读取到IRAM(剩余的80KB)然后运行
第三步:从IRAM运行BL2,BL2初始化DRAM,然后将OS读取到DRAM中,然后启动OS,启动过程结束。
大致流程图:
在这里插入图片描述

4、SD卡启动流程(bin文件小于16KB时和大于16KB时)
(1)启动的第一种情况是整个镜像大小小于16KB。这时候相当于我的整个镜像作为BL1被steppingstone直接硬件加载执行了而已。
(2)启动的第二种情况就是整个镜像大小大于16KB。(只要大于16KB,哪怕是17KB,或者是700MB都是一样的)这时候就要把整个镜像分为2部分:第一部分16KB大小,第二部分是剩下的大小。然后第一部分作为BL1启动,负责去初始化DRAM并且将第二部分加载到DRAM中去执行(uboot就是这样做的)。

5、最重要的但是却隐含未讲的东西
问题:iROM究竟是怎样读取SD卡/NandFlash的?
三星在iROM中事先内置了一些代码去初始化外部SD卡/NandFlash,并且内置了读取各种SD卡/NandFlash的代码在iROM中。BL0执行时就是通过调用这些device copy function来读取外部SD卡/NandFlash中的BL1的。

6、SoC支持SD卡启动的秘密(iROM代码)
(1)三星系列SoC支持SD卡/NandFlash启动,主要是依靠SteppingStone技术具体在S5PV210中支持steppingstone技术的是内部的iROM代码。

7、再看iROM application note:block device copy function
在这里插入图片描述
在这里插入图片描述

8、扇区和块的概念
在这里插入图片描述

(1)早期的块设备就是软盘硬盘这类磁存储设备,这种设备的存储单元不是以字节为单位,而是以扇区为单位。 磁存储设备读写的最小单元就是扇区,不能只读取或写部分扇区。这个限制是磁存储设备本身物理方面的原因造成的,也成为了我们编程时必须遵守的规律。
(2)一个扇区有好多个字节(一般是512个字节)。早期的磁盘扇区是512字节,实际上后来的磁盘扇区可以做的比较大(譬如1024字节,譬如2048字节,譬如4096字节),但是因为原来最早是512字节,很多的软件(包括操作系统和文件系统)已经默认了512这个数字,因此后来的硬件虽然物理上可能支持更大的扇区,但是实际上一般还是兼容512字节扇区这种操作方法。
(3)一个扇区可以看成是一个块block(块的概念就是:不是一个字节,是多个字节组成一个共同的操作单元块),所以就把这一类的设备称为块设备。常见的块设备有:磁存储设备硬盘、软盘、DVD和Flash设备(U盘、SSD、SD卡、NandFlash、Norflash、eMMC、iNand)
(4)linux里有个mtd驱动,就是用来管理这类块设备的。
(5)磁盘和Flash以块为单位来读写,就决定了我们启动时device copy function函数只能以整块为单位来读取SD卡。

9、用函数指针方式调用device copy function
(1)第一种方法:宏定义方式来调用。好处是简单方便,坏处是编译器不能帮我们做参数的静态类型检查。

// 第一种方法:宏定义
#define CopySDMMCtoMem(z,a,b,c,e) (((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned int *)0xD0037F98)))(z,a,b,c,e))

(2)第二种方法:用函数指针方式来调用。

typedef unsigned int bool;
// 第二种方法:用函数指针方式调用
typedef bool(*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);

// 实际使用时
pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)0xD0037F98;
p1(x, x, x, x, x);		// 第一种调用方法
(*p1)(x, x, x, x, x);	// 第二种调用方法
*p1(x, x, x, x, x);		// 错误,因为p1先和()结合,而不是先和*结合。

四、S5PV210的SD卡启动实战

1、bin文件大于16KB时的SD卡启动

1、任务:大于16KB的bin文件使用SD卡启动
(1)总体思路:
将我们的大于16KB的代码分为2部分:第一部分BL1小于等于16KB,第二部分为任意大小,iROM代码执行完成后从SD卡启动会自动读取BL1到SRAM中执行;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)。

2、代码划分为2部分(BL1和BL2)
在这里插入图片描述
总Makefile

all:
	make -C ./BL1	#进入子目录BL1下的Makefile进行make
	make -C ./BL2	#进入子目录BL2下的Makefile进行make


clean:
	make clean -C ./BL1
	make clean -C ./BL2

烧录脚本write2sd:

#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=./BL1/BL1.bin of=/dev/sdb seek=1		#进入子目录把BL1烧录到1扇区开始的地方
sudo dd iflag=dsync oflag=dsync if=./BL2/BL2.bin of=/dev/sdb seek=45	#进入子目录把BL2烧录到45扇区开始的地方

BL1下的文件:
在这里插入图片描述
BL1中的start.S:
在BL1中的start.S中做的就是关看门狗、设置SVC栈、打开icache、初始化ddr、重定位。

#define WTCON		0xE2700000

#define SVC_STACK	0xd0037d80

.global _start					// 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
	// 第1步:关看门狗(向WTCON的bit5写入0即可)
	ldr r0, =WTCON
	ldr r1, =0x0
	str r1, [r0]
	
	// 第2步:设置SVC栈
	ldr sp, =SVC_STACK
	
	// 第3步:开/关icache
	mrc p15,0,r0,c1,c0,0;			// 读出cp15的c1到r0中
	//bic r0, r0, #(1<<12)			// bit12 置0  关icache
	orr r0, r0, #(1<<12)			// bit12 置1  开icache
	mcr p15,0,r0,c1,c0,0;

	// 第4步:初始化ddr
	bl sdram_asm_init		//调用ddr的初始化函数
	
	bl led1
	bl delay
	
	// 第5步:重定位,从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000
	bl copy_bl2_2_ddr		//调用在BL1中写的重定位函数
	
	bl led2
	bl delay
	
// 汇编最后的这个死循环不能丢
	b .

3、BL1中的重定位

#define SD_START_BLOCK	45
#define SD_BLOCK_CNT	32
#define DDR_START_ADDR	0x23E00000

typedef unsigned int bool;

// 通道号:0,或者2
// 开始扇区号:45
// 读取扇区个数:32
// 读取后放入内存地址:0x23E00000
// with_init:0
typedef bool(*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);

typedef void (*pBL2Type)(void);


// 从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000,然后跳转到23E00000去执行
void copy_bl2_2_ddr(void)
{
	// 第一步,读取SD卡扇区到DDR中
	pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)(*(unsigned int *)0xD0037F98);//注意这里的这个地址0xD0037F98并不是函数的起始地址,而是以个指针
																//指向函数的起始地址,所以在对这个地址使用时解引用让他指向函数的首地址。
	//pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)0xD0037F98);	//例如这种使用方法就是有问题的
	
	led2();
	delay();
	p1(2, SD_START_BLOCK, SD_BLOCK_CNT, (unsigned int *)DDR_START_ADDR, 0);		// 读取SD卡到DDR中
	led1();
	delay();
	// 第二步,跳转到DDR中的BL2去执行
	pBL2Type p2 = (pBL2Type)DDR_START_ADDR;
	p2();
	
	led3();
	delay();
}

4、BL2远跳转
在这里插入图片描述

(1)因为我们BL1和BL2其实是2个独立的程序,链接时也是独立分开链接的,所以不能像以前一样使用ldr pc, =main这种方式来通过链接地址实现元跳转到BL2.
(2)我们的解决方案是使用地址进行强制跳转。因为我们知道BL2在内存地址0x23E00000处,所以直接去执行这个地址即可。

BL1与BL2的Makefile的对比
在这里插入图片描述
BL1与BL2的连接脚本对比:
在这里插入图片描述

2、S5PV210的SD卡启动实战的烧录及总结

1、烧录启动实验
(1)插入SD卡->在VMware上边的栏目中点击虚拟机->可移动设备->找到你的SD卡点击连接命令行输入ls /dev/sd,出现下图中就是SD卡在Linux中识别成功。
在这里插入图片描述
(2)进去目录make编译之后,./writesd执行烧录脚本即可,烧录完成之后,插入开发板,led灯亮的节奏为12313闪烁,实验就是成功的。

2、代码分为2部分启动(上一节讲的)的缺陷
(1)代码分为2部分,这种技术叫分散加载。这种分散加载的方法可以解决问题,但是比较麻烦。
(2)分散加载的缺陷:第一,代码完全分2部分,完全独立,代码编写和组织上麻烦;第二,无法让工程项目兼容SD卡启动和Nand启动、NorFlash启动等各种启动方式。

3、uboot中的做法
(1)第二种思路:程序代码仍然包括BL1和BL2两部分,但是组织形式上不分为2部分而是作为一个整体来组织。它的实现方式是:iROM启动然后从SD卡的扇区1开始读取16KB的BL1然后去执行BL1,BL1负责初始化DDR,然后从SD卡中读取整个程序(BL1+BL2)到DDR中然后从DDR中执行利用ldr pc, =main这种方式以远跳转从SRAM中运行的BL1跳转到DDR中运行的BL2)。

4、再来分析uboot的SD卡启动细节
(1)uboot编译好之后有200多KB,超出了16KB。uboot的组织方式就是前面16KB为BL1,剩下的部分为BL2.
(2)uboot在烧录到SD卡的时候,先截取uboot.bin的前16KB(实际脚本截取的是8KB)烧录到SD卡的block1~bolck32;然后将整个uboot烧录到SD卡的某个扇区中(譬如49扇区)
(3)实际uboot从SD卡启动时是这样的:iROM先执行,根据OMpin判断出启动设备是SD卡,然后从S卡的block1开始读取16KB(8KB)到SRAM中执行BL1,BL1执行时负责初始化DDR,并且从SD卡的49扇区开始复制整个uboot到DDR中指定位置(0x23E00000)去备用;然后BL1继续执行直到ldr pc, =main 时BL1跳转到DDR上的BL2中接着执行uboot的第二阶段。

总结:uboot中的这种启动方式比上节讲的分散加载的好处在于:能够兼容各种启动方式。

文章由朱有鹏老师的嵌入式核心课程总结而来
参考博主文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值