考研
計算機組織
如上圖所示,計算機組織這門學問探討軟體與硬體的交界處那2層,如何藉由control unit, datapath(前兩者合稱processor), memory, I/O device這些由數位邏輯電路所建構的計算機架構,要能夠執行指令集中的每一道指令,也就是軟體裡面最硬的、硬體裡面最軟的部分。
前面提及ISA = 指令集 + 根據指令集所制定硬體的規範,這些硬體包括4類 - 記憶體、暫存器、指令格式、定址模式,接下來會依序介紹
Type | Number | Name | Usage |
---|---|---|---|
Assembler Related | $1 | $at | Preserved for Assembler |
OS Related | $26 - $27 | $k0, $k1 | Preserved for OS |
Constants | $0 | $zero | Constant zero |
Variables/Constants | $8 - $15 | $t0 - $t7 | Temporaries |
Variables/Constants | $24 - $25 | $t8, $t9 | More temporaries |
Variables/Constants | $16 - $23 | $s0 - $s7 | Save |
Procedure Call | $4 - $7 | $a0 - $a3 | Arguments(引數) |
Procedure Call | $2 - $3 | $v0, $v1 | Values for results |
Procedure Call | $31 | $ra | Return address |
Memory Management | $28 | $gp | Global pointer |
Memory Management | $29 | $sp | Stack pointer |
Memory Management | $30 | $fp | Frame pointer |
當OS把程式call到memory裡面後,分為5個記憶體的區塊 - stack, heap, static, code, reserved。
stack是一種memory讀取的結構,規則為first in, last out,當一個procedure在執行時會把他自己的local變數放到stack裡面,而一個procedure的local變數在stack中的集合,稱為procedure frame / activation record。
隨著副程式呼叫stack區會往下成長,每一次呼叫都會產生一層procedure frame ,fp和sp永遠指向正在執行的procedure frame的頭和尾。而程式執行時只會fp和sp之間讀取變數,避免程式在呼叫來呼叫去誤用到別人的local變數。
大部分的指令集都只有sp,而沒有fp,但MIPS有別於其他指令集有fp,這是因為正在執行的procedure時,local變數放到stack的時間是不確定的,因此sp的值會變動,而fp都是固定的,因此fp較適合作為參考點。
人透過高階語言寫程式,透過compiler轉為低階的assembly language,但是assembly language一樣是由英文數字組成,機器看不懂;所以就需要assembler轉為機器看得懂0101的machine language;由於各自的procedure可以各自compile, assemble,因此會產生數個machine language,以及呼叫用到library的machine language,就需要linker把這個program相關的machine language去merge起來一個大的檔案,為可執行檔;當使用者執行這個可執行檔,OS會派出另一個系統程式稱為loader,load會執行6個步驟
int main()
括號裡面的argument。
C語言的執行流程稱為compilation(translation),但並不是所有高階語言都遵照這個流程,但還有一部分的程式語言像是Java,執行流程稱為interpretation。首先用compiler,把Java程式轉為一種中間的語言稱為Java byte code,接著使用透過不同種的JVM(x86版,ARM版, …),JVM讀一行解釋給hardware聽,讓hardware執行一行,但是這樣執行效率實在太差,為了改善執行效率,所有Just In Time compiler (JIT)的出現,會把最常用到的method轉為machine code,當程式執行到最常用到的method時,就可以直接調用machine code,剩餘的就改JVM逐行解釋給解釋給hardware聽。
lw
到暫存器,再從暫存器sw
回記憶體位置B。編譯程式有3個步驟
由於編譯程式第1個與第3個步驟是NP-complete問題,機制過於複雜,因此題目上只需要執行步驟2的轉譯即可,如以下例題
給定register assignment為A -> $s0, B -> $s1, C -> $s2, D -> $s3, E -> $s4, F -> $s5
,與高階C語言
A = B + C + D;
E = F - A;
因此MIPS的組合語言為
add $t0, $s1, $s2 # binary operation
add $s0, $t0, $s3
sub $s4, $s5, $s0
對MIPS而言,資料轉移指令只有2個,一是從記憶體轉到暫存器的load,二是從暫存器轉到記憶體。要存取的記憶體地址採用基底/位移(base or displacement)表示方式,就是一個基底暫存器(base register)內容加上一個限定為"常數"資料型態的位移量。
由高階語言轉換為組合語言例題如下,給定register assignment為A -> $s0, H -> $s1
,與高階C語言
A[12] = H + A[8]
首先理解一下機制,compiler會把必要的資訊放在可執行檔裡面,OS照做即可,所以loader的第5個步驟時就會把暫存器$s0初始化為array的起始位置;而高階語言中H
在暫存器,A
陣列由於數字過多,所以在記憶體裡面,因此需要先將陣列數值從memory中load進入暫存器才能執行運算,故MIPS的組合語言為
lw $t0, 32($s0)
add $t0, $s1, $t0
sw $t0, 48($s0)
其中lw, sw
代表load word, store word,一次存取的基本單位為word,因此offset需要乘以4才代表實際數值,計算機會將基底暫存器$s0
的數值與offset常數32
丟進ALU相加即為要存取memory的地址,接下來對load指令而言,會讀取該地址的data,存進暫存器$t0
裡面。
若offset並非常數,在高階語言轉組語的題目當中,我們就必須模擬計算機的行為,先將"不是常數"的offset與base register相加,給定例題 - register assignment為f, g, h, i, j
在$s0 - $s4
;A, B
在$s6 - $s7
,與高階C語言
f = g - A[ B[4] ]
MIPS的組合語言為
lw $t0, 16($s7) # t0 = B[4]
s11 $t0, $t0, 2 # 4 * t0
add $t0, $s6, $t0 # base register of array A + offset
lw $s0, 0($t0)
sub $s0, $s1, $s0
最後探討lw,sw
的變化指令lh, lhu, sh, lb, lbu, sb
。
char, short
等佔較少位元的資料型態,同理也要有對應的組合語言。lh
與 lhu
一個視讀入資料為有號數(signed)、一個視讀入資料為無號數,因此把資料從記憶體讀入register做符號擴充(sign extension),無號數空格直接補0,而有號數要複製MSB位元至所有空格。sh, sb
對上lh, lb
與sw
對上lw
的關係雷同,差別在於lh, lb
是少數memory位元要讀取進入多位元的register,會有多餘空格需要考慮符號擴充;但是sh, sb
是多位元的register填入少數memory位元,因此只需要將register靠近右邊的位元擷取出來寫入memory即可,不會有符號擴充的問題。流程控制指令並不做任何運算,其唯一的目的便是改變指令執行順序,否則一般指令都是由上而下依序執行,分為2種 - 一是有條件的改變指令執行順序稱為分支(branch) ,二是無條件的改變指令執行順序稱為跳躍(jump)。而分支目的以"label"表示,label值為要跳至執行的記憶體位置;當條件成立,要跳指令時,program counter會用這個label的數值去更新。
給定register assignment為f, g, h, i, j
在$s0 - $s4
則以下為高階C語言轉MIPS組合語言
If (i == j) go to L1;
f = g + h;
L1 : f = f - i;
觀察if
語句若相等要跳,不相等不要跳,且繼續依序執行所有指令,因此用beq
指令,則MIPS組合語言
beq $s3, $s4, L1 # go to L1 if i equals j
add $s0, $s1, $s2 # f = g + h (skipped if i equals j)
L1: sub $s0, $s0, $s3 # f = f- i (always executed)
If (i == j)
f = g + h;
else
f = g - h;
觀察if
語句若相等不要跳,不相等要跳,因此用bne
指令,則MIPS組合語言
bne $s3, $s4, Else # go to Else if i not equals j
add $s0, $s1, $s2 # f = g + h (skipped if i not equals j)
j Exit # go to Exit
Else: sub $s0, $s1, $s2 # f = g - h skipped if i = j)
Exit:
Loop: g = g +. A[i];
i = i + j;
if (i != h) go to Loop;
觀察if
語句若相等不要跳,不相等要跳,因此用bne
指令,則MIPS組合語言
Loop: s11 $t1, $s3, 2 # Temp reg $t1 = 4 * i
add $t1, $t1, $s5 # $t1 = address of A[i]
lw $to, O($t1) # Temporary reg $t0 = A[i]
add $s1, $s1, $t0 # g = g + A[i]
add $s3, $s3, $s4 #i=i+j
bne $s3, $s2, Loop # go to Loop if i is not equal h
While (save[i] == k)
i = i + j
MIPS組合語言
Loop: s11 $t1, $s3, 2 # Temp reg $t1 = 4 * i
add $t1, $t1, $s6 # $t1 = address of save[i]
lw $t0, 0($t1) # Temp reg $t0 = save[i]
bne $to, $s5, Exit # go to Exit if save[i] <> k
add $s3, $s3, $s4 #i = i + j
j Loop # go to Loop
Exit:
基本區塊(basic block)是編譯程式執行最佳化的最基本單位,其定義為沒有分支與沒有分支目的的指令,若有分支也只能在區塊的最後一個指令,若有分支目的也只能在區塊的第一個指令。以上面MIPS組合語言為例,拆成1到4行、5、6行、7行之後的3個basic block。
因此程式在執行時,compiler會以下3步驟
所謂最佳化就是指使用最少、最精簡的指令完成其程式要執行的功能、以減少程式碼佔用的記憶體空間並加速程式執行。
虛擬指令(pseudo-instruction or directive instruction)是指實際機器並不存在但組譯程式卻可以接受的指令,MIPS並不提供
等分支指令,但我們可以使用beq, bne, slt(set if less than)等指令造出以上4種虛擬指令。
虛擬指令 | MIPS指令 | 虛擬指令 | MIPS指令 |
---|---|---|---|
blt $s1, $s2, L |
slt $t0, $s1, $s2 bne $t0, $zero, L |
bgt $s1, $s2, L |
slt $t0, $s2, $s1 bne $t0, $zero, L |
bge $s1, $s2, L |
slt $t0, $s1, $s2 beq $t0, $zero, L |
ble $s1, $s2, L |
slt $t0, $s2, $s1 beq $t0, $zero, L |
位移(shift)指令理論上有分邏輯上的左移與右移(sll, srl),算數上的左移與右移(sla, sra),邏輯與算數的差別在於邏輯視數字為無號數,算數視數字為有號數,但MIPS不存在sla指令,因為與sll功能重複。
MIPS提供and / or / nor / xor指令,其中nor為先做or後做not,xor (exclusive-or)為相同數為0,相異數為1。
nor $t0 $s0 $0
代替,任何數與0做or都是任何數,或是改用nor $t0 $s0 $s0
代替,自己跟自己做or還是自己,再做not,綜合以上這兩部就具有not指令的功能。$s0, $s1
要互相交換,而規定只能用兩個暫存器,可以使用XOR的特性來實現xor $s0, $s0, $s1 # $s0 ⊕ $s1 $s1
xor $s1, $s0, $s1 # $s0 ⊕ $s1 ($s0 ⊕ $s1) ⊕ $s1 = $s0
xor $s0, $s0, $s1 # ($s0 ⊕ $s1) ⊕ $s0 = $s1 $s0
由於程式經常使用常數當作運算元a = b + 5
,根據前面所學過的指令,就必須assign一個變數為5,並從記憶體載入這個變數到register做運算,如此就會增加指令的延遲時間,因此MIPS對幾乎每個運算指令額外設計其對應的立即版本指令(immediate instruction)。
一個指令有32 bit,扣除6 bit的opcode、2個5 bit的暫存器地址,剩下能表示常數的只有16個 bits,使用2的補數來表示正數與負數,因此能表示範圍為
由於立即指令只能存取16-bit的數字,因此若需要將完整32-bit常數放入暫存器,就需要使用lui
(load upper immediate)指令,其功能為將16-bit常數載入暫存器左半邊16 bit,剩下右半邊16 bit則清為0;由於ori
是邏輯運算,加上0與任何數OR會是任何數,所以接下來就使用ori
指令將16 bit常數載入暫存器右半邊。
前面提到immediate使用2的補數表示,可以是正數與負數,因此MIPS就沒有設計subi
指令,其功能等同於addi
。
根據指令參與運算的元素不同,分為3種不同格式的機器指令表示,MIPS有3種指令格式
參與運算的元素 | 組合語言指令 | 機器指令格式 |
---|---|---|
3個暫存器 | 算數與邏輯指令 | R-type |
2個暫存器加上16-bit的常數或地址 | load, store, branch, 立即版本指令 | I-type |
26-bit的地址 | jump | J-type |
機器指令中幾個連續位元所構成有意義的資訊稱為欄位,指令不同欄位的排列方式就稱為指令格式(instruction format),而不管哪種指令長度都是一樣為32 bit。
不管哪種指令opcode都需要等長,否則機器就無法執行指令,其中R-type有共用相同的opcode為000000
,這是因為假設所有指令有128個,因此需要7 bit的opcode,但是I-type和J-type的位置有限,只能塞6 bit的opcode,而R-type會有多出來的6個bit,因此把R-type的opcode設定相同,共用相同的opcode,剩下多出來的6個bit稱為function code就可以來區別即可。
再來rs, rt的field為來源1與來源2暫存器,如果指令只有用到一個來源暫存器的話,根據MIPS規定則將該暫存器填入後者 - 來源2暫存器,前面 - 來源1暫存器則全部填0。shamt為shift amount的全名,其數值"有效"的位移量,由於data是32bit所以有效位移量是從0到31,佔5個bit,如果沒有用到shamt這個field則根據規定全部填0。
組語翻譯機器語言有3個步驟
另外需要組語翻譯機器語言時需要注意 - 組合語言目的暫存器要放前面,來源暫存器要放後面;但機器語言則相反,原因有以下兩點,一是早期使用CISC,指令不等長,所以為了讓目的暫存器的位置固定,所以把目的暫存器放在前面;二是考慮到實際硬體設計,硬體要先放牧目的暫存器再放來源暫存器,才能確保線路不會交錯,而有電磁干擾等問題。
branch指令翻譯 - 由於branch指令通常不會跳躍太遠,因此採用PC相對定址法(PC-relative addressing),以branch指令的下一個指令的地址為基準點往上用負數,往下用正數,計算經過多少指令(word)可以到達跳躍目的的數值,因此分支目的位置 = program counter值 + 4 * 16-bit address;而其最大跳躍空間就是相對上下
當執行程式,此時特殊暫存器PC = 80012時,從記憶體80012的位置抓指令執行,同時PC = PC+4 = 80016,當確定要跳時,就會抓reference值(假設等於4)進行運算,使得PC = 80016 + 2*4 = 80024。
由於branch能跳的最大距離為下一個指令的地址相對上下
jump指令翻譯 - 使用虛擬絕對定址法(pseudo-direct addressing),白話來說存的是"假的"記憶體絕對位置。MIPS作業系統於記憶管理時會將
當CPU執行jump指令要還原原本的地址時,只需要把頭串接(concatenate)上program counter的前4個bit,尾巴串接上2個0就可以還原出原本記憶體的位置。
執行程式呼叫時有以下6大步驟
$ra
,讓procedure B執行後有辦法跳回procedure A。return
。接下來callee回復原本被破壞的save暫存器,從stack pop出原來的數值。jr
後面的register內的數值,在此就是$ra
值,返回procedure A的,下一個執行add指令。當procedure A呼叫procedure B,而procedure B又繼續呼叫procedure C,這樣繼續呼叫下去,稱為遞迴呼叫,此時callee既是caller,給定算階層的C語言
int fact(int n){
if (n < 1) return (1);
else return (n * fact(n - 1));
}
先決定哪些變數要存,哪些變數不要存,caller有責任存argument與temp暫存器,callee有責任存save與ra暫存器,在此情況下沒有local變數,因此只需要存argument和ra暫存器。
接下來照結構翻譯即可
fact:
addi $sp, $sp, -8 # Adjust stack for 2 items
sw $ra, 4($sp) # Save the return address
sw $a0, 0($sp) # Save the argument n
slti $t0, $a0, 1 # Test for n < 1
beq $t0, $zero, Ll # If n >= 1 go to Ll
addi $v0, $zero, 1 # Return 1
addi $sp, $sp, 8 # Pop 2 items off stack
jr $ra # Return to caller
Ll:
addi $a0, $a0, -1 # n ~ 1; argument gets (n - 1)
jal fact # Call fact with (n - 1)
lw $a0, 0($sp) # Return from jal: restore argument n
lw $ra, 4($s p) # Restore the return address
addi $sp, $sp, 8 # Adjust stack pointer to pop 2 items
mul $v0, $a0, $v0 # Return n*fact (n - 1)
jr $ra # Return to the caller
定址模式(addressing mode)為說明指令如何取得運算元或是指令如何計算跳躍目的位置。
有3種取得運算元的方法
定址模式 | 指令 | 運算元位置 |
---|---|---|
立即(Immediate addressing) | 立即版本的運算指令 | 在指令本身的16位元常數 |
暫存器(Register addressing) | add, sub, and | 暫存器 |
基底或位移(base or displacement addressing) | load, store | 記憶體,而記億體位址的計算方式為將基底暫存器的內容加上指令中16位元的常數 |
有2種計算跳躍目的位置的方法
定址模式 | 指令 | 跳躍目的位置 |
---|---|---|
程式計數器相對(PC-relative addressing) | beq, bne | PC + 指令中的常數 |
虛擬直接(Pseudodirect addressing) | j, jal | 指令的26位元與程式計數器高4位元串接 |
有4種不同型態的指令集
數在計算機中的表示有以下幾類
無號數只有正沒有負,包含0,若二進位的無號整數有n個bit,則能表示的範圍為
sign and magnitude為用一個sign bit表示正負號,sign bit為1表示負,sign bit為0表示正,但有以下3個缺點
1's complement要表示一數值之負數形式只要將各個位元取補數即可但有以下2個缺點
2's complement為現代計算機採用有號數的表示方式,數值為1's complement再+1,而另一種轉換方式 - 從右往左邊的bit掃過去,碰到第1個1之前(包含1)保持原狀,接下來取補數。2's complement每一位元的權重除了MSB的權重是負的之外,其餘位元的權重和二進位無號數是—樣的。若二進位的2's complement整數有n個bit,則能表示的範圍為
用無號數會讓範圍檢查的組語縮短,檢查用有號數指令檢查
許多圖學系統使用3個8 bit(R、G、B三原色),來記錄一個點的顏色,用另一個8 bits(alpha)來記錄位置。但是運算上一次只會有1 byte的資料量,若以一般1個word的方式運算,過於浪費空間,因此以64位元加法器中的進位鏈分割開來處理器一次可處理8組 8 bits 運算元、或四組16 bits運算元、或2組32bits運算元的短向量進行運算(若是MIPS 的話,一次ALU為32 bits,故可處理4組 8bits資料量),此種運算稱為Vector或SIMD。
若是有超出或低於範圍的計算結果出現時,則將結果設為以最大的值與最負的值,此種方式稱為飽和運算(saturating operations)。
無號數的乘法分為傳統乘法器與硬體最佳化乘法器。
傳統乘法器,首先會檢查乘數最右邊的bit是0還是1,是0則不做任何事情,是1則執行"積 = 被乘數 + 積",由於運算子都是64 bit,因此也要有64 bit大的ALU,當執行完後,最後將乘數往右邊移1個bit,被乘數往左邊移1個bit(提高被乘數的權重),並反覆執行上述步驟32次。
硬體最佳化乘法器將被乘數固定起來,而改移動乘數,由於積與乘數每一次運算都是往右移,因此把乘數一同放到積(product)的64 bit暫存器的右半邊。首先會檢查product暫存器最右邊的bit是0還是1,是0則不做任何事情,是1則執行"左半部product = 被乘數 + 左半部product",當執行完後,最後product暫存器往右移1個bit,並反覆執行上述步驟32次。
有號數的乘法就是將乘數與被乘數轉為正數,使用上述無號數的乘法器求得結果,再根據原本正負號設定乘積的正負號。而這邊另外介紹一種有號數乘法的演算法 - Booth演算法,可以直接對兩個有號數運算,且"平均"運算速度也比傳統乘法器、硬體最佳化乘法器還快。
Booth演算法的硬體與硬體最佳化乘法器的硬體類似,差別是多加了一個mythical bit,用以一次檢查連續2個bit的資訊,因此會有以下4種處理方式
可以將Booth演算法想像為用連續1字串的頭bit減去尾bit,而傳統/硬體最佳化乘法器則是乘以每個bit代表的權重。
總結來說無號數乘法器有傳統、硬體最佳化,有號數乘法器有Booth演算法,這三種乘法器都屬於序向(sequential)電路,輸出由目前電路狀態(記憶元件)與輸入共同決定,步驟有3大點檢查(check)、動作(action)、位移(shift)。
接下來是使用組合電路做的乘法器,觀察乘法運算就是一個輸入是被乘數、1個的位元乘數做AND 動作的結果形成partial product,再將這些partial product相加即可,根據partial product做加法的方式分為快速乘法器與平行樹乘法器。
快速乘法器速度較快的原因有3個
如左上圖,每一層依序將最右邊的bit拉下來就是答案,不需要跟別人加,有32個partial product,需要31個加法器,假設1個加法器的延遲時間是T,此電路需要經過31T的延遲時間。如右上圖,另一種架構 - 平行樹乘法器將partial product兩兩相加,此電路需要經過
無號數的除法分為傳統除法器與硬體最佳化除法器。
傳統除法器首先會"被除數 = 被除數 - 除數";若結果為正代表夠減,將商數左移並塞入一個1;若結果為負代表不夠減,要累加回來以還原回原來的值,並將商數左移並塞入一個0,;當執行完後,最後除數暫存器往右移1個bit,並反覆執行上述步驟33次,需要注意要做33輪,因為第1輪是白做的。
硬體最佳化除法器將除數固定起來,而改移動被除數,由於被除數與餘數每一次運算都是往左移,因此把被除數一同放到餘數(remainder)的64 bit暫存器的右半邊。為了避免傳統除法器第1輪是白做的問題,首先先將remainder暫存器往左移才進入迴圈,執行"左半部remainder = 左半部remainder - 被除數",若結果為正代表夠減,將remainder暫存器向左移並塞入一個1;若結果為負代表不夠減,要累加回來以還原回原來的值,並將remainder暫存器並塞入一個0;當執行完後,放在remainder暫存器左半部的餘數要向右移1個bit做修正。
總結來說,傳統除法器和硬體最佳化除法器有4大步驟 - 相減(subtract)、檢查(check)、動作(action)、位移(shift)。
有號數的除法就是將除數與被除數轉為正數,使用上述無號數的除法器求得結果,若除數與被除數異號則將商數加以變號,另外MIPS規定於餘數與被除數必須同號還避免定義上的不同。
在MIPS中,乘法器與除法器共用相同的硬體,差別只在於乘法運算往右移動;除法運算往左邊移動。
64 bit暫存器中右半部為特殊暫存器Lo,存放商數或是乘積的低位數;左半部為特殊暫存器Hi,存放餘數或是乘積的高位數,對應使用的組合語言為
# multiplication
mult $s2, $s3 # {Hi, Lo} <- $s3 * $s2
mfhi $s0 # $s0 <- Hi
mflo $s1 # $s1 <- Lo
# division
div $s2, $s3 # Lo <- $s2 / $s3; Hi <- $s2 % $s3
mfhi $s0 # $s0 <- Hi
mflo $s1 # $s1 <- Lo
"廣義"上的科學記號有不唯一的表示方式,因此正規化數(normalized number)就是規定科學記號在小數點左邊只能有一個非0的數值,而二進位的正規化數以有限硬體空間表示就稱為浮點數(floating point)。
IEEE754規範2種浮點數的表示方式,一是使用1個word的單精度格式、二是使用2個word的雙精度格式。
exponent | fraction | object represented |
---|---|---|
0 | 0 | |
0 | nonzero | |
1 - 254 | anything | |
255 | 0 | |
255 | nonzero | NaN (not a number) |
因此正規化數能表示的範圍在
由於非正規化數的數值實在太小,一般非科學性值的計算都幾乎不會跑到這個區間,因此不是每種指令集都支援非正規化數的數值範圍,若指令集有支援非正規化數,而數值小到不在非正規化數能表示的範圍,稱為gradual underflow;若指令集不支援非正規化數,而數值小到不在正規化數能表示的範圍,稱為underflow;若數值大於正規化數能表示的範圍則稱為overflow。
以下4種運算會產生NaN -
除了IEEE754浮點規定,另一種需要記憶的就是IBM format,一樣32個bit,但指數只有7個bit,bias為64,且base為16。
浮點數的加法有以下4步
浮點數的乘法有以下5步
從十進位開始,以方便說明,需要多保留2個bit的額外硬體空間,稱為保護位元(guard bit)和捨入位元(round bit),簡寫為GR,若GR = 0~49則捨去;若GR = 51 ~99則進位;若GR = 50,需要再1個bit的硬體空間,稱為黏著位元(sticky bit),若運算時round bit右邊的bit被截掉,sticky bit便會設定為1;反之,則就會根據LSB數值依據,執行"round to nearest even (unbiased rounding)"原則。
sticky bit硬體上使用OR gate實現,只要被捨去的bit中有任何一個bit非0,結果就會是1。
將上述觀念改為二進位,影響結果的有4個bit - LSB, guard bit, round bit, sticky bit,如下面流程圖所示
IEEE754共有4種捨去模式 - 朝向正無限大、朝向負無限大、截尾(不做四捨五入)、round to nearest even,上面介紹的是round to nearest even的捨去模式,而MIPS採用此種捨去模式。
由於快速傅立葉轉換經常使用大量加法和乘法,因此在一些指令集(ex : ARM)會設計乘法和加法連做的指令,IEEE754規定單一個指令既做乘又做加,只能最後結果做一次四捨五入,稱為fused multiply add。
實數的加法具有結合率,而浮點數的加法是用來表示實數的,卻無結合率,這是因為浮點數是用有限硬體空間來表示實數,會有精確度上面限制的問題。
在無號數與有號數的正數中,右移等於除以2的冪次方運算;但有號數的負數不一定,當被移掉的bit不是全0,則"右移結果 + 1 = 商數",因此需要將運算做修正 - 右移幾個bit,就先加上幾個bit 1的數值,例如負數除以8需要先+7做修正,再向右移3個bit;負數除以16需要先將+15做修正,再向右移4個bit。
有號數用組語偵測overflow
addu $t0, $tl, $t2 # $t0 = sum, but don't trap
xor $t3, $t1, $t2 # Check if signs differ
slt $t3, $t3, $zero # $t3 = 1 if signs differ
bne $t3, $zero, No_overflow # $tl, $t2 signs !=, so no overflow
# signs =; sign of sum match too?
xor $t3, $t0, $t1 # $t3 negative if sum sign different
slt $t3, $t3, $zero # $t3 = 1 if sum sign different
bne $t3, $zero, Overflow # All three signs #; go to overflow
add和addu用的是同一個ALU,運算方式都一模一樣,差別只是在於對結果的解釋不同,add會偵測overflow,addu不會偵測overflow;如果一開始用add發生overflow,後面就不會執行了,會跑到OS去,OS把程式kill掉。
接下來前三行是做異號數相加一定不會有overflow;後三行是做正數加正數卻是負數,負數加負數卻是正數。
無號數用組語偵測overflow
addu $t0, $tl, $t2 # $t0 = sum
nor $t3, $tl, $zero # $t3 = NOT $tl
sltu $t3, $t3, $t2 # $t3 < $t2 -> set t3 = 1
bne $t3, $zero, Overflow # t3 = 1 -> overflow
想法如下
不同的效能定義會影響評估的結果,因此在實施評估前先要對效能的定義有明確的了解。個人電腦會以執行時間做為效能評估的標準;而伺服器由於服務多人,則以生產量做為效能評估的標準。
本章主要考慮個人電腦,因此以以執行時間做為效能評估的標準,對電腦
計算機從工作從開始到完成所花的時間稱為elapsed time,其中包括等待I/O的時間與處理器時間(CPU time),CPU time又可進一步分為使用者處理器時間(user CPU time)與花在作業系統(服務使用者的程式運行)上的時間,稱為系統處理器時間(system CPU time)。當我們在run的程式個數不同,I/O time和system CPU time這兩個時間都是變動的,因此我們以user CPU time做為執行時間(execution time)的測量。
使用絕對時間user CPU time,由於時間過短難以測量。而由於計算機都需要clock來同步快慢有別的訊號源,因此改使用一個cycle所花的時間長度clock cycle time作為衡量之一的參數,再乘上一個程式使用cycle的個數clock cycle就是user CPU time,而clock rate又為clock cycle time的倒數,是一秒鐘clock訊號同步的次數。
對於2部不同硬體實踐架構但同一ISA的機器,當要比較效能也就是execution time時,首先要確保跑的是相同的程式(組合語言),也就代表instruction count相同,因此真正要比的就是instruction time。
以下為軟體與硬體會影響到的效能參數
值得注意的是compiler不會影響個別指令的CPI,個別指令的CPI在硬體建構好就已決定,compiler會選擇較佳組合的指令,影響的會是平均的CPI。
Amdahl's law是用來計算當某一機器改善了其中一部分後的執行時間。
而speedup是指機器在經過某種策略的改良之後·相對於原本效能之提升程度。
假設工作負載中每個程式的執行次數皆相同,則使用算術平均數(AM);若不是,則根據每個程式在工作負載中的出現頻率給予加權值,即使用加權平均數(WAM);平均數越小,代表機器效能越好。
同時評量2台以上機器的效能時,我們常會將某一部機器對某一程式的執行時間除以各機器的執行時間,稱為正規化得其比值,稱為spec ratio;spec ratio越大,代表機器效能越好。
目前最普及的CPU效能評估程式就是SPEC(System Performance Evaluation Corporation)的benchmark(效能評估程式組),SPEC是由許多電腦廠商建立與支援的評效程式,目的是要為現代的電腦系統提供—組標準的評效程式,以SPEC CPU2000其中包括整數程式與浮點數程式的benchmark。
建構計算機要先搞清楚SPEC,這個SPEC就是ISA,ISA = 指令集 + 根據指令集所制定硬體邏輯的規範。我們要造計算機的指令集只有MIPS中的9個指令
再來決定硬體規範
建構計算機的方法使用抽象化設計(design abstraction),是指設計複雜系統時先將系統較底層的細節隱藏以簡化設計時的複雜度。建構計算機時,並非拿電晶體造計算機,而是用數位邏輯建構出來的基本零件(ex: ALU, decoder)來造計算機,至於零件內部的細節不需要理會。
數位邏輯是拿邏輯閘當作基本元件,至於邏輯閘零件內部的細節不需要理會。
為什麼write要control線,但read就不用?
假設不小心讀出來,只要不要有人誤用就沒事;但是寫入,只要不小心寫入,後面的人要讀入使用通通會錯,因此寫入要慎重,就需要控制訊號來控制。
single-cycle machine在同一個cycle中,要在單一線路同時輸出指令與data,會導致控制訊號線出錯,因此需要將記憶體拆成兩部分。
CPU由datapath和control unit兩個電路單元組成。
先造出個別指令所需要的datapath,造完之後再經由合併成最後一個完整的datapath,此種方式稱為divide and conquer。
RTL(Register Transfer Language)只能描述資料從某一個儲存元件出發,中間經過基本算術運算(加減乘除,add, or, not)或是不經過運算,到達另一個儲存元件,其中儲存元件可以是暫存器或是memory。接下來要用RTL來描述datapath的行為。
instruction fetch的datapath
任何指令的執行都需要先將指令從記憶體讀取出來,而program counter這個特殊目的暫存器提供當下要去哪裡抓記憶體的位置,每次指令從記憶體取出的同時,program counter會更新為PC = PC + 4,等到下一個clock來的時候,提供下一筆指令的位置。
R-type指令的datapath
R-type指令的rs和rt放要運算的暫存器號碼,把這兩個暫存器號碼分別放到read register 1和read register 2,抓出兩個要運算的暫存器內容,經過ALU做適當的運算(add, subtract, add, …)後,拉回來寫入暫存器,寫入暫存器的號碼指定為rd。
load word指令的datapath
load word指令功能為讀取data memory內容並寫入暫存器,讀取記憶體的位置需要計算,I-type指令的rs放的是base register的號碼,放到read register 1,抓出base register的內容,放在ALU的上面;同時間把指令的常數imm16,經過sign extension unit擴充為32個bit,放在ALU的上面,讓ALU執行加法運算,ALU運算結果就是記憶體位址。根據這個記憶體位址到data memory把這個位址的data抓出來,送到write data,寫入暫存器。
至於rt放的是目的暫存器的號碼,所以要把rt拉到write register指定寫入暫存器的位置,另外還需要控制訊號線RegWrite設為1,才能成功寫入。
store word指令的datapath
store word指令功能為把暫存器內容抓出來並寫入data memory,寫入記憶體的位置需要計算,I-type指令的rs放的是base register的號碼,放到read register 1,抓出base register的內容,放在ALU的上面;同時間把指令的常數imm16,經過sign extension unit擴充為32個bit,放在ALU的上面,讓ALU執行加法運算,ALU運算結果就是記憶體位址。
至於要寫入記憶體的暫存器號碼放在指令的rt,放到read register 2,抓出要寫入data memory的資料,把訊號送到data memory的write data輸入,另外還需要控制訊號線MemWrite設為1,才能成功寫入。
beq指令的datapath
需要有2條路線同時進行 - 一是比兩個暫存器是否相等、二是計算跳躍目的的位置。
I-type指令的rs, rt放的是要比兩暫存器是否相等的暫存器號碼,送至read register 1和read register 2,抓出暫存器內容分別放入ALU的上面與下面,使用控制訊號線ALU operation,讓ALU執行減法運算,ALU減完結果若是0,zero線會變成1,代表兩暫存器數值相等,需要跳;ALU減完結果若是非0,代表兩暫存器數值不相等,不需要跳。
PC+4代表beq下一行的指令,放在adder的上面與I-type指令的常數imm16,經過sign extension unit擴充為32個bit,再向左移2個bit,代表乘以4,放在adder的下面,兩者相加就是跳躍目的的位置。
合併lw和sw指令
以lw的datapath基礎上把rt另外拉到read register 2,read data2輸出與write data輸入相連。因為register file和data memory都有write enable的控制訊號線,因此就算額外多拉條線,也不會因此寫入,而造成結果改變。
合併R-type和lw,sw指令
觀察R-type和lw,sw指令,有3個"不同來源的資料要送到相同的地方"的衝突,因此就需要MUX,MUX的功能是選擇多個輸入任一條作為單一輸出。
衝突發生位置 | MUX的控制訊號 | R-type指令的來源位置 | lw和sw指令的來源位置 |
---|---|---|---|
ALU下面的輸入 | ALUSrc | register file讀出read data 2 | 經過sign extension後的常數imm16 |
register file中write data的輸入 | MemtoReg | ALU運算後的結果 | memory的read data |
register file中write register的輸入 | RegDst | R-type指令的rd欄位 | I-type指令的rt欄位 |
{NextPC[31:28], Instruction[25:0], 2'b00}
,首先將原指令中26 bit向左移增加2bit,右邊就會補0;再來將下一個指令最左邊4個bit的線路拉過來與原線路並排,就可合成出實際要跳躍的目的位址。
multi-level control意思是將控制單元拆分為多個子單元,某一些控制子單元受控於另一個控制子單元,由於電路面積大小與輸入個數呈現指數正比,因此這樣設計就可降低控制單元電路面積大小,也可以加快控制電路的速度。
single-cycle machine使用multi-level control,拆成main control與ALU control兩個控制子單元,main control可藉由2個bit ALUOp的訊號線,控制ALU control。
main control根據指令中6 bit的opcode判斷出屬於哪個指令,並透過2 bit的ALUOp傳遞給ALU control。
指令 | ALUOp | ALU operation |
---|---|---|
lw/sw | 00 | + |
beq | 01 | - |
R-type | 10 | + / - / and / or / slt |
若ALUOp為00,則ALU operation control會輸出給ALU控制訊號進行加法運算;若ALUOp為01,則ALU operation control會輸出給ALU控制訊號進行減法運算;若ALUOp為10,則會進一步根據R-type指令中6 bit的function field來判斷ALU要執行的運算。
因此就可建立6個輸入對4個輸出的truth table,改部分訊號don't care以在之後步驟獲得更大的化簡。由於沒有用到ALUOp為11的訊號線,就可改01 -> X1、10 -> 1X;在此計算機設計中,沒有用到function field第4、5個bit因此改為X。
由於輸入與輸出超過4個,無法用人工的K-map化簡,需要藉助Quine-McCluskey Tabular Method化簡,最後就可畫出最簡電路圖。
main control的輸入為指令中6 bit的opcode,輸出則包含4條控制MUX的訊號線,分別是RegDst, ALUSrc, MemtoReg, PCSrc (Branch中MUX的控制訊號),以及3條控制基本元件讀寫的訊號線,分別是RegWrite, MemRead, MemWrite。
嘗試從CPU架構圖去trace各個指令資料流,就可得該指令需要設定的控制訊號,不過也可從控制訊號線的名稱來快速判斷,舉例來說MemtoReg設為1,代表data memory到register file的線是true,是通的;反之,MemtoReg設為0,代表data memory到register file的線是false,不通的,而是改由ALU result傳至register file。
接下來查表寫上各指令的opcode就可得到有輸入與輸出關係的truth table,在此不使用跟設計ALU control內部電路一樣的standard logic process,而是使用PLA,如此就可避免繁雜的化簡過程(Quine-McCluskey Tabular Method)。
PLA實作時,會放棄部分的don't care項,在此放棄全部的don't care項。
實踐電路有3種方法
- standard logic process - 化簡過程繁雜,但電路會最簡。
- PLA - 介於兩者之間。
- ROM - 過程最簡單,但硬體成本最高。
有jump的datapath的話,只要在原控制訊號線的基礎下加上jump訊號線即可,此外此計算機架構,"剛好"也可以執行addi指令。
參照課本P383練習,由於線路與ground line或是power line短路,就可能會產生訊號線永遠是0或是1,稱為stuck-at-0 / 1 fault,會造成以下影響,考試時需要自行判斷。
ch3 效能定義中在此效能的定義方式為execution time的倒數,而execution time = IC * CPI * cycle time;對於2部不同硬體實踐架構但同一ISA的機器,當要比較效能也就是execution time時,首先要確保跑的是相同的程式(組合語言),也就代表instruction count相同,因此真正要比的就是instruction time。
single-cycle machine顧名思義每個指令都只需要1個cycle的時間就可以完成,故CPI為1;而用最長指令的時間做為single-cycle machine的minimum clock cycle time,下圖分別trace這9個指令(R-type包含5個指令)的資料流
考試上會給各種零件需要運算的時間,如此從多個路徑計算,花最久的路徑就是critical path,就可計算critical path delay。由於每個指令critical path的零件都是load word指令critical path的零件的子集合,因此load word指令所需要花的時間最長,決定了minimum clock cycle time的時間。
由此可知一筆要寫入memory的資料除了寫到對的位址之外,還會寫到另一筆錯誤的位址。因此data memory要加clock,用clock邊緣來觸發資料寫入,避免一筆資料寫入2個不同記憶體的位址。同理register file也應該要加clock避免同一筆資料多寫入錯誤的位址。
在single-cycle machine中data memory要加clock,以同步MemWrite, Adress, Write data這三個訊號線,但在pipeline中EX/MEM的pipeline register會做好這3個訊號的同步工作。
第1題題目問在"避免增加critical path的前提"下,是為了避免critical path增加,而影響到single-cycle machine的performance。接下來要計算最晚產生MemWrite的contro訊號線的時間,是因為不需要讓control unit那麼快產生控制訊號,讓main control的設計更有彈性,可以使用最差的IC製程技術,讓線做的最窄,電阻最大,使control unit的晶片面積縮小,降低成本,時間慢一點沒關係,只需要在時間內產生我所需要的控制訊號就好,而不影響clock cycle time,代表不影響single-cycle machine的performance。
第3題題目問"哪個訊號線需要最早產生?",首先畫出資料流
讀取instruction memory抓出指令(不需要控制訊號線) -> 讀取暫存器(不需要控制訊號線) -> 開始計算(需要控制訊號線),因此最早需要產生控制訊號的是ALUSrc或是ALUOp。
RegDst和RegWrite的控制訊號線跟寫入暫存器有關,不需要太早產生。
實際上,lw才是花最長的指令,而lw只會走rs和imm16那兩條,因此不可以算中間那條critical path,因此送至ALU的運算元的critical path delay為200ps,代表main control至少要在(200 - 50)ps就要將ALUOp設定好,但還是以原文書答案為主。
single-cycle machine固定clock cycle time為最長指令執行(lw)的時間,效率不佳,理想上是可變clock cycle time效能會最好,但實務上又造不出clock cycle time隨每個指令變動。因此退而求其次,想辦法讓長指令多用點時間,短指令少用點時間,只要有呈這個趨勢就好。
實踐方式就是將每個指令拆成"執行時間較平均"的步驟。課本提供分步驟的原則 - 一個步驟只能使用一個主要功能單元;因此clock cycle time由最長的主要功能單元時間所決定。
其中IF = instruction fetch, ID = instruction decode, RF = register fetch, MA = memory access, WB = write back,個別指令所需要的CPI各不相同,需要注意切步驟時會有額外的clock cycle time的時間,這是因為每個步驟結束的時候,要把當下步驟的結果存入暫存器,在下一個clock來之後,提供給下一個步驟使用。
pipeline是一種計算機的實作技術,允許多個指令同時存在於datapath裡面,使指令重疊地、平行地被執行。pipeline跟multiple cycle machine一樣,需要將single-cycle machine分成若干階段,分步驟的原則是時間要平均分配 - 一個步驟只能使用一個主要功能單元,而最長步驟的時間就是pipeline的clock cycle time。
pipeline並不會對單一個指令的執行時間(latency)有所幫助,反而會比single-cycle machine的latency還長,因為每個pipeline的每個stage執行結束後,會卡在"柵欄"上,等下一個CLK來,柵欄"打開才能繼續執行。pipeline是藉由多個指令在pipeline中使用不同部分的硬體資源,以提高硬體使用率,以增進整體工作的生產量(throughput)。
假設pipeline不會停下來(stall),也就是理想pipeline的機器,給定週期時間
根據一個步驟只能使用一個主要功能單元的原則,在MIPS指令執行時分為5個步驟
劃分完stage後,要在相鄰的stage之間加上pipeline register,來儲存上一stage的執行結果,並在下一clock來之後提供下一個stage使用。pipeline register以相鄰的兩個stage來命名,其容量需要能夠容納上一stage的資料。
接下來會出現一個問題 - WB stage的指令無法寫入自己的目的暫存器,而是會寫入正在占用ID stage的指令的目的暫存器。解法 - 在ID stage選完的目的暫存器號碼,跟著指令沿著pipeline傳下去,最後在WD stage再往回拉,指定目的暫存器號碼。
設計完pipeline的datapath,接下來是設計pipeline的control unit,與single-cycle machine的訊號線一樣,差別只在於pipeline在ID階段control unit解完碼後,這些控制訊號線需要跟隨指令沿著pipeline傳下去,直到指令執行完畢。
需要注意要把原本在single-cycle machine中RegDst的MUX往下移至pipeline的EXE階段。假設main control需要花的時間特別長,dominate任何主要功能單元的時間,因此決定pipeline的clock cycle time,如果main control解完碼後,又用拉到RegDst的MUX去控制,還需要MUX的延遲時間,導致pipeline又需要多花上MUX的延遲時間,clock cycle time更長,這並不是一個很好的設計。
因此把MUX往後拉,在ID階段main control專心解碼就好,不用再弄其他控制訊號線,把MUX往後移一個stage到EX階段即可,也不需要刻意移到更後面的stage,因為這會導致pipeline register要額外的空間存rd和rt這64 bit的data。
因此各個stage需要設定的控制訊號線為
stage | IF | ID | EX | MEM | WB |
---|---|---|---|---|---|
control signal |
X | X | ALUOp0 ALUOp1 ALUSrc RegDst |
MemRead MemWrite branch |
MemtoReg RegWrite |
畫出MIPS指令集5個stage pipeline的"原始設計",而branch在MEM時決定要不要跳。
總結畫pipeline的"原始設計"的圖有4個步驟
接下來探討一個問題 - 在single-cycle machine中,一個指令既要讀又要寫register file,在同一個clock發生2次硬體資源access的衝突。解決方法 - 把讀與寫的時間錯開,register file在clock前半周寫,在clock後半周讀。
同理pipeline,我們把register file設計在clock前半周寫、在clock後半周讀,所以WB stage的指令在clock前半周寫,ID stage的指令在clock後半周讀,時間上會錯開,因此不會產生register file硬體資源在同一時間access的衝突。
hazard為pipeline不能在下一個clock執行下一道指令的狀況,分為以下3類
不論是哪種pipeline的hazard都可以藉由暫停(stall)來避免,但是暫停pipeline會造成clock cycle數目變長,進而影響pipeline的performance,因此接下來對各種hazard要使用更有效的解決方式,在確保能解決問題的前提下,也不會影響到過多的performance。
假設我們只有一個記憶體,而非像前面single-cycle machine或是pipeline一樣有兩個分離的記憶體,則在一指令IF階段與另一指令MEM階段時,需要同時read相同的硬體資源,造成structural hazard。
解決方法1加入足夠多的硬體;解決方法2是讓先進入pipeline的指令有較高使用硬體資源的priority,而讓將會發生structural hazard後進來的指令先stall。
而sw, beq一定不會和後面指令發生data dependency,因為沒有寫入暫存器的動作。
addi $0, $0, 0
,sll $0, $0 0
,而MIPS選擇sll $0, $0 0
作為nop指令,翻成指令的機器語言剛好全都是0。採用nop指令解data hazard的優點是簡單,但缺點是會占用clock cycle,讓效率變差。首先需要有一個forward unit的控制單元來偵測是否需要forward,分別偵測EX stage和MEM stage之間、EX stage和WB stage之間有沒有存在data hazard。課本沒有實踐forwarding unit的內部電路,但有寫偵測碼來描述電路行為。
綜合以上3個條件可得EX hazard的偵測碼
if (EX/MEM.RegWrite & (EX/MEM.RegisterRd != 0) & (EX/MEM.RegisterRd = ID/EX.RegisterRs))
ForwardA = 10
if (EX/MEM.RegWrite & (EX/MEM.RegisterRd != 0) & (EX/MEM.RegisterRd = ID/EX.RegisterRt))
ForwardB = 10
if (MEM/WB.RegWrite
and (MEM/WB.RegisterRd != 0)
and not (EX/MEM.RegWrite & (EX/MEM.RegisterRd != 0) & (EX/MEM.RegisterRd = ID/EX.RegisterRs))
and (MEM/WB.RegisterRd = ID/EX.RegisterRs))
ForwardA = 01
if (MEM/WB.RegWrite
and (MEM/WB.RegisterRd != 0)
and not (EX/MEM.RegWrite & (EX/MEM.RegisterRd != 0) & (EX/MEM.RegisterRd = ID/EX.RegisterRt))
and (MEM/WB.RegisterRd = ID/EX.RegisterRt))
ForwardB = 01
最後在EX stage的forward unit的輸出會控制ALU運算元前面的3對1的MUX,ForwardA控制ALU第一個運算元的數值,ForwardB控制ALU第二個運算元的數值,若為00則代表不需要forwarding,按照原本在ID stage抓取的暫存器數值計算;若為01則代表存在MEM hazard,需要在前前一個指令的WB stage往前forward給目前指令,在ALU運算之前及時更正運算元;若為10則代表存在EX hazard,需要在前一個指令的MEM stage往前forward給目前指令,在ALU運算之前及時更正運算元。
需要注意當pipleline的stage數越多,解決data hazard的代價就越大。
首先需要有一個hazard detection unit的控制單元來偵測是否需要stall指令。課本沒有實踐hazard detection unit的內部電路,但有寫偵測碼來描述電路行為。
綜合以上3個條件可得load-use data hazard的偵測碼
if (ID/EX.MemRead and
((ID/EX.RegisterRt = IF/ID.RegisterRs) or (ID/EX.RegisterRt = IF/ID.RegisterRt)))
stall the pipeline
若是檢測發生load-use data hazard,在ID stage的hazard detection unit,其中2條輸出線PCWrite
與IF/ID.Write
會控制PC和IF/ID pipeline register不要正常接收到clock訊號,因此還是會保留原來暫存器的數值;其中1條輸出線Stall
會把接在lw後面指令的9根控制訊號線全清為0,也就是殺掉原本指令改成對程式執行無任何影響的nop。
對相同rt欄位的load word與store word的load-use data hazard,不必像一般的load-use data hazard一樣需要stall + forwarding,而是可以直接forwarding及時更正要送至data memory中write data的資料,因為sw不需要在EX stage就需要正確的暫存器內容,而是等到下一個stage,也就是在MEM stage才需要將正確的暫存器內容寫入記憶體。
data dependency分為RAW、WAR、WAW。判別方式如下
在MIPS的5-stage pipeline架構中,只有RAW會發生data hazard,RAW稱為true data dependency,其餘WAR、WAW稱為false data dependency。對比在superscalar pipeline中亂序執行,後面指令可以超前前面指令提早執行,因此三種data dependency都有可能產生。
ch5 pipeline的control unit設計MIPS指令集5個stage pipeline的"原始設計"是在MEM stage決定要不要跳。branch指令走到MEM stage才知道後面指令要跟誰,但此時後面指令已經跟上了,如果branch一跳,後面指令就跟錯了,因此解法是是將抓錯的指令flush掉。
flush掉越多指令會降低pipeline的效能(throughput),因此我們嘗試將決定要不要跳往前面的stage移動,以減少branch猜錯要flush掉的指令個數, branch不使用ALU進行減法運算比較兩暫存器是否相等,而是多在ID stage增加XOR array這個硬體元件比較兩暫存器是否相等,由於XOR array沒有像ALU一樣需要進位,因此運算速度會比ALU還快。
如果branch一跳,硬體需要清除後面跟上來在IF stage的指令,使用控制線IF.Flush
,把IF/ID pipeline register全清為0,原指令就會轉換為sll $0 $0 0
,也就是MIPS官方版本的nop指令。
上述將branch決定要不要跳的地方從MEM stage移到ID stage後,會讓data hazard變嚴重,需要解決。
總結一下有4條forwarding path。
分支預測分為2類
動態分支預測使用兩個硬體元件 - 分支歷史表(BHT, branch history table)的索引使用branch指令位址較低的位元,另外使用1個bit來指示前一次branch是否有發生跳躍。BHT告訴我們分支是否會成立,但是分支目的位址的仍需計算,意味還需要一個clock cycle的計算懲罰,因此還需要分支目的位址緩衝器(BTB, branch target buffer)作為快取(cache) ,來存放目的地PC或是目的地指令來消除這個懲罰。
預測方式有1-bit與2bit方式,2bit的BHT存11, 10, 01, 00,但BHT只會輸出最左邊的bit,代表T(taken)或是NT(not taken),兩者預測方式都屬於counter-based(假設分支是發生的則下一個狀態值為目前狀態值加 1否則減1),且加、減都是採用飽和運算。
此外更複雜的預測方式有同時使用local(自己的branch)與global(其他地方的branch)為判斷依據的關聯預測器(correlating predictor),以及使用多個預測器並追蹤看是哪個預測器產生比較好的效果的競賽預測器(tournament predictor)。
compiler會將不論分支條件是否成立總是會執行的指令(稱為安全指令)放到分支延遲插槽(branch delay slot) 中,而延遲分支總是執行後面的指令。因此不論分支條件是否成立都不需要丟棄其後的指令。插入安全指令有以下3種方法
若第1點無法無法執行則做第2、3點,而第2、3點再無法執行,則只能插入nop。
注意這是用軟體解決的方式,compiler排完指令順序後,所有指令一定都會被執行,不會像硬體一樣flush指令。
但是安全指令實在不好找,若是隨著pipeline切更細或是每個clock issue的指令變多,就需要更多的branch delay slot,如此delayed branch的解決方法就已經失去優勢。
pipeline充分利用指令之間潛在的平行度。這類型的平行度稱為指令層次平行度(Instruction-Level Parallelism, ILP) 。
此外更高層級也有job-level parallelism, thread-level parallelism。
增加ILP有2種方法
multiple issue由軟體(compiler)處理稱為static multiple issue,由硬體處理稱為dynamic multiple issue,又稱superscalar。
無論是static或是dynamic的multiple issue都可以用speculation加速。speculation是猜某個指令的性質,來決定跟這個指令相關後續的指令可以提早被執行,如果原本在後面的指令可以提早被執行,就可以讓後面指令跟前面指令混在一起,讓指令包能盡量填滿,以提高ILP。軟體的speculation會重排指令順序,並加入其他指令以檢查猜測的正確性,提供一組修補程式以在猜錯的時候使用;硬體的speculation會將猜測結果暫時另外存到buffer,直到真正知道結果後,如果猜對會將結果寫回暫存器或是記憶體以完成指令,如果猜錯直接把buffer flush掉即可。
static multiple issue會介紹3個例子
凡是無法被打包成instruction group(指令群)的指令,每隔3個指令,硬體會把它們打包成instruction bundle,丟到特製的硬體上面去執行,這個特製的硬體有5個功能單元,因此就不會存在data dependency;加上特製的硬體有大量的forwarding path,縱使有data dependency,也能盡早獲得該拿的資料,加速硬體的執行。
下列指的東西相同,只是名稱不同
- 在MIPS-64稱為instruction package(指令包)
- 在VLIW稱為very long instruction (超長指令)
- 在IA-64稱為instruction group(指令群)
dynamic multiple issue( = superscalar)使用硬體來多指令分發,雖然是dynamic issue pipeline,但是compiler還是要盡量把指令執行順序排到最好,解決compiler有能力解決的hazard問題,當遇到compiler沒辦法解決的hazard問題,再丟給硬體做處理。
最核心的技術是dynamic pipeline scheduling - 選擇下一群要執行的指令並將這些指令重新排序,由於硬體資源足夠多,因此pipeline幾乎不會stall。分為3個主要單元
其中dynamic pipeline scheduling的第2階段 - 亂序執行可能會遇到RAW、WAR、WAW,其中output dependency (= WAW)與anti-dependency (= WAR)都是重複使用硬體資源造成,而不是"真正"的data dependency,可用register renaming來消除。
處理器內部發生非預期事件干擾程式執行稱為例外(exception),可能是使用者呼叫OS(system call),算數類運算所發生的overflow、ARM程式拿到MIPS機器運行而產生illigal instruction;處理器外部發生非預期事件干擾程式執行稱為中斷(interrupt),可能是I/O外部設備請求。
處理exception的步驟分為硬體與OS的部分
硬體先做3件事情
硬體根據特定的記憶體位址把控制權轉交給OS
pipeline處理exception的步驟,以算數類運算所發生的overflow為例
IF.Flush, ID.Flush, EX.Flush
控制線。要確切紀錄到exception的指令位址不是一件簡單的事情,像是x86有四十幾個stage的pipeline,發生exception時,不知道哪個stage的指令發生exception,因此就不會記錄到發生exception的指令位址,稱為非精確例外(imprecise exception),此時OS只有一個選擇就是kill原程式,因為也不知道是該從哪個地方繼續執行原程式。
ch1 記憶體(memory)提及memory的功能是用來儲存正在執行中的程式,包含資料(data)與程式碼(code)。
兩者需要在設計memory時就產生牴觸,幸好可以再利用一個程式執行的特性 - locality of reference。
來設計memory hierarchy,建構一層一層速度由快到慢、容量有小到大、每單位容量的價格由貴到便宜的memory,而前面memory存的資料是後面memory存的資料的子集合。
而現今memory hierarchy的架構設計依序是cache(由SRAM實作)、main memory(由DRAM實作)、hard disk,本章節前半部分會探討由cache和main memory之間的對應,稱為cache system;後半部分會討論由memory和hard disk之間的對應,稱為virtual memory。
對應方式為每個memory位址都直接對應到cache的唯一位置,對應方式為memory block address除以cache中的block個數等於cache的位址,而使用tag來判別是哪個memory block。
CPU丟出memory byte address,會切2刀
另外還會加上valid bit,防止CPU拿到前面program的資料,一開始程式運行會把所有valid bit清為0。總結得到direct-mapped cache的電路結構圖如下
計算direct-mapped cache的容量題型會給定3個必要資訊
首先依序計算
接下來探討block size與miss rate的關係。若一個block只包含1個word,則無法利用到程式執行的spatial locality特性,因此block會設計存放多個word才能利用到spatial locality,以降低cache miss rate。
但是block size過大,反而導致miss rate上升,有2點解釋的方式
另外一個增加block size的問題是miss penalty會增加。CPU存取cache發生miss,CPU會停下來,CPU停的時間就是miss penalty的時間,cache control unit會從main memory搬CPU要的word到cache,正常情況下,cache control unit會把CPU要的word所在的block全部搬完,才通知CPU過來存取cache。因此block size越大,需要搬的時間越長,CPU停的時間越長,miss penalty就越大。
有以下2個方式可以降低miss penalty的時間
當CPU讀取cache經由tag比對,發現要讀取的資料不在cache時,稱為cache miss,處理方式如下
給定sw指令,我們只有將資料寫入cache,而沒有寫入main memory,因此cache的資料與memory的資料不同,稱為memory inconsistence,在write hit與write miss情況下,各有2種解決方式。
為了方便起見
如果write-through與write-allocate搭配,處理起來會十分麻煩,write hit時,write-through保證cache和main memory的資料一致;但是write miss時,cache control unit把CPU要寫入資料的block搬上cache,再給CPU寫入後就結束了,因此cache和main memory的資料就不一致。
miss penalty的時間為將下層memory取代上層memory的時間加上將上層memory傳給CPU的時間,因此要降低miss penalty,有兩種方法
當在讀同一個block時,row address幾乎不變,而DRAM會把row讀取的所有資料放到buffer上,因此只需要解碼column address,移動I/O gate mask選擇要存取哪個word即可,此方式稱為page mode。
SDRAM(synchronous DRAM)切成好幾個橫切面,變成立體狀,當解碼完row和column得知要讀的位址後,接下來會進入burst access,不需要再提供地址,而是藉由clock快速讀取DRAM一串連續的資料。而DDR SDRAM(double date rate SDRAM)在clock上下緣都會存取資料,因此有成倍的讀取量;QDR SDRAM(quad date rate SDRAM)把讀和寫的電路分開,可同時進行,讀取量再翻倍,變成4倍。
direct-mapped是一個很極端的方法,很容易產生block與block之間資源的競爭,進而導致miss rate上升,因此使用set associative cache解決,將cache分為數個集合,每個set都有n個block,稱為n-way或是associativity等於n的set associative cache。
cache中block總數 = set個數 * 關聯度(一個set包含的block個數)。增加關聯度可以減少block之間的競爭程度,miss rate會降低;但是比的tag數增加,導線拉得越長,訊號延遲的時間越長,因此比的時間會拉長,因此hit time增加。
而ch6 direct-mapped cache介紹的direct-mapped cache其實就是set associative cache的特例。
CPU丟出memory byte address,會切2刀
而tag拿來跟這個set中所有的block的tag同時做比對,考量到電路的速度,故使用平行比較。總結得到4-way set associative cache的電路結構圖如下
在direct-mapped cache發生cache miss時原本佔據的block就會被置換下來,而在set associative cache一個集合中的所有block都有機會被置換下來,因此可以使用random或是LRU(least recently used)的置換方式,LRU比起random雖然miss rate較低,但是實作電路複雜,也未必是一個好方法,各有各的優缺點。
計算set associative cache的容量題型會給定4個必要資訊
首先依序計算
multi-level cache的設計目是為了減少miss penalty,當L1發生cache miss時,L2幾乎包含大部分的資料,因此L1的miss penalty就由L2(SRAM)的讀取時間所決定,遠比讀取main memory(DRAM)的讀取時間還快數百倍。
multi-level cache在L1與L2設計著重的點不同
L1的hit time會決定CPU的clock cycle time,所以要盡量設計小。
L2注重miss rate,要夠大才能涵蓋所有大部分需要的資料。
對combined cache來說,multi-level cache的extra CPI的公式有兩個版本
在上述公式缺條件時,有一些基礎假設
一般miss rate題目沒特別指名都是只global miss rate,還有另一種是local miss rate。
最後在定義一個衡量標準,AMAT(average memory access time)代表CPU存取記憶體一次所需要的平均(絕對)時間。
可與extra CPI的公式做比較,兩者公式都有stall cycle per access的項,欲求AMAT per instruction
設計virtual memory的2大動機
取得資料的過程
memory management bits有3種
virtual memory與cache system的觀念相同,只是因為歷史脈絡,造就有不同的術語。在設計virtual memory時,與設計cache system不同點在於 - 避免存取硬碟,因為main memory跟hard disk速度差數百萬倍!
page table容量大小計算與cache不同點在於page table的entry需要round up to word的冪次方。原因是cache是SRAM可以量身訂造,但是page table是放在main memory,需要遵照memory的alignment規則,且計算memory位址時要用向左移替代乘法運算。
page table存放在main memory,代表程式每一次memory access都要花2次存取的時間,第一次讀取memory的page table轉出physical address,第二次拿著physical address存取memory獲取資料或是指令,如此速度太慢,由於page table的存取也具有locality of reference,為了加速virtual address到pysical address之間的轉換速度,因此設計page table的cache,稱為TLB (translation-lookaside buffer)。
方式如同cache,也可以使用軟體實作。由於page很大,一次可以用很久,所以TLB需要的entry很少,故TLB一般使用fully associative cache,以降低miss rate,反正要比的tag也不多,此外選用random的置換方式。
cf. L1的hit time會決定CPU的clock cycle time,所以使用direct-mapped cache,只比一個tag而已,電路延遲時間較短,hit time最小。
欲計算TLB的容量大小,可以由physical address求TLB的physical page number,由virtual address求TLB的tag。
把TLB整合到pipeline,由於要讀取指令與要讀取資料的記憶體位址都是virtual address,都需要經過TLB轉換為physical address,接下來才能去讀取cache;分為instruction的TLB接在instruction memory前面;data的TLB接在data memory前面,但由於MEM stage時間過長,所以有時候會把data的TLB放在EX stage。
最後再把cache整合進TLB與virtual memory系統,如下面3張圖所示
CPU丟出virtual (byte) address,根據page size的大小切成virtual page number與page offset,由於TLB在此使用fully associative cache,index值為0,故virtual page number等於tag值,拿著這個tag值跟TLB中的所有tag值做比對,若沒有相符項(TLB miss),再帶virtual page number找對應編號的page table,檢查valid bit是否為1,若valid bit = 0,則會觸發OS的exception - page fault;若valid bit = 1,則會把在main memory要存取的page搬至TLB。
順利從TLB存取到physical page number後,會將physical page number與page offset合成為physical address,physical address即為前面探討cache system的memory byte address,由ch6 set associative cache觀念會切2刀
TLB的資料為page table的子集合,因此TLB hit代表page table一定也會hit;cache的資料為memory的子集合,因此cache hit代表資料存在memory中;page table與在memory中的資料又存在一對一的映射關係,因此page table hit與資料存在memory中是等價關係。欲判斷TLB / page table / cache hit or miss中各種可能與不可能的情況,若皆符合3者條件則有可能發生,反之只要有一個條件不滿足就不可能發生。
前面都是假設讀取cache之前都需要將virtual address經過TLB轉換為physical address,也就是physically addressed cache的機制,存取一次記憶體,需要考慮到TLB和cache的時間,速度太慢。
因此改良版是直接使用virtual address作為cache的index與tag值,稱為virtually addressed cache,在cach hit的情況下,不需要額外存取到TLB;而在cache miss的情況下,才需要將virtual address經過TLB轉換為physical address,從main memory抓需要的block到cache。
這邊需要另外介紹aliasing - compiler在compile時,會把"兩個程式共用資料"的訊息告訴OS,所以當OS把兩程式的virtual space複製到physical memory時,只會複製一份"兩個程式共用資料"。若使用virtually addressed cache發生aliasing情況,就會造成aliasing問題。
解法是改使用virtually indexed, physically tagged cache。CPU發出virtual address切出index,到cache中找到對應的set;同一時間透過TLB轉出physical address,切出tag,與cache中所找到的set中所有tag做比對,若cache hit,則順利找到資料回傳給CPU;此外,需要配合軟體機制,兩個block不能同時有效,需要一個無效、另一個有效。由於cache中不同資料的index雖然不同,但tag都是一致的,因此不會產生aliasing問題。
ch6 虛擬記憶體(virtual memory)提及設計virtual memory有2大動機,其中之一就是virtual memory讓單一memory可以由多個process共享,為了達成這個目的,硬體需要支援以下3個能力
根據發生cache miss的原因做分類標準,分為3種 (3C)
至於set associative置換策略的選擇,選擇複雜置換策略好處是減少競爭程度,以降低conflict misses,但需要maintain的資訊變多,要判斷的資訊變多,造成決策時間變長。
降低compulsory miss的另一個方法是使用sequential prefetching。prefetching為發生cache miss後,一次抓2個block的資料到cache上,分為sequential (stride = 1)和stride。
sequential prefetching可以很直接理解就是利用程式存取的spatial locality特性,一次同時抓取下一個block;而stride存在原因 - 當陣列資料是2維,但存入memory變成一維資料並使用row-major順序存資料,當指令需要使用column major存取資料時,如果設定prefetching的stride為3,就可以抓到下一筆要執行的資料。
3C實戰題分為3步
compulsory miss跟程式占用幾個memory block有關,與使用哪種架構的cache無關。
前面講的cache使用都是blocking cache,在cache還沒把資料搬完前,CPU會停下來;但在superscaler (= dynamic multiple issue)中,發生cache miss時,non-blocking cache允許CPU繼續執行存取cache的指令,利用後面的hit time來蓋過前面的miss penalty,稱為hit under miss;利用後面的miss penalty來蓋過前面的miss penalty,也就是允許多個未完成(outstanding) cache miss,稱為miss under miss。
virtual machine的技術讓一部computer能run多個OS,有以下3個好處
使用的是virtualization技術 - VMM (virtual machine monitor) / hypervisor會把"虛擬"的硬體mapping到"真"的硬體
VMM處在supervisor mode,而guest OS處在user mode,以確保guest OS不會直接改變真實硬體配置。
multiple level page table與允許分頁表的分頁都是page table一部分放hard disk;一部分放main memory,只是機制上的不同。
對於CPU與memory著重於效能與成本,而I/O設備則是強調可靠度(dependability)與成本,可用3種特性來區別不同I/O設備
評估I/O效能,一般使用I/O bandwidth,有以下2種測量方式
disk藉由表面覆蓋磁性物質的plate與利用一個可移動的讀寫頭(read /write head)來存取資料,每個plate區分成多個同心圓,稱為track,而track又可分成sector,sector即為進出硬碟的實體基本單位。
disk access time計算分為以下5類相加
讀寫頭會停在最後讀完資料的地方,因此若是寫回一樣的地方,讀寫頭就在原本的track上,就不需要seek time。
flash memory設計目的是用來取代hard disk的,但是由於磨損(wear out)之前能存取的次數太少,因此沒辦法直接取代hard disk,具有以下特性
dependability是一個系統所能提供服務的品質,由於太過抽象,所以靠reliability與availability來"量化"一個系統的dependability。
其中MTTF = mean time to failure,平均發生失敗時間;MTTR = mean time to repair,平均修復時間;MTBF = mean time between failure,平均失敗間隔,MTBF = MTTF + MTTR。增加MTTF有3個方法 - fault tolerance (系統錯誤依然可以繼續執行);fault avoidance (投票制,多個相同元件執行,選多數結果作為系統輸出);fault forecasting (估計常常發生錯誤的位置,當發生錯誤時,直接移除)。
RAID (redundant arrays of inexpensive disks)主要想法是使用多個容量較少、較便宜的硬碟來獲得跟容量大、較昂貴的硬碟,有以下2種技術
雖然在small read中throughput變差,但是latency會是所有RAID最好。
當遇到large read / write(資料量遠超過一個block)時,RAID 4, RAID 5, RAID 6優勢不再,throughput會跟RAID 3一樣差。
要下一個命令給I/O設備,處理器就必須要能夠定址I/O設備,由於每個I/O設備都會有data, status等暫存器,所以我們就可以對該暫存器編號,以定址I/O設備,分為以下2種
以上3種I/O設備與處理器的傳輸資料的排序
多處理器定義為至少含有2個處理器以上的computer,有以下2個好處
處理器又可稱為核心(core),單一處理器稱為microprocessor,多個處理器放在同一個晶片稱為CMPs(chip multicore microprocessor)。非平行程式與平行程式都可以在一個處理器或是多個處理器運行。
撰寫平行程式會遇到4個難點
而且並不是所有程式都可以被平行分解(concurrent program),多處理器的效能會受限於無法平行分解程式(sequential progam)的比例。
strong scaling由於problem size是固定的,處理器個數也是相同,也就是
要讓多處理器之間達到資料交換的目的有兩種做法
lw, sw
記憶體中的共用變數,以達到資料交換的目的,又可細分為2種
但SMP會遇到2個問題
而在MPP不會遇到這2個問題,因此SMP為了要解決這2個問題,在硬體實作上會比MPP複雜許多。
send(processor ID, message), receive()
。MPP從程式碼就可以知道不同處理器間有交換資料,是"外顯"的;但是SMP從程式碼看不出來不同處理器間有交換資料,是"內隱"的;此外MPP的硬體實作較SMP簡單。在shared memory mutiprocesser系統中,一個memory的block可能同時存在在多個cache之中,如果任由處理器寫入各自的cache,就會造成橫向之間cache資料的不一致,稱為cache coherence problem。
對比ch6 cache的機制講到cache system中,cache的資料與memory的資料不一致,稱為memory inconsistence。
UMA的cache coherence problem可以使用snooping protocol解決,每個cache都會窺探(snoop)bus上資料,以決定自己是否也要獲得一份相同資料的copy。使用2種方式解決
對比ch6 cache的機制講到cache system中,解決memory inconsistence問題時,在write hit情況下使用write-through或是write-back;在write miss情況下使用write allocate或是write around( = no write allocate)。
在商用UMA中解決cache coherence問題,使用write-invalidate;解決memory inconsistence問題使用write-back,這是因為這兩種方式,對bus的頻寬要求比較小,避免造成bus頻寬上的瓶頸。
hardware multithreading允許多個thread交錯使用在單一個處理器上執行,來增加單一處理器功能單元的使用率。根據切換thread時機分為2種
由Flynn提出,根據指令流(instruction streams)與資料流(data streans)分為
vector processor跟SIMD一樣,運用到data-level parallelism適合做向量運算,vector processor將ALU pipeline與大量暫存器存輸入與輸出結果,效能與SIMD差不多,但成本卻可以大大降低。
network topology討論處理器之間如何連結,跟據線路切換開關次數分為single stage與mutliple stage,single stage在此介紹5種接法 - fully, linear, ring, 2-D mesh(一堆ring所組成), cube,mutliple stage在此介紹2種接法 - crossbar, omega network。
我們使用4個參數(2個用於評估效能 + 2個用於評估容錯)衡量不同network topology的優劣程度
下圖對3種不同single stage network topology,計算4個參數