在第一章中,介紹了Exynos4412的iROM、啓動方式、源碼組成等;在第二章中,介紹
uboot 編譯等。經過前面對編譯的詳細分析,瞭解到 uboot 源碼中有如下幾個文件是很是重
要的:
「cpu/ARM_cortexa9/start.S」
「board/samsung/smdkc210/lowlevel_init_SCP.S 或者 lowlevel_init_POP.S」
「include/configs/itop_4412_android.h 或者 itop_4412_ubuntu.h」
其中「cpu/arm_cortexa9/start.S」是 uboot 代碼入口文件,分析 uboot 通常是從
「start.S」文件開始,「lowlevel_init_SCP.S」文件是內存初始化、時鐘初始化和串口初始化
等的文件,start.S 文件在運行過程當中會跳到這個文件中。
「itop_4412_android.h 或者 itop_4412_ubuntu.h」文件是重要的配置頭文件,裏面的
宏配置,會影響以上文件如何編譯和運行,包括在下一章節中 uboot 源碼的 C 語言部分,很
多代碼編譯和運行都會受到這個頭文件的影響。
本章主要內容是,從「start.S」文件開始分析全部彙編代碼,截止於 uboot 開始執行 C
代碼。其中涉及到不少不經常使用概念,須要咱們去了解和掌握;涉及到彙編語法,須要咱們去了
解。
3.1 分析 uboot 彙編源碼必要的知識和學習方法彙總
本小節,結合 datasheet 介紹 4412 的物理地址概念,這部分和單片機中相似;介紹彙編
語法如何學習以及要掌握到什麼程度;彙編部分調試方法。
3.1.1 4412 的物理地址和虛擬地址介紹
若是用戶學習過迅爲的 linux 驅動教程,其中有一期,專門介紹物理地址和虛擬地址的概
念。幾乎在全部現代操做系統中,物理地址都是經過 MMU(內存管理單元)映射爲虛擬地
址。可是在 uboot 彙編部分,仍是直接操做物理地址的。
物理地址的概念。
MPU 地址總線傳來的地址,由硬件電路控制其具體含義。物理地址中很大一部分是留給
內存條中的內存的。物理地址空間,一部分給內存用,一部分給總線用,這是由硬件設計來決
定的,所以在 32 bits 地址線的處理器中,物理地址空間是 2 的 32 次方,即 4GB,但物理
RAM 通常不能上到 4GB,由於還有一部分要給總線用(總線上還掛着別的許多設備)。
對於有單片機基礎的用戶來講,物理地址仍是比較好理解,例如在 51 單片機中,P0.0 表
示小燈的輸出寄存器,給這個寄存器寫 1 小燈滅,寫 0 小燈亮,寄存器 P0.0 的地址就是物理
地址。php
P0 = 0xfe;//小燈亮html
P0 = 0xff;//小燈滅linux
P0 在 51 寄存器頭文件中,有一個宏定義它的實際地址,也就是物理地址。android
在 4412 中,物理地址太多了,根本沒有辦法所有介紹,2000 多頁的 datasheet 中大部
分都是介紹寄存器,一個一個介紹是沒法實現的。可是咱們有必要掌握和理解其中的寄存器框
架和典型寄存器。
在 4412datasheet 第三章「Memory Map」中,以下圖所示,這是 4412 所有基地址的描述。
注意上表中,0x4000_0000~0xA000_0000,0xA000_0000~0x0000_0000 這兩個地址
區間,這兩個區間是 DMC 內存控制器的尋址地址,也就是內存的物理地址。實際上 4412 最
大支持的內存能夠達到 3G,32 位處理器理論上能夠支持 2 的 32 次方(最大 4G),如上表
所示,其中 1G 的地址給了 iROM、iRAM 等等這些 MPU 內部寄存器使用,因此 32 位 MPU
是不可能達到 4G 內存的。
現代操做系統廣泛採用虛擬內存管理(Virtual Memory Management)機制,這須要
MMU(Memory Management Unit)的支持。MMU 一般是 CPU 的一部分,若是處理器
沒有 MMU,或者有 MMU 但沒有啓用,CPU 執行單元發出的內存地址將直接傳到芯片引腳
上,被內存芯片(物理內存)接收,這稱爲物理地址(Physical Address),若是處理器啓用
了 MMU,CPU 執行單元發出的內存地址將被 MMU 截獲,從 CPU 到 MMU 的地址稱爲虛擬
地址(Virtual Address),而 MMU 將這個地址翻譯成另外一個地址發到 CPU 芯片的外部地址
引腳上,也就是將虛擬地址映射成物理地址。經過內存管理單元,能夠實現 4G 的虛擬內存。
在 uboot 代碼中,須要屢次用到以上地址的概念,其中內存管理單元被開啓或者關閉,
因此有必要先介紹一下這幾個地址的概念。
3.1.2 關於彙編語法
若是學習過單片機課程,會發現大部分都是使用 C 語言去編碼,彙編使用的很是少了。
那麼還有必要去學習彙編麼?實際上是沒有必要的,由於在 uboot 中彙編代碼量很是少,以
4412 的 uboot 源碼爲例,其中有效的彙編代碼不足 200 行,咱們根本不須要爲了讀懂 200
行代碼專門去學習一門編程語言。
做者這裏建議,首先咱們的目標是必定要把這些代碼讀明白,若是不明白會影響後面 C
代碼的閱讀,以及 uboot 的移植;其次,咱們要弄清楚每一行有效彙編代碼的語法。
如今咱們已經知道彙編是從「cpu/arm_cortexa9/start.S」這個文件開始執行,那麼咱們
就從第一行代碼的語法開始學習,代碼執行到或者跳到哪一行,咱們就學習這一行代碼的語
法。
在手冊的附錄部分,咱們會依次介紹彙編代碼中出現的語法,你們也能夠經過互聯網學習
每一行執行的彙編語法。
3.1.3 uboot 彙編代碼初始化串口以前的簡易調試方法
在前面教程中咱們介紹過,從 A9 開始,開發板通常都不配 jtag,jtag 價格昂貴,在 A9
以前,因爲引導程序 uboot 必須經過 jtag 來燒寫,可是在 A9 處理器上,大部分都是支持 tf
卡引導,這樣能夠免去 jtag 的費用,燒寫變的簡單高效。
那麼沒有 jtag,對於 uboot 的調試,咱們無法單步調試,若是有一行代碼咱們不是很確
定到底執行了沒,或者跳到哪一行。若是代碼已經執行到串口初始化階段,固然是能夠經過串
口打印字符來實現,在串口初始化以前,其實能夠經過控制 LED 燈來跟蹤代碼。
如下是開發板上兩個小燈控制的代碼,能夠將小燈點亮。
點亮 LED2 燈:ldr r0, =0x11000104 /* GPL2(0) */
ldr r1, =0x00000001 /* GPL2(0 output high) */
str r1, [r0]
ldr r0, =0x11000100 /* GPL2(0) */
ldr r1, =0x00000001 /* GPL2(0 output high) */
str r1, [r0]
點亮 LED3:
ldr r0, =0x11000060
ldr r1, =0x00000010
str r1, [r0]
ldr r0, =0x11000064
ldr r1, =0x00000002
str r1, [r0]
這裏簡單介紹下這幾行彙編代碼的含義。
ldr r0, =0x11000104
ldr 是將 0x11000104 值賦給 r0 寄存器。這個值地址爲 GPL2DAT。
ldr r1, =0x00000001
ldr 是取 0x11000104 地址的值賦給 r1 寄存器。
str r1, [r0]
str 是將 r1 的值寫入到 r0 數值對應物理地址寄存器中。將 0x00000001 寫入到
0x11000104 地址寄存器中,0x11000104 地址是 GPL2DAT 寄存器。
ldr r0, =0x11000100 /* GPL2(0) */
ldr r1, =0x00000001 /* GPL2(0 output high) */
str r1, [r0]
將 0x00000001 寫入到 0x11000100 地址寄存器中,0x11000100 地址是 GPL2CON 寄
存器。執行這兩步就能夠將 LED2 點亮。
點亮 LED3 和點亮 LED2 相似。
在串口初始化以前能夠經過點燈來實現調試,串口初始化以後能夠經過打印字符來跟蹤調
試代碼。編程