嵌入式知識-ARM裸機-學習筆記(9):SD卡啓動詳解(S5PV210)

嵌入式知識-ARM裸機-學習筆記(9):SD卡啓動詳解(S5PV210)

一、SD卡介紹

1. SD卡背景知識和特點

SD卡、MMC卡、MicroSD、TF卡:這些卡其實內部就是Flash存儲顆粒,比直接的Nand芯片多了統一的外部封裝和接口。卡都有統一的標準,譬如SD卡都是遵照SD規範來發布的。這些規範規定了SD卡的讀寫速度、讀寫接口時序、讀寫命令集、卡大小尺寸、引腳個數及定義。這樣做的好處就是不同廠家的SD卡可以通用。

MMC標準比SD標準早,SD標準兼容MMC標準。MMC卡可以被SD讀卡器讀寫,而SD卡不可以被MMC讀卡器讀寫。

SD卡/MMC卡等卡類有統一的接口標準,而Nand芯片沒有統一的標準(各家產品會有差異)。

)SD卡有寫保護而TF卡沒有,TF卡可以通過卡套轉成SD卡使用。

2. SD卡物理接口

在這裏插入圖片描述
SD卡由9個針腳與外界進行物理連接,這9個腳中有2個地,1個電源,6個信號線。
在這裏插入圖片描述

3. SD協議與SPI協議

SD卡與SRAM/DDR/SROM之類的東西的不同:SRAM/DDR/SROM之類的存儲芯片是總線式的,只要連接上初始化好之後就可以由SoC直接以地址方式來訪問;但是SD卡不能直接通過接口給地址來訪問,它的訪問需要按照一定的接口協議(時序)來訪問。
SD卡雖然只有一種物理接口,但是卻支持兩種讀寫協議SD協議和SPI協議

(1)SPI協議特點(低速、接口操作時序簡單、適合單片機)
SPI協議是單片機中廣泛使用的一種通信協議,並不是爲SD卡專門發明的。
SPI協議相對SD協議來說速度比較低。
SD卡支持SPI協議,就是爲了單片機方便使用。(單片機如果想要外部擴展SD卡,SPI協議是最合適的)。

(2)SD協議特點(高速、接口時序複雜,適合有SDIO接口的SoC)
SD協議是專門用來和SD卡通信的。
SD協議要求SoC中有SD控制器,運行在高速率下,要求SoC的主頻不能太低。

4. S5PV210的SD/MMC控制器

SD卡內部除了存儲單元Flash外,還有SD卡管理模塊,我們SoC和SD卡通信時,通過9針引腳以SD協議/SPI協議向SD卡管理模塊發送命令、時鐘、數據等信息,然後從SD卡返回信息給SoC來交互。工作時每一個任務(初始化SD卡、讀一個塊、寫、擦除····)都需要一定的時序來完成(所謂時序就是先向SD卡發送xx命令,SD卡回xx消息,然後再向SD卡發送xx命令····)。

SD卡是支持熱插拔的,也就是有某一個寄存器的某一位用來判斷SD卡是否插入,如果插入了該位置1,證明SD卡插入了;如果該位爲0,證明沒有插入SD卡。
在這裏插入圖片描述

5. 扇區和塊的概念

早期的塊設備就是軟盤硬盤這類磁存儲設備,這種設備的存儲單元不是以字節爲單位,而是以扇區(相當於一個存儲單元)爲單位。磁存儲設備讀寫的最小單元就是扇區,不能只讀取或寫部分扇區。這個限制是磁存儲設備本身物理方面的原因造成的,也成爲了我們編程時必須遵守的規律。
一個扇區有好多個字節(一般是512個字節)。早期的磁盤扇區是512字節,實際上後來的磁盤扇區可以做的比較大(譬如1024字節,譬如2048字節,譬如4096字節),但是因爲原來最早是512字節,很多的軟件(包括操作系統和文件系統)已經默認了512這個數字,因此後來的硬件雖然物理上可能支持更大的扇區,但是實際上一般還是兼容512字節扇區這種操作方法。
一個扇區可以看成是一個塊block(塊的概念就是:不是一個字節,是多個字節組成一個共同的操作單元塊),所以就把這一類的設備稱爲塊設備。常見的塊設備有:磁存儲設備硬盤、軟盤、DVD和Flash設備(U盤、SSD、SD卡、NandFlash、Norflash、eMMC、iNand)。
linux裏有個mtd驅動,就是用來管理這類塊設備的

二、SD卡啓動詳解

1. SD卡與SoC的關係

說到兩者之間的關係,我們需要解決一個問題就是SoC爲什麼要支持SD卡啓動
一個普遍性的原則就是:SoC支持的啓動方式越多,將來使用時就越方便,用戶的可選擇性就越大,SoC的適用面就越廣。
SD卡有一些好處:譬如可以在不借用專用燒錄工具(類似Jlink)的情況下對SD卡進行刷機,然後刷機後的SD卡插入卡槽,SoC既可啓動;譬如可以用SD卡啓動進行量產刷機(這樣的做法比一個一個用燒錄工具燒錄方便了很多)。像我們X210開發板,板子貼片好的時候,內部iNand是空的,此時直接啓動無啓動;板子出廠前官方刷機時是把事先做好的量產卡插入SD卡卡槽,然後打到iNand方式啓動;因爲此時iNand是空的所以第一啓動失敗,會轉而第二啓動,就從外部SD2通道的SD卡啓動了。啓動後會執行刷機操作對iNand進行刷機,刷機完成後自動重啓(這回重啓時iNand中已經有image了,所以可以啓動了)。

2. SD卡啓動難點

SRAM、DDR都是總線式訪問的,SRAM不需初始化既可直接使用,而DDR需要初始化後才能使用,但是總之CPU可以直接和SRAM/DRAM打交道;而SD卡需要時序訪問,CPU不能直接和SD卡打交道;NorFlash讀取時可以總線式訪問,所以Norflash啓動非常簡單,可以直接啓動,但是SD/NandFlash不行。
以前只有Norflash可以作爲啓動介質,臺式機筆記本的BIOS(類似於Uboot)就是Norflash做的。後來三星在2440中使用了SteppingStone的技術,讓Nandflash也可以作爲啓動介質。SteppingStone(翻譯爲啓動基石)技術就是在SoC內部內置4KB的SRAM,然後開機時SoC根據OMpin判斷用戶設置的啓動方式,如果是NandFlash啓動,則SoC的啓動部分的硬件直接從外部NandFlash中讀取開頭的4KB到內部SRAM作爲啓動內容。
隨着該技術的成熟,210中有96KB的SRAM,並且有一段iROM代碼作爲BL0,BL0再去啓動BL1

3. SD卡啓動過程

在這裏插入圖片描述
210啓動時首先執行內部的iROM(也就是BL0),BL0會判斷OMpin來決定從哪個設備啓動,如果啓動設備是SD卡,則BL0會從SD卡讀取前16KB到SRAM中去啓動執行(這部分就是BL1,這就是steppingstone技術)。BL1執行之後剩下的就是軟件的事情了,SoC就不用再去操心了。
在這裏插入圖片描述
iROM究竟是怎樣讀取SD卡/NandFlash的?
三星在iROM中事先內置了一些代碼去初始化外部SD卡/NandFlash,並且內置了讀取各種SD卡/NandFlash的代碼在iROM中。 BL0執行時就是通過調用這些device copy function來讀取外部SD卡/NandFlash中的BL1的。並且這些function函數可供我們使用。
磁盤和Flash以塊爲單位來讀寫,就決定了我們啓動時device copy function只能以整塊爲單位來讀取SD卡

利用SD卡啓動分爲兩種情況:
情況一(bin文件小於16KB):啓動的第一種情況是整個鏡像大小小於16KB。這時候相當於我的整個鏡像作爲BL1被steppingstone直接硬件加載執行了而已。
情況二(bin文件大於17KB ):啓動的第二種情況就是整個鏡像大小大於16KB。(只要大於16KB,哪怕是17KB,或者是700MB都是一樣的)這時候就要把整個鏡像分爲2部分:第一部分16KB大小,第二部分是剩下的大小。然後第一部分作爲BL1啓動,負責去初始化DRAM並且將第二部分加載到DRAM中去執行(uboot就是這樣做的)。

4. SD卡啓動代碼分析

對於情況一來說,在啓動時BL0進行判斷完成後,會將全部16KB的bin文件讀取到SRAM中執行,因此不涉及到分散加載的問題,則與之前的實驗做法相同。本文主要針對情況二,也就是bin文件大小大於16KB時,該如何做。

思路: 根據上面的分析,當bin文件大小大於16KB時,需要將鏡像文件拆分爲2部分,第一部分BL1小於等於16KB,第二部分爲任意大小,iROM代碼執行完成後從SD卡啓動會自動讀取BL1到SRAM中執行;BL1執行時負責初始化DDR,然後手動將BL2從SD卡copy到DDR中正確位置,然後BL1遠跳轉到BL2中執行BL2。
因此在BL1中要完成:關看門狗、設置棧、開iCache、初始化DDR、從SD卡複製BL2到DDR中特定位置,跳轉執行BL2。

對於SD卡內部,官方文檔給出了一個推薦的區域劃分:
在這裏插入圖片描述
根據圖我們能得到,最前面要空出來一個扇區,BL1在SD卡中必須從Block1開始(Block0不能用,這個是三星官方規定的),長度爲16KB內,我們就定爲16KB(也就是32個block);BL1理論上可以從33扇區開始,但是實際上爲了安全都會留一些空扇區作爲隔離,例如可以從45扇區開始,長度由自己定(實際根據自己的BL2大小來分配長度,我們實驗時BL2非常小,因此我們定義BL2長度爲16KB,也就是32扇區)。

DDR初始化好之後,整個DDR都可以使用了,這時在其中選擇一段長度足夠BL2的DDR空間即可。我們選0x23E00000(因爲我們BL1中只初始化了DDR1,地址空間範圍是0x20000000~0x2FFFFFFF)。

Makefile文件
在這裏插入圖片描述
由於我們將代碼分爲了BL1和BL2兩部分,因此需要一個總的Makefile文件對兩部分代碼實現協同管理。
在這裏插入圖片描述
BL1部分
(1)BL1部分要完成關看門狗、設置棧、開iCache、初始化DDR、從SD卡複製BL2到DDR中特定位置。
因此我們可以在start.S文件中可以完成:
在這裏插入圖片描述
(2)在sd_relocate.c文件中,我們要實現將SD卡中的BL2部分複製到DDR中的特定位置去執行,這裏就用到了重定位的方法。
在這裏插入圖片描述
官方代碼爲了方便我們進行SD卡的讀取操作,實現給了我們一個將SD卡中的內容拷貝到內存中的函數,即爲CopySDMMCtoMem,該函數的地址我們不知道,但是它是通過一個函數起始地址的指針(0xD0037F98)指向了這個函數
在使用時,我們先將內容轉換爲一個unsigned in *類型,因爲0xD0037F98中就是存了一個int型的數字,這個int類型的數字就是真正的copy函數的首地址,轉換完之後,我們利用 *(地址)的方式對該指針進行解引用,即可定位到該copy函數,並使用它。

typedef bool(pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int, bool); ,相當於我們指定了一個函數指針類型,後面一大部分是函數要傳入的參數,前面表示返回值爲bool類型。
在實際使用時, pCopySDMMC2Mem p1=(pCopySDMMC2Mem)(*(unsigned int *)0xD0037F98); ,將上述函數強制轉換爲這種函數指針類型,然後讓它等於p1。在調用時,我們通過p1(2, SD_START_BLOCK, SD_BLOCK_CNT, (unsigned int *)DDR_START_ADDR, 0); 即可實現函數調用。

BL2部分
(1)在BL1中,我們通過start.s文件中的bl copy_bl2_2_ddr,實現了拷貝函數的調用從而對SD卡中第45扇區開始的32個扇區的拷貝,拷貝到DDR中地址爲0x23E00000的位置。在BL2部分,我們在start.s文件中不再需要對看門狗等任務進行初始化(因爲在BL1中已經做過了),只需要實現一個長跳轉指令,跳轉到DDR的對應位置,繼續執行接下來的操作(BL2)。
在這裏插入圖片描述
(2)由於在BL1中實現了將剩餘代碼拷貝到了DDR上的0x23E00000,因此在鏈接腳本中,需要指定重定位後代碼運行的位置。這個地址一定要和代碼拷貝到的DDR種的地址對應。
0x23E00000
在這裏插入圖片描述
根據上述做法可以實現:
(1)iROM,也就是BL0先啓動,判斷OMpin來決定通過SD卡啓動,因此BL0從SD卡讀取前16KB(我們編寫的BL1部分)到SRAM中去啓動執行。
(2)在執行BL1時,會進行start.s中的相應動作,最重要的是在BL1中會初始化DDR和複製BL2部分到DDR中,到這BL1完成了它的任務,並通過p2()函數跳轉到了DDR的0x23E00000位置的代碼部分。
(3)由於BL2部分的代碼被拷貝到了DDR上的0x23E00000位置,則繼續執行接下來的代碼,在宏觀來看實現了整個代碼的連續。

5. 通過write2s進行燒寫

整個代碼進行make之後會生成一個write2s文件,用於在linux中通過dd命令進行SD卡的燒寫任務。
在這裏插入圖片描述
if=./BL1/BL1.bin:表示輸入文檔爲BL1文件夾下的BL1.bin文件。
of=/dev/sdb:表示輸出目錄爲/dev下的/sdb目錄(也就是對應SD卡的位置)。
seek=1:表示燒寫位置是扇區1。
綜上可以理解爲:將BL1中的bin文件燒寫到SD卡的第1個扇區位置(因爲第0個扇區需要空出來),這一部分是BL1對應的16KB內容。將BL2中的bin文件燒寫到SD卡的第45個扇區的位置,這一部分是多與16KB的部分(之後這部分將會在DDR初始化之後被拷貝到DDR上進行執行)

因爲我們BL1和BL2其實是2個獨立的程序,鏈接時也是獨立分開鏈接的,所以不能像以前一樣使用ldr pc, =main這種方式來通過鏈接地址實現遠跳轉到BL2。我們的解決方案是使用地址進行強制跳轉。因爲我們知道BL2在內存地址0x23E00000處,所以直接去執行這個地址即可。

如何在linux下進行SD卡燒寫?
燒錄過程:
1.先將SD卡通過讀卡器插到電腦上
在這裏插入圖片描述
2.默認情況是在windows電腦上,所以需要先與虛擬機進行連接(虛擬機->可移動設備->連接,這樣從devices中就可以發現SD卡,通過ls /dev/sd*可以查看,如果有/dev/sdb即成功)
在這裏插入圖片描述
3…/write2sd執行完成,顯示32+0 records in……即爲燒錄成功
在這裏插入圖片描述

總結:
代碼分爲2部分,這種技術叫分散加載。這種分散加載的方法可以解決問題,但是比較麻煩。分散加載的缺陷:第一,代碼完全分2部分,完全獨立,代碼編寫和組織上麻煩;第二,無法讓工程項目兼容SD卡啓動和Nand啓動、NorFlash啓動等各種啓動方式。

uboot中的做法:
程序代碼仍然包括BL1和BL2兩部分,但是組織形式上不分爲2部分而是作爲一個整體來組織。它的實現方式是:iROM啓動然後從SD卡的扇區1開始讀取16KB的BL1然後去執行BL1,BL1負責初始化DDR,然後從SD卡中讀取整個程序(BL1+BL2)到DDR中,然後從DDR中執行(利用ldr pc, =main這種方式以遠跳轉從SRAM中運行的BL1跳轉到DDR中運行的BL2)。uboot這種做法的好處是,能夠兼容各種啓動方式。

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