[project X] tiny210(s5pv210)從存儲設備加載代碼到DDR

[uboot] uboot流程系列
[project X] tiny210(s5pv210)上電啓動流程(BL0-BL2)
[uboot] (第一章)uboot流程——概述
[uboot] (第二章)uboot流程——uboot-spl編譯流程
[uboot] (第三章)uboot流程——uboot-spl代碼流程
[project X] tiny210(s5pv210)從存儲設備加載代碼到DDR

參考文檔:
S5PV210-iROM-ApplicationNote-Preliminary-20091126
S5PV210_UM_REV1.1

建議先看《[project X] tiny210(s5pv210)上電啓動流程(BL0-BL2)》,根據例子瞭解一下上電之後的BL0\BL1\BL2階段,以及各個階段的運行位置,功能。

========================================================================================================

一、說明

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》
先一段文檔裏面的說明:
_The S5PV210 internally has a ROM code of block copy function for boot-u device. Therefore,
developer may not needs to implements device copy functions. These internal functions can copy any
data from memory devices to SDRAM. User can use these function after ending up the internal ROM
boot process completely._
簡單翻譯爲如下:
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拷貝(加載)塊到內存中的函數。
函數結構如下(也就是一個簡單的使用範例)

/**
* 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校驗的函數結構如下(也就是一個簡單的使用範例)

/**
* 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)首先需要獲取到函數指針
        因爲函數指針存放在了0x10的位置上,所以先對0x10進行強制類型轉換成一個指針(unsigned int *)0x10,
        然後*((unsigned int *)0x10)就可以獲取到函數指針變量。
        暫時表示如下
            f_ptr=(*((unsigned int *)0x10))
(2)根據函數原型可以得到對應的函數指針類型是int (*)(int, int),所以需要對(1)的函數指針進行強制類型轉換
        (int (*)(int, int))f_ptr,
        暫時表示如下
            ok_f_ptr=((int (*)(int, int))f_ptr)
(3)調用該函數的方法爲ok_f_ptr(a, b)
        等價於*ok_f_ptr(a, b),一般都是使用ok_f_ptr(a, b)
綜上,展開則爲((int (*)(int, int))(*((unsigned int *)0x10)))(a,b)

2、tiny210從SD上加載代碼實現

目前project X的設計中,tiny210的BL1(uboot-spl)是從SD上加載BL2的鏡像(uboot.bin)到ddr上的。
並且BL2(uboot)也是從SD卡上加載kenrel、rootfs、dtb到ddr上的。原理都是一樣的。
以下,我們以“BL1(uboot-spl)是從SD上加載BL2的鏡像(uboot.bin)到ddr上的”的代碼copy_bl2_to_ddr爲例。
理解了上述1之後,以下代碼就很好理解了。
代碼copy_bl2_to_ddr如下:
board/samsung/tiny210/board.c中

#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);
}

通過上述代碼uboot-spl調用copy_bl2_to_ddr就可以實現把uboot.bin從SD中拷貝到0x23E0_0000,後面跳轉到0x23E0_0000後,就可以直接進入uboot了。
uboot中把kernel、rootfs、dtb從SD中拷貝到對應位置上也是通過一樣的方法,具體請自行參考代碼copy_kernel_to_ddr的實現。

綜上,就實現了利用IROM的代碼從SD上拷貝鏡像、代碼到ddr中。
nand flash後續實現之後再進行補充。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章