s5pv210从存储设备加载代码到DDR

2019-04-14 21:48发布

建议先看《[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)》,根据例子了解一下上电之后的BL0BL1BL2阶段,以及各个阶段的运行位置、功能。

一、说明

1、疑问

前面文章中《[uboot] (第三章)uboot流程——uboot-spl代码流程》中,最后uboot-spl的操作是调用board_init_f了,在board_init_f中实现了加载BL2镜像(uboot.bin)到ddr上,然后跳转到相应位置。
那么前面有个坑,就是tiny210的board_init_f中,是 如何实现加载BL2镜像(uboot.bin)到ddr上,然后跳转到BL2的相应地址上?
这个也就是本文里面的主要介绍的:
s5pv210是如何实现把镜像从存储设备上加载到ddr上的?

2、原理

通过《[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)》, s5pv210的BL0会根据启动模式从相应存储设备上获取BL1的镜像到DDR上。
例如,当启动模式是SD boot的时候,BL0会从SD卡上获取BL1镜像,加载到对应位置上。当启动模式是nand flash boot的时候,BL0会从nand flash上获取BL1镜像,加载到对应位置上。
如下表格(从《[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)》截取的一部分) 模式 硬件支持 BL1镜像存放起始位置 BL1镜像是否需要header 加载位置 跳转位置 Nand Boot Nand flash page0 是 0xD002_0000 0xD002_0010 SD / MMC Boot SD / MMC block1 是 0xD002_0000 0xD002_0010 同时,BL0是固化在s5pv210的IROM中的,是芯片在出厂的时候已经实现好了的。相应的,其从存储设备(SD/nand/eMMC)上获取镜像的代码就也就必须是已经实现好的。
既然,从存储设备(SD/nand/eMMC)上获取镜像的代码已经实现好了,也就是存储设备代码拷贝函数,并且在BL1阶段和BL2阶段可能也要需要使用到相同的功能,于是s5pv210将这部分代码的也是固化在IROM中,并且将这些函数接口的函数指针存放到某一个固定的地方。
当BL1或者BL2需要使用到从存储设备(SD/nand/eMMC)上获取镜像的函数接口时,就可以从对应的地方获取函数指针。
而剩下的,也就是最重要的是,这些函数指针被存放在上什么地方?这也是我们后续重点关注的地方。
为什么是函数指针而不是函数地址呢,是因为可以直接使用函数指针来调用函数。

二、s5pv210代码拷贝函数介绍

现在,我们要关心的是s5pv210的存储设备代码拷贝函数的函数原型以及函数指针的存放位置。
主要参考文档:《S5PV210-iROM-ApplicationNote-Preliminary-20091126》
先一段文档里面的说明:
s5pv210的IROM中集成了一些启动存储设备的块拷贝函数。因此,开发者不需要实现对应的存储设备的拷贝函数了。这些拷贝函数可以实现从存储设备到memory的拷贝。当IROM启动完成之后,开发者也可以直接使用这些拷贝函数了。
这和我们第一节中,说明的是一致的。

1、存储设备代码拷贝函数(Device Copy Function)地址存储位置

这里要特别注意,这里既不是指函数指针,也不是指函数地址,而是指函数指针被存放到的位置。
可以通过函数指针被存放到的位置找到函数指针,调用函数指针就可以调用到相应的函数了。
函数指针的地址 函数名 功能 0xD0037F90 NF8_ReadPage_Adv is advanced NF8_ReadPage function. 0xD0037F94 NF16_ReadPage_Adv is advanced NF16_ReadPage function. 0xD0037F98 CopySDMMCtoMem can copy any data from SD/MMC device to SDRAM. 0xD0037F9C CopyMMC4_3toMem can copy any data from eMMC device to SDRAM. 0xD0037FA0 CopyOND_ReadMultiPages can copy any data from OneNand device to SDRAM. 0xD0037FA4 CopyOND_ReadMultiPages_Adv can copy any data from OneNand device to SDRAM. 0xD0037FA8 Copy_eSSDtoMem can copy any data from eSSD device to SDRAM. 0xD0037FAC Copy_eSSDtoMem_Adv can copy any data from eSSD device to SDRAM. 0xD0037FB0 NF8_ReadPage_Adv128p is advanced NF8_ReadPage function. 更详细的功能介绍建议参考文档《S5PV210-iROM-ApplicationNote-Preliminary-20091126》。
这些地址是递归的,位于0xD003_7F90-0xD003_7FB0
通过文档《[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)》,我们知道了0xD002_0000-0xD003_7FFF是属于IRAM的区域,所以说’函数指针存放地址’是放在IRAM上的,按照我猜,应该是在启动过程中,BL0把这些IROM上这些固化的函数的地址写到IRAM对应的位置上,例如0xD0037F90,对应位置就可以当作一个函数指针来使用。

2、函数原型说明

文档中的函数原型和使用过程中的函数原型有所差异。个人感觉可能是文档有问题,但是这里还是以文档为准。使用介绍中,再以实际的使用为准。
因为tiny210只支持SD boot和nand flash的方式,下面我就以SD和nand flash作为存储设备的拷贝函数做介绍。 (1)CopySDMMCtoMem
从SD/MMC拷贝(加载)块到内存中的函数。
函数结构如下(也就是一个简单的使用范例) ? 1 2 3 4 5 6 7 8 9 10 11 /** * This Function copy MMC(MoviNAND/iNand) Card Data to memory. * Always use EPLL source clock. * This function works at 20Mhz. * @param u32 StartBlkAddress : Source card(MoviNAND/iNand MMC)) Address.(It must block address.) * @param u16 blockSize : Number of blocks to copy. * @param u32* memoryPtr : Buffer to copy from. * @param bool with_init : determined card initialization. * @return bool(u8) - Success or failure. */ #define CopySDMMCtoMem(z,a,b,c,e) (((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned int *)0xD0037F98)))(z,a,b,c,e)) 可以简单推测出函数原型为bool CopySDMMCtoMem(int, unsigned int, unsigned short, unsigned int*, bool)。 argv0=起始块号 argv1=块数量 argv2=要拷贝到内存的什么位置上,也就是目标地址指针。 argv3=是否需要检测初始化 argv4=返回参数,用于决定是成功还是失败。 注意:以上是文档的说明,
但是在实际使用中,实际的函数原型如下:
boot copy_sd_to_ddr(u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init);
可见,文档是有问题的。后续会说明。 (2)NF8_ReadPage_Adv
从nand flash拷贝(加载)页到内存中的函数。
8bit ECC校验的函数结构如下(也就是一个简单的使用范例) ? 1 2 3 4 5 6 7 8 /** * This Function copies a block of page to destination memory.( 8-Bit ECC only ) * @param uint32 block : Source block address number to copy. * @param uint32 page : Source page address number to copy. * @param uint8 *buffer : Target Buffer pointer. * @return int32 - Success or failure. */ #define NF8_ReadPage_Adv (a,b,c) (((int(*)(uint32, uint32, uint8*))(*((uint32 *) 0xD0037F90)))(a,b,c)) 其参数意义参考注释,这里不加以说明了。

三、s5pv210代码拷贝函数使用示例

s5pv210代码拷贝函数是以函数指针的方式进行使用的,因为,在这里先介绍一个简单的函数指针的使用示例。

1、函数指针简单示例说明

假设有一个函数原型是int function(int a, int b),其函数指针被存放在了0x10的位置上。
那么我们调用该函数的方法如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #define CopySDMMCtoMem 0xD0037F98 // 上述我们已经说明了,CopySDMMCtoMem的函数指针是存放在0xD0037F98的。   typedef u32(*copy_sd_mmc_to_mem)         (u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init); // 这样就定义了copy_sd_mmc_to_mem为u32 (*)(u32, u32, u16, u32 *, u32)类型,后续可以直接用于函数指针变量的定义和类型强制转换 // argv0=通道号 // argv1=起始块号 // argv2=块数量 // argv3=要拷贝到的目标地址 // argv4不解   // 以下就是加载BL2镜像到DDR中的主体了,是在BL1中执行的。 // 主要关心一下函数指针的获取、转化和调用过程 void copy_bl2_to_ddr(void) {     u32 sdmmc_base_addr;     copy_sd_mmc_to_mem copy_bl2 = (copy_sd_mmc_to_mem)(*(u32*)CopySDMMCtoMem); // 这个是重点函数,也是要重点理解的地方 // (u32*)CopySDMMCtoMem先将0xD003_7F98强制类型转换成一个指针 // (*(u32*)CopySDMMCtoMem)从0xD003_7F98中获取函数指针 // (copy_sd_mmc_to_mem)对得到的函数指针进行强制类型转换,赋值给copy_bl2,后续直接调用copy_bl2即可。       sdmmc_base_addr = *(u32 *)SDMMC_BASE; // 获取通道,SD有两个通道,这部分在文档上没有说明,这里我们也不关心。       if(sdmmc_base_addr == SDMMC_CH0_BASE_ADDR)         copy_bl2(0, MOVI_BL2_SDCARD_POS, MOVI_BL2_BLKCNT, (u32 *)CONFIG_SYS_TEXT_BASE, 0); // 直接调用copy_bl2,就可以调用到具体的函数指针了 // MOVI_BL2_SDCARD_POS表示我们把BL2的镜像放到了哪个块上 // MOVI_BL2_BLKCNT表示BL2占用的块长度 // CONFIG_SYS_TEXT_BASE=0x23E0_0000,定义在include/configs/tiny210.h中,表示我们要把BL2的镜像(uboot.bin)放到什么位置上。       if(sdmmc_base_addr == SDMMC_CH2_BASE_ADDR)         copy_bl2(2, MOVI_BL2_SDCARD_POS, MOVI_BL2_BLKCNT, (u32 *)CONFIG_SYS_TEXT_BASE, 0);