原文:http://blog.csdn.net/hlchou/article/details/6538226
ARM與Cortex筆記-
ARMMPCore (Multi-Processor Core) 多核心架構解析.
hlchou@mail2000.com.tw
byloda
Android/Linux Source Code Tags
App BizOrz
BizOrz.COM
BizOrz Blog
隨著目前SmartPhone的應用與複雜度增加,這類消費性電子產品,必須要能在考量功耗與持久性的前提下,達成使用者可接受的高效能,並且還要有足夠的使用與待機時間,基於如此,ARMMPCore多核心架構,就是一個在消費性電子產品上可以考慮的處理器架構選擇.
參考ARM網站有關CortexA9Performance的介紹(http://www.arm.com/products/processors/cortex-a/cortex-a9.php),以雙核心架構在TSMC40G性能優化的版本中,效能與功耗比為5.26(DMIPS/mW),而CortexA9單一核心的Dhrystone性能比為2.50DMIPS/MHz,也就是說,如果希望要達到10000DMIPS,以單核心而言就需要達到2GHz的時脈,此時的總功耗約為1.9W(=10000/5.26),但如果是以多核心的架構,就可以在時脈不需要大幅拉到2GHz的情況下,透過增加處理器數量,來達到所期待的運算DMIPS總數,而且系統功耗增加幅度也低(當然晶片的面積會增加),可以參考這份ARM的投影片"ParallelComputing in your Pocket"(參考網址:http://www.iet-cambridge.org.uk/arc/seminar07/slides/JohnGoodacre.pdf),可以看到單核心要達到三核心的效能時,透過拉高時脈達到一樣的效能時,核心的總功耗會是採用三核心方案的三倍以上,也就是說,如果作業系統上有適度的Muti-Task多工,用多核心去滿足總體效能的期待,會比單核心的架構,更符合省電的功耗效益.
在多核心的架構下,還需要去檢視作業系統的排程機制,若OSScheduling Tick是透過InterruptDistributor發給每個處理器,則每個處理器都會透過執行排程的程式碼,選擇需要執行的Task,進行TaskContext-Switch的流程,這樣的想法對於讓系統Best-Effort運作是比較合理的,但對於消費性產品而言,若可以讓系統盡可能省電,而又只會減損些微的效能,反而是種加分,
也因此,除了要確保每個處理器可以透過WFI進入省電狀態外,若是把OSScheduling Tick固定發給PrimaryProcessor,其它目前沒有Task執行的處理器,就進入WFI的狀態,由該PrimaryProcessor進行排程與工作分配,non-PrimaryProcessor接收到來自PrimaryProcessor的IPI中斷後,就會被喚醒,進行對應的TaskContext-Switch動作,如果系統是處於不忙碌的狀態下,就會有機會讓non-PrimaryProcessor的處理器,有機會維持比較長時間的StandBy休眠的狀態,MPCore的消費性產品,可以維持比較長時間的運作.
本文主要以ARMv7與CortexA9為主要討論的範圍(可供參考的文件例如:ARMv7-ARArchitecture Reference Manual),所討論的內容,主要以筆者認為值得深入討論的項目,並不一定符合每個人對於ARMMPCore所需的範圍,最後,本文雖盡可能提供正確的資訊,若有不盡完善之處,請以ARM的文件為依歸.
多核心架構的概念
多核心的架構下,開機時,只會有一個處理器在運作稱為PrimaryProcessor(或導引處理器BSP“bootstrap processor “),其它處理器則稱為non-PrimaryProcessor(或應用處理器AP“Application processor”),在系統初始化與關機過程中,都是由PrimaryProcessor來負責,主要執行如下流程
1,InvalidateData Cache
2,InvalidateSCU(Snoop Control Unit) duplicate tags for all processors
3,InvalidateL2 Cache
4,EnableSCU
5,EnableData Cache
6.Enable L2 Cache
7.Set SMP mode with ACTLR.SMP.
等到作業系統初始一個段落後,才會去啟動其它的non-PrimaryProcessor,並由這些處理器執行如下流程
1.Invalidate Data Cache
2.Enable Data Cache
3.Set SMP with ACTLR.SMP.
整個系統的排程機制就會根據這些啟動的Processor來分派工作,如果有用不到的處理器資源,也可以透過WFI(WaitFor Interrupr)讓處理器處於省電的模式.
基於多處理器的架構,處理器也會提供硬體層級支援記憶體同步的指令,例如:LDREX,STREX, SWP與SWPB,避免當有一個以上的處理器對同一個記憶體內容存取時,當該內容在處理器A中有做變動,以致使該內容在處理器B中失效時,可以透過這類指令確保處理器B可以同步到最新的內容,維持整個系統運作的正確性.
而在作業系統中SpinLock的機制,也會透過LDREX/STREX來進行,確保當作業系統進行SpinLock動作時,可以得到硬體層級的記憶體同步確保,避免SpinLock在多核心架構下的運作失誤.(透過軟體,要做到比硬體支援更有效率的自旋鎖是相對困難),
參考LinuxKernel在ARM平台上的SpinLock實作(檔案位置arch/arm/include/asm/spinlock.h),如下所示
static inline void__raw_spin_lock(raw_spinlock_t *lock)
{
unsigned long tmp;
__asm__ __volatile__(
"1: ldrex %0, [%1]/n"
" teq %0, #0/n"
#ifdef CONFIG_CPU_32v6K
" wfene/n"
#endif
" strexeq%0, %2, [%1]/n"
" teqeq %0, #0/n"
" bne 1b"
: "=&r"(tmp)
: "r"(&lock->lock), "r" (1)
: "cc");
smp_mb();
}
static inline int__raw_spin_trylock(raw_spinlock_t *lock)
{
unsigned long tmp;
__asm__ __volatile__(
" ldrex %0, [%1]/n"
" teq %0, #0/n"
" strexeq%0, %2, [%1]"
: "=&r"(tmp)
: "r"(&lock->lock), "r" (1)
: "cc");
if (tmp == 0) {
smp_mb();
return 1;
} else {
return 0;
}
}
此外,
1,處理器之間可透過IPI(Inter-ProcessorInterrupt)彼此溝通,
2,處理器之間是合作關係,彼此沒有從屬的關係,
3,所有的處理器都看到同樣的記憶體空間,彼此所定址的實體記憶體空間也是一樣,在同樣的記憶體位置上都是存取同樣的記憶體內容.並且,共同基於同一個作業系統程式碼,來對所有的處理器進行Task的排程工作.
4,每個處理器都是以Task為單位去進行多工,但進入到KernelMode (Ring0 or Supervisor Mode)時,所看到的記憶體內容是同一塊被保護的區間,但是切到UserMode (Ring 3 or User Mode)時,就是根據MMU看到各自的記憶體區塊.
5,所有的處理器都共享同樣的I/O周邊與中斷控制器,每個處理器都可以收到來自任何周邊的中斷觸發.
檢視ARMMPCore架構
ARM系列在ARM11(例如:ARM1176)時,就已經導入Multi-ProcessorCore的架構,Cortex系列,包括A5,A9跟A15都支援MPCore的架構,目前可以支援4個核心架構(可以參考文件: ARM11 MPCore Processor Technical Reference Manual inhttp://infocenter.arm.com/help/topic/com.arm.doc.ddi0360f/DDI0360F_arm11_mpcore_r2p0_trm.pdf或Cortex-A9MPCore Technical Reference Manual inhttp://infocenter.arm.com/help/topic/com.arm.doc.ddi0407e/DDI0407E_cortex_a9_mpcore_r2p0_trm.pdf),多核心的架構會透過SnoopControl Unit介面同步每個處理器各自的L1Data Cache內容,並以DistributedInterrupt Controller支援既有的ARMInterrupts,每個處理器都有一個專屬的Timer與WatchDog,支援Level2 AMBA(AXI high-speed Advanced Microprocessor BusArchitecture)介面,每個處理器都有一個IntegralEmbeddedICE-RT Logic用以提供JTAG除錯介面,與各自的Pipeline,BranchPrediction with Return Stack,與CoProcessors14 and 15,每個處理器都有自己的MMU(Instruction and Data Memory ManagementUnits),主要的差異在於處理器對分頁的處理不是直接跟單核心架構一樣去操作TLB,而是每個處理器都維護自己的MicroTLB,並透過共用的MainTLB同步,每個處理器都有L1Instruction/Data Cache,每個處理器都具備對外的32-bitInstruction Interface與64-bitData Cache,每個處理器都支援硬體的DataCache Coherence,每個處理器都可提供VectorFloating-Point (VFP) Coprocessor支援
ARMMPCore架構與周邊運作示意圖
InterruptDistributor
MPCore的架構下會透過InterruptDistributor統一管理MPCore上所有處理器的中斷來源,並且依據中斷優先級分派中斷給個別的處理器.每個中斷來源,都可以設定優先級,以及當該中斷發生時,哪些處理器要收到該中斷要求.在硬體支援上,會確保一個發送給多處理器的中斷,一次只有一個處理器在處理.
以CortexA9為例,InterruprDistributor支援224個中斷來源,每個中斷源都有唯一的ID識別(ID0-ID223),有關中斷來源分類如下所示
來源 | 說明 |
Software GeneratedInterrupts (SGI) | 可用於Inter-ProcessorInterrupts (IPI).每個MPCore中的處理器都會有PrivateInterrupt範圍從ID0到ID15,並且只能由軟體觸發中斷.中斷的優先級,會由每個接收中斷的處理器自行設定決定,發出中斷的處理器無法決定接收端的優先級. |
Global timer (PPI(0)) | 透過InterruptDistributor使用中斷ID27 |
A legacy nFIQ pin (PPI(1)) | 如果選擇LegacyFIQ mode,就會跳過InterruptDistributor直接把中斷發給每個MPCore處理器. 反之,就會藉由DistributedInterrupt Controller把FIQ以中斷ID28發給MPCore的處理器. |
Private timer, PPI(2) | 每個MPCore中的處理器會以中斷ID29作為PrivateTimer中斷源. |
Watchdog timers, PPI(3) | 每個MPCore中的處理器會以中斷ID30作為WatchdogTimer中斷源. |
A legacy nIRQ pin, PPI(4) | 如果選擇LegacyIRQ mode,就會跳過InterruptDistributor直接把中斷發給每個MPCore處理器. 反之,就會藉由DistributedInterrupt Controller把IRQ以中斷ID31發給MPCore的處理器. |
Shared PeripheralInterrupts (SPI) | 用以銜接周邊裝置中斷之用,可設定為EdgeSensitive (posedge)或 LevelSensitive(high level),並從中斷編號ID32開始.(InterruptDistributor支援最多224個中斷源) |
InterruptDistributor 的Prioritizationand Selection功能,會去找出目前最高優先級的Pending中斷源(其中:0x00為最高優先級,0x0f為最低優先級),並將該中斷透過CPUInterface進行觸發.
InterruptDistributor會幫每個處理器維護一個尚未處理的中斷列表,並且選擇最高優先級的中斷發給對應的處理器,若中斷優先級相同,則選擇最低的中斷源編號(ID0-ID223)進行觸發.中斷列表中會包括:優先級,中斷觸發的目標處理器.
InterruptDistributor 支援1-N與N-N兩種中斷模式,說明如下所示
中斷模式 | 說明 |
1-N | 所觸發的中斷可以被任一的處理器清除,並且其他尚未處理該中斷的處理器對該中斷的狀態也會被清除. |
N-N | 每個處理器對該中斷的處理行為各自獨立.個別處理器對該中斷的清除,並不影響到其他尚未處理到該中斷處理器的中斷狀態. |
當收到來自處理器發出的'End of InterruptInformation (EOI)' ,確認對應中斷在處理器已被處理完畢(Activeto Inactive transition),或是通知正在進行處理(Pendingto Active transition), Interrupt Distributor就會改變所維護的中斷清單狀態.MPCore處理器的中斷可以處於以下三種狀態,
Inactive:該中斷可能尚未被觸發,或是已經觸發,並且在該處理器中被處理完畢.同時,該中斷源也可能在其他處理器中還處於Pending或是Active的狀態,會根據每個處理器處理中斷的情況而定.
Pending:該中斷已發生,但尚未在對應處理器中觸發執行.
Active:該中斷已經被執行,但尚未執行結束.
當InterruptDistributor偵測到中斷發生時,就會設定對應目標處理器該中斷的狀態為Pending.如果該中斷為Level-Sensitive,有任一MPCore處理器,對該中斷還處於Active的狀態時,則該中斷就不能設定為Pending.如果是Edge-Sensitive,當前一個中斷尚未處理完畢,下一個中斷又發生時,在MPCore中,對不同處理器可能同時存在Pending與Active的狀態.
InterruptDistributor運作的概念,如下圖所示
我們可以透過SoftwareGenerated Interrupt Register或 InterruptSet-Pending Register觸發軟體中斷,給特定或是一組處理器,InterruptDistributor對Hardware與Software中斷處理的行為完全一致.只是一個來自硬體,一個是透過軟體主動觸發的.軟體中斷可提供在多核心架構下,跨處理器的中斷通知機制,包括可以把一個正在WFI狀態的處理器喚醒.
MPCore每個處理器的CPUInterface可支援中斷PriorityMasking與Preempted中斷(讓高優先級的中斷可以插斷當前的中斷),一個Pending中的中斷,如果通過PriorityMask,並且優先級高於目前處理器正在執行中的Active中斷,就會被該MPCore處理器插入執行.當處理器透過InterruptAcknowledge Register讀取目前要處理的中斷編號時,CPUInterface就會記錄該中斷的優先級,並通知InterruptDistributor將該中斷標示為Active.如果在處理器讀取InterruptAcknowledge Register前,該中斷因為PriorityMask更改或是透過InterruptPending Clear Register被取消了,則會從InterruptAcknowledge Register讀取到1023,表示沒有需要被處理的中斷.當中斷處理完畢,就會需要處理器設定Endof Interrupt Register,用以透過CPUInterface通知InterruptDistributor將該中斷標示為Inactive.
Cachecoherence機制
在ARM多核心的架構下,每個處理器都會有自己的L1Cache,並共用同一塊L2Cache,也因為如此,當兩個處理器的Cache有暫存到同一個位址的記憶體資料時,如果沒有一個協同確認機制的就有機會導致某個處理器對同一個位址的資料做了修改,但是另一個處理器上讀出的卻是尚未修改過的內容,如此就會導致系統潛藏的錯誤問題.
ARM多核心架構下採用的是TightlyCoupled Memory,所有的處理器會共享同一塊外部記憶體,可以更自進行工作安排,由於各自的處理器有自己的L1Cache與共享的L2Cache,也因此,外部記憶體,L2與各處理器中的L1Cache Coherency就會變得重要.(Loosely-coupled各處理器就不會共享同一塊外部記憶體,各處理單元可透過Message-Passing機制進行溝通)
CacheCoherence主要會同步每個處理器L1與L2的Cache,如果有處理器更新到另一個處理器Cache中也有同位址的資料內容時,就會透過這機制把有同位址資料的處理器Cache進行更新,參考有關CacheCoherence的資訊例如http://en.wikipedia.org/wiki/Cache_coherency.
ARM所採用的SnoopingControl Unit主要行為為會監控個別處理器Cache存取資料的位址,如果有一個寫入的動作發生在其他Cache也有複製到的位址的資料,CacheController就會把該監控的記憶體位置設定為失效.Snooping Protocol優點為,速度快,由與所有的Request/ResponseTransaction都會Broadcast到系統中所有單元,被所有的處理器監控到,缺點就是SnoopingProtocol不適合更大型的多核新處理器架構,要不就是必須提高Bus的Bandwidth.
其它的作法還包括Directory-BasedCoherence,這是一個目錄式的架構,屬於每個Cache所共同使用到的資料會被放置在一個共通的目錄下,這個目錄工作行為就像是一個過濾器,處理器必須要透過該機制才可以把資料從外部記憶體載入到自己的Cache中.如果該共享的資料被改變了,目錄內容就會同步更新,或是會把相關Cache中的資料設定為失效.缺點就是,資料的取得會有比較長的延遲(必須有3 hop流程Request/Respond/Forward),好處是Transaction過程,只需要跟Directory-basedController同步就好,不需要對系統中所有的單元Broadcast.一般來說,超過64個處理器單元的架構,就會採用Directory-based.
SnoopControlUnit
SCU主要用以連結1-4個MPCore處理器,透過AXIBus去存取Memorysystem,主要功能包括
1,同步每個MPCore處理器的DataCache內容(不包括InstructionCache的同步)
2,初始化L2Cache與AXIMemory Access的行為
3,仲裁每個MPCore處理器對L2Cache的存取行為
4,管理ACP(AcceleratorCoherency Port)介面的存取
ACP(AcceleratorCoherency Port)主要用於連接原本不被處理器Cache管理的AXIMaster週邊,例如:DMAEngine.過往的設計中,如果有一塊記憶體是會被硬體DMA直接更動內容,則該記憶體我們就會設定為non-Cached,以避免因為處理器的Cache把該記憶體內容暫存,但該記憶體內容在外部記憶體中卻已經被硬體修改了,導致兩者不一致的執行正確性問題.為了規避這問題,選擇把對應記憶體的Cache關閉,帶來的缺點就是處理器必須去等待外部相對Cache而言較慢速的記憶體進行資料的存取,導致效能上的減損.也因此,透過SCU上的ACP,就可以讓硬體DMA更動資料內容時與處理器內部的Cache保持一致性,就算硬體DMA要更動對應記憶體位址的內容,基於ACP的同步機制,就可以在開啟處理器Cache的狀態下,確保Cache內容被更新到,讓整體運作效能維持在高檔.
ACP主要目的為讓其它裝置也可以共享並存取L1/L2Cache中的資料內容,以期可以在不增加系統功耗的情況下(減少到外部記憶體存取的次數)增加系統效能.ACP裝置Read/Write的行為如下所示
READ | ACP上的裝置,要進行Read動作時,會先確認資料是否有在L1Cahce,反之,則確認是否有在L2Cache中,最後才是從外部記憶體中取得,也就是說如果資料有在L1或是L2Cache中,ACP上的裝置會直接從Cache中抓取資料,加速運作的效率 |
WRITE | ACP上的裝置,要進行Write動作時,會確認L1Cache中是否有暫存同一位址的資料,若有,會InvalidateL1上的資料,並把該筆更新的資料內容配置到L2Cache中. |
WatchDogReset
在系統設計時,為了避免軟體遇到無法Recovery的錯誤,WatchDog會是系統最後一到防線,而在MPCore架構下,每個處理器都有自己的WatchDogCounter,一旦該Counter太久沒有被踢到,導致Counter倒數為0,就會觸發Reset的機制.目前MPCore提供的Reset設定包括(所有的設定都為Active LOW)
名稱 | 說明 |
nSCURESET | 用以ResetMPCore Processor Logic,但並不包括個別MPCoreCPU Logic |
nCPURESET[3:0] | 用以Reset目標MPCoreCPU Logic (但不包括CP14Debug Logic) |
nWDRESET[3:0] | 用以ResetWatchDog Reset Status Flag,但如果這次系統Reset,是由特定處理器的WatchDogReset所觸發,則對應的StatusFlag就不會被重置. |
nNEONRESET[3:0] |
|
DBGnTRST | 用以進行DBGTAPreset |
nDBGRESET[3:0] or nPORESET[3:0] | 用以在PowerOnReset初始化CP14Debug Logic |
RESETREQ[3:0] | 可針對目標處理器或是所有的處理器,觸發Reset流程.被用在WatchDog倒數為0時觸發. |
基於上述的設定選項,參考CortexA9 MPCore文件,Reset總共可以包括以下的組合,其中包括整個MPCore的Reset,或是針對個別CPULogic,以及針對Debug與WatchDogStatus Flag的動作.
| nSCURESETand nPERIPHRESET | nCPURESET[3:0] | nNEONRESET[3:0] | nDBGRESET[3:0] | nWDRESET[3:0] |
Cortex-A9MPCore Poweron reset | 0 | All 0 | All 0 | All 0 | All 0 |
Cortex-A9MPCore Softwarereset (CP14Debug Logic不做Reset) | 0 | All 0 | All 0 | All 1 | All 0 |
Perprocessor Poweron reset | 1 | [n]=0 | [n]=0 | [n]=0 | [n]=0or All 1 |
Perprocessor Softwarereset | 1 | [n]=0 | [n]=0 | All 1 | [n]=0or All 1 |
SIMDMPE poweron | 1 | All 1 | All 1 | All 1 | All 1 |
Cortex-A9MPCore Debug | 1 | All 1 | All 1 | All 0 | All 1 |
Perprocessor Debug | 1 | All 1 | All 1 | [n]=0 | All 1 |
Perprocessor Watchdogflag | 1 | All 1 | All 1 | All 1 | [n]=0 |
當作業系統核心遇到無法修復的錯誤時,選擇MPCore Power onreset讓系統重置,會是比較好的選擇.
PowerManagement
消費性電子最重要的感受就是功耗,ARMMPCore處理器同樣提供了Run/Standby/Dormany/Shutdown四種電源模式,如下表所示,Wake-Up速度最快的省電模式是StandbyMode,此時處理器的Logic並沒有斷電,相關暫存器與CoProcessor的狀態都維持住,只有處理器進入ClockGating的狀態.若希望包括處理器Logic也斷電,則進一步把相關狀態暫存到處理器TCM中,若LeakageCurrent過高,也可採用Shutdown模式,但把記憶體與狀態回存到Storage上,在下次Wake-Up時,重新回復系統的正常執行.
Mode | CortexProcessor Logic | OnChip RAM | Wake-Up |
Run | Power Up並且所有Logic都有Clock輸入 | Power Up |
|
Standby | PowerUp但除了Wake-UpLogic外,是處於ClockGating的狀態 | Power Up | 此時外部記憶體會進入Low-PowerMode,ARM CPU會處於WFI(Wait For Interrupt)或WFE(WaitFor Event)狀態,當處理器有收到中斷觸發,DebugRequest....etc時,就會立刻喚醒. 通常要看SoC本身的LeakageCurrent,會決定在這狀態下底電流的消耗情況. |
Dormant (最省電的待機狀態) | Power Off | Retentionstate/voltage | 外部記憶體會進入Low-PowerMode.中斷會透過硬體Wake-UpModule讓處理器PowerOn,此時處理器要重新初始化,在進入DormantMode前處理器的暫存器與相關狀態會儲存在OnChipRAM上,通常會透過一個32bitsRegister記住在OnChipRAM上WakeUp後要執行的程式碼記憶體位址,以便醒來後,跳過去執行,恢復進入DormantMode前的運作狀態. |
Shutdown | Power Off | Power Off | 執行完整重新開機的流程. |
以cortexA9 MPCore四核心的架構來說,總共可以分出14個PowerDomain,包括
1,針對四個CortexA9 Processor的PowerDomain (4)
2,針對四個CortexA9 Processor Data Engine的PowerDomain (4)
3,針對四個CortexA9 Processor Cache與TLBOnChip RAM的PowerDomain (4)
4,一個供SCU(SnoopControl Unit) duplicated TAG RAMs的PowerDomain (1)
5,一個其它Logc(例如:SCULogic,Private Peripherals..etc)的PowerDomain (1)
上述PowerDomain並不包括SoC中其它週邊的PowerDomain(例如:USBPhy/Logic,Storage..etc),如果要達到最佳省電效益的話,就必須要考慮相關應用的軟體行為,搭配對應的PMIC(PowerManagement IC)的PowerGroup來做區分,維持系統中只有必須的處理器與週邊是PowerOn與有ClockInput的狀態.
若系統的Standby或DormantCurrent夠低,在實際的Android手機上,也有快開(Fast-Boot)機制是透過這類機制設計的,讓系統不需要真的走到Shutdown模式,而在下次開機時,又可以非常快的回復到正常運作的模式下.
記憶體管理系統
MMU(MemoryManagement Unit)主要是負責把虛擬記憶體位址(32-bits為0-4GB)對應到實體記憶體中,而在ARM的架構下負責這層對應機制的就是TLB(TranslationLookaside Buffer). 參考ARM的TechnicalReference Manual,在ARM9下,MMU可以支援1MB(Section),64KB(LargePage),4KB(Small Page)與1KB(TinyPage)四種分頁的大小,不過在ARM11與Cortex時,對應的MappingSize變為4KB,64KB, 1MB與16MB(SuperSection),其中1KB的分頁已經不存在了(應該也是因應目前ApplicationProcessor記憶體的實際配置也較大),對分頁與實體記憶體的對應,舉以下例子來說明:使用者在虛擬記憶體位址所使用到的4KBPage,在實體記憶體中就會是連續的實體記憶體區塊,但如果是兩個在虛擬記憶體中連續的4KBPages,在實體記憶體中就有可能是分離的兩塊實體記憶體區塊,只是因為透過硬體MMUTLB的配置,在虛擬記憶體空間中,應用程式的執行與使用會當成是兩個連續的記憶體區塊.
MMU對多工環境尤其重要,例如在Linux環境中,每個Task都會在自己的虛擬記憶體空間中對應到一組共用的共享函式庫,這些共享函式庫就可以透過MMU把每個Task共用的共享函式內容,用同一塊實體記憶體對應到每個Task各自的虛擬記憶體空間中,如此雖然系統中有多個Task同時運作,但每個Task所共用的記憶體內容部分就可以透過MMU支援對應到同一塊實體記憶體中,減少Run-Time的實體記憶體需求.另一個例子就是,AndroidDalvik的Java應用,每個MMI的應用程式都會基於一個DalvikVM運作,同時每個VM也都會載入相關動態函式庫.so/.jar,基於MMU的機制,雖然是每個MMI應用都基於一個DalvikVM,但實際上共用的部份就會透過虛擬記憶體分頁配置對應到同一塊實體記憶體中,降低實際的記憶體需求.
MMU也可以把記憶體範圍依據UserMode與KernelMode設定不同的權限,像是Non-Access,ReadOnly,Read/Write,如果有發生違反權限的存取行為時,就會導致ARM觸發Abort例外.
在ARM平台上,負責查表由虛擬記憶體對應到實體記憶體的動作是由硬體TLB機制來實現的,在單一ARM處理器的架構下,通常只有一個TLB分頁配置位址,也就是說目前TaskContext-Switch到Task#A就會把該Task的TLB分頁配置位址載入,如果是又切換到Task#B就會載入該Task的TLB分頁配置位址.單核心的架構下,同一時間處理器只會處理一個Task,因此TLB的配置只需要符合這樣的行為即可.如下圖所示
然而,在多核心的架構下,同一時間會有多個Task被執行,因此會在每個核心都提供MicroTLB,用以載入目前該核心正在執行中的TaskTLB記憶體分頁,並且整個系統會有一組MainTLB,用以同步每個處理器MicroTLB共用的部份.
以ARMv7架構為例,要控制TLBTranslation Table,可以透過CP15的暫存器c2,其中c2主要提供以下Translationtable base registers
名稱 | 說明 | ||||
TranslationTable Base Register 0 (TTBR0) | 用來記錄User-Mode應用Task的記憶體分頁架構所在的BaseAddress,通常大小為128bytes到16kbytes(也就是說每個Task的1st LevelTable可以有32到4k筆Items(也就是1st LevelTable Index的最大長度),可透過TTBCR.N值決定),當作業系統進行ContextSwitch時,會把這個暫存器的值,指到新的Task的記憶體分頁架構的BaseAddress,並更新TTBCR與CONTEXTIDR暫存器.如果TTBCR設定為0,則以ARMv6以前的架構來操作TTBR0.(也就是說只有一個TTBR,User-Mode與Kernel-Mode的記憶體分頁都透過它描述,相對的當Context-Switch發生時,就缺少分出TTBR0與TTBR1的彈性). | ||||
TranslationTable Base Register 1 (TTBR1) | 用來記錄作業系統特權等級與I/O空間的記憶體分頁架構所在的BaseAddress,屬於這類的記憶體規劃,並不會隨著應用TaskContext-Switch而改變.通常這Table大小都為16Kbytes.(就是說1stLevel Table可以有最多4k筆Items(=1st Level Table Index的最大長度)). | ||||
TranslationTable Base Control Register (TTBCR) | 在沒有 TrustZone Security Extensions支援的環境下,對應的欄位如下所示
其中N[2:0]用以表示TTBR0的寬度,也就是說TTBR0的BaseAddressBits數為[31:14-N],如果N=0,表示TTBR0對應的Table大小為14bits=16kbytes,如果N=b111=7,表示TTBT0對應的Table大小為7bits=128bytes.
|
可透過如下指令存取
MRCp15,0,<Rt>,c2,c0,0 ; Read CP15 Translation Table Base Register0
MCRp15,0,<Rt>,c2,c0,0 ; Write CP15 Translation Table Base Register0
MRCp15,0,<Rt>,c2,c0,1 ; Read CP15 Translation Table Base Register1
MCRp15,0,<Rt>,c2,c0,1 ; Write CP15 Translation Table Base Register1
MRCp15,0,<Rt>,c2,c0,2 ; Read CP15 Translation Table Base ControlRegister
MCRp15,0,<Rt>,c2,c0,2 ; Write CP15 Translation Table Base ControlRegister
有關Domain控制參數,,可以透過CP15的暫存器c3並取得DomainAccess Control Register 32-bits的值.DACR暫存器只能在特權等級下被存取,該暫存器如下圖所示會以各2bits被區分為16個欄位.
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
D15 | D14 | D13 | D12 | D11 | D10 | D9 | D8 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
每個欄位各2bits的屬性意義如下所示
(參考這個連結http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0434b/CIHBCBFE.html)
欄位值 | 說明 |
b00 | Noaccess. Any access generates a domain fault. |
b01 | ClientMode.會對每個存取對應TLB記憶體分頁的動作,執行AccessPermission的檢查.若有違反權限的存取行為,就會觸發PermissionFault. |
b10 | Reserved.Any access generates a domain fault. |
b11 | ManagerMode.將部會對存取記憶體分頁的動作進行AccessPermission的檢查,就算該記憶體設定為Read-Only或是eXecuteNever(XN),去寫入或是執行,都不會觸發PermissionFault. |
可透過如下指令存取
MRCp15,0,<Rt>,c3,c0,0 ; Read CP15 Domain Access Control Register
MCRp15,0,<Rt>,c3,c0,0 ; Write CP15 Domain Access Control Register
有關處理器切換的TaskID設置,,可以透過CP15的暫存器c13取得ContextID Register (CONTEXTIDR) 32-bits的值. CONTEXTIDR對應欄位意義如下所示
31-- 8 | 7-- 0 |
PROCID | ASID |
欄位說明如下所示
欄位名稱 | 說明 |
PROCID | 用以儲存每個處理器正在執行的Task唯一的識別碼. |
ASIC | ApplicationSpace IDentifier Address Space Identifier用以記錄目前使用的記憶體空間識別碼. |
可透過如下指令存取
MRCp15,0,<Rt>,c13,c0,1 ; Read CP15 Context ID Register
MCRp15,0,<Rt>,c13,c0,1 ; Write CP15 Context ID Register
除了TaskID本身,CP15的c13還提供TPIDRURW(User Read/Write Thread ID Register),
TPIDRURO(UserRead-only Thread ID Register),TPIDRPRW(Privileged Only Thread IDRegister)分別提供在User-Mode/KernelMode Read/Write,User-Mode Read-Only Kernel Mode Read/Write或只有KernelMode可以Read/Write用以設定ThreadId的對應暫存器值.
可透過如下指令存取
MRCp15, 0, <Rt>, c13, c0, 2 ; Read CP15 User Read/Write Thread IDRegister
MCRp15, 0, <Rt>, c13, c0, 2 ; Write CP15 User Read/Write Thread IDRegister
MRCp15, 0, <Rt>, c13, c0, 3 ; Read CP15 User Read-only Thread IDRegister
MCRp15, 0, <Rt>, c13, c0, 3 ; Write CP15 User Read-only Thread IDRegister
MRCp15, 0, <Rt>, c13, c0, 4 ; Read CP15 Privileged Only Thread IDRegister
MCRp15, 0, <Rt>, c13, c0, 4 ; Write CP15 Privileged Only Thread IDRegister
接下來,說明一個32bits虛擬記憶體是如何透過TLB對應到實體記憶體的位址,如下表所示,可以區分為三個部分,分別為12bits的第一級TableIndex,10bits的第二級TableIndex與最後10bits的PageIndex,以此細分最多可以把每個Page的大小細分到1Kbytes,並對應到完整的4GB虛擬記憶體空間.(2^12 * 2^10 * 1kbytes = 4GB.)
31 -- 20 (12-bits) | 19 – 10 (10 bits) | 9 – 0 (10bits) |
1st Table Index | 2nd Table Index | Page Index |
第一級Table的描述內容如下所示
第二級Table的描述內容如下所示
參考http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babifihd.html,有關Outer與InnerCache在有L1跟L2Cache下的定義為InnerCache指的就是L1Cache,而OuterCache指的就是L2Cache,而在記憶體分頁的設定中,就可以根據L1或L2Cache的相關行為,決定是否Cacheable,Write-Through,Write-Back或是Write-Allocate.一般而言,L1跟L2的Cacheable與Bufferable行為都會設定成一致,但搭配TEX,C與B欄位,也可以根據系統實際的狀況,讓L1跟L2Cahce可以依據系統所需的行為,而作差異化的調整.
對應欄位的意義說明如下,
欄位名稱 | 說明 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
C | Cacheable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
B | Bufferable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TEX[2:0] | TEX Type Extension (TEX) bit
如下以SCTLR.TRE=0(透過CP15的c1取得System Control Register32bits值的bit28來設定),也就是TEXRemap disabled模式,來說明記憶體區段屬性的配置與意義.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
XN | 為ExecuteNever的屬性,若該記憶體分頁XNBit設定為1,表示該分頁不會被處理器Fetch指令進來執行,在ClientDomain (也就是會稽核 AccessPermission狀態)下,記憶體分頁必須要XNBit為0,且記憶體屬性是設定為可讀取,同時沒有其他PrefechAbort發生的狀態下,才可以被執行.如果該記憶體分頁是屬於ManagerDomain,XN Bit就不會被當做稽核的條件.(所以就可以嘗試去執行,而不會導致例外發生). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
NS | 這個屬性在支援 TrustZone Security Extensions環境下,才會有作用. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Domain | 參考ARMv7的文件,VMSA()會以4-bits表示Domain的Index,也就是說最大可以定義到16個Domain,每個DomainIndex會依序對應到DomainAccess Control Register 32-bits值中以各2-bits依序產生的16個欄位. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AP[2],AP[1:0] |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
S | 用以定義該記憶體分頁是否為Shareable,S為0表示該記憶體分頁為Non-shareable,S為1表示該記憶體分頁為Shareable. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
nG | 這屬性為Non-Global,用來定義該記憶體分頁是否為Global,如果nG為0,表示該記憶體分頁為Global,如果為1,表示該記憶體分頁屬於目前正在運作的ASID(AddressSpace Identifier),該值會對應到正在運作的Task(請參考CONTEXTIDR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bit [18] | when bits [1:0] == 0b10 0 Descriptor is for aSection 1 Descriptor is for aSupersection. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
有關3bitsAP (Access Permission)與4bitsDomain對應的行為,說明如下所示
Manager Domain | 若 Domain=1,且參考DomainAccess Control Register對應到DomainField 1的值為0b11,也就是ManageDomain,此時如果,AP[2:0]值為0b000,也就是Privileged/UserMode都是NoAccess,由於此時為ManagerDomain,因此對任何記憶體範圍的存取都不會進行權限的檢查動作,因此不管此時是處於Privileged/UserMode,都可以對該記憶體內容進行存取的動作. |
Client Domain | 若 Domain=2,且參考DomainAccess Control Register對應到DomainField 2的值為0b01,也就是ClientDomain,此時如果,AP[2:0]值為0b010,也就是PrivilegedMode為Read/Write,UserMode為ReadOnly. 如果此時CPU處於PrivilegedMode,可以對該記憶體進行讀寫 若此時CPU處於UserMode,則只允許對該記憶體進行讀取,寫入的動作會導致Permissionfaults |
Client Domain | 若 Domain=3,且參考DomainAccess Control Register對應到DomainField 3的值為0b01,也就是ClientDomain,此時如果,AP[2:0]值為0b001,也就是PrivilegedMode為Read/Write,UserMode為NoAccess. 如果此時CPU處於PrivilegedMode,可以對該記憶體進行讀寫 若此時CPU處於UserMode,對該記憶體的存取動作,都會導致Permissionfaults |
根據TLB的設定參數組合,接下來分別以基於16MB(SuperSection),1MB(Section),64KB(LargePage),4KB(Small Page)與1KB(TinyPage)不同分頁的組合,來說明TLB1級與2級Table的運作概念,
如下所示為16MB(SuperSection)配置下,TLB分頁運作的概念
如下所示為1MB(Section)配置下,TLB分頁運作的概念
如下所示為64KB(LargePage)配置下,TLB分頁運作的概念
如下所示為4KB(SmallPage)配置下,TLB分頁運作的概念
如下所示為1KB(TinyPage)配置下,TLB分頁運作的概念
FastContext-Switch Extension (FCSE)
快速行程切換主要支援以32MB記憶體範圍為單位,把VirtualAddress轉為ModifiedVirtualAddress對應的空間,如下圖所示,虛擬記憶體位址會透過FCSE邏輯,轉為修正後的虛擬記憶體位址,再透過MMU對應到實體記憶體空間(PhysicalMemoryAddress),也就是說,基於FCSE整個系統可以共用同一份虛擬記憶體空間配置表,不需要在每個Task切換時,更新整個虛擬記憶體空間配置表,只需要把各自對應的32MB起點位置透過FCSE對應到目前所在虛擬記憶體空間的對應位置(ModifiedVirtual Memory Address)即可.
FCSE最多只能切割128個32MB記憶體空間,並對應到4GB的虛擬記憶體配置,同時,每個Task各自擁有的虛擬記憶體空間必須是0x00000000起點的記憶體位置,並依據7bits的PID來對應到各自的32MB記憶體區塊,運作行為可用以下的簡式來說明
if(VA[31:25]==0) then
MVA= VA | (PID<<25)
else
MVA= VA
參考ARM1176JZ-STechnical Reference Manual,ARM為了讓早期WindowsCE每個行程各自32MB虛擬記憶體空間的設計,可以有效的運作,因此提供了快速行程切換(FCSE)機制.當發生Task切換時,可以透過設定CP15c13暫存器中FCSEPID的值,決定FCSE所要轉換的記憶體空間,對應帶來的好處就是,由於整個系統在Task切換後,還是基於同一份TLB的虛擬記憶體配置表,同時配置表中的內容仍舊是有效的,只是透過FCSE把運作的Task虛擬32MB記憶體空間轉換到對應的位置,因此,可以減少TLBFlush的成本,減少TaskContext-Switch切換的成本.
可透過如下程式碼讀/寫FCSEPID
MRCp15, 0, <Rd>, c13, c0, 0; Read FCSE PID Register
MCRp15, 0, <Rd>, c13, c0, 0; Write FCSE PID Register
暫存器格式如下所示
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
FCSE PID | SBZ |
參考網頁http://en.wikipedia.org/wiki/Windows_Embedded_CE_6.0的說明,在WindowsCE 6以前,每個應用程式只能擁有屬於各自32MB的虛擬記憶體空間,並且最多只能有32個Tasks被載入到記憶體中,到了WindowsCE 6之後的核心,選擇支援完整的記憶體分頁機制,提供每個Task各自2GB的虛擬記憶體空間,並支援最多32768(2^15)個Tasks被載入到記憶體中.對應到WindowsMobile的版本,則是直到WindowsPhone 7 才採用WindowsCE 6.0 R3的核心,解除了每個應用程式32MB虛擬記憶體的限制.
ARM在ARM11(ARMv6)並不建議採用FCSE,而在Cortex(ARMv7)方案中,FCSE為一個Optional選項.
MicroTLB與MainTLB(Translation Lookaside Buffer)架構
參考相關Two-LevelTLB(Micro/Main)架構的文件,為了要減少TLB查詢對外部記憶體存取的成本,因此在On-Chip上增加SRAM,透過高速的記憶體,MicroTLB可以在1個CPUClock就完成查詢的結果,若Miss,才是到MainTLB查詢,若還是Miss,才是到外部記憶體上的TLB查詢.另外這篇論文"AnAdvanced Filtering TLB for Low PowerConsumption"(參考網址:http://supercom.yonsei.ac.kr/paper/An%20advanced%20filtering%20TLB%20for%20low%20power%20consumption.pdf),以StrongARM為例(記憶中時脈約為200MHz),在功耗的消耗上,個記憶體區塊大約各佔InstructionCache:27%,TLB(Translation Lookaside Buffer ):17%與DataCache:16%,由此可知InstructionCache與TLB在處理器運作時,是相當頻繁被存取使用的.同時,當Micro/MainTLB支援筆數增加,虛擬記憶體查詢MissRate就會降低,且對外部記憶體TLB查詢的次數減少,也可降低功耗.
TLB本身為一個On-Chip的記憶體區塊,當處理器要去查詢外部記憶體的記憶體分頁表格時,如果要查詢的目的記憶體位址存在於TLB暫存的記憶體中,就會由TLB直接返回,反之如果該記憶體位置不在目前TLB暫存的範圍中,就會到外部記憶體分頁表格中查詢,並且把查詢的結果記錄到TLB中,以便在下次又查詢同樣的記憶體位址時,可以很快速的回應,省去要到外部記憶體查詢的成本.
當外部記憶體的記憶體分頁表格因為TaskContext-Switch切換而變動時,我們會透過CP15c2暫存器來修改TLBBase Address,並且FlushTLB快取暫存中的內容(因為已經對應到新的Task記憶體分頁內容),相關Flush的動作可以透過CP15c8暫存器來執行,如果有些虛擬記憶體對應的內容並不希望因為TaskContext-Switch切換,被TLBFlush所清除,則可以透過CP15c10暫存器,進行TLBLock-Down的動作,讓該記憶體對應位址的內容可以保存在TLB暫存記憶體中,進而加入相關記憶體轉換的速度.
在CortexA(ARMv7)的VirtualMemory System Architecture對記憶體管理部分有如下的演進,
1,記憶體分頁包括4KB,64KB,1MB與16MB.
2,支援記憶體分頁16個Domains
3,支援記憶體分頁Global與ASID(Addressspace identifiers)機制,避免當TaskContext-Switch發生時,TLB被Flush的成本.(屬於Global的分頁是不屬於特定的Task,且ASID8-bits可用來識別不同Task記憶體配置的ID)
4,延伸AccessPermission的能力,
在CortexA9中,為了支援多核心的架構,記憶體管理機制做了以下的演進,
1,支援32筆項目的InstructionMicro TLB
2,支援32筆項目的DataMicro TLB
3,支援一致的MainTLB
4,支援L1Data Cache的PageTable查詢硬體.(可以用來確保每個處理器L1Data Cache共用的Page一致性.)
在多核心的架構下,ARM為了減少每個處理器出去外部TLB查詢Table的次數,因此提供了2Level的TLB架構(Micro與MainTLB),
Micro TLB | 每個處理器都會有自己的MicroTLB,提供Instruction與Data各32筆對應的項目(ARM11MPCore為各八筆),可以在1個CPUCycle把VirtualAddress轉為PhysicalAddress,包括該記憶體分頁的保護屬性,或是否要觸發Prefech/DataAbort.如果該VirtualAddress不在這Instruction或DataTLB的紀錄中,就會往MainTLB查詢. 如果外部的MainTLB有更新,或是發生TaskContext-Switch更新ContextID Register都會導致MicroTLB被Flush.
|
Main TLB | MainTLB可以對應到16MB,1MB,64KB與4KB不同的記憶體分頁,一旦所要查詢的VirtualAddress在MicroTLB中查詢不到,就會到MainTLB中查詢.Main TLB為2ways的配置,可以為2*32的64筆項目的MainTLB,或2*64的128筆項目的MainTLB.每個MainTLB項目都會包括 1,VirtualAddress 2,Page Size 3,PhysicalAddress 4,MemoryProperties 每一組上述的項目,都會對應到一個特定的Application空間,或是Global讓全系統共享,每個ARM核心都會透過設定CONTEXIDR暫存器,記錄目前該核心所運作的Application空間,每次TLB項目比對時,只要符合以下條件就算是對比成功 1,虛擬記憶體跟TLB項目一致(bits長度可為[31:N],要視記憶體分頁大小而定) 2,Non-secure TLB ID(NSTID)跟目前Secure狀態一致.(在有支援TrustZone Security Extension的環境) 3,跟TLB中的ASID跟目標TaskASID一致或是為Global數性.
其它有關MainTLB lockdown相關暫存器,如下所示
MCRp15,5,<Rd>,c15,c4,2 #Select Lockdown TLB Entry for Read(Main TLB Index) MCRp15,5,<Rd>,c15,c4,4 #Select Lockdown TLB Entry for Write(Main TLB Index) MRCp15,5,<Rd>,c15,c5,2 #Read Lockdown TLB VA Register Data MCRp15,5,<Rd>,c15,c5,2 #Write Lockdown TLB VA Register Data MRCp15,5,<Rd>,c15,c6,2 #Read Lockdown TLB PA Register Data MCRp15,5,<Rd>,c15,c6,2 #Write Lockdown TLB PA Register Data MRCp15,5,<Rd>,c15,c7,2 #Read Lockdown TLB attributes RegisterData MCRp15,5,<Rd>,c15,c7,2 #Write Lockdown TLB attributes RegisterData
|
結語
隨著SmartPhone與平板市場的風行,ARMMPCore架構絕對會是最受矚目的方案,尤其,包括Android上用NDK開發的應用,或是其它基於ARM平台的方案,都讓ARM處理器累積了大量專屬的應用與越來越難以取代的角色.
本文主要以筆者所需的資訊為主來彙整,對ARMMPCore有更進一步需求的開發者,請自行參閱ARMMPCore技術文件.