1 計算機指令架構(gòu)
1.1 基本概念
MIPS ——內(nèi)部無互鎖級微處理器( Microprocessor without interlocked piped stages ),采用RISC 指令集,所有的指令長度相同,運行周期也相同。
計算機硬件的基本功能就是執(zhí)行 指令 ,指令在馮 · 諾伊曼計算機中由二進制數(shù)字進行編碼。計算機的全部二進制機器指令組成了一種可供人與計算機進行交流的語言,稱為 機器語言 。
助記符形式的指令的集合組成了 匯編語言 。
編譯 ——將高級語言編寫的程序翻譯成等價的二進制指令序列來代替,計算機執(zhí)行等價的機器語言程序。 解釋 ——以高級語言程序作為輸入數(shù)據(jù),順序地檢查它的每一條語句,并直接執(zhí)行等價的機器語言指令序列。
1.2 MIPS指令格式
簡潔性 ——所有指令長度相同,都是32位
區(qū)分性 ——opcode用于區(qū)分不同操作
硬件設(shè)計角度 ——不同的opcode編碼方式硬 件結(jié)構(gòu)會有差異
1.2.1 R型指令
操作碼 ——R,I,J都會包含6bit,用于區(qū)分最多可以用于區(qū) 分 2^6=64 種指令,這個數(shù)字并不足夠,因此還需要于后面的 6bit 的功能碼一起確定不同的指令。
源操作數(shù) 1、2 (rs,rt)——R 型指令的兩個操作數(shù)均來自于寄存器,按照寄存器的編號確定使用哪兩個寄存器。因為在 MIPS 當(dāng)中一共只有 32 個寄存器,所以 5bit 足以編號。
目標(biāo)寄存器 (rd)——與源操作數(shù)一樣,按照寄存器的編號確定使用哪個寄存器,并用 5bit 進行編號。
位移量 (shamt)——用于對寄存器內(nèi)的數(shù)字進行位移。
功能碼 (funct)——在同一操作碼下區(qū)分不同的操作
1.2.2 I型指令
6+5+5+16=32bit
操作碼 (opcode)——與 R 相同,I 型指令不需要功能碼進行輔助 區(qū)分。
源操作數(shù) (rs,rt)——I 型指令的源操作數(shù)可能有一個,也可能有兩個,其中第一個源操作數(shù)的寄存器編號存儲在 rs 中。
目標(biāo)寄存器 (rd)——當(dāng) I 型指令沒有第二個源操作數(shù)時,則第二個寄存器編號代表了目標(biāo) 寄存器。
立即數(shù) (Imm)——16bit 數(shù)字,根據(jù)操作碼的區(qū)別對應(yīng)不同的含義。
1.2.3 J型指令
偽直接尋址 ——在當(dāng)前指令的一定的范圍內(nèi)進行尋址。跳轉(zhuǎn)地址為指令中的 26 位常數(shù)與 PC 中的高位拼接得到,也就是說:新的 PC = { PC[31..28], target address, 00 } (00:+4)
1.3 尋址方式
1.4 指令系統(tǒng)
CPU 可以利用RIJ三類指令構(gòu)成一套指令系統(tǒng),完成一系列指定的任務(wù)。
1.4.1 數(shù)據(jù)處理指令
算數(shù)運算指令:加法與減法
add $t0, $t1, $t2 # $t0 = $t1 + $t2
sub $t2, $t3, $t4 # $t2 = $t3 - $t4
算數(shù)運算指令:立即數(shù)加法與減法
addi $t2, $t3, 5 # $t2 = $t3 + 5
addi $t2, $t3, -5 # $t2 = $t3 - 5
邏輯運算指令:或、與等
and $t0, $t1, $t2 # $t0 = $t1 & $t2
or $t0, $t1, $t2 # $t0 = $t1 | $t2
xor $t0, $t1, $t2 # $t0 = $t1 ⊕ $t2
nor $t0, $t1, $t2 # $t0 = ~($t1 | $t2)
nor $t0, $t1, $zero # $t0 = ~$t1
邏輯運算指令:移位運算
sll $t0, $t1, 10 # $t0 = $t1 < < 10,立即數(shù)邏輯左移
srl $t0, $t1, 10 # $t0 = $t1 > > 10,立即數(shù)邏輯右移
sra $t0, $t1, 10 # $t0 = $t1 > > 10,立即數(shù)算術(shù)右移
sllv $t0, $t1, $t3 # $t0 = $t1 < < ($t3%32),邏輯左移
srlv $t0, $t1, $t3 # $t0 = $t1 > > ($t3%32),邏輯右移
srav $t0, $t1, $t3 # $t0 = $t1 > > ($t3%32),算術(shù)右移
比較指令 :u 代表無符號(unsigned), i 代表立即數(shù)(immediate)
slt $t1,$t2,$t3 # if ($t2 < $t3) $t1=1;
slt $t1,$t2,$t3 # else $t1=0
sltu $t1,$t2,$t3 # 無符號比較
slti $t1, $t2, 10 # 與立即數(shù)比較
sltui $t1, $t2, 10 # 與無符號立即數(shù)比較
1.4.2 數(shù)據(jù)傳送指令
lw 和 sw ——寄存器與存儲器之間的數(shù)據(jù)傳輸
lw $t1, 30($t2) # Load worda
sw $t3, 500($t4) # Store word
壓棧操作 ——根據(jù)**sp 指向的存儲器的位置,可以將寄存器的數(shù)據(jù)進行壓棧操作,每壓棧一次,**sp 的內(nèi)容就會指向原來位置-4。
addi $sp, $sp, -12
sw $s1, 8($sp)
sw $s2, 4($sp)
sw $s3, 0($sp)
出棧操作 ——將數(shù)據(jù)出棧并保存到寄存器中,每出棧一次,$sp 的內(nèi)容就會 指向原來位置+4。
lw $s1, 8($sp)
lw $s2, 4($sp)
lw $s3, 0($sp)
addi $sp, $sp, 12
裝入高位立即數(shù)
lui $t1, 0x1234
ori $t1, $t1, 0xabcd #將32位立即數(shù)0x1234abcd裝入$t1寄存器
1.4.3 分支與跳轉(zhuǎn)指令
分支指令
beq $t0, $t1, Target # 如果$t0 =$t1,則分支執(zhí)行標(biāo)號為 Target 的指令
bne $t0, $t1, Target # 如果$t0!=$t1,則分支執(zhí)行標(biāo)號為 Target 的指令
無條件跳轉(zhuǎn)
j Label#無條件跳轉(zhuǎn)到標(biāo)號 Label 處
這是一條 J 型指令 ,前面 在介紹 J 型指令 也提到過 ,對于 Label 對應(yīng)地址的方式為偽直接尋址 。
while循環(huán)
Loop: sll $t1, $s3, 2 # 以 4 的倍數(shù)尋址,將 i 的值存入 $t1
add $t1, $t1, $s6 # 將偏移量加上基地址( save )存入 $t1
lw $t0, 0($t1) #從 $t1 指向的位置讀出數(shù)據(jù),存入 $t0
bne $t0, $s5, Exit #判斷 $t0 是否等于 k ,若等于則結(jié)束
addi $s3, $s3 #上面一句不等于的話執(zhí)行這步,i=i+1
j Loop #繼續(xù)循環(huán)
Exit:…… #執(zhí)行其他代碼
過程調(diào)用
jal Procedure #將返回地址 (PC+ 保存在 $ra 寄存器中,程序跳轉(zhuǎn)到過程 Procedure處執(zhí)行
jr $ra #跳轉(zhuǎn)到寄存器指定的地址,子程序返回通過寄存器跳轉(zhuǎn)指令jr進行
葉過程 ——不調(diào)用其他過程的過程,僅需要將返回地址寄存器 ra和在被調(diào)過程中修改了的保存寄存器s0~~$s7進行壓棧操作即可。
主過程 ——調(diào)用了葉過程的過程,除了需要將ra和s0~~**s7 壓棧外,還需要使用的參數(shù)寄存器 **a0~**a3 和臨時寄存器 **t0~~$t9 壓棧,用于保存當(dāng)前程序執(zhí)行的中間變量。
1.5 評價計算機性能的指標(biāo)
響應(yīng)延時 ——系統(tǒng)從開始做一項任務(wù)到任務(wù)完成所需要的總時間,包括CPU 運算,磁盤讀寫,內(nèi)存讀寫等
吞吐量 ——系統(tǒng)單位時間內(nèi)處理的任務(wù)總數(shù),服務(wù)器以及工作站更看重這一點,吞吐量更大的計算系統(tǒng)在面對大量任務(wù)請求時,能夠更快的完成所有任務(wù)(這時大部分的任務(wù)都在隊列中等待完成,等待的時間遠大于任務(wù)的響應(yīng)延時)。
指令平均周期數(shù)( Clock cycles Per Instruction CPI ) ——平均一條指令所需要的周期數(shù)
CPU執(zhí)行時間 =指令數(shù)×CPI×時鐘周期。通過優(yōu)化編譯器減少指令總數(shù),或者通過增加 CPU 的復(fù)雜性降低 CPI ,或者優(yōu)化 CPU 的關(guān)鍵路徑降低時鐘周期。但是對其中任意一項的優(yōu)化,都有可能導(dǎo)致另外兩項的提升,同時也有可能增加成本,功耗等參數(shù)。
不影響其他兩項的情況下對其中一項進行優(yōu)化:編譯器優(yōu)化使得同樣一段高級語言的代碼翻譯成匯編后的指令數(shù)更少,代價是增加了編譯程序的時間;選擇 更快的電路實現(xiàn)與生產(chǎn)工藝 ,增加生產(chǎn)成本和功耗;流水線或超標(biāo)量通過設(shè)計處理器的體系架構(gòu),在時鐘周期變化不大的情況下,讓原本只能一個周期完成一條指令的系統(tǒng)變?yōu)榭梢砸粋€周期完成多條指令,也可以成倍的增加系統(tǒng)的性能。
2 存儲器
2.5 虛擬內(nèi)存和外設(shè)
面臨的問題:
編譯時編譯器無法獲知物理地址 ——由于編譯器在編譯時無法獲知程序運行時使用的地址空間分配情況,程序必須使用邏輯地址尋址,這就需要在邏輯地址和物理地址之間轉(zhuǎn)換。
多個程序需要共享物理內(nèi)存空間 ——同一系統(tǒng)上可能需要同時運行多個程序,這些程序無法共享各自的邏輯地址,但物理內(nèi)存空間有限,如果為所有程序都分配獨立的物理地址空間將造成嚴(yán)重的浪費。我們希望更高效地共享物理內(nèi)存,避免某一程序長期獨占物理內(nèi)存,浪費空間。
2.5.1 頁式管理
將地址空間劃分為小的、等大的頁( page ),以頁為單位進行分配和共享 。在運行時進行物理地址和邏輯地址之間翻譯的硬件稱為存儲器管理單元( Memory Management Unit, MMU )。操作系統(tǒng)以頁為單位管理內(nèi)存、完成邏輯地址與物理地址之間的轉(zhuǎn)換;操作系統(tǒng)負責(zé)將每個程序中活動的頁放入物理內(nèi)存,這樣一來各個程序只需要使用邏輯地址,并且邏輯地址空間可以高于其實際使用的物理內(nèi)存大小,看起來有一種獨享內(nèi)存和地址空間的感覺,大大簡化了編程的難度。同時,由于各個程序的頁相互獨立,頁式管理也便于操作系統(tǒng)提供保護機制 ,避免程序之間內(nèi)存空間的互相訪問。有了分頁管理,不用的頁不必放在內(nèi)存中,這大大提高了物理內(nèi)存的使用效率。
頁式管理需要維護一個頁位置和地址轉(zhuǎn)換關(guān)系的數(shù)據(jù)結(jié)構(gòu),稱為頁表。與頁式管理相對的還有段式管理,即以不等大的內(nèi)存段為單位管理內(nèi)存。這種管理方式目前很少使用。
2.5.2 虛擬存儲器
頁式管理當(dāng)中不活動的頁不需要放入內(nèi)存中。實際計算機系統(tǒng)中,不用的頁通常是被放入磁盤上的。實際上,磁盤可以看作是主存的一種擴充,可以把主存和磁盤一起看成一個大的虛擬的存儲器 。這樣就用磁盤來將那些在主存儲器內(nèi)放不下的數(shù)據(jù)保存起來 ,這樣做的好處有 :編程時不必考慮因為內(nèi)存不足而帶來的各種約束可以通過操作系統(tǒng)有效的管理有限的內(nèi)存從而在各個程序進程 之間共享 。
虛擬存儲器(虛擬內(nèi)存)是建立在主存輔助存儲器結(jié)構(gòu)基礎(chǔ)之上,由軟件操作系統(tǒng) 和硬件 ( 相結(jié)合管理的存儲系統(tǒng) 。編好的程序由操作系統(tǒng)裝入輔助存儲器中,程序運行時,操作系統(tǒng)把輔存的程序一塊塊自動調(diào)入主存由 CPU 執(zhí)行 。當(dāng)發(fā)生主存儲器分配溢出時,操作系統(tǒng)透明地將主存儲器中的段或頁移到輔助存儲器中,并將其標(biāo)記為無效,然后可以將物理內(nèi)存分配用做其他用途。
引入虛擬存儲管理后,程序的邏輯地址也稱為虛擬地址。在發(fā)生缺頁異常(訪問的頁不在主存中)時,CPU 自動轉(zhuǎn)到缺頁中斷處理程序進行處理 。缺頁中斷處理程序由操作系統(tǒng)提供。它通過頁故障寄存器中的虛擬內(nèi)存地址,計算出相應(yīng)的頁表項地址,根據(jù)頁表項中查得的外存地址,從磁盤中讀出新的頁到主存中,然后允許程序重新訪問。
在實際的虛擬存儲器應(yīng)用中,頁的大小通常是4-16 KB 。虛擬存儲器是全相聯(lián)的,一個虛擬的頁可以映射到內(nèi)存中(幾乎)任何一個位置。頁的替換規(guī)則通常采用 : LRU Least Recent Used) 。由于磁盤的寫入代價非常大,通常采用寫回機制處理臟頁。
2.5.3 地址和地址轉(zhuǎn)換
每個進程都有自己的地址空間,操作系統(tǒng)和硬件協(xié)同工作,為每個進程 進行各自的 地址轉(zhuǎn)換 。為了完成這一操作,需要維護一個地址轉(zhuǎn)換 表 頁表 。只有操作系統(tǒng)才能修改頁表 。通過適當(dāng)操作頁表、轉(zhuǎn)換地址,可以限制 一個進程 無法 獲得訪問其他進程地址空間的權(quán)限 。
由于現(xiàn)代存儲器技術(shù)的發(fā)展,頁表越來越大,將全部頁表存在內(nèi)存中往往不現(xiàn)實,也不高效。解決這一問題的方法是使用兩級頁表,第一級頁表始終放在內(nèi)存中,第二級頁表的 1024 項有一部分在內(nèi)存中 另外一部分在磁盤上或者可以不分配。
快表 (TLB)—— 頁式管理使每 次存儲器訪問都帶來額外的存儲器訪問,即在訪問所需數(shù)據(jù)之前要訪問 1 次頁表 采用多級頁表結(jié)構(gòu)時額外的訪問次數(shù)更多 。這些花銷通常可以通過TLB(Translation Look aside Buffer ,地址變換高速緩存 來避免 。TLB 是一種 Cache ,用來存儲最近用過的頁轉(zhuǎn)換關(guān)系,由硬件實現(xiàn) 。TLB 也稱為快表,而頁表則稱為慢表 。
2.5.4 虛擬存儲器與Cache
相同之處都把存儲器劃分為一個個信息塊,運行時都能自動地把信息塊從慢速存儲器向快速存儲器調(diào)度,信息塊的調(diào)度都采用一定的替換策略,新的信息塊將淘汰最不活躍的舊的信息塊,以提高繼續(xù)運行時的命中率。新調(diào)入的信息塊需遵守一定的映射關(guān)系變換地址后來確定其在存儲中的位置。
不同之處Cache 存儲器采用與 CPU 速度匹配的快速存儲元件來彌補主存和 CPU 之間的速度差距,而虛擬存儲器雖然最大限度地減少了慢速輔存對 CPU 的影響,但它的主要目的是為了彌補了主存的容量不足,具有容量大和程序編址方便的優(yōu)點 。
兩個存儲體系均以信息塊作為存儲層次之間基本信息的傳遞單位,Cache 存儲器每次傳遞是定長的 信息塊,長度只有幾十字節(jié),而虛擬存儲器信息塊劃分方案很多,有頁、段等等,長度均在幾百字節(jié)至幾千字節(jié)左右 。
主存——Cache 存儲體系中 CPU 與 Cache 和主存都建立了直接訪問的通路,一旦在 Cache未命中, CPU 就直接訪問主存,并同時向 Cache 調(diào)度信息塊,從而減少了 CPU 等待的時間;輔助存儲器與 CPU 之間沒有直接通路,一旦在主存中不命中,則只能從輔存調(diào)度信息塊到主存.因為輔存的速度與 CPU 的速度差距太大,調(diào)度需要毫秒級 時間,因此, CPU 一般將改換執(zhí)行另一個程序,等到調(diào)度完成后再返回原程序繼續(xù)工作 。
主存——Cache 存儲器存取信息的過程、地址變換和替換策略全部用硬件實現(xiàn),所以對程序員來說是透明的。虛擬存儲器則是由硬件 ( 和軟件 操作系統(tǒng) ) 相結(jié)合管理的存儲系統(tǒng)。地址變換由 MMU 硬件負責(zé),頁表的設(shè)置由操作系統(tǒng)負責(zé),頁面調(diào)度由操作系統(tǒng)實現(xiàn),所以對設(shè)計存儲管理軟件的系統(tǒng)程序員來說,虛擬存儲器是不透明的 。
2.5.5 外圍設(shè)備
首先是訪問地址。虛擬內(nèi)存地址是邏輯的、可以被擴展,不但可以映射到物理內(nèi)存,還可以映射到外設(shè)。
然后是訪問時機。CPU 有兩種訪問外設(shè)的策略:輪詢和中斷。輪詢方式是 CPU 每隔一段時間詢問外設(shè)是否需要 I/O 。這種方式可能會浪費大量的 CPU 時間 在無意義的外設(shè)訪問上,但其實現(xiàn)簡單,不需要復(fù)雜的中斷處理程序。中斷方式是當(dāng)外設(shè)需要 I/O 時, 向 CPU 發(fā)送 一個中斷 。只有出現(xiàn)中斷時, CPU 才停下來與比較慢的外設(shè)通信 。這種方式比較經(jīng)濟,但處理中斷比較復(fù)雜。
3 微處理器設(shè)計原理
3.1 單周期MIPS處理器
3.1.1 數(shù)據(jù)通路設(shè)計
不能直接并行連接每種類型需要的數(shù)據(jù)通路,這樣會增加太多的冗余硬件單元。為了復(fù)用硬件單元,要用到多路選擇器和相應(yīng)的控制邏輯。增加多路選擇器比增加 ALU增加寄存器堆訪問端口要更加經(jīng)濟。
指令集子集按照實現(xiàn)的功能分為四類:寄存器-寄存器運算、寄存器-立即數(shù)運算、訪存操作(寄存器-內(nèi)存搬運)、分支和跳轉(zhuǎn)操作。若按照指令格式分類,可以分為 R 型、I 型、J 型。
存儲單元
存儲器 ——內(nèi)存存儲指令和/或數(shù)據(jù)
寄存器堆 ——R、I需要訪問。R需要兩個讀端口一個寫端口;不是所有指令都寫所以有寫使能信號RegWr。可以為RS和RT提供讀取,為RT或RD 提供寫入
程序計數(shù)器PC ——指向當(dāng)前正在執(zhí)行的指令在內(nèi)存的地址,需要電路根據(jù)是否分支跳轉(zhuǎn)更新PC
運算電路
算數(shù)/邏輯運算ALU ——支持操作數(shù)的加減、移位等運算。
擴展電路 ——I型操作數(shù)是16位立即數(shù)時根據(jù)指令作符號擴展或零擴展
PC更新電路 ——可以加4或立即數(shù)擴展
寄存器-寄存器運算數(shù)據(jù)通路
指令從Rs, Rt 讀出兩個操作數(shù)并送入 ALU 進行某種運算,運算輸出存入Rd中。數(shù)據(jù)通路主要由PC單元、寄存器堆、ALU之間的連接組成。
寄存器-立即數(shù)運算數(shù)據(jù)通路
ALU 操作數(shù)一個從寄存器 (Rs)中讀出,另一個由指令中給出的立即數(shù)(imm16)做擴展得到。結(jié)果寫回Rt。
訪存操作數(shù)據(jù)通路
LW指令使用ALU用Rs和16位立即數(shù)擴展的結(jié)果進行加法運算,得到的結(jié)果作為內(nèi)存地址從內(nèi)存地址中讀出數(shù)據(jù)寫回 Rt。
SW指令同樣計算出內(nèi)存地址。從 Rt 中讀出數(shù)據(jù)寫入該內(nèi)存地址,內(nèi)存的寫使能 WrEn應(yīng)該置1。
分支和跳轉(zhuǎn)操作數(shù)據(jù)通路
Beq 指令判斷 Rs 和 Rt 是否相等,如果相等則修改 PC 至相對當(dāng)前 PC 位置為 imm16 符號擴展并×4 的字節(jié)位置。由于單周期一 個 ALU 模塊不能被用于兩個計算,這個符號擴展和相加的邏輯將在 Instruction Fetch Unit 這個模塊內(nèi)部由 一個單獨的 ALU 解決,但是符號擴展單元可以復(fù)用。所以 數(shù)據(jù)通路只需要將 ALU 減法的輸出是否為 0 輸入給 PC 模塊即可。
跳轉(zhuǎn)指令的數(shù)據(jù)通路較為簡單,只需將 26 位立即數(shù)左移兩位作為 內(nèi)存地址 更新 PC 值即可。
3.2 多周期MIPS處理器
單周期處理器的數(shù)據(jù)通路主要是組合邏輯,時鐘同步行為主要是PC更新、寫入寄存器和內(nèi)存。
多周期將指令可能經(jīng)過的幾個階段拆分開來,不同的指令經(jīng)過的階段數(shù)不同,執(zhí)行時間不同,希望減少固定周期帶來的時間浪費。
- 首先在單周期基礎(chǔ)上添加寄存器負責(zé)在一條指令的幾個周期間的信息傳遞,每一個階段的輸出結(jié)果都應(yīng)該有寄存器保存;
- 取指令和訪問數(shù)據(jù)內(nèi)存肯定會分在兩個周期進行,所以存儲器復(fù)用,數(shù)據(jù)存儲和指令存儲采用相同的地址空間,用同一套端口訪問;
- 不同階段之間 ALU 可以共用,不再需要額外為分支指令和順序PC+4 增加加法器;
- 由于 一條指令跨多個周期,控制信號邏輯不再是簡單的組合邏輯,而是一個根據(jù)指令類型進行轉(zhuǎn)移的有限狀態(tài)機。
3.3 異常和中斷
異常和中斷是除了分支和跳轉(zhuǎn)指令以外,特殊的改變程序控制流的方式。
中斷主要指的是來自處理器外部 ,外設(shè)在有事件發(fā)生比如新的網(wǎng)絡(luò)包到來等時向處理器提出中斷處理請求。中斷產(chǎn)生與程序執(zhí)行是異步的。
異常是來自處理器內(nèi)部的被處理器檢測到的事件。分為軟件導(dǎo)致的異常比如被零除、 訪問段錯誤等,和硬件異常比如MMU檢測到非法內(nèi)存訪問時。異常通常與程序執(zhí)行流是同步的。
為什么需要在硬件上實現(xiàn)中斷和異常處理機制
如果我們不使用中斷的方式處理異常,還可以讓處理器忙輪詢外設(shè)來和外設(shè)交互 。IO設(shè)備比處理器速度慢很多數(shù)量級,大量浪費了處理器的能力;如果使用中斷處理 外部事件,可以讓處理器在有外部事件導(dǎo)致中斷時再來處理,其他時間都可以繼續(xù)進行計算。現(xiàn)代操作系統(tǒng)中,當(dāng)一個進程開始等待外部 IO 事件時,這個進程常被 暫時掛起,處理器開始運行另一個進程,直到該進程等待的中斷來臨,才將該進程重新標(biāo)記為可運行,等待被調(diào)度 。
很多異常本身就很不容易通過軟件檢測,比如未知指令錯誤會導(dǎo)致硬件進入未知狀態(tài)。另外本來也存在硬件異常,軟件很難處理 。同時我們也想要將設(shè)計一些異常處理策略的權(quán)利交給軟件設(shè)計者,所以我們不能只在硬件上用單一的方式處理異常,而是要實現(xiàn)一個能提供給軟件設(shè)計者異常處理彈性的機制。
作為硬件設(shè)計者,我們想要實現(xiàn)異常處理的機制,然后把實現(xiàn)機制的具體策略的權(quán)利提供給軟件開發(fā)者,又是一個將機制和策略明確分離的典例 。所以我們將異常處理做成一個隱式 的過程調(diào)用,由軟件設(shè)計者提供異常處理程序,硬件上實現(xiàn)發(fā)生異常時調(diào)用該程序的機制。
硬件知道某種異常要調(diào)用哪個異常處理程序一般有兩種方法:
- (x86) 向量方式 ——每個異常都有自己的異常處理程序,其入口保存在 一個固定地址處。
- (MIPS) 原因寄存器 ——只使用一 個通用的異常處理程序,每個異常事件必須將足夠的信息加載到原因寄存器中,以便異常處理程序知道是何種異常發(fā)生并采取相應(yīng)措施 。
3.4 MIPS處理器流水線設(shè)計
硬件設(shè)計中,流水線通常是通過在較長的組合邏輯之間添加寄存器來實現(xiàn)的。通過插入寄存器,可以將延時較長的組合邏輯分解為多步來完成,不同的步驟可以在多次計算上并行。由于組合邏輯被拆解,延時降低,從而時鐘頻率可以提升,達到加速的目的。所有的寄存器采用同一時鐘觸發(fā)。
3.4.1 MIPS處理器流水化
IF ——根據(jù) PC 讀取指令寄存器中的指令,并更新 PC 為 PC+4 或 EX 階段計算出的跳轉(zhuǎn)地址。
ID ——指令的譯碼,省略了控制信號的解碼部分。同時,根據(jù)寄存器地址讀出兩個用于計算的寄存器中的值,完成立即數(shù)的擴展。
EX ——擴展的立即數(shù)和寄存器讀出的第二個數(shù)通過多路選擇器得到 ALU 需要的一個操作數(shù)。另一個操作數(shù)固定為從寄存器堆中讀出的第一個數(shù)。兩者通過 ALU 完成計算。同時,由于 j 指令的需要,擴展的立即數(shù)經(jīng)過移位后與 PC+4 的結(jié)果進行相加,得到需要跳轉(zhuǎn)到的地址。從寄存器讀出的第二個數(shù)作為可能的讀寫數(shù)據(jù)存儲的地址也被單獨寄存。
MEM ——根據(jù)需要有三種選擇:將 ALU 輸出結(jié)果寫入數(shù)據(jù)存儲;根據(jù)地址讀取數(shù)據(jù)緩存;將 ALU 輸出結(jié)果傳至下一級。
WB ——將 MEM 階段寄存的數(shù)據(jù)通過多路選擇器進行選擇后,寫回寄存器。 寫回地址在 ID 階段即得到,跟隨流水線逐級傳遞到 WB 階段。為了使數(shù)據(jù)盡早地寫回寄存器以便被后面的指令使用,可以令寄存器組采用時鐘下降沿觸發(fā)寫入 ,而流水線的 中間寄存器采用時鐘的上升沿觸發(fā)寫入 。這樣,相當(dāng)于在前半個時鐘周期寫入寄存器組,后半個時鐘周期將數(shù)據(jù)讀出。
3.4.2 流水線處理器的性能
吞吐率 ——單位時間內(nèi)處理的指令數(shù)。吞吐率分為實際吞吐率和最大吞吐率。實際吞吐率為系統(tǒng)運行時實際在單位時間內(nèi)處理的指令數(shù)。最大吞吐率則為系統(tǒng)在達到理想的穩(wěn)定運行狀態(tài)時的吞吐率。理想吞吐率為 1 指令每周期 。
加速比 ——不使用流水線的執(zhí)行時間和使用流水線的執(zhí)行時間之比。類似于吞吐率的定義,我們可以定義實際加速比和最大加速比。k 級流水線可以達到的最大的最大加速比為 k 。
無法達到最大加速比:
- 最長的一段決定了流水線運行的最小時鐘周期,成為流水線運行的 瓶頸 ;
- 插入寄存器引入額外的建立時間和保持時間,每級流水結(jié)算的邏輯路徑長度之和超過純粹的組合邏輯路徑長度,最長的會超過原路徑的1/k。這說明無限制地增加流水級不能無限制提升性能。
流水線設(shè)計對于指令數(shù)并沒有任何的優(yōu)化。相比于單周期設(shè)計而言,流水線設(shè)計可以有效縮短時鐘周期;相比于多周期的設(shè)計而言,流水線設(shè)計主要降低了 CPI 。流水線的不同階段在執(zhí)行的是不同的指令,因此是在完成指令級的并行處理。流水線是指令級并行的基本技術(shù)之一。其他的指令集并行技術(shù)還包括超標(biāo)量以及超長指令字。
3.4.3 流水線冒險
解決冒險的最簡單和直接的方式是阻塞流水線。通過在指令之間插入空指令(bubble)將相鄰的指令間隔拉開。直觀的認識是,如果插入足夠的空指令使得相鄰指令在流水線上完全不重疊,那么流水線處理器的運行就和單周期處理器的運行方式一樣,也就不會有冒險存在。當(dāng)然這也嚴(yán)重影響了流水線處理器的吞吐率。因此冒險的解決辦法是以盡量不阻塞流水線為目標(biāo)來制定的。
結(jié)構(gòu)冒險——硬件資源使用沖突
流水線中的兩條指令需要同時訪問相同的硬件資源。通常出現(xiàn)在某個單元未完全按照流水線方式實現(xiàn)時。比如,對 load 指令采用 5 級流水,第5 級時寫回寄存器。而對于 R 型指令,由于 EX 階段之后即得到結(jié)果,可以在第 4 階段就寫回,那么當(dāng) load 指令和 R 型指令相鄰依次出現(xiàn)時就會出現(xiàn)同時向寄存器寫入兩個數(shù)的情況,發(fā)生結(jié)構(gòu)冒險。又比如,當(dāng)數(shù)據(jù)存儲器和指令存儲器共享同一個 RAM 資源時,取指令和數(shù)據(jù)的讀寫也可能發(fā)生沖突。
為了避免結(jié)構(gòu)冒險,一方面需要將所有硬件資源固定分配到指定的流水級,另一方面需要所有的指令都順序完成所有的流水級,不能跳過或停留。當(dāng)資源沖突時,往往需要增加資源來避免沖突。比如將數(shù)據(jù)存儲器和指令存儲器分開,將 ALU 與計算 PC 的單元分開。
數(shù)據(jù)冒險——數(shù)據(jù)因指令關(guān)聯(lián)不可獲得
當(dāng)前指令讀取的寄存器,應(yīng)該被之前的一條指令寫入后再使用。但是之前的指令在流水線中尚未完成。
數(shù)據(jù)冒險出現(xiàn)在當(dāng)前指令和其相鄰的前兩條指令之間。因此,如果不采用任何措施,需要至多插入 2 條空指令來阻塞流水線。采用的解決策略是盡可能將已經(jīng)得到的結(jié)果向前傳遞(forwarding)至需要的位置,而不等待其寫入寄存器堆之后再去使用。
為了縮短代碼長度,必要的阻塞我們也采用硬件的方式來完成。
1)ALU輸入端數(shù)據(jù)選擇
(00——寄存器堆;10——EX/MEM;01——MEM/WB)
2)無法解決的load-use冒險
(stall=1阻塞流水線,IF 暫停,并且在 ID 插入空指令。只需要將 RegWrite 和 MemWrite 信號置 0 即可。PC 通過 stall 信號禁止其寫入)
前一條指令為 load 指令,而當(dāng)前指令需要用 load 得到的數(shù)據(jù)進行計算。這種情況我們稱之為 load use hazard 。判斷需要阻塞流水線的條件為:當(dāng) ID/EX 階段的寄存器表明當(dāng)前指令需要讀內(nèi)存,而 IF/ID 階段表明下一條指令需要使用寄存器,且該寄存器將要被當(dāng)前指令寫入
硬件處理load use hazard 會帶來性能上的損失。防止流水線阻塞的另一個方法是在代碼編譯時進行優(yōu)化。一些編譯器通過將無關(guān)的指令放在 load 指令和需要使用其數(shù)據(jù)的指令之間,相當(dāng)于將本來需要插入空指令的地方利用了起來,從而提高了執(zhí)行效率。
3)MEM寫入端的數(shù)據(jù)選擇
當(dāng)load-use是由于復(fù)制存儲器引起的,則可以不必阻塞一個時鐘周期,可以選擇增加新的 forward 路徑來完成這個轉(zhuǎn)發(fā)過程,通過判斷寄存器 id 和操作碼來決定是否轉(zhuǎn)發(fā)。
控制冒險——跳轉(zhuǎn)分支指令使PC無法及時確定
控制冒險定義為:下一條需要執(zhí)行的指令,依賴于當(dāng)前正在流水線中執(zhí)行的指令的結(jié)果。控制冒險的出現(xiàn)頻率通常比數(shù)據(jù)冒險要低,但是也不容易解決。可能的解決方案包括:阻塞流水線、提前判斷、預(yù)測、延遲決定。
j和 jr 指令在 ID/Reg 階段能夠確定之后 應(yīng)該執(zhí)行的指令。而 beq 指令則需要經(jīng)過 ALU計算結(jié)果之后再判斷才能確定是否要跳轉(zhuǎn)。因此,如果純粹地插入空指令來等待的話, j 和jr 指令要損失一個周期,而 beq 指令則要損失 2 個時鐘周期。
1)阻塞流水線
阻塞流水線能夠省去無謂的空指令。但是為了支持對 j jr beq 指令的支持, hazard unit 中的邏輯會更復(fù)雜一些,需要添加對 IF/ID 寄存器中指令類型以及 ID/EX 寄存器中指令類型的判斷。另外,還需要將 j 指令中跳轉(zhuǎn)地址的計算也轉(zhuǎn)移到 ID 階段來執(zhí)行。
2)提前判斷
由于在指令譯碼之后我們才能確定指令的類型,因此進一步提前j 和 jr 指令的跳轉(zhuǎn)相對困難。將 beq 指令的判斷放在 ID 階段執(zhí)行:
- 在 ID 階段的寄存器堆的輸出端增加一個比較器用來判斷是否分支跳轉(zhuǎn)
- 將判斷的結(jié)果接入跳轉(zhuǎn)地址的多路選擇器的選擇端
- 對于比較器的輸入,需要額外的 forward 邏輯來處理
可能從 EX, MEM 或者 WB 階段轉(zhuǎn)發(fā)。從 EX 階段的轉(zhuǎn)發(fā)是不太實際的。因為需要的是 EX 階段計算的結(jié)果而不是輸入,如果進行轉(zhuǎn)發(fā),意味著存在著一條新的邏輯路徑,從 ID/EX 寄存器出發(fā),經(jīng)過 ALU ,比較器以及 PC 的選擇端到 PC 寄存器。這很可能會延長關(guān)鍵路徑,降低處理器性能。從 WB 階段的轉(zhuǎn)發(fā)沒有必要,因為我們寫入寄存器堆和寫入流水線中的寄存器采用不同的時鐘沿。因此我們只需要從 MEM 階段到 ID 階段的 forward 邏輯。
3)分支預(yù)測
一段程序中出現(xiàn)的跳轉(zhuǎn)應(yīng)該主要來自于循環(huán)操作。如果我們可以通過某種比較可靠的方式來猜測一條 beq 指令是否跳轉(zhuǎn),那么我們就能在大多數(shù)時候不阻塞處理器。比如通過一個表來存儲 beq 指令的地址和上一次是否跳轉(zhuǎn),然后每次執(zhí)行時查表并執(zhí)行和之前一次同樣的行為。
然而這樣的預(yù)測不可能 100% 準(zhǔn)確,因此我們還需要處理預(yù)測出錯的情況。通常,當(dāng)我們發(fā)現(xiàn)預(yù)測跳轉(zhuǎn)出錯時,可以將已經(jīng)在流水線中的指令清空( flush )。相比于提前判斷,分支預(yù)測對于長流水線的處理器更友好。
4)延遲槽
在采用了提前判斷的技術(shù)之后,我們已經(jīng)將分支所需要阻塞的時鐘周期降低到1 個。進一步提高處理器性能可以利用這一個周期。具體的做法是在這個周期中插入和分支無關(guān),總是要執(zhí)行的指令。這便是延遲槽技術(shù)。
延遲槽技術(shù)屬于軟件技術(shù),通常由高級語言的編譯器或匯編程序的編寫者來決定。延遲槽技術(shù)通常會打亂原有程序的順序,造成可讀性降低。并且不總是存在可以放到延遲槽中的指令。但是這種方法不需要額外的硬件。
3.4.4 流水線異常和中斷
為了在硬件上支持異常和中斷處理,我們需要:
- 根據(jù)異常或中斷的具體 情況,將 PC跳轉(zhuǎn)到處理程序的入口;
- 將已經(jīng)在流水線中的指令清空。考慮以下三種情況:溢出,錯誤的操作碼,外部中斷。認為前兩種情況都在 EX 階段被發(fā)現(xiàn)和處理。所以這里我們需要將 EX 、 ID 以及 IF 階段的指令清空。清空的操作很簡單,只需要在流水線寄存器的寫入端添加一個二路選擇器,一端為 0 ,另一端為正常輸入當(dāng)需要清空時選 0 ,否則選正常輸入。
評論
查看更多