AR/QCA SPI 启动原理和 ART 地址定位原理
生活随笔
收集整理的這篇文章主要介紹了
AR/QCA SPI 启动原理和 ART 地址定位原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
轉自:http://www.right.com.cn
本貼主要講解 Bootloader 是如何在使用 SPI Flash 的 AR/QCA 的芯片上啟動的,以及 OpenWrt 代碼 ar71xx 的 mach 文件中類似于 u8 *art = KSEG1ADDR(0x1fff0000) 中 0x1fff0000 是如何得來的。
樓主之前在 U-Boot 編譯教程中進行過簡單的描述,但是因為實在是太簡略了,所以打算寫一個詳細版的。下面樓主將按照必要的順序依次講解。
1. CPU 地址、總線地址、映射
對于 MIPS CPU 來說,只有一種地址,即 CPU 地址。32 位 CPU 的尋址范圍稱為它的地址空間,也就是 4GB,從 0x00000000 到 0xffffffff。
而總線,則是用來連接外設的,外設的寄存器、RAM、ROM等,都要通過總線來訪問。總線也有自己的地址空間。
CPU 地址跟總線地址是相互獨立的。因此,要讓 CPU 能夠訪問到外設,就必須要讓 CPU 地址跟總線地址產生某種關聯,這就是映射。
這就好比數學里面的映射。MIPS CPU 上的映射是從 CPU 上一段地址空間到總線上一段地址空間的一一映射。
有了這樣的關聯后,就很容易通過對 CPU 地址的操作來變成對總線上外設的操作了。
2. AR/QCA 的總線地址布局
以 AR9344 為例,如圖:
?
可以看到 AR/QCA 的 CPU 使用了總線地址的 0x00000000 ~ 0x1fffffff 共 512MB 的地址空間。
注意到內存也是通過總線來訪問的。由圖可知 AR/QCA 只有最大 256MB 的內存尋址能力。
3. MIPS 的內存布局
MIPS32 (即 32 位) 的內存模型都是一致的,如圖:
?
(圖片來自 MIPS32 74K Processor Core Family Software User’s Manual)
由于 Bootloader 跟 Linux 內核都運行在 Kernel Mode 下,所以這里也只就 Kernel Mode 進行討論。
由圖可以看到,Kernel Mode 下,CPU 的地址空間被分為了5段: kuseg kseg0 kseg1 kseg2 kseg3。
kuseg 空間為 2GB,是用于用戶態程序訪問用的,kseg2 kseg3 是用作內核的內存分頁用的。
這里只重點討論 kseg0 和 kseg1。kseg0 和 kseg1 都占用 512MB 的地址空間。
由 MIPS 手冊可知,kseg0 和 kseg1 都映射到了總線地址上的相同區域,也就是總線地址的 0x00000000 ~ 0x1fffffff 共 512MB 的空間。
這個映射是固定映射,也就是不會經過 MMU 的轉換,訪問 kseg0 跟 kseg1 都會直接被映射到總線上 0x00000000 ~ 0x1fffffff 的對應位置。
由于 AR/QCA 使用的總線地址空間也是這個范圍,因此通過 kseg0 或 kseg1 就都能訪問到整個總線地址空間。
如圖:
?
從這里可以看到,kseg0 的范圍是 0x80000000 ~0x9fffffff,kseg1 的范圍是 0xa0000000 ~ 0xbfffffff。
又,總線地址空間范圍是 0x00000000 ~ 0x1fffffff,那么:
1. 從總線地址映射到 kseg0 的方法是:在保證總線地址有效 (即總線地址在 0x00000000 ~ 0x1fffffff 內) 的情況下,將總線地址加上 kseg0 的起始地址,即:
kseg0(addr) = 0x80000000 + addr
2. 從總線地址映射到 kseg1 的方法與從總線地址映射到 kseg0 的方法類似,即:
kseg1(addr) = 0xa0000000 + addr
3. 從 kseg0 和 kseg1 映射到總線的方法是:將 CPU 地址除以 kseg0 或 kseg1 段長度,取余,得到的就是總線地址,即:
bus(addr) = addr % 0x20000000
實際上,在 Linux 內核中,arch/mips/include/asm/addrspace.h 提供了相應的宏來進行上述的地址轉換,簡化后如下:
#define virt_to_bus(_virt) ((_virt) & 0x1fffffff)
#define KSEG0ADDR(_addr) ((_addr & 0x1fffffff) | 0x80000000)
#define KSEG1ADDR(_addr) ((_addr & 0x1fffffff) | 0xa0000000)
virt_to_bus 即為總 CPU 地址轉換到總線地址, 與 0x1fffffff (即低 512MB 的掩碼) 進行按位與操作,相當于除以 0x20000000 取余,即舍棄掉 512MB 之上的部分,只剩下低 512MB 的部分;
KSEG0ADDR 與 KSEG1ADDR 都先保證輸入地址是有效的,再進行轉換,這里的按位或運算,在結果上等同于加法運算
雖然 kseg0 跟 kseg1 都映射在總線相同的地址空間上,但是,它們的作用卻并不相同:
kseg0 經過了緩存,kseg1 沒有經過緩存。
kseg0 經過了緩存,也就是說從這個段讀取出來的數據,可能是緩存過的,跟總線上實際的數據可能不同;向其寫入的數據,可能會被延遲寫入總線。
kseg1 則沒有經過緩存,對這個段的任何讀寫操作都將立即反映在總線上。
所以:
kseg0 主要用于需要加速的內存訪問和 ROM 訪問
kseg1 主要用于設備寄存器的訪問
4. MIPS 上的啟動地址
MIPS 的 CPU 在復位后,會從 CPU 地址的 0xbfc00000 開始執行,也就是總線地址的 0x1fc00000。
可以看出 CPU 是在 kseg1 段上開始執行的,這是因為在 CPU 復位后,緩存還沒用初始化,可能并不能使用。在不能保證 kseg0 段一定能使用的情況下,那么肯定只有從 kseg1 段開始執行了。
5. SPI Flash 和映射
這里的 SPI Flash 特指 SPI 接口的 NOR Flash (當然也有 SPI NAND Flash)。
NOR Flash 有個特點,即給出一個確切的地址,那么它就能連續輸出從這個地址開始的數據。這個特點跟 DRAM 類似,因此它可以被用作啟動設備。
這個特性被稱為 XIP (eXecute-In-Place)。
為了實現這個特性,就需要 CPU 能夠通過 CPU 地址空間訪問到 Flash 上的數據。
由于 Flash 是一個外設,因此對它的訪問是通過總線來進行的。
這里,又用到了映射:
Flash 有自己的地址空間,即存儲數據的地址。
由于 Flash 是外設,因此它的地址空間會被映射在總線上面。這又是一個映射,只不過是從總線到 Flash 的映射。
那么,通過 CPU 訪問 Flash,實際上經過了兩次映射:第一次是 CPU 地址到總線地址的映射,第二次是總線地址到 Flash 地址的映射。
這樣,CPU 就能夠直接讀取 Flash 的數據了。
6. AR/QCA 的 CPU 在 SPI Flash 上的啟動
以 AR9344 為例,其它的型號也都基本相同
AR9344 遵循 MIPS 的要求,CPU 復位后,從 0xbfc00000 開始執行。
那么,總線地址 0x1fc00000 對應了什么外設呢?
從第 2 節可以看出, 0x1f000000 ~ 0x1fffffff 對應的正是 SPI Flash。
但是,Flash 是從 0x1f000000 開始映射的,那么 0x1fc00000 處映射的又是什么呢?
實際上,Flash 確實是從 0x1f000000 開始映射的,總共可以映射 16MB 的 Flash 空間。
為了兼容 MIPS 要求的 0xbfc00000 的復位地址,AR9344 在 0x1fc00000 處又重新對 Flash 進行了一次映射,映射了 Flash 開頭的 4MB
但是,如果 0x1fc00000 處映射了 Flash, 那么從 0x1f000000 處就只能訪問 12MB 的 Flash 空間了。
因此 AR9344 專門設置了一個寄存器,用來控制是否從 0x1fc00000 處重新映射 Flash。
這樣的話,當 Bootloader 完成了啟動過程,就可以關閉在 0x1fc00000 的重新映射,這樣就能從 0x1f000000 處訪問到完整的 16MB 的 Flash 空間了。
7. 如果 Flash 容量小于 16MB,那么會怎樣
上一節已經說了,從 0x1f000000 可以映射 16MB 的 Flash 空間,那么如果 Flash 容量小于 16M,會怎么樣?
實際上,Flash 對于地址有個回繞功能,也就是說,如果給定的地址超過了 Flash 的地址范圍,那么 Flash 會自動讓地址變成 0,即又從頭開始。
Flash 的內部處理方式是將地址中超出容量的那些位 置零(或者說溢出),效果上相當于用給出的地址除以 Flash 的容量取余數。
這樣對 Flash 來說,地址總是有效的,對外界來說,就像是 Flash 的數據在循環。
例如:某個 Flash 的容量是 4MB,那么它的地址范圍就是 0x000000 ~ 0x3fffff。
如果訪問 0x400000,就變成了訪問 0x000000;如果訪問 0x7f0000,就變成了訪問 0x3f0000;如果訪問 0xff0000,也是變成了訪問 0x3f0000。
回到 AR9344 的 16MB 映射,如果 Flash 容量小于 16MB,那么在這 16MB 上的體現就是 Flash 數據重復映射了多次
例如:Flash 是 8MB 的,那么它就在 0x1f000000 和 0x1f800000 上各映射了一次;
如果 Flash 是 4MB 的,那么它就在 0x1f000000、0x1f400000、0x1f800000、0x1fc00000 上各映射了一次;
8. OpenWrt 的 mach 文件中,ART 數據地址的來歷
在上面幾節說來這么多之后,對于類似于 u8 *art = KSEG1ADDR(0x1fff0000) 這樣的地址就應該很好理解了:
假設 ART 位于 Flash 的最后 64KB,那么它在 Flash 中的地址就是 Flash 容量 - 64KB。
例如:
Flash 是 4MB 的,那么 ART 在 Flash 中的起始地址就是 0x400000 - 0x10000 = 0x3f0000;
Flash 是 8MB 的,那么 ART 在 Flash 中的起始地址就是 0x800000 - 0x10000 = 0x7f0000;
Flash 是 16MB 的,那么 ART 在 Flash 中的起始地址就是 0x1000000 - 0x10000 = 0xff0000;
那么,這個地址在總上的映射就是 0x1f000000 + ART 地址,即:
Flash 是 4MB 的,那么 ART 在總線中的起始地址就是 0x1f000000 + 0x3f0000 = 0x1f3f0000;
Flash 是 8MB 的,那么 ART 在總線中的起始地址就是 0x1f000000 + 0x7f0000 = 0x1f7f0000;
Flash 是 16MB 的,那么 ART 在總線中的起始地址就是 0x1f000000 + 0xff0000 = 0x1fff0000;
現在,假設 OpenWrt 并不知道 Flash 的容量:
如果在 mach 中使用 8MB Flash 的 ART 地址 0x1f7f0000,如果 Flash 是 16MB 的,那么通過 0x1f7f0000 就無法獲取正確的 ART 數據。
那么解決方法是什么呢:
那就是假設 Flash 都是 16MB 的,這樣的話就會使用 0x1fff0000 這個地址。
當 Flash 容量小于 16MB 時,根據上一節所說的地址回繞功能,通過 0xff0000 訪問到的就是 Flash 實際最后 64KB 的內容。
這就是 mach 文件中這個地址的來歷。
9. (補充) 為什么從 AR7240 開始 U-Boot 的基址是 0x9f000000
如果編譯過 U-Boot,那么可能會發現 AR7161/AR913X 之類的 CPU,其啟動地址,即 U-Boot 的基址 (TEXT_BASE) 是 0xbf000000;而之后的 CPU (從 AR7240 開始),其 U-Boot 的基址是 0x9f000000。
但是如第6節所說,CPU 復位后的執行地址是 0xbfc00000,那么為什么 U-Boot 能從這兩個地址啟動呢?
就 AR7161/AR913X 來說,這很容易根據解釋:
首先必須明確,無論 U-Boot 在編譯時指定的基址是什么,CPU 總是從 0xbfc00000 開始執行的。
根據第6節所述,0xbf000000 跟 0xbfc00000 映射了相同的 Flash 地址,也就是說這兩個地址最多能映射4MB相同的數據。
那么 U-Boot 在執行時,就可以根據 (當前執行地址 - 0xbfc00000 + 0xbf000000) 的方式跳轉到與 0xbf000000 相對應的地址來繼續執行。
在執行跳轉后,U-Boot 會立即關閉 0x1fc00000 處 Flash 的重映射,因此 U-Boot 必須在進入 C 環境 (即使用棧幀) 之前完成跳轉,否則棧中會保留 0xbfc00000 相關的地址,在禁用 Flash 重映射后會失效。
就 AR7240 開始的 CPU 來說,就要考慮到緩存了:
從 AR7240 開始,CPU 支持 Flash 映射的讀緩存和指令緩存。但是根據第4節的描述,CPU 初始化時,kseg0 段的緩存并未初始化,不能訪問。
因此 U-Boot 會先將緩存初始化,然后使用和 AR7161/AR913X 相同的方式 (當前執行地址 - 0xbfc00000 + 0x9f000000) 來跳轉到與 0x9f000000 相對應的地址 ,即 kseg0 段來執行。
AR7161/AR913X 不支持 Flash 映射的指令緩存,雖然可以從 0x9f000000 讀取 Flash 數據,但是從 0x9f000000 執行則會導致 CPU 異常。
從 kseg0 執行 U-Boot 的好處顯而易見:有緩存,能加快 U-Boot 的執行速度。
10. 后記
嵌入式設備開發需要掌握很多的知識,并不是只會編程就行了。例如還需要學習計算機組成原理、操作系統原理等等。
很多開發者對這些并不了解,因此對于這些文件都會有相當多的疑問。
樓主盡量將文章內容簡化,以使其變得能夠容易理解,并希望能夠起到拋磚引玉的作用。
希望通過此文讓開發者能過對嵌入式設備開發有更深入的了解,消除疑惑。
總結
以上是生活随笔為你收集整理的AR/QCA SPI 启动原理和 ART 地址定位原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: go 变量大写_28. 一文了解Go语言
- 下一篇: 成人的世界