http://www.360doc.com/content/17/0930/22/1751130_691457644.shtml
(1)MMC标准比SD标准早,SD标准兼容MMC标准。
(2)MMC卡可以被SD读卡器读写,而SD卡不可以被MMC读卡器读写。
SD卡/MMC卡等卡类有统一的接口标准,而Nand芯片没有统一的标准(各家产品会有差异)
体积大小区别而已,传输与原理完全相同。
(1)外观上,SD卡大而TF卡小;用途上,SD卡用于数码相机等而TF卡广泛用于手机、GPS等;
(2)时间上,SD卡1999年推出,TF卡于2004年推出;SD卡由日本松下、东芝与美国SanDisk共同推出,而TF卡由Motorola与SanDisk共同推出。
(3)SD卡有写保护而TF卡没有,TF卡可以通过卡套转成SD卡使用。
SD卡由9个针脚与外界进行物理连接,这9个脚中有2个地,1个电源,6个信号线。
(1)SD卡与SRAM/DDR/SROM之类的东西的不同:SRAM/DDR/SROM之类的存储芯片是总线式的,只要连接上初始化好之后就可以由SoC直接以地址方式来访问;但是SD卡不能直接通过接口给地址来访问,它的访问需要按照一定的接口协议(时序)来访问。
(2)SD卡虽然只有一种物理接口,但是却支持两种读写协议:SD协议和SPI协议。
(1)SPI协议是单片机中广泛使用的一种通信协议,并不是为SD卡专门发明的。
(2)SPI协议相对SD协议来说速度比较低。
(3)SD卡支持SPI协议,就是为了单片机方便使用。
(1)SD协议是专门用来和SD卡通信的。
(2)SD协议要求SoC中有SD控制器,运行在高速率下,要求SoC的主频不能太低。
(1)SD/ MMC主机控制器是SD和MultiMediaCard的组合主控制器。这个主控制器是
基于SD协会(SDA)主机标准规范。
SD/MMC主机控制器是系统和SD/MMC之间的接口。这个主机的性能是非常强大的,因为时钟速率是52兆赫并且同时访问8位数据引脚
(2)SD卡内部除了存储单元Flash外,还有SD卡管理模块,我们SoC和SD卡通信时,通过9针引脚以SD协议/SPI协议向SD卡管理模块发送命令、时钟、数据等信息,然后从SD卡返回信息给SoC来交互。工作时每一个任务(譬如初始化SD卡、譬如读一个块、譬如写、譬如擦除····)都需要一定的时序来完成(所谓时序就是先向SD卡发送xx命令,SD卡回xx消息,然后再向SD卡发送xx命令····)
(1)一个普遍性的原则就是:SoC支持的启动方式越多,将来使用时就越方便,用户的可选择性就越大,SoC的适用面就越广。
(2)SD卡有一些好处:譬如可以在不借用专用烧录工具(类似Jlink)的情况下对SD卡进行刷机,然后刷机后的SD卡插入卡槽,SoC即可启动;譬如可以用SD卡启动进行量产刷机(量产卡)。像我们X210开发板,板子贴片好的时候,内部iNand是空的,此时直接启动无启动;板子出厂前官方刷机时是把事先做好的量产卡插入SD卡卡槽,然后打到iNand方式启动;因为此时iNand是空的所以第一启动失败,会转而第二启动,就从外部SD2通道的SD卡启动了。启动后会执行刷机操作对iNand进行刷机,刷机完成后自动重启(这回重启时iNand中已经有image了,所以可以启动了)。刷机完成后SD量产卡拔掉,烧机48小时,无死机即可装箱待发货。
(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根据OM pin判断用户设置的启动方式,如果是NandFlash启动,则SoC的启动部分的硬件直接从外部NandFlash中读取开头的4KB到内部SRAM作为启动内容。
(3)启动基石技术进一步发展,在6410芯片中得到完善,在210芯片时已经完全成熟。210中有96KB的SRAM,并且有一段iROM代码作为BL0,BL0再去启动BL1(210中的BL0做的事情在2440中也有,只不过那时候是硬件自动完成的,而且体系没有210中这么详细)。
(1)210启动首先执行内部的iROM(也就是BL0),BL0会判断OMpin来决定从哪个设备启动,如果启动设备是SD卡,则BL0会从SD卡读取前16KB(不一定是16,反正16是工作的)到SRAM中去启动执行(这部分就是BL1,这就是steppingstone技术)
(2)BL1执行之后剩下的就是软件的事情了,SoC就不用再去操心了。
(1)启动的第一种情况是整个镜像大小小于16KB。这时候相当于我的整个镜像作为BL1被steppingstone直接硬件加载执行了而已。
(2)启动的第二种情况就是整个镜像大小大于16KB。(只要大于16KB,哪怕是17KB,或者是700MB都是一样的)这时候就要把整个镜像分为2部分:第一部分16KB大小,第二部分是剩下的大小。然后第一部分作为BL1启动,负责去初始化DRAM并且将第二部分加载到DRAM中去执行(uboot就是这样做的)。
问题:iROM究竟是怎样读取SD卡/NandFlash的?
(1)三星在iROM中事先内置了一些代码去初始化外部SD卡/NandFlash,并且内置了读取各种SD卡/NandFlash的代码在iROM中。BL0执行时就是通过调用这些device copy function来读取外部SD卡/NandFlash中的BL1的。
(2)三星系列SoC支持SD卡/NandFlash启动,主要是依靠SteppingStone技术,具体在S5PV210中支持steppingstone技术的是内部的iROM代码。
S5PV210内部有ROM代码块复制功能,用于引导-u设备。因此,
开发人员可能不需要实现设备复制功能。这些内部函数可以复制任何
从内存设备到SDRAM的数据。用户完成内部ROM后可以使用这些功能
完全引导过程。
(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卡。
(1)第一种方法:宏定义方式来调用。好处是简单方便,坏处是编译器不能帮我们做参数的静态类型检查。
(2)第二种方法:用函数指针方式来调用。
typedef unsigned int bool;
// 第一种方法:宏定义
#define CopySDMMCtoMem(z,a,b,c,e)(((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned int *)0xD0037F98)))(z,a,b,c,e))
// 第二种方法:用函数指针方式调用
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先和()结合,而不是先和*结合。
任务:大于16KB的bin文件使用SD卡启动
总体思路:将我们的代码分为2部分:第一部分BL1小于等于16KB,第二部分为任意大小,iROM代码执行完成后从SD卡启动会自动读取BL1到SRAM中执行;BL1执行时负责初始化DDR,然后手动将BL2从SD卡copy到DDR中正确位置,然后BL1远跳转到BL2中执行BL2.
细节:
一般流程
根据上面的分析,我们将程序分为两个部分BL1和BL2,文件框图如下:
BL1中要完成:
#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
bl led1
bl delay
// 第5步:重定位,从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000
bl copy_bl2_2_ddr
bl led2
bl delay
// 汇编最后的这个死循环不能丢
b .
sd_relocate.c
#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);
//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();
}
BL2 start.S中实现长跳转
在link.lds 中设置运行地址0x23E00000,所以直接去执行这个地址即可。
start.S
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
//run_on_dram:
// 长跳转到led_blink开始第二阶段
ldr pc, =main // ldr指令实现长跳转
// 汇编最后的这个死循环不能丢
b .
link.lds
SECTIONS
{
. = 0x23E00000;
.text : {
start.o
* (.text)
}
.data : {
* (.data)
}
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
all:
make -C ./BL1
make -C ./BL2
clean:
make clean -C ./BL1
make clean -C ./BL2
当make的目标为all时,-C $ (KDIR) 指明跳转到源码目录下读取那里的Makefile;M=$(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile。
在此工程中,通过最外层的Makefile 中的make -C 指令 对两个文件夹中的分工程进行了编译链接,生成了对应的bin文件(BL1 /BL2)
#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=./BL1/BL1.bin of=/dev/sdb seek=1
sudo dd iflag=dsync oflag=dsync if=./BL2/BL2.bin of=/dev/sdb seek=45
dd 命令的使用方法
(转自https://blog.csdn.net/baidu_37973494/article/details/82734594)
主用功能是用于拷贝文件,也就是用指定大小的块去拷贝一个文件,并在拷贝的同时进行指定的转换(默认从标准输入拷贝到标准输出,这意味dd可以在管道中使用)。这个拷贝过程简单理解就是读取,转换并输出数据。
语 法:dd [bs=<字节数>][cbs=<字节数>][conv=<关键字>][count=<区块数>][ibs=<字节数>][if=<文件>][obs=<字节数>][of=<文件>][seek=<区块数>][skip=<区块数>][–help][–version]
常用选项说明
bs=比特数 一次读写的比特数
cbs=比特数 一次转换的比特数
conv=CONVS 依照每个逗号分割的标志列表转换文件
count=块数 只将指定个块数复制到块
ibs=比特数 一次读取的比特数(默认:512)
if=文件 从指定文件中读取
iflag=flag 按照以逗号分隔的符号列表指定的方式读取
obs=比特数 一次写入指定比特数(默认:512)
of=文件 写入到指定文件
oflag=flag 按照以逗号分隔的符号列表指定的方式写入
seek=块数 在输出开始处跳过指定的块数
skip=块数 在输入开始处跳过指定的块数
status=noxfer 禁止传输统计
flag参数说明
append 追加模式(仅对输出有意义;隐含了conv=notrunc)
direct 使用直接I/O 存取模式
directory 除非是目录,否则 directory 失败
dsync 使用同步I/O 存取模式
sync 与上者类似,但同时也对元数据生效
fullblock 为输入积累完整块(仅iflag)
nonblock 使用无阻塞I/O 存取模式
noatime 不更新存取时间
nocache 丢弃缓存数据
noctty 不根据文件指派控制终端
nofollow 不跟随链接文件
sudo dd iflag=dsync oflag=dsync if=./BL1/BL1.bin of=/dev/sdb seek=1
解析:sudo是超级用户权限,dd是linux 的命令;dsync 读写数据采用同步IO;
iflag=flag 使用iflag来控制输入(读取数据)时的行为特征。
oflag=flag 使用oflag来控制输出(写入数据)时的行为特征。
seek=1 表示跳过第零个扇区,从第一个扇区开始
因此意思是:使用超级用户权限把BL1.bin读取进来,经过处理再输出到设备sdb上,跳过该设备的第一个block(每个block的大小为512B)
sudo dd iflag=dsync oflag=dsync if=./BL2/BL2.bin of=/dev/sdb seek=45
因此意思是:使用超级用户权限把BL2.bin读取进来,经过处理再输出到设备sdb上,跳过该设备的前45个block(每个block的大小为512B)
代码分为2部分启动(上一节讲的)的缺陷
(1)代码分为2部分,这种技术叫分散加载。这种分散加载的方法可以解决问题,但是比较麻烦。
(2)分散加载的缺陷:第一,代码完全分2部分,完全独立,代码编写和组织上麻烦;第二,无法让工程项目兼容SD卡启动和Nand启动、NorFlash启动等各种启动方式。
(1)第二种思路:程序代码仍然包括BL1和BL2两部分,但是组织形式上不分为2部分而是作为一个整体来组织。它的实现方式是:iROM启动然后从SD卡的扇区1开始读取16KB的BL1然后去执行BL1,BL1负责初始化DDR,然后从SD卡中读取整个程序(BL1+BL2)到DDR中,然后从DDR中执行(利用ldr pc, =main这种方式以远跳转从SRAM中运行的BL1跳转到DDR中运行的BL2)。
(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中的这种启动方式比上节讲的分散加载的好处在于:能够兼容各种启动方式。
注:以上内容来自朱老师物联网大讲堂裸机课件笔记整理