# 計算機組織與結構 L1 指令 : 計算機的語言 ## 基本概念 - **計算機的 Command 等於 :** - **Statements :** in high level language - **Instructions :** in low level language - **Instruction & Instruction Set & Instruction Set Architecture:** - **Instruction :** CPU 可執行的最基本運算 - **Intruction Set :** 計算機可執行的指令集合,相同 Instruction Set 建構的 CPUs 是 compatible - **Instruction Set Architecture :** 又叫 ISA。ISA = 基本硬體架構 + 指令集 - **RISC & CISC :** | RISC (Reduced Instruction Set Computer) | CISC(Complicated Instruction Set Computer) | | -------- | -------- | | 以 CPU 執行效率為主要考量 | 以減輕程式設計者的負擔為主要考量 | | 提供簡單的基本指令以求較快</br>的執行速度 | 提供較佳程式設計環境,除了基</br>本指令,還有較強的複雜指令 | | 指令長短相同 (擷取指令快) | 指令長短不同 | | 只有少數幾種 addressing mode | 支援多種 addressing mode | | 只有少數幾種 instruction format | 支援多種 instruction format | | **算術指令的運算元只能來自暫存器** | **算術指令的運算元能來自記憶體** | ## 內儲式程式概念 - **Stored-program (Von Neuman Machine) 建構原則:** - 指令以數字的形式表示 - 要被執行的程式先放置在記憶體中,像數字一樣被存取 </br> > buttleneck : > an instruction fetch and a data operation cannot occur at the same time for they share the same bus ## 指令集架構 - **ISA 分成 :** - **Instruction Set** - **Hardware Information** - **Memory** - **Register** - **Instruction Format :** - R-Type - I-Type - J-Type - **Addressing Mode :** - Immediate Addressing - Register Addressing - Base or Displacement Addressing - PC-relative Addressing - Pseudodirect Addressing - **Computer 的五個 components :** - **Control Unit :** 解碼指令,產生控制訊號線 - **Datapath :** 真正執行指令,處理資料的電路 - **Memory** - **Input Device** - **Output Device** </br> > **Processor(CPU) = Control Unit + Datapath** - **Byte-Order or Endianness** - **Big Endian :** MSB 在最低記憶體位置 - **Little Endian :** LSB 在最低記憶體位置 ![image](https://hackmd.io/_uploads/BkDrLHU5p.png =40%x)![image](https://hackmd.io/_uploads/ryar_SUq6.png =60%x) - **Register & Memory :** - **Register :** - Register 在 Processor 裡的 Datapath 中 ![image](https://hackmd.io/_uploads/r11qeS89a.png) - 又分為 - **一般目的暫存器 :** 32 個 32 bits ![image](https://hackmd.io/_uploads/ryt0orUqa.png =80%x) - **特殊目地暫存器 :** 如 Program Counter (PC) 32 bits、Hi (存乘積高位元或除法餘數) 32 bits、Lo (存乘積低位元或除法商數) 32 bits - **Spilling Register :** Compiler 將不常用的變數存在 memory 的程序 - **Memory :** - byte addressing 或 word addressing ![image](https://hackmd.io/_uploads/BJYpWSI96.png =40%x)![image](https://hackmd.io/_uploads/S1lkGBI5p.png =40%x) - 使用 $n$ bits 表示記憶體位址,代表記憶體最大容量為 $2^n$ bytes - **Alignment :** 所有 word 都要按照 byte address 為 4 的倍數放 ![image](https://hackmd.io/_uploads/BkiHEH85T.png =40%x) - **Register & Memory :** - 一個程式在記憶體的 address space ![image](https://hackmd.io/_uploads/rks3hSLcT.png =50%x) ![image](https://hackmd.io/_uploads/H1skAvIcT.png) - Memory 的一般目的暫存器 ![image](https://hackmd.io/_uploads/S1gdJ889T.png =70%x) ![image](https://hackmd.io/_uploads/rJSWRw8qp.png =70%x) ## 不同的指令集 - 4 種不同型態的 **Instruction Set** | Accumulator | Memory - Memory | Stack | Load - Store | | -------- | -------- | -------- | -------| | 所有運算都在一個</br>暫存器和記憶體之間 | 運算指令有三個運</br>算元都在記憶體裡 | 所有運算都在</br>stack 頂端 | 所有運算都在</br>暫存器中 | | 1 open | 3 open | 0 open | 3 open | | CISC | CISC | CISC | RISC 圖示 - **Accumulator** ``` load AddressB add AddressC store AddressA ``` ![image](https://hackmd.io/_uploads/Sym2Qo896.png =70%x) - **Memory - Memory** ``` add AddressA, AddressB, AddressC ``` ![image](https://hackmd.io/_uploads/HJcpmi8c6.png =70%x) - **Stack** ``` push AddressB push AddressC add pop AddressA ``` ![image](https://hackmd.io/_uploads/rkw07oL5p.png =70%x) - **Load - Store** ``` load $1, AddressB load $2, AddressC add $3, $1, $2 store $3, AddressA ``` ![image](https://hackmd.io/_uploads/ry4yEjI5a.png =70%x) ## 程式之轉譯與執行 ![image](https://hackmd.io/_uploads/H1SB5DL5p.png =70%x) 經過 - **Compiler** - **Assembler** 包含 - **Header :** 描述目的暫存器的內容,包含大小及位置資訊 - **Text Segment** - **Static Data Segment** - **Relocation Information :** 當程式載入記憶體時,指令和資料的相對位址 - **Symbol Table :** 儲存參考資料等等未定義標籤 - **Debug Information :** 連結原始程式碼 - **Linker :** 連結多個目的模組及資料庫解決位址的參考問題 - **步驟一 :** 將程式碼和資料模組放置在記憶體中 - **步驟二 :** 決定資料與 instruction label 的位址 - **步驟三 :** 決定內部與外部指令參考 - **Loader :** 將 machine code 放置在適當的記憶體位址以供 processor (CPU) 處理執行 - **步驟一 :** 讀 executable file 的 header 決定 text 和 data segment 的大小 - **步驟二 :** 產生足夠的記憶體空間 - **步驟三 :** copy executable file 的 instruction 和 data 至記憶體 - **步驟四 :** copy parameters 到 stack 中 - **步驟五 :** initialize registers,並將 \$sp 指向 stack 的第一個可用空間 - **步驟六 :** 將 PC 指向 start-up routine,啟動 start-up routine,呼叫要被執行的程式,結束程式後,再用 exit 這個 system call 終結程式 ![image](https://hackmd.io/_uploads/ry1q7OLqa.png =70%x) > 有些系統以 linking loader (linker + loader) 執行最後兩個步驟 > Java > ![image](https://hackmd.io/_uploads/S1QtB_Iqa.png =70%x) > > 因為 JVM 太慢,所以常常利用 JIT 加速 > - JVM 是直譯,翻譯出來的 bytecode 跑起來比 machine code 慢很多 > - JIT 把常見的 bytecode 轉乘 machine code 存在 cache 中 ## 一般指令類別 **MIPS 指令類別**有 : - **Data Movement** (load and store) - **Arithmetic** (add, sub, mul ...) - **Logical** (sra, sll, srl) - **Flow Control** (branch and jump) - **Procedure Call** (jal and return) ![image](https://hackmd.io/_uploads/rkK3P_L9T.png =70%x) ## 算術指令 - **Addition** - **add rs, rt, rd** - add \$1, \$2, \$3 - **Subtraction** - **sub rs, rt, rd** - sub \$1, \$2, \$3 ## 資料轉移指令 採用 base-offset 表示方式 - **Load** - **lw** (load word) - **lb** (load byte) : 從記憶體載入 1 byte,存到 register 的最右邊的 byte,左邊看目前最左位元是 1 或 0,填 1 或 0 - **lbu** (load byte unsign) : 從記憶體載入 1 byte,存到 register 的最右邊的 byte,左邊全填 0 - **lw rt, immediate*4(rs)** - lw \$t0, 32(\$s0) → $A[8]$ - **Store** - **sw** (store word) - **sb** (store byte) - ~~sbu (store byte unsign)~~ : 因為沒有擴充性問題存在 - **sw rt, immediate*4(rs)** - sw \$t0, 48(\$s0) → $A[12]$ ## 流程控制指令 - **Branch** - occurs when **if statement** or **loops** - 可跳 $2^{16} * 4B$ ($2^{16} word$) - 利用 **PC-relative addressing (PC 相對定址法)** - **beq rs, rt, address*4** ![image](https://hackmd.io/_uploads/r1q7x58q6.png =50%x) - 用 MIPS 現有 Instruction 實現 Pseudo Instruction | Pseudo Instruction | MIPS Instruction | | -------- | -------- | | blt \$s1, \$s2, L | slt \$t0, \$s1, \$s2 </br> bne \$zero, \$t0, L | | bgt \$s1, \$s2, L | slt \$t0, \$s2, \$s1 </br> bne \$zero, \$t0, L | | ble \$s1, \$s2, L | slt \$t0, \$s2, \$s1 </br> beq \$zero, \$t0, L | | bge \$s1, \$s2, L | slt \$t0, \$s1, \$s2 </br> beq \$zero, \$t0, L | </br> > **Basic Block :** > - 編譯程式要最佳化的最基本單位 (先 Basic Block 內最佳化,再 Basic Block 間最佳化) > - 一連串 > **沒有分支**(如果有只能出現在區塊最後一個指令處), > **沒有分支目的**(如果有只能出現在區塊第一個指令處) > 的指令 - **Jump** - occurs when **procedure calls and returns** or **case/switch statement** - 可跳 **256MB** - 最左邊 4 bits : MIPS 將 4GB 記憶體切為 16 個大小相等的 256MB 的區塊,Jump只能在同一區塊,所以最左邊 4 bits 是相同的 - 最右邊 2 bits : 因為要符合 Alignment,所以最右邊兩個 bits 都是 0 ## 邏輯運算 ![image](https://hackmd.io/_uploads/SyBYzYL9p.png =60%x) - **Shift** - **sll** (shift left logically) - **srl** (shift right logically) - **sra** (shift right arithematically) - **And** - 一種 **mask**,將感興趣位元保留,其餘清為零 - **Or** - 一種 **set**,將感興趣位元設為1,其餘保持不變 - **Not** - MIPS 用 **nor** 代替 **not** 運算 - A NOR 0 → NOT A ## 常數 - **Immediate Instruction** - **lui** (load upper immediate) : 將 16 位元的常數載入到暫存器左半部 16 位元,再將右半部 16 位元清為 0,配合 ori 可將 32 位元常數存入暫存器 ## MIPS 指令格式 **Instruction Format : 指令不同欄位的排列方式** | 參與運算的元素 | 組合語言指令 | 機器語言格式指令 | | -------- | -------- | -------- | | 3 registers | add, sub, and, srl, sll, ... | R-Type | | 2 registers & 1 constant/address | lw, sw, bne, addi, ... | I-Type | | 26 bit address | j, jal, ... | J-Type | ![image](https://hackmd.io/_uploads/rJhoDt85T.png =60%x) ![image](https://hackmd.io/_uploads/rJBGFYU9T.png =70%x) ![image](https://hackmd.io/_uploads/SybXYtIq6.png =70%x) ![image](https://hackmd.io/_uploads/HyAaYtUqT.png =70%x) - 常見 OP Code 和 Funct Code | Operation | OPCode | | -------- | -------- | | lw | 35 | | sw | 43 | | bne | 5 | | beq | 4 | | j | 2 | | Operation | funct code | | -------- | -------- | | add | 32 | | sll | 0 | ## 程序呼叫 ![image](https://hackmd.io/_uploads/ryhagc8qT.png =50%x) - **程序呼叫常用暫存器** - **\$a0 - \$a3 :** arguments - **\$v0 - \$v1 :** return value - **\$ra :** return address - 當讀到 jal (jump and link) 時,會先跳到 callee 程序起始位址,同時將 caller 下一個指令的位址存在 \$ra - 當 callee 程序執行結束,會有一行 jr 供程序返回 caller 時使用 - **常考 MIPS Assembly** - Factorial C Code ```cpp= int fact (int n) { if (n < 1) return 1; else return n * fact(n - 1); } ``` MIPS Assembly Code ```MIPS= fact : addi $sp, $sp, -8 sw $ra, 4($sp) sw $a0, 4($sp) slti $t0, $a0, 1 beq $t0, $zero, ELSE addi $v0, $zero, 1 addi $sp, $sp, 8 jr $ra ELSE : addi $a0, $a0, -1 jal fact lw $a0, 0($sp) lw $ra, 4($sp) addi $sp, $sp, 8 mul $v0, $a0, $v0 jr $ra ``` - Sum C Code ```cpp= int sum(int n) { if (n == 0) return 0; else return n + sum(n - 1); } ``` MIPS Assembly Code ```MIPS= sum : addi $sp, $sp, 8 sw $ra, 4($sp) sw $a0, 0($sp) bne $a0, $zero, ELSE addi $v0, $zero, 0 addi $sp, $sp, 8 jr $ra ELSE : addi $a0, $a0, -1 jal sum lw $a0, 0($sp) lw $a0, 4($sp) addi $sp, $sp, 8 add $v0, $a0, $v0 jr $ra ``` ## MIPS 定址模式 **Addressing Mode : 指令取得運算元或計算目的位址的方法** - **Addressing Mode 包含:** - **Immediate Addressing :** 運算元包含 16 位元的常數 ![image](https://hackmd.io/_uploads/S1Kaiq8q6.png =40%x) - **Register Addressing :** 運算元在暫存器中 ![image](https://hackmd.io/_uploads/ryQb3cLqa.png =70%x) - **Base or Displacement Addressing :** 運算元在記憶體中 ![image](https://hackmd.io/_uploads/SkUr29L56.png =70%x) - **PC-relative Addressing :** 目的位址是 PC 加上指令中的常數 ![image](https://hackmd.io/_uploads/rkyw2985T.png =70%x) - **Pseudodirect Addressing :** 跳躍目的位址為址中的 26 位元與 PC 的結合 ![image](https://hackmd.io/_uploads/S1WFh9856.png =70%x) - **\$jr** 非 Pseudodirect Addressing,而是 **Register Addressing** (因為 \$ra 是一般目的暫存器) ## 指令集設計原則 - **五大指令集設計原則** - **Simplicity favors regularity** - Instruction size same - Arithematic instruction always needs 3 registers - Different instruction format has same register fields - **Smaller is faster** - Smaller registers, faster speed - **Make the common case fast** - PC-relative addressing is used in branches - Immediate addressing is used to get the constant - **Good design demands good compromises** - provides 3 type of instruction format instead of 1 → compromises between larger memory address and keep all the instruction size the same