开发板——X210开发板的SD卡启动方式

news2024/11/26 16:56:05

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

参考博客:

S5PV210 SD卡启动 - 简书

关于存储器的相关基础知识,见博文:

外存——SD卡/iNand芯片与S5PV210的SD/MMC/iNand控制器-CSDN博客

RAM、ROM和FLASH三大类常见存储器简介

一、SteppingStone技术简介

1、SoC为何支持SD卡启动?

首先,SoC支持的启动方式越多,将来使用时就越方便,用户的可选择性就越大,SoC的适用面就越广。

其次,SD卡也有一些好处:(1)比如可以在不借用专用烧录工具(比如Jlink)的情况下对SD卡进行刷机,然后刷机后的SD卡插入卡槽,SoC即可启动。(2)比如可以用SD卡启动进行量产刷机。像X210这个开发板,刚开始时内部的iNand是空的,无法直接启动。官方刷机时,把事先准备好的SD卡插入SD卡卡槽,然后打到SD卡方式启动。因为此时iNand是空的所以第一启动失败,转而第二启动,也就是从外部SD2通道的SD卡启动了。启动后再执行刷机操作,对iNand进行刷机,这时iNand中已经有image了,所以可以直接启动了。刷机完成后将SD卡拔掉,烧机48小时无死机即可装箱待发货。

2、SD卡启动的难点在哪里?

SRAM、DDR都是总线式访问的,CPU可以直接和SRAM、DRAM打交道。

NorFlash也可以总线式访问,所以Norflash启动非常简单,可以直接启动。

SD卡需要时序访问,CPU不能直接和SD卡打交道,这是SD卡启动的难点所在。

3、SteppingStone技术简介

以前只有Norflash可以作为启动介质,比如台式机笔记本的BIOS就是Norflash做的。

后来三星在2440中使用了SteppingStone的技术,让Nandflash也可以作为启动介质。

SteppingStone 技术,翻译为启动基石技术,就是在 SoC 内部内置 4KB 的 SRAM,然后开机时 SoC 根据 OMpin 判断用户设置的启动方式,如果是 NandFlash 启动,则 SoC 的启动部分的硬件直接从外部 NandFlash 中读取开头的 4KB 到内部 SRAM 作为启动内容。

启动基石技术进一步发展,在6410芯片中得到完善,在210芯片时已经完全成熟。210 中有 96KB 的 SRAM,并且有一段 iROM 代码作为 BL0,BL0 再去启动 BL1。210中的BL0做的事情在2440中也有,只不过2440是硬件自动完成的,而且体系没有210中这么详细。

简单地理解,对于210而言,SteppingStone 技术就是“ 96KB的内部SRAM + 写死在64KB iROM 中的代码”。

4、iROM 如何读取 SD卡 的数据?

我们知道,210启动流程是这样的:210上电时,首先执行内部的iROM(也就是BL0),BL0 会根据 OMpin 来判断从哪个设备启动,如果启动设备是SD卡,则 BL0 会从SD卡读取前16KB(也就是BL1)到SRAM中去启动执行。这个过程就是SteppingStone 技术的体现。

而BL1代码是怎样的以及之后的事情,就是编程人员的事情了,SoC不用去操心。

那 iROM 内部到底是如何读取SD卡(或者说NandFlash)的呢?

在 iROM 内部,烧录了一些用来初始化SD卡的代码,以及一些用来将 SD 卡某些扇区内容拷贝到SRAM 中的代码。在 BL0 执行阶段,SoC 调用这些代码来初始化 SD 卡,并将 SD 卡中以第 1 扇区为开始位置的 16KB 内容(也就是BL1)拷贝到 SRAM 中。

(1)三星规定的SD卡各扇区内容

BL0 是写死的,它规定了将 SD卡中以第1扇区为开始位置的16KB 内容(也就是BL1)拷贝到SRAM中,所以我们在烧写SD卡时,要将BL1烧写到SD卡的第1扇区开始的地方。

换句话说,三星规定了 BL1 要烧写在SD卡的第1扇区开始的地方。

另外也规定了BL2,Kernel,FS这些内容的分布,如下所示:

附:扇区与块的概念

早期的块设备就是软盘硬盘这类磁存储设备,这种设备的存储单元不是以字节为单位,而是以扇区为单位。磁存储设备读写的最小单元就是扇区,不能只读取或写部分扇区。这个限制是磁存储设备本身物理方面的原因造成的,也成为了我们编程时必须遵守的规律。

一个扇区有许多字节。早期的磁盘扇区是512字节,后来的磁盘扇区可以做得比较大,比如1024字节、2048字节、4096字节。但是因为最早是512字节,很多操作系统和文件系统已经默认了“一个扇区512个字节”,所以后来的磁盘虽然物理上可以支持更大的扇区,但是实际上一般还是兼容512字节扇区这种操作方法。

块(Block)是指由多个字节组成的一个共同的操作单元块。

由于一个扇区可以看成一个块,所以我们把这一类设备统称为块设备,包括:磁存储设备如硬盘、软盘;光学存储设备如DVD、CD;Flash存储设备如U盘、SSD、SD卡、NandFlash、Norflash、eMMC、iNand芯片等等。Linux里有个MTD驱动,就是用来管理这类块设备的。

由于磁盘和Flash以块为单位来读写,因此下面的拷贝函数只能以块为单位读取SD卡的数据。

(2)拷贝函数简介

具体见手册《S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf》,以下内容源于手册的第14页前后。

由上表格可知,210内置有很多拷贝函数,用于从不同外部存储器中拷贝数据到内存中。

由于这里讨论的是SD卡启动方式,因此只关注将SD卡数据拷贝到内存(包括 IRAM 和SDRAM)的拷贝函数,即 0xD0037F98 这个地址处的拷贝函数 CopySDMMCtoMem。

该函数的使用声明如下:

/**
* This Function copy MMC(MoviNAND/iNand) Card Data to memory.
* Always use EPLL source clock.
* This function works at 20Mhz.
* @z : 这个参数是SD卡的通道号,在我的开发板中通道0连接的是内部的iNAND,通道2是我要用的。
* @a : param u32 StartBlkAddress : Source card(MoviNAND/iNand MMC)) Address.(It must block address.) 起始的block号。
* @b : param u16 blockSize : Number of blocks to copy. 共复制的block数目。
* @c : param u32* memoryPtr : Buffer to copy from. 复制到SDRAM中的地址。
* @e : param bool with_init : determined card initialization. 一般置0,不做处理。
* @return bool(u8) - Success or failure.
*/

/*
参数1:表示从哪个通道拷贝数据。这里可选0(表示iNand)或者2(表示SD卡)。
参数2:表示从SD卡或者iNand的第几个扇区开始拷贝数据。
参数3:表示一共拷贝拷贝多少个扇区的数据。
参数4:表示将数据拷贝到哪个地址。
参数5:一般置为0,不作处理。
*/

#define CopySDMMCtoMem(z,a,b,c,e) (  ((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool)) (*((unsigned int *)0xD0037F98)))(z,a,b,c,e)  )
                                     //这个是强制类型转换--------------------------------------------- //

上面是以宏定义方式来调用拷贝函数,好处是简单方便,坏处是编译器不能帮我们做参数的静态类型检查。 

分析一下上面的宏定义右边的内容,0xD0037F98是函数指针的指针,这基于两个事实,一个是0xD0037F98是iRAM上的地址,而拷贝函数的本体一定是写在iROM中的,所以(*((unsigned int *)0xD0037F98) 代表的就是指向拷贝函数的指针(即函数指针),而(bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))是函数指针类型的强制数据类型转换。

我们也可以用函数指针的方式调用拷贝函数,如下所示(具体案例见开发板——在X210开发板上进行裸机开发的细节第7点):

// 第二种方法:用函数指针方式调用
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、三星推荐的SD卡启动方式

如上所示,根据S5PV210的用户手册,三星推荐的启动方式如下(假定 bootloader 文件为 80KB):

(1)开机上电后BL0运行,BL0 从SD卡或者iNand 的第1扇区处,读取bootloader的前16KB(即BL1)到 SRAM 中运行。

(2)BL1运行时,会将BL2(bootloader中80-16=64KB)加载到SRAM(SRAM中第16KB处)中运行。

(3)BL2运行时会初始化DDR,并且将OS搬运到DDR,然后去执行OS,启动完成。

也就是说,三星推荐将BL1放在SRAM中运行,将BL2也放在SRAM中运行,这意味着bootloader必须小于96KB。

2、分散加载方式启动

上面所讲的三星推荐的SD卡启动方式只是一种推荐,我们不一定要遵守。

另外这种推荐方式要求bootloader必须小于96KB,不适用于bootloader大于96KB的情形。

(1)分散加载的含义

这里介绍一种名为“分散加载”的启动方式,它适用于bootloader大于16KB的所有情形。

如果文件大于16KB(只要大于16KB,哪怕是17KB,或者是700MB都是一样的),则需要将整个文件分割成两个独立的部分BL1和BL2,其中BL1小于等于16KB,BL2为任意大小,然后分别烧录到SD卡的不同扇区。

当系统上电时,BL0将BL1加载到SRAM中运行。BL1负责将DDR初始化,然后将BL2从SD卡中加载到DDR中合适的位置,然后在BL1最后(使用绝对地址)强制跳转到BL2所在的地址,转而执行BL2。这种启动方式就叫做分散加载。

(2)代码示例

完整的代码文件见链接,下面是文件组织结构:

 

(3)注意事项

1)BL1必须烧写在SD卡的第1扇区开始的地方(这是三星官方规定的),我们将 BL1 定为16KB大小(也就是32个Block),则BL2理论上可以从33扇区开始,但实际上会留一些空扇区作为隔离,比如可以从45扇区开始,并根据BL2的大小来分配长度。

因此write2sd文件的内容如下:

#!/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

2)BL1将DDR初始化好之后,整个DDR都可以使用了,这时在其中选择一段长度足够存储BL2的DDR空间即可,因为我们的BL1中只初始化了DDR1,地址空间范围是0x20000000~0x2FFFFFFF,这里我们选择一个合适的地址:0x23E00000。

由于我们这里选择0x23E00000,所以BL2文件夹中的链接脚本的链接地址是0x23E00000(如上图所示)。

另外BL1中调用拷贝函数拷贝BL2到DDR中时,表示将数据拷贝到哪儿的参数4要设置为0x23E00000,比如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)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();
}

3)BL1需要这些任务:关看门狗、设置栈、开iCache、初始化DDR、从SD卡复制BL2到DDR中特定位置,并跳转到BL2的开始位置。

比如BL1文件夹中start.S文件内容如下:

#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

	// 第5步:重定位,从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000
	bl copy_bl2_2_ddr
		
	// 汇编最后的这个死循环不能丢
	b .

4)BL1和BL2其实是2个独立的程序,链接时也是独立分开链接的(从代码可知 BL2的是 0x23E00000,BL1的是0xD0020010),因此不能使用ldr pc, =main这种通过链接地址的方式跳转到BL2。但是可以使用地址(函数指针)进行强制跳转,因为BL1知道BL2将被加载到哪个地址,所以BL1最后直接去执行这个地址即可(见 copy_bl2_2_ddr 函数中的最后两行代码)。

(4)分散加载方式的缺点

第一,代码完全分2部分,完全独立,代码编写和组织上麻烦;

第二,无法让工程项目兼容SD卡启动和Nand启动、NorFlash启动等各种启动方式。

3、uboot的启动方式

uboot文件大小一般超过200KB,不适合用三星推荐的启动方式;另外如果采用分散加载,则又无法兼容SD卡启动、Nand启动、NorFlash启动等启动方式,于是uboot采取了另外一种启动流程:

1)上电后BL0运行,BL0从SD卡扇区1开始读取16KB内容(即BL1)到SRAM中。

2)BL1运行时会初始化DDR,然后从SD卡的49扇区开始拷贝整个uboot(BL1+BL2)到DDR中,然后利用 ldr pc, =main 这种远跳转方式,从SRAM中运行的BL1跳转到DDR中运行的BL2。

要执行上面1)2)流程,需要事先使用sd_fusing.sh脚本将uboot烧写到SD卡中,该文件部分内容如下所示。可见它把uboot前8KB内容烧写到了SD卡的扇区1开始的地方,把整个uboot烧写到了第49扇区开始的地方。虽然这个脚本实际截取8KB的内容烧写至SD卡第1扇区,但我猜想BL0还是会从SD卡扇区1开始读取16KB内容。

#<BL1 fusing>
bl1_position=1
uboot_position=49
 
echo "BL1 fusing"
./mkbl1 ../u-boot.bin SD-bl1-8k.bin 8192
dd iflag=dsync oflag=dsync if=SD-bl1-8k.bin of=$1 seek=$bl1_position
rm SD-bl1-8k.bin
 
####################################
#<u-boot fusing>
echo "u-boot fusing"
dd iflag=dsync oflag=dsync if=../u-boot.bin of=$1 seek=$uboot_position

3)然后在DDR中继续执行BL2。

uboot的这种启动方式,和分散加载启动方式一样,把代码分为了BL1和BL2两部分;但uboot的这种启动方法,把整个uboot当做一个整体加载到DDR中的链接地址处,因此可以在BL1末尾处使用“ldr pc, =main ”这种远跳转方式,从BL1跳转到BL2。

注意,这里的“uboot的启动方式”只是一种思路与方法,这个文件不一定就是uboot,但课程中没有提供案例代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1426138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

了解WPF控件:TreeView常用属性与用法(十五)

引言 TreeView控件是WPF&#xff08;Windows Presentation Foundation&#xff09;中用于显示分层数据的常用控件。这个控件允许用户以树形结构展示数据&#xff0c;使得数据更加清晰易懂。在创建文件浏览器、组织结构图等应用程序时&#xff0c;TreeView控件非常有用。 Tree…

R语言入门笔记2.0

1.创建数据框 在R语言中&#xff0c;可以使用data.frame函数来创建数据框。以下是一个简单的示例&#xff0c;这段R语言代码创建了一个名为student的数据框&#xff0c;其中包含了学生的ID、性别、姓名和出生日期&#xff0c;首先创建一个包含学生出生日期的向量&#xff0c;再…

探秘本庄村果园预售系统的技术之旅

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

【MCAL】TC397+EB-tresos之GPT配置实战 - 定时器

本篇文章介绍了在TC397平台使用EB-tresos对GPT驱动模块进行配置的实战过程,不仅介绍了使用GTM来实现定时器的方案&#xff0c;还介绍了基于GPT12来实现连续定时器的实例。因为GTM是德国博世公司开发的IP&#xff0c;而英飞凌的芯片集成了这个IP&#xff0c;并在这个基础上搭建了…

Pytest 与allure测试报告集成

通过Feature, story, step 记录测试的功能&#xff0c;场景及测试步骤 # login.pylogin_func函数 传入参数是name 和 password 当输入的name和password与数据库db_data中数据一致时&#xff0c;返回“XXX成功登录系统&#xff01;” 当输入的name存在于数据库db_data但密码不正…

双非本科准备秋招(13.1)—— 力扣 栈、队列与堆

1、103. 二叉树的锯齿形层序遍历 昨天做的二叉树的层序遍历&#xff0c;把代码直接拿过来。 这个题要求的是一个Z型遍历&#xff0c;如下图。 用一个变量f记录正反顺序&#xff0c;然后使用LinkedList记录答案&#xff0c;下图可以看到LinkedList继承了Deque&#xff0c;所以…

【知识图谱--第一讲概论】

深度学习–连接主义 知识图谱–符号主义 表示 有属性图和RDF图两种 RDF由三元组表示&#xff1a;Subject - Predicate - Object 存储 图数据库 抽取 融合 推理 问答 图算法

Linux:进程信号的概念与产生原理

文章目录 信号的概念实践信号关于前台和后台进程的操作 操作系统与外设信号的产生signal系统调用 前面的篇章结束了信号量的话题&#xff0c;那么接下来引入的是信号的话题&#xff0c;信号和信号量之间没有任何关系&#xff0c;只是名字比较像 信号的概念 在生活中存在各种各…

linux麒麟系统安装mongodb7.0

1.mogedb下载 下载的是他tar包 https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel80-7.0.5.tgz wget -o https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel80-7.0.5.tgz 也可以下载rpm包 2.将包上传至服务器并解压 #进入目录 并解压 cd /opt/ tar…

Linux ---- Shell编程之免交互

一、Here Document 多行重定向 1、Here Document定义 使用I/O重定向的方式将命令列表提供给交互式程序标准输入的一种替代品Here Document 是标准输 入的一种替代品&#xff0c;可以帮助脚本开发人员不必使用临时文件来构建输入信息&#xff0c;而是直接就地生产出一个文件…

技术科普 | 机器视觉5大关键技术及其常见应用

计算机视觉是指&#xff1a;让机器通过数字图像或视频等视觉信息来模拟人类视觉的过程&#xff0c;以达到对物体的理解、识别、分类、跟踪、重建等目的的技术。它是人工智能领域中的一个分支&#xff0c;涉及图像处理、模式识别、机器学习、深度学习等多个领域。 随着人工智能和…

7.2、子集求和问题与背包密码系统

7.2、子集求和问题与背包密码系统 一、数学描述 1.1、第一种描述 20 世纪 70 年代末&#xff0c;默克尔和赫尔曼首次尝试将密码系统建立在一个 NP-完全问题上。他们使用了以下数学问题的一个版本&#xff0c;该问题是对经典knapsack问题的概括。 子集和问题 假设你有一个正…

数据结构——链式二叉树(3)

本篇文章我们依然讲解链式二叉树的OJ题&#xff1b; 一、二叉树的层序遍历 层序遍历即从根节点开始一层一层的遍历。我们可以运用队列的先进先出特性实现&#xff01; //层序遍历 void a(BTNode* root) {Que qhead;Queueinit(&qhead);//先入队根节点if(root)QueuePush(&…

C#,计算几何,随机点集之三角剖分的德劳内(Delaunay)算法的源代码

一、三角剖分Delaunay算法简介 点集的三角剖分&#xff08;Triangulation&#xff09;&#xff0c;对数值分析&#xff08;比如有限元分析&#xff09;以及图形学来说&#xff0c;都是极为重要的一项预处理技术。尤其是Delaunay三角剖分&#xff0c;由于其独特性&#xff0c;关…

LeetCode: 160.相交链表(令人赞叹的优雅)

160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 目录 官方双指针解法&#xff1a; 博主的辣眼代码&#xff1a; 每日一表情包&#xff1a; 博主还未学习哈希表&#xff0c;所以介绍的是双指针法&#xff0c;此题的哈希表解法时O&#xff08;nm&#xff09;空O&…

MySQL窗口函数--lead()函数

lead()函数&#xff1a; 查询当前行向下偏移n行对应的结果 该函数有三个参数&#xff1a;第一个为待查询的参数列名&#xff0c;第二个为向下偏移的位数&#xff0c;第三个参数为超出最下面边界的默认值。 如下代码&#xff1a; 查询向下偏移 2 位的年龄 SELECT user_id,user…

JavaScript高级:深浅拷贝

目录 1 引言 2 浅拷贝 2.1 拷贝数组 1.2 拷贝对象 3 赋值操作和浅拷贝的比较 4 深拷贝 4.1 前置知识 --> 递归函数 4.2 使用递归实现深拷贝 4.3 js库中的lodash里面的cloneDeep内部实现深拷贝 4.4 利用JSON实现深拷贝 深浅拷贝只针对引用数据类型 1 引言 假如我们…

leetcode 19 , 118

19 .删除链表倒数第n个节点 思路1&#xff1a; 我首先想到的就是使用两个loop来进行解决&#xff1a; 遍历所有节点&#xff0c;得到需要删除节点的位置。再遍历一边所有节点&#xff0c;找到需要删除节点进行删除。 解决方案1&#xff1a; class Solution {public ListNod…

DevOps落地笔记-07|案例分析:如何有效管理第三方组件

上一讲主要介绍了如何通过代码预检查的方式提高入库代码的质量&#xff0c;将代码检查尽可能前置&#xff0c;降低修复问题的成本&#xff0c;从而提高交付软件的质量。除了代码本身的问题&#xff0c;依赖组件也是经常困扰开发者的一个问题。比如&#xff0c;依赖组件的某个版…

项目管理构建不只是Maven,还有更优越的它!

教程全文阅读请转至《项目管理构建不只是Maven,还有更优越的它&#xff01;》 Gradle简介 Gradle是一种现代化的构建工具&#xff0c;用于构建Java、C、Python、Android等项目。它是一种基于Groovy语言的自动化构建工具&#xff0c;可以自动化执行各种构建任务&#xff0c;例…