在第一章中,介紹了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 的地址就是物理
地址。 P0 = 0xfe;//小燈亮 P0 = 0xff;//小燈滅 P0 在 51 寄存器頭文件中,有一個宏定義它的實際地址,也就是物理地址。 在 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 類似。
在串口初始化之前可以通過點燈來實現調試,串口初始化之后可以通過打印字符來跟蹤調
試代碼。
|