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中斷