ARM經歷了幾十年的發(fā)展,其核心架構已經從最初商用的ARMv4發(fā)展到了如今的ARMv7,據(jù)說ARMv8正處于開發(fā)之中,各個版本的內核大同小異,掌握了ARM的思想,其實看著都差不多,就是可配置模塊不太一樣,或者在指令集,實現(xiàn)方式上做些改變,從軟件工程師的角度來看,基本都是一樣的,當然如果你是做驗證級別的軟件工程師,還是要掌握非常細節(jié)的部分。
在ARM的官方文檔中,對RISC的特性描述如下(當然ARM也都實現(xiàn)了):
1. 較大的寄存器文件(換句話說就是有較多的通用寄存器)
2. load/store架構,就是所有除load/store的數(shù)據(jù)操作都是在寄存器之間完成,不能夠對內存中的數(shù)據(jù)直接進行。寫過單片機程序的人應該有體會,51,96這些都是可以直接對內存中的數(shù)進行數(shù)學操作的。
3. 簡單地尋址模式,所有的load/store地址都是通過寄存器內容和指令算出來的。這點也是區(qū)別于CISC的復雜尋址模式,總之就是一條,什么事都盡量交給寄存器。
ARM還對基本的RISC架構進行了如下擴展:
1. 增加了算術移位指令
2. 地址自增和地址自減模式以優(yōu)化循環(huán)操作
3. 多重load/store指令以增大數(shù)據(jù)處理速度
4. 幾乎所有指令都是可以條件執(zhí)行的,以增加指令處理速度。
關于ARM到底屬于什么類型的CPU,一直以來說法不一,有說他們是RISC(至少他們自己這么認為,公司名字就是Advanced RISC Machines的簡寫)。
有說他們是類RISC的,說他們是類RISC的理由是ARM的指令并不是定長的,而且并沒有實現(xiàn)真正的流水線。當然這些區(qū)別現(xiàn)在已經變得越來越不重要,甚至各領域之間也在互相滲透,比如MIPS的14K和24K系列也已經引入了16位的指令,支持16/32位指令(ARM/THUMB interworking)混編,而較新的ARM架構中也已經實現(xiàn)了對亂序執(zhí)行的支持。
可能有人會反駁,怎么可以這么說ARM沒有實現(xiàn)流水線呢?ARM7中就已經實現(xiàn)了三級流水線!請注意,這里所說的是真正的流水線,如果對MIPS有所了解或者看過MIPS陣營的經典著作《See MIPS Run》,再結合AMBA文檔中對AHB和APB的描述,就能明白真正的流水線的含義。下面我們先對其他架構的流水線結構做一個分析。
提到流水線就要了解一個概念叫做delay slot,也就是我們所說的延遲槽,出現(xiàn)這個概念的原因是因為在RISC中,絕大多數(shù)指令的執(zhí)行時間是可預測的,一般為一個時鐘周期。而有些指令譬如load,jmp之類的指令無法在一個周期內完成,這樣就造成一個問題,在執(zhí)行jmp,load時,處于譯碼階段的指令就要多等一個周期,等前面的指令執(zhí)行結束它才能進入執(zhí)行階段(早期的ARM就是采用這種方式)。而在真正的流水線架構中,遇到這種情況,可以把jmp,load之后的指令在jmp,load進入執(zhí)行階段準備,但是沒有真正執(zhí)行的時候提前執(zhí)行,這樣就節(jié)省了之前被浪費的那個周期。處于延遲槽中被提前執(zhí)行的指令必須是對后續(xù)指令無害的,有害還是無害一般是由編譯器來決定,判斷有害還是無害的標準主要是看延遲槽中的指令結果是否和它前面的指令有關聯(lián),當無法插入無害指令時,編譯器會向其中插入NOP。千萬不要小看這一個周期,因為在真正的軟件系統(tǒng)運行過程中,內存讀寫以及跳轉操作隨處可見,所占的比例相當大,就這一點點效率的提升就導致了MIPS在整數(shù)運算性能上的優(yōu)勢,即使是在如今ARM如此盛行的時代,高端的路由器交換機以及大型圖形服務器等依然還是MIPS占據(jù)優(yōu)勢(情況可能有變,ARM已經收購了MIPS的64位技術)。
個人曾經遇到過幾次關于延遲槽的陷阱,舉一個最近的例子來說,在MIPS的bootloader中,一般會在匯編代碼前有.set noreorder選項,也就是禁止編譯器將代碼按照編譯器所認為的優(yōu)化執(zhí)行順序重新排序,這主要是因為bootloader相對敏感,并且對那么幾行代碼重排序也沒必要。在bootloader中有一段代碼拷貝的地方:
1: addui t1,4
Addui t2,4
Sw t1,[t2]
Jne t2,t4,1b
Mfl t2,$14
麻煩就出在了最后一句,按照順序思維,應該是執(zhí)行完了循環(huán)再執(zhí)行它,可是由于他在延遲槽中,每次循環(huán)都會去執(zhí)行它,于是修改了t2的值,造成了地址錯誤的異常。在ARM中你就不用擔心這個陷阱,因為ARM沒有延遲槽一說,該等你就得等…。
而支持ARM屬于RISC的理由主要在于四點:
1. 使用了精簡指令集,Reduced Instruction Set Computer,雖然ARM的指令相對其他種類的RISC架構核心來比較還是算多的,但畢竟處于可接受范圍,不像x86架構那樣不斷添加各種指令,浩瀚無窮。RISC的宗旨就是讓硬件盡量簡單,更多的處理交給編譯器和軟件,這樣就可以讓cpu的核心結構更加簡單,比較容易實現(xiàn)低功耗高頻率。
2. 實現(xiàn)了一定程度的流水線,學過51的人應該還記得51的工作方式,指令都是一條一條地取指,譯碼,執(zhí)行,只有上一條指令執(zhí)行結束,下一條指令才能進入取指階段。也就是說cpu在相當長一段時間內只能為一條指令服務,而在RISC架構中,每一時刻基本都會有幾條指令處于不同的階段。
3.ARM核心中有較多的通用寄存器,其數(shù)目為31個(不算狀態(tài)寄存器),要比CISC的通用寄存器數(shù)目多很多。但是這31個寄存器并不是同時存在的,有些寄存器是模式專有的,只有處理器處于那種模式才能夠對其進行操作,術語稱為Banked,在某種模式下可見的通用寄存器的個數(shù)是16個。
4.采用了Load-Store的工作方式,所謂的Load-Store方式,也就是說一切的計算操作都只針對寄存器,內存中的數(shù)據(jù)需要先load到寄存器,在寄存器中進行計算之后再store回內存。而在CISC中,內存數(shù)據(jù)是可以直接參與計算的,這是雙方一個巨大的差別。