通過前面十篇博文,我們已經完成了對BL1階段的分析,通過這些分析相信我們對u-boot已經有了一個比較深入的認識,在BL2階段大部分是對外設的初始化,並且有的我們已經分析過,在這篇博文我打算對BL1階段沒有分析到的重要外設進行簡單分析,並結束對u-boot的分析,同時對后面自己的博文進行簡單的規划,希望有興趣的朋友跟我一塊學習和研究嵌入式。
今天我們會分析到以下內容:
1. MMU分析(內容出自我以前的博客)
2. 裸機開發總結
3. 后期學習規划
1. MMU作用
MMU就是負責虛擬地址(virtual address)轉化成物理地址(physical address)。
2. 基本概念
下面我來說一下ARM CPU上的地址轉換過程涉及三個概念:虛擬地址(VA)(CPU內核對外發出VA),變換后的虛擬地址(MVA)(VA被轉換為MVA供cache和MMU使用,在此將MVA轉換為PA),物理地址(PA)(最后使用PA讀寫實際設備)。
1. CPU看到的用到的只是VA,CPU不管VA最終是怎樣到PA的。
2. cache、MMU也是看不到VA的,它們使用的是MVA(VA到MVA的轉換是由硬件自動完成的)。
3. 實際設備看不到VA、MVA,讀寫設備使用的是PA物理地址。
3. 如何地址轉換
在此過程中要用到以下兩個必備的東西:
1. cp15協處理器的c2寄存器(這個里面的東西需要程序員自己裝載)
2. 虛擬地址MVA。
接下來我說以下mmu對虛擬地址到物理地址的轉化過程。
1. 無論進行那種有效轉換都必須進行一級頁表的轉換,所以我們先去產生一級頁表。產生一級頁表經過以下步驟
a) 由協處理器CP15中的寄存器C2(高18位,即[31:14]為轉換表基地址,低14位為0)為一級轉換表基地址,即TTB該地址指向了一個2^14=16KB大小的存儲區,即一級轉換表。
b) 將MVA的高12位,即位[31:20]作為一級轉換表的地址索引,因此一級轉換表具有2^12=4096項,這4096項的地址為32位,最高的18位[31:14]為寄存器C2的高18位,中間12位為MVA的高12位[31:20],最低2位為0b00。這4096項中的內容稱為一級描述符。至此一級頁表產生完畢。
2. mmu拿到一級描述符首先看它的后兩位也就是bits[1:0],根據這兩位的取值mmu會進入不同的轉換模式,這兩位會出現下面四種取值。其中這四種取值分別對應四種模式,下面我一一解釋。
a) 00 無效不進行轉換
b) 01 進行粗頁式轉換
c) 10 進行段式轉換
d) 11 進行細頁式轉換
3. 接下來對於不同的轉換模式,就要進行不同的轉換。我下面將對三種轉換模式進行一一分析。
a) 0b10(段式轉換),把段式轉換放在第一個說的原因是在ARM中大量用到了段式和細頁式轉換。
段式轉換是最簡單的一種轉換方式,它是將我們剛才產生的一級描述符(即4096項中的內容)的高12位作為段的基地址,用MVA的低20位作為每個段的偏移量,由此我們可以輕松的推出每個段的大小為2^20=1MB,至此我們就得到了一個物理地址PA。
b) 0b11(細頁式轉換)
細頁式轉換,它是將我們剛才產生的一級描述符的高20位bits[31:12],再加上MVA的bits[19:10](第二級表索引)合體,bits[1:0]補0,獲取第二級描述符的地址,至此其一級轉換結束,因為其二級轉換方式和粗頁式的二級轉換方式相同所以我下面一塊分析。
c) 0b01(粗頁式轉換)
粗頁式轉換,它是將我們剛才產生的一級描述符的高22位bits[31:10],再加上MVA的bits[19:12](第二級表索引)合體,最后兩位補0,獲取第二級描述符的地址(32位),至此其一級轉換結束。
4. 下面我們將進入二級轉換,對於二級轉換只針對細頁式和粗頁式,沒有段式轉換。
a) 經過一級轉換我們得到了二級描述符的地址,我們通過此地址可以得到二級描述符,我們根據二級描述符的低兩位bits[1:0]同樣可以分為四種轉換模式:
i. 00 無效不進行轉換
ii. 01 大頁描述符
iii. 10 小頁描述符
iv. 11 極小頁描述符
b) 接下來我將對各個模式進行分析。
i. 01(大頁描述符)
此時我們取出二級描述符中的bits[31:16]——大頁基址,它和MVA的bits[15:0]組成一個32位的物理地址,這就是MVA對應的PA。
ii. 10(小頁描述符)
此時我們取出二級描述符中的bits[31:12]與MVA的bits[11:0]組成一個32位的地址,這就是MVA對應的PA。
iii. 11(極小頁描述符)
此時我們取出二級描述符中的bits[31:10]與MVA的bits[9:0]組成一個32位的地址,這就是MVA對應的PA。
l 裸機開發總結
在前十篇博文中我們分析了整個u-boot的工作流程,並對其中重要的代碼和外設進行了分析,對於嵌入式的外設是相當多的,我們要全部分析的話是不太現實的,所以我們應該在學習中總結方法,下面我將自己對於裸機的方法跟大家進行分享,不足之處還望大家互相交流。
1) 熟悉外設大體工作原理,比如我們在前幾篇博文中首先分析的就是該外設的工作原理,這些工作原理以及運作方式大部分都可以在該外設的手冊中找到相應的說明。
2) 從該外設的芯片手冊中找到其控制方法,比如時序圖,通信方式等等可控制信息。
3) 從我們使用的控制芯片中找到有沒有專門為此外設提供的控制接口,比如我們以前說到的內存控制器、串口控制器、nand控制器等。
4) 從控制芯片手冊中找到其對該外設提供的控制方式,比如控制流程等信息。
5) 檢查硬件連接方式,確定管腳等信息
6) 根據從外設芯片得到的所需要的控制信息,配置我們控制芯片所提供的控制信息,得到所需數據。
7) 根據6)得到的內容進行代碼的編寫。
8) 編譯調試(這個過程可以用到串口、示波器、萬用表、j-link等進行調試)
以上就是自己簡單總結的外設裸機開發步驟,有很多不足之處,希望各位提寶貴意見。
l 后期學習規划
通過對u-boot的分析,我們對arm裸機的開發有了一定認識,在接下來我會去分析linux中的各種子系統、驅動模型等內容,整個過程必然很困難,自己只是進行簡單的分析,有不足之處還請各位大神及時指導,讓我們向內核進軍。