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