第五章 深入研究指令集架構
第五章 教學目的 了解指令集架構設計的相關問題. 熟悉記憶體定址模式. 了解指令層級的管線化概念, 以及它對執行效能的影響.
我們深入來介紹不同的指令格式, 運算元類型, 以及記憶體存取方法. 我們將會看到機器組織和指令格式間的相互關係. 5.1 簡介 本章的概念是架構在第四章之上的. 我們深入來介紹不同的指令格式, 運算元類型, 以及記憶體存取方法. 我們將會看到機器組織和指令格式間的相互關係. 這讓我們能更深入通盤了解電腦架構
5.2 指令格式 指令是由下列各項來進行區別的: 每個指令的位元個數. Stack-based 或 register-based. 5.2 指令格式 指令是由下列各項來進行區別的: 每個指令的位元個數. Stack-based 或 register-based. 每個指令所能表示的運算元個數. 運算元的位置. 運算的類型. 運算元的類型和大小.
5.2 指令格式 指令集架構是依據下列來衡量好壞: 程式所佔的主記憶體空間. 指令的複雜度. 指令的長度 (in bits). 指令集中有多少不同的指令.
5.2 指令格式 設計指令集時要考慮下列: 指令長度. 運算元數目. 可定址的暫存器數目. 記憶體組織. 定址模式. 短, 長, 或是可變動長度. 運算元數目. 可定址的暫存器數目. 記憶體組織. byte- 或 word 定址. 定址模式. 提供那幾種: 直接, 間接, 或是索引法.
5.2 指令格式 Byte 順序, 或是 endianness, 是架構上的另一種考量. 如果我們有一個 two-byte的整數, 那這個整數的儲存可能是低權重byte接著高權重byte, 或是相反. 在 little endian 機器中, 低權重byte後是高權重byte. Big endian 機器則是將高權重襬在低位址.
5.2 指令格式 舉例來說, 假設我們有一個十六進制數12345678. 那 big endian 和 small endian 的排列如下.
5.2 指令格式 Little endian: Big endian: 較為直覺. 數目的正負號可以經由最低位元查出. 字串和整數的儲存順序相同. Little endian: 要將值放在非字組邊界比較容易. 從16-bit 整數位址轉換到32-bit 整數位址時不需要額外的算數運算.
5.2 指令格式 下一個要考慮的架構設計問題是CPU如何儲存資料. 我們有三種選擇: 1. 堆疊架構 2. 累加器架構 3. 通用暫存器架構. 在選擇時, 會考慮到硬體設計的複雜度(及成本)及執行速度和使用的簡單性.
5.2 指令格式 在堆疊架構中, 指令和運算元都是由堆疊取出的. 在累加器架構中, 二元運算中的其中一個運算元是在累加器中. 堆疊無法隨機存取. 在累加器架構中, 二元運算中的其中一個運算元是在累加器中. 一個是在記憶體, 提高了匯流排的流量. 在通用暫存器架構中 (GPR), 可以用暫存器代替記憶體. 比累加器架構快. 對編譯器製作上來說比較有效率. 會造成指令比較長.
5.2 指令格式 現在很多系統都是 GPR系統. 有三種類型: 運算元的數目和可用的暫存器數目對指令的長度有直接的影響. 記憶體-記憶體, 可能有二或三個運算元在記憶體中. 暫存器-記憶體, 至少有一個運算元在暫存器中. 載入-儲存, 沒有運算元是在記憶體的. 運算元的數目和可用的暫存器數目對指令的長度有直接的影響.
5.2 指令格式 堆疊機器使用1或0個運算元指令. LOAD 和 STORE 指令需要一個運算元的記憶體位址 其它的指令內定從堆疊取出運算元. PUSH 和 POP 只會存取堆疊的最上端元素. 二元指令 (e.g., ADD, MULT) 使用堆疊最上面的二個項目進行運算.
5.2 指令格式 使用堆疊駕構時, 我們需要以不同的角度來思考算術運算的表示式. 我們很熟悉用中置表示法來寫式子, 像是: Z = X + Y. 堆疊算術則需要後後置式表示法: Z = XY+. 又名為 reverse Polish notation, (有點) 為了紀念發明者, Jan Lukasiewicz (1878 - 1956).
5.2 指令格式 後置式的主要優點是不需要用括號. 例如, 中置表示式, Z = (X Y) + (W U), 變成: 這樣的後置表示.
5.2 指令格式 在一個堆疊的 ISA 中, 後置表示, Z = X Y W U + 看起來就像是: PUSH X PUSH Y MULT PUSH W PUSH U ADD STORE Z 注意: 運算完的結果內定是儲存在堆疊的上方!
5.2 指令格式 在 1個-位址的 ISA, 像 MARIE, 中置表示式, Z = X Y + W U 看起來會是: LOAD X MULT Y STORE TEMP LOAD W MULT U ADD TEMP STORE Z
5.2 指令格式 在二個位址的 ISA, (e.g.,Intel, Motorola), 中置表示式是, Z = X Y + W U 看起來會是: LOAD R1,X MULT R1,Y LOAD R2,W MULT R2,U ADD R1,R2 STORE Z,R1 注意: 一個位址的 ISAs 通常有一個運算元要在暫存器中.
5.2 指令格式 在二個位址的 ISA, (e.g.,mainframes),中置表示式是, Z = X Y + W U 看起來會是: MULT R1,X,Y MULT R2,W,U ADD Z,R1,R2 這個程式執行起來會比stack-based ISA (程式較長)花更長的時間嗎??
5.2 指令格式 我們看到了ISA運算元個數是如何影響指令的長度. 在任何的指令集中, 並不是所有的指令都要相同的運算元個數. 像一些不用運算元的指令, HALT, 如果是用固定長度的指令就會浪費空間. 使用擴充碼可以利用到這些浪費的空間.
5.2 指令格式 某系統有16個暫存器和 4K 的記憶體. 我們需要 4 bits 來選擇其中一個暫存器. 我們需要 10 bits 來當記憶體位址. 如果該系統要有 16-bit 的指令, 我們會有二種選擇:
5.2 指令格式 如果我們讓opcode是可變動的, 那我們就可以有很多的指令集可用: 這些指令集少了什麼?
5.3 指令類型 指令可以分成幾個你熟知的大類: 資料搬移. 算術. 布林. Bit 處理. I/O. 控制權轉移. 特殊用途. 能各別舉例嗎?
5.4 定址 定址模式用來說明運算元是放在那個位置. 它們可以指出是常數, 暫存器, 或是一個記憶體位置. 運算元的實際位址就是有效位址. 可靠的定址模式讓我們可以動態的求出運算元的記憶體位址.
5.4 定址 立即定址(Immediate addressing)表示資料在指令中. 直接定址(Direct addressing) 表示資料的位址在指令中. 暫存器定址(Register addressing) 表示資料在暫存器中. 間接定址(Indirect addressing)將指向資料位址的位址放入指令中. 暫存器間接定址(Register indirect addressing )使用暫存器來存儲存資料的位址.
索引定址(Indexed addressing) 將偏移值存在暫存器中, 將此偏移值加上運算元的值後求得有效位址. 5.4 定址 索引定址(Indexed addressing) 將偏移值存在暫存器中, 將此偏移值加上運算元的值後求得有效位址. 基底定址(Based addressing) 跟索引定址類似, 差別在於用基底暫存器來代替索引暫存器. 這二種的差別在於索引暫存器存的是相對於指令中的偏移值, 而基底暫存器則是存的是基值, 偏移量則是在指令中.
5.4 定址 在堆疊定址(stack addressing)中, 運算元內定在堆疊最上方. 以上的定址方式有很多變形: 間接索引(Indirect indexed). 基底/偏移(Base/offset). 自身-相對(Self-relative) 自動遞增-遞減(Auto increment – decrement). 我們不打算全部介紹它們. 我們介紹一些常見定址模式的範例.
5.4 定址 不同的定址模式下, 累加器會載入什麼值?
5.4 定址 不同的定址模式下, 累加器所載入的值.
5.5 指令層級之管線化 某些 CPU 會將fetch-decode-execute 週期切成更小的步階. 這些更小的步階通常能平行執行來增進產能. 這種平行執行的方式就是指令層級的平行化(instruction-level pipelining). 這個名稱有時會簡寫成ILP. 下一張投影片會介紹指令層級平行化的範例.
5.5 指令層級之管線化 假設一個 fetch-decode-execute 週期可以分成下列更小的步階 : 假設我們有一個 6-階的管線. S1 擷取指令, S2 解碼, S3 決定運算元的位址, S4 擷取運算元, S5 執行指令, 最後 S6 儲存結果. 1. Fetch instruction. 4. Fetch operands. 2. Decode opcode. 5. Execute instruction. 3. Calculate effective 6. Store result. address of operands.
5.5 指令層級之管線化 每一個時脈週期, 就會完成一個步階, 這些步階是重疊的. S1. Fetch instruction. S4. Fetch operands. S2. Decode opcode. S5. Execute. S3. Calculate effective S6. Store result. address of operands.
5.5 指令層級之管線化 管線化的理論增速可以由下列方式求得: 令 tp 是每個步階所需的時間. 每個指令表示管線中的一件工作, T. 在一個k-階的管線中, 第一件工作 (指令) 需要 k tp 時間來完成. 其餘的 (n - 1) 個工作會在管線的每個週期完成一個. 所以完成這n-1個工作要 (n - 1)tp. 因此, 使用這k-階管線完成這 n 個工作需要 : (k tp) + (n - 1)tp = (k + n - 1)tp.
假設要完成n個工作, 如果我們將不用管線化的執行時間除以使用管線化的時間, 我們得到: 5.5 指令層級之管線化 假設要完成n個工作, 如果我們將不用管線化的執行時間除以使用管線化的時間, 我們得到: 如果我們將 n 趨近於無限大, (k + n - 1) 就會趨近於 n, 那理論增速就為:
再來, 我們假設無時無刻都是塞滿工作的. 這是不可能的. 管線可能發生危障造成衝突和暫停. 5.5 指令層級之管線化 這個方程式忽略了很多事. 首先, 我們假設架構可以同時擷取指令和資料 再來, 我們假設無時無刻都是塞滿工作的. 這是不可能的. 管線可能發生危障造成衝突和暫停.
5.5 指令層級之管線化 指令管線可能會暫停, 或者因為下列的原因而被沖刷(be flushed) : 資源衝突 (Resource conflicts). 資料相依 (Data dependencies). 條件分支 (Conditional branching). 我們可以在軟體層級或硬體層級來偵測這些危障, 但是並無法完全避免它們的發生.
5.6 ISA的實例 我們簡短的回到上一章最後介紹的Intel和MIPS 架構. Intel在其 Pentium 系列產品開始引入管線化. 第一顆 Pentium 有二個 5-階的管線. 後來每一個新出的Pentium 處理器都比上一代有更多的步階, 到了 Pentium IV 更高達了 24-階. 而 Itanium (IA-64) 確只有10-階的管線.
5.6 ISA的實例 Intel 處理器支援各式各樣的定址模式. 原始的 8086 有 17 種記憶體定址方式, 大多數是本章介紹的變形. 由於要考慮相容的緣固 Pentium 也支援這 17種記憶體定址模式. Itanium採用 RISC核心, 只提供: 可選擇性遞增之暫存器間接定址.
5.6 ISA的實例 MIPS 是 Microprocessor Without Interlocked Pipeline Stages 的簡寫. 這個架構是 little endian 而且是 word-addressable, 有三個位址固定長度的指令. 如同 Intel, MIPS 的管線大小也是一直在增加: R2000 和 R3000 為五-階管線; R4000 和 R4400則為8-階管線.
R10000 有三組管線: 一組五階的整數指令管線,一組七階的浮點數指令管線, 一組六階的LOAD/STORE 指令管線. 5.6 ISA的實例 R10000 有三組管線: 一組五階的整數指令管線,一組七階的浮點數指令管線, 一組六階的LOAD/STORE 指令管線. 在所有的 MIPS ISA, 只有 LOAD 和 STORE 指令可以存取記憶體. ISA 只用基底定址模式. 組譯器讓程式設計人員可以使用不同的定址模式來寫程式.
Java 程式語言是一種直譯式的語言, 它是在一個名為 Java Virtual Machine (JVM)上的軟體執行 5.6 ISA的實例 Java 程式語言是一種直譯式的語言, 它是在一個名為 Java Virtual Machine (JVM)上的軟體執行 JVM 是一最原始的語言所撰寫的, 包含了 MIPS 和 Intel. 就像實際的機器一樣, JVM 也有自己的 ISA, 稱為 bytecode. 這個 ISA 和任何有JVM在上面執行的機器相容. 下一張投影片會看到它們是如何結合在一起的.
5.6 ISA的實例
5.6 ISA的實例 Java bytecode 是一種 stack-based 語言. 大多數指令沒有位址欄位. JVM 有四個暫存器, 用來存取主記憶體的五個區域. 所有參照記憶體的位址都是相對於這些暫存器的偏移量. Java 沒有指標也沒有絕對位址 Java 的設計原則是要在多台機器間共用, 並不是以效能為考量!
結論 ISA 會因為指令長度, 指令的運算元數目, 運算元的位置, 運算元的類型和大小而有所不同. Endianness 是另一個架構上的設計考量點. CPU 可以基於以下方式儲存資料 1. 堆疊架構 2. 累加器架構 3. 通用暫存器架構.
結論 指令可以是固定長度或是變動長度 為了讓固定長度指令更有彈性, 我們可以擴充 opcodes. ISA的定址模式是另一個重要的因素 is also. 我們介紹了: 立即 Immediate – 直接 Direct 暫存器 Register – 暫存器間接 Register Indirect 間接 Indirect – 索引Indexed 基底 Based – 堆疊Stack
結論 比起沒有管線化的架構一個 k-階的管線理論上可以增速 k 倍. 管線危障, 像是資源衝突, 條件分支等問題都會影響增速. Intel, MIPS,和JVM 架構為本章提供了很好的解釋範例.
End of Chapter 5