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

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

参考博客:

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,但课程中没有提供案例代码。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt6入门教程 15:QRadioButton

目录 一.简介 二.常用接口 三.实战演练 1.径向渐变 2.QSS贴图 3.开关效果 4.非互斥 一.简介 QRadioButton控件提供了一个带有文本标签的单选按钮。 QRadioButton是一个可以切换选中&#xff08;checked&#xff09;或未选中&#xff08;unchecked&#xff09;状态的选项…

Flink 流式读取 Debezium CDC 数据写入 Hudi 表无法处理 -D / Delete 消息

问题场景是&#xff1a;使用 Kafka Connect 的 Debezium MySQL Source Connector 将 MySQL 的 CDC 数据 &#xff08;Avro 格式&#xff09;接入到 Kafka 之后&#xff0c;通过 Flink 读取并解析这些 CDC 数据&#xff0c;然后以流式方式写入到 Hudi 表中&#xff0c;测试中发现…

Java Springboot解决很多页面Whitelabel Error Page(404)问题

前言 最近接手了一个前后端一体的项目&#xff0c;发现其默认路径不是主机端口&#xff08;如&#xff1a;http://localhost:3453/&#xff09;的形式。很多页面的访问是加了一个层级。只要访问就会出现如下提示&#xff1a; Whitelabel Error Page This application has no …

双目相机立体匹配基础

双目匹配就是用左相机和右相机去拍摄同一个点&#xff0c;目的是找到三维世界的同一个点&#xff0c;也就是在左相机和右相机中的成像点之间的像素差&#xff08;视差&#xff09;&#xff0c;根据视差去求解深度&#xff0c;那么找到左相机点到右相机的同一个对应点这个过程就…

草图导入3d后模型贴材质的步骤?---模大狮模型网

3D模型在导入草图大师后出现混乱可能有多种原因&#xff0c;以下是一些可能的原因和解决方法&#xff1a; 模型尺寸问题&#xff1a;如果3D模型的尺寸在导入草图大师时与画布尺寸不匹配&#xff0c;可能导致模型混乱。解决方法是在3D建模软件中调整模型的尺寸&#xff0c;使其适…

【NodeJS】005- MongoDB数据库

1.简介 1.1 Mongodb 是什么 MongoDB 是一个基于分布式文件存储的数据库&#xff0c;官方地址 https://www.mongodb.com/ 1.2 数据库是什么 数据库&#xff08;DataBase&#xff09;是按照数据结构来组织、存储和管理数据的 应用程序 1.3 数据库的作用 数据库的主要作用就是…

目标检测:1预备知识

开始涉及目标检测内容&#xff0c;总结一下学习记录 1、目标检测的基本概念 &#xff08;一&#xff09;什么是目标检测 目标检测&#xff08;Object Detection&#xff09; 的任务是找出图像中所有感兴趣的目标&#xff08;物体&#xff09;&#xff0c;不同于分类和回归问题…

react-virtualized实现行元素不等高的虚拟列表滚动

前言&#xff1a; 当一个页面中需要接受接口返回的全部数据进行页面渲染时间&#xff0c;如果数据量比较庞大&#xff0c;前端在渲染dom的过程中需要花费时间&#xff0c;造成页面经常出现卡顿现象。 需求&#xff1a;通过虚拟加载&#xff0c;优化页面渲染速度 优点&#xff1…

如何批量删除文件名里的多余文字?

如何批量删除文件名里的多余文字&#xff1f;删除文件名中多余的文字可以提高文件管理的效率和可读性。简洁性&#xff1a;删除多余的文字可以使文件名更简洁&#xff0c;减少冗余信息。这样可以更轻松地浏览和识别文件&#xff0c;尤其是当文件数量较多时。可读性&#xff1a;…

tcp/ip模型中,帧是第几层的数据单元?

在网络通信的世界中&#xff0c;TCP/IP模型以其高效和可靠性而著称。这个模型是现代互联网通信的基石&#xff0c;它定义了数据在网络中如何被传输和接收。其中&#xff0c;一个核心的概念是数据单元的层级&#xff0c;特别是“帧”在这个模型中的位置。今天&#xff0c;我们就…

代码随想录day17--二叉树的应用5

LeetCode654.最大二叉树 题目描述&#xff1a; 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后…

后端性能优化的一些总结

目录 1、背景 2、优化实现 2.1查询数据表速度慢 2.2调别人接口速度慢 2.3导入速度慢、 2.4导出速度慢的做出介绍 2.5统计功能速度慢 3、总结 1、背景 系统上线后&#xff0c;被用户反应系统很多功能响应时长很慢。用户页面影响速度有要求&#xff0c;下面针对查询数据表…

C#,入门教程(36)——尝试(try)捕捉(catch)不同异常(Exception)的点滴知识与源代码

上一篇&#xff1a; C#&#xff0c;入门教程(35)——哈希表&#xff08;Hashtable&#xff09;的基础知识与用法https://blog.csdn.net/beijinghorn/article/details/124236243 1、try catch 错误机制 Try-catch 语句包含一个后接一个或多个 catch 子句的 try 块&#xff0c;这…

深度学习(7)--Keras项目详解(卷积神经网络)

目录 一.项目介绍 二.卷积神经网络构造 2.1.判断是否是channels first的back end 2.2.卷积层构造 2.3.添加激活函数 2.4.池化层构造 2.5.全连接FC层构造 三.完整代码 3.1.学习率衰减设置 四.首次运行结果 五.数据增强对结果的影响 六.BatchNormalization对结果的影…

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

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

R语言入门笔记2.0

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

网络时间协议NTP

网络时间协议NTP(Network Time Protocol)是TCP/IP协议族里面的一个应用层协议。NTP用于在一系列分布式时间服务器与客户端之间同步时钟。NTP的实现基于IP和UDP。NTP报文通过UDP传输,端口号是123。 随着网络拓扑的日益复杂,整个网络内设备的时钟同步将变得十分重要。如果依靠…

Skywalking的Trace Profiling 代码级性能剖析功能应用详解

代码级性能剖析 Skywalking 提供了Trace Profiling功能对具体出现问题的span进行代码级性能剖析。 代码级性能剖析就是利用方法栈快照&#xff0c;并对方法执行情况进行分析和汇总。并结合有限的分布式追踪 span 上下文&#xff0c;对代码执行速度进行估算。性能剖析激活时&a…

配置支持 OpenAPI 的 ASP.NET Core 应用

写在前面 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。 本文记录如何配置基于Swagger 的 ASP.NET Core 应用程序的 OpenAPI 规范。 需要从NuGet 安装 Swashbuckle.AspNetCore 包 代码实现 var builder WebApplicati…

ChatLaw:基于LLaMA微调的法律大模型

文章目录 动机数据组成模型框架模型评估 北大团队发布首个的中文法律大模型落地产品ChatLaw&#xff0c;为大众提供普惠法律服务。模型支持文件、语音输出&#xff0c;同时支持法律文书写作、法律建议、法律援助推荐。 github地址&#xff1a;https://github.com/PKU-YuanGroup…