参考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技术文件.