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