# 計算機組織 - 劉一宇 > Key word: CPU time, Amdahl's Law, R/I/J Format, Design Principle, Booth's Algorithm ## Basic Inforamtion - 期末 Project - RISC-V 相關 ## Intro ### Computer Architecture Software 層用來定義高抽象等級的問題並實踐 solution Hardware 層用來實踐主要的計算功能 - Software Layer - Application - Operating System - Compiler - Assembler - Hardware Layer - Processor / Memory / I/O System - Datapath & Control - Digital Design - Cicuit Design - Transistors Software 和 Hardware 之間的溝通使用特定的 Instruction Set Architecture, 例如 x86 / MIPS / ARM ### High Level to Low Level Transofrmation High Level Language 透過 Compiler 轉換成 Assembly Language, 再透過 Assembler 轉換成 Machine Language - High Level Language ```clike tmp = v[0]; v[0] = v[1]; v[1] = temp; ``` - Assembly (by Compiler) ```asm lw $15, 0($2) lw $16, 4($2) sw $16, 0($2) sw $15, 4($2) ``` - Machine Language (by Assembler) ``` 1000 1100 0100 1111... ``` ### MIPS R3000 ISA (Instruction Set Architecture) - Instruction 功能 - Load / Store - Computational - Jump and Branch - Floating Point - coprocessor - Memory Management - Special - Registers - R0 ~ R31 - PC - HI - LO - Instruction Formats - OP | rs | rt | rd | sa | funct - OP | rs | rt | immediate - OP | jump target ### ISA Examples 從 32 bit 擴展到 32/64 bit - HP PA-RISC (1986-96) - Digital Alpha (1992-97) - Oracle Sparc (1987~2019) - IBM Power (1990-2020) - Intel x86 (1978-2019 - 現役) - CISC - Complex Instruction Set Computer - MIPS (1985-2014 - 快死了) - ARM (1985-2024 - 現役) - RISC-V (2010-2021) ## Chapter 1 - Computer Abstractions and Technology ### Computer 電腦是一種 Programmable 的電子裝置, 用來對資料進行複雜處理 - 第一台電腦 - ENIAC (1943) - 1947 年電晶體被發明 - UNIVAC (1951) / IBM 701 (1952) 等電腦被發明 - 第一台使用電晶體的電腦 - IBM 1401 (1959) - 1958 年德州儀器開發了 IC - 第一個 CPU - Intel 4004 (1971) - Apple II (1977) / Intel 8088 (1981) 等使用 CPU 的電腦被發明 - 第一個圖形化介面 - Alto (1973) - 第一個 Application - VisiCalc for Appie II (1979) - 1980 年 IC 的集成進入 VLSI ### Instruction Count and CPI - Clock Cycles = Instruction Count (IC) $\times$ Cycles per Instruction - CPU Time - Instruction Count (IC) $\times$ CPI $\times$ Clock Cycle Time - $\frac{Instruction \space Count (IC) \times CPI}{Clock \space Rate}$ (Freq) ![image](https://hackmd.io/_uploads/ryO4F2Gcke.png) - 第 i 個 IC 意思是第 i 個類型的 IC #### CPI Example ![image](https://hackmd.io/_uploads/HyezKnz51g.png) ### Amdahl's Law 先區分出可以加速和不能加速的部分 將可以加速的部分加速完成加上不能加速的部分就是進行系統優化 Speedup (加速) - $\frac{Execution Time w/o E}{Execution Time w/E} = \frac{Performance w/E}{Performance w/o E}$ - $Execution \space Time(w/E) = ((1-F)+\frac{F}{S}) \times Execution \space Time(w/oE)$ - $Speedup(w/E) = \frac{1}{(1-F)+\frac{F}{S}}$ ## Chapter 2 - Instruction Set Architecture ### Memory Operation- 在 MIPS 中最高可計算的範圍是 $2^{32}$ (32-bit), 這代表了支援 RAM 容量的最高值,若超過的話則無法進行定址 - 記憶體單位 ![image](https://hackmd.io/_uploads/Bk9DvyEsJl.png) ### MIPS Architecture * MIPS 架構中用的是 10 進制 * MIPS 架構用的是 Big-Endian - `{opcode} {unit 1} {unit 2}` - `$s0` = Base Register - `$t0` = Temp Register * 如果 opcode 後面帶一個 i 代表指令中含有常數 - `lw` - Load Word to Register - `lw $t1, 12($s0)` = `mov eax, [ebx+12]` - `sw` - Store Word to Location - `sw $t0, 12($s0)` = `mov [ebx+12], eax` - `add` - Add Values - `add $s1, $s2, $t0` = Add `$s2` and `$t0` to `$s1` ## 指令大補帖 ### **系統呼叫(syscall)基本指令碼** | 系統呼叫碼 | 功能 | 輸入暫存器/操作 | 範例程式碼片段 | | ---------- | --------------------- | ---------------------- | --------------------------------------------------- | | 1 | 輸出整數 (print int) | 將要輸出的整數存入 $a0 | ```assembly<br>li $v0, 1<br>move $a0, VALUE<br>syscall<br>``` | | 4 | 輸出字串 (print string) | 將字串位址存入 $a0 | ```assembly<br>li $v0, 4<br>la $a0, string_label<br>syscall<br>``` | | 5 | 輸入整數 (read int) | 結果會存入 **$v0** | ```assembly<br>li $v0, 5<br>syscall<br># 輸入整數結果在 $v0<br>``` | | 8 | 輸入字串 (read string) | $a0: 儲存字串的緩衝區位址<br>$a1: 緩衝區大小 | ```assembly<br>li $v0, 8<br>la $a0, buffer<br>li $a1, size<br>syscall<br>``` | | 10 | 程式結束 (exit) | - | ```assembly<br>li $v0, 10<br>syscall<br>``` | ### Register ![image](https://hackmd.io/_uploads/rJxDJj0Tkx.png) ``` $v0,$v1 -> 回傳參數 $a0~$a4 -> 傳入參數 $t0~$t7 -> temporary 暫時參數 無須保存 $s0~$s7 -> save 必須在函式內保存與還原 $sp -> stack pointer 堆疊參數 $ra -> 返回位址 (jump) ``` ### Jump ``` j: jump -> 跳過去就再也不會回來了 jal: jump and link -> 會存 $ra,可以瞬移回來 jr: jump register -> jr $ra, 跳到 $ra 位置 ``` ### Branch - **beq** **英文名稱:** *Branch if Equal* **說明:** 若兩個暫存器相等則跳轉。 **記憶簡稱:** BEQ → "Equal" - **bne** **英文名稱:** *Branch if Not Equal* **說明:** 若兩個暫存器不相等則跳轉。 **記憶簡稱:** BNE → "Not Equal" --- ### **Zero-Comparison Branch Instructions** - **blez** **英文名稱:** *Branch if Less than or Equal to Zero* **說明:** 若暫存器值 ≤ 0 則跳轉。 - **bgtz** **英文名稱:** *Branch if Greater Than Zero* **說明:** 若暫存器值 > 0 則跳轉。 - **bltz** **英文名稱:** *Branch if Less Than Zero* **說明:** 若暫存器值 < 0 則跳轉。 - **bgez** **英文名稱:** *Branch if Greater than or Equal to Zero* **說明:** 若暫存器值 ≥ 0 則跳轉。 --- ### **Pseudo Branch Instructions (Pseudoinstructions)** - **blt** **英文名稱:** *Branch if Less Than* **說明:** 若第一個暫存器小於第二個則跳轉。 - **bgt** **英文名稱:** *Branch if Greater Than* **說明:** 若第一個暫存器大於第二個則跳轉。 - **ble** **英文名稱:** *Branch if Less Than or Equal* **說明:** 若第一個暫存器 ≤ 第二個則跳轉。 - **bge** **英文名稱:** *Branch if Greater Than or Equal* **說明:** 若第一個暫存器 ≥ 第二個則跳轉。 ## R-format ![image](https://hackmd.io/_uploads/BywirHIpkx.png) ![image](https://hackmd.io/_uploads/r1BmwrLpJl.png) > shift instruction 是 R-Format ## I-format ![image](https://hackmd.io/_uploads/SkgBwHIayx.png) ![image](https://hackmd.io/_uploads/B1YIXIL6yg.png) > Branch 是 I-Format ## J-format ![image](https://hackmd.io/_uploads/S1ewEiRT1g.png) ## Branch Addressing #### **Branch Immediate 指定字位址 (Word Address)** 1. **指令對齊 (Word Aligned):** - MIPS 指令的位址是**字對齊 (word aligned)**,也就是說,**每條指令的位址都是 4 的倍數**。 - 位址在二進位表示上,會以 **00** 作為結尾(因為 4 在二進位是 `100`)。 2. **位址加法的特性:** - 因為指令位址總是 4 的倍數,當分支指令要計算跳轉位址時,會使用**字單位 (word)** 來指定。 - **立即值 (immediate)** 是用**字數 (words)** 來表示,而非**位元組數 (bytes)**。 3. **最大分支範圍:** - MIPS 分支指令可以往**正負 $2^{15}$ 個字 (words)** 跳轉。 - 換算為位元組數: $$ \pm 2^{15} \times 4 = \pm 2^{17} \text{ bytes} (131072 bytes) $$ - 這意味著,可以處理**4 倍範圍**的迴圈。 --- #### **Immediate 指定下一個 PC (PCbranch + 4)** 1. **分支基址:** - MIPS 使用**PC + 4** 作為基址(這是因為 MIPS 在執行指令時,PC 已經自動更新至下一條指令)。 2. **跳轉位址計算:** - **未跳轉:** $$ PC_{next} = PC_{branch} + 4 $$ - **跳轉成功:** $$ PC_{next} = (PC_{branch} + 4) + (immediate \times 4) $$ - **解釋:** - 若條件成立,PC 跳轉的位址是:**當前 PC + 4 + (立即值 × 4)**。 - **immediate × 4** 的原因是,立即值是**字單位 (word)**,而非位元組單位。 #### **指令範例:** ``` Loop: beq $9, $0, End add $8, $8, $10 addi $9, $9, -1 j Loop End: ... ``` - **beq $9, $0, End** - 若 `$9 == $0`,則跳至標籤 `End`。 - **opcode:** 4 (beq 指令) - **rs:** 9 (第一個運算元) - **rt:** 0 (第二個運算元) - **immediate:** 3 (**從當前指令的下一條指令開始計算的位移**,因此中間隔了三條指令) - 當分支條件成立時,PC 會跳轉到**PC + 4 + (immediate × 4)**。 - 由於指令之間是按**字數 (word)** 計算,因此 Immediate 為 3。 > 考古有出 ## Design Principle * Design Principle 1 : Simplicity favors regularity(簡單有易於規則) * Design Principle 2 : Smaller is faster(小就是快) * Design Principle 3 : Make the common case fast * Design Principle 4 : Good design demands good compromises(好的設計需要好的折衷) ## MIPS Addressing Modes 1. Immediate Addressing (立即位址模式) `op rs rt immediate` 2. Register Addressing (暫存器位址模式) `op rs rt rd` 3. Base Addressing (基址位址模式) `op rt offset(rs)` ($reg + address) ![image](https://hackmd.io/_uploads/SyiyUiRpyx.png) 4. PC-Relative Addressing (程式計數器相對位址模式) `op rs rt offset` ex: `beq $t0, $t1, Label` ![image](https://hackmd.io/_uploads/HkGlIoR6kl.png) 5. Pseudo-Direct Addressing (偽直接位址模式) `op target` ex: `j label` ![image](https://hackmd.io/_uploads/SJtxUjRpJx.png) ## Compile > 考古 ![image](https://hackmd.io/_uploads/BJyqUo0pyx.png) | 名稱 | 中文 | 主要用途 | 運作時間 | |------|------|----------|----------| | **Compiler** | 編譯器 | 將高階語言(如 C/C++)轉成低階的中間碼或組合語言(如 Assembly) | 編譯時 | | **Assembler** | 組譯器 | 將組合語言轉成機器碼(object file) | 編譯時 | | **Linker** | 連結器 | 將多個 object files 和函式庫合併成一個可執行檔 | 編譯時 | | **Loader** | 載入器 | 將可執行檔載入記憶體,並準備執行(分配記憶體、初始化等) | 執行時 | ## Instruction count and CPI are not good performance > 考古 單獨使用,無法全面反映系統的真實效能: 1. 指令集架構或許不同(RISC, CISC) 2. CPI 取決於指令類型 綜合考量(IC, CPI, Clock Rate): $$ CPU \space Time = \frac{IC \times CPI}{Clock \space rate} $$ ## Multiplication in MIPS ![image](https://hackmd.io/_uploads/Hk12GFrp1l.png) ![image](https://hackmd.io/_uploads/B1nQQtHaye.png) mul - 用於有符號整數的乘法運算,結果儲存在一般暫存器中。 mult / multu - 有符號和無符號乘法運算,結果儲存在特殊暫存器 HI 和 LO 中。(32 x 32 位元可能會產生 64位元,因此拆成 hi 和 lo 存放結果) `mflo, mfli: move from lo/hi` ## Booth's Algorithm Booth 演算法是一種 **有符號二進位乘法演算法**,用來有效計算兩個補數二進位數字的乘積。該演算法主要用來減少乘法操作次數,特別是處理 **連續 1 或 0** 的情況。 - 將 **乘數 (Multiplier)** 中的 **連續 1** 簡化為加法與減法操作,減少操作次數。 - 使用 **補數表示法**,以方便處理正負數乘法。 ### 重點: - 每次觀察 **乘數的最低兩位元 (Q₀ 和 Q₋₁)**: - **`01`**:加法操作 (加上被乘數) - **`10`**:減法操作 (減去被乘數) - **`00` 或 `11`**:不操作 (右移) - 重複操作,直到完成所有位元的運算。 ![image](https://hackmd.io/_uploads/B1QJoFHT1g.png) ![image](https://hackmd.io/_uploads/S1DmTYBpkx.png) --- ## Division ![image](https://hackmd.io/_uploads/SkiyMrFRJe.png) ## **Signed Division Rules(有號除法規則)整理** 1. **記住符號處理:** - 執行除法時,先將除數與被除數都視為正數進行除法 - 最後再根據原始的符號決定 **商與餘數的符號** 2. **餘數(Remainder)要跟被除數(Dividend)**有相同符號! 3. **商(Quotient)的符號:** - 如果被除數與除數 **符號相同** → 商為正 - 如果兩者 **符號相反** → 商為負 4. **例子說明:** ```text -7 ÷ 2 = -3,餘數 = -1 -7 ÷ -2 = 3,餘數 = -1 ``` 你會發現餘數仍然是 -1,**跟被除數 -7 同號** 5. **結果要滿足這個恆等式:** $$ \text{Dividend} = (\text{Quotient} × \text{Divisor}) + \text{Remainder} $$ ✔️ 驗證 `-7 ÷ 2`: $$ -7 = (-3) × 2 + (-1) = -6 -1 = -7 $$ ## 單精度浮點數(Single-Precision Floating-Point) 單精度浮點數是 IEEE 754 標準中常見的一種表示實數的方法,**總共使用 32 個位元(bits)**,分為三個部分: | 符號位元(Sign) | 指數(Exponent) | 尾數 / 有效數字(Mantissa / Fraction) | |------------------|------------------|----------------------------------------| | 1 bit | 8 bits | 23 bits | ### 📐 各部分的意義 #### 1. **符號位元(Sign bit, S)** - 1 bit 表示正負號 - `0`:正數 - `1`:負數 #### 2. **指數(Exponent, E)** - 8 bits,表示數字的「大小尺度」 - 使用 **偏移碼(bias)**:`127`,實際指數 = `E - 127` - 例如 `E = 130` → 實際指數 = `130 - 127 = 3` #### 3. **尾數(Fraction / Mantissa, M)** - 23 bits,表示有效數字部分(但實際上是 24 位有效位元,因為有隱藏的前導 1) - 預設第一位為 `1.`(稱為 **hidden bit** 或 **implicit bit**),除非是特殊情況(如非規格化數) --- ### 🧮 表示方式(數學式) IEEE 754 單精度浮點數的表示公式如下: $$ \text{Value} = (-1)^S \times 1.F \times 2^{E - 127} $$ - `S`:符號位元 - `F`:尾數欄的 23 bits(視為小數點後的二進位) - `E`:偏移後的指數(8 bits) --- ## 雙精度浮點數(Double-Precision Floating-Point) 雙精度浮點數是 IEEE 754 中的另一種表示法,用 **64 個位元(bits)** 來儲存一個實數,適用於需要更高準確度的計算。 | 符號位元(Sign) | 指數(Exponent) | 尾數 / 有效數字(Mantissa / Fraction) | |------------------|------------------|----------------------------------------| | 1 bit | 11 bits | 52 bits | --- ### 📐 各部分說明 #### 1. **符號位元(Sign bit, S)** - 1 bit,代表數的正負: - `0` → 正數 - `1` → 負數 #### 2. **指數(Exponent, E)** - 11 bits,使用偏移表示法(**bias = 1023**) - 實際指數 = `E - 1023` #### 3. **尾數(Mantissa / Fraction, F)** - 52 bits,代表小數點後的有效數字 - 預設有一個隱含的 `1.` 作為開頭(除非是特殊數) --- ### 🧮 數值表示公式 $$ \text{Value} = (-1)^S \times 1.F \times 2^{E - 1023} $$ ## IEEE 754 Standard ### 1. **單精度(Single Precision, 32-bit)** | 部分 | 位元數 | 偏移值 | |----------|--------|--------| | 符號 Sign | 1 bit | 無 | | 指數 Exponent | 8 bits | Bias = 127 | | 尾數 Mantissa | 23 bits | 有隱含 1 | $$ \text{Value} = (-1)^S \times 1.F \times 2^{E - 127} $$ --- ### 2. **雙精度(Double Precision, 64-bit)** | 部分 | 位元數 | 偏移值 | |----------|--------|--------| | 符號 Sign | 1 bit | 無 | | 指數 Exponent | 11 bits | Bias = 1023 | | 尾數 Mantissa | 52 bits | 有隱含 1 | $$ \text{Value} = (-1)^S \times 1.F \times 2^{E - 1023} $$ --- ### 目標:用 IEEE 754 單精度格式表示 $\frac{1}{7}$ #### ✅ 步驟一:取得 $\frac{1}{7}$ 的二進位小數 $$ \frac{1}{7} ≈ 0.142857142857...₂(重複循環) $$ #### ✅ 步驟二:正規化(Normalized Form) 我們要寫成: $$ 1.xxxxx × 2^e $$ 把 \( 0.001001... \) 左移 3 位可得: $$ \frac{1}{7} = 1.001001001... × 2^{-3} $$ #### ✅ 步驟三:計算 Exponent 欄位 實際 exponent = -3 IEEE 754 使用 **bias = 127** $$ \text{encoded exponent} = -3 + 127 = 124 \Rightarrow 01111100₂ $$ #### ✅ 步驟四:填入 Fraction(取 23 位) 從 `1.001001001...` 中取小數部分: ``` 00100100100100100100100 ``` 這是 **循環的 001** #### ✅ 步驟五:組成 IEEE 754 Binary 格式 - Sign = 0(因為 $\frac{1}{7}$ 是正數) - Exponent = `01111100` - Fraction = `00100100100100100100100` 完整 32-bit Binary: ``` 0 01111100 00100100100100100100100 ``` #### ✅ 最終表示: ### 🔢 Binary: ``` 00111110000100100100100100100100 ``` #### 🔡 Hex: - 分成 4 位元一組轉換: ``` 0011 1110 0001 0010 0100 1001 0010 0100 → 3 E 1 2 4 9 2 4 ``` ✅ **Hex 表示:`0x3E124924`** #### 📌 驗算公式: IEEE 754 應滿足: $$ \frac{1}{7} ≈ (1 + \text{fraction}) × 2^{-3} ≈ 1.142857 × 2^{-3} ≈ 0.142857 $$ ![image](https://hackmd.io/_uploads/HyZ-OBYC1x.png) ## --- 期末考分界線 --- ## Floating Point Addition ### 步驟 #### 1. **比較階數(exponent)** * 若 A 和 B 的 exponent 不同,需**將小的對齊到大的**。 * 對齊方式是將尾數右移,並調整 exponent。 #### 2. **加法運算** * 將對齊後的尾數(mantissa)相加(注意符號) * 若符號不同,就變成減法 #### 3. **規範化(Normalization)** * 加完可能導致進位或尾數太小 * 將結果左移或右移,使尾數的最高位為 1,並調整 exponent #### 4. **四捨五入** * 根據 IEEE 的捨入模式(例如 round to nearest even)進行尾數的截斷 #### 5. **處理特殊情況** * 包括 overflow、underflow、NaN、無限大(Infinity)等 --- ### 📘 範例 ``` A = 1.5 = 0 01111111 10000000000000000000000 B = 2.25 = 0 10000000 00100000000000000000000 ``` * A exponent = 127, B exponent = 128 * 將 A 的尾數右移 1 位對齊 B:變成 0.75(尾數變 010000…) * 相加尾數:0.75 + 1.125 = 1.875 * 規範化:1.875 = 1.111… → exponent = 128 * 最終結果為 3.75(確認:1.5 + 2.25 = 3.75) ![image](https://hackmd.io/_uploads/r1dsy_oell.png) ### Associativity 浮點數加減法不具備結合律(位數太小了) ![image](https://hackmd.io/_uploads/SJD78Kjgex.png) ## Datapath ![image](https://hackmd.io/_uploads/H1Z3FQgWlx.png) ![image](https://hackmd.io/_uploads/H1vYtmeZgl.png) ![image](https://hackmd.io/_uploads/rkve9QgZxe.png) ![image](https://hackmd.io/_uploads/SJ94q7ebel.png) ![image](https://hackmd.io/_uploads/ryk257gWgl.png) ![image](https://hackmd.io/_uploads/SygM2mxWlg.png) ![image](https://hackmd.io/_uploads/ryJEzExbee.png) ## 一、**整體流程記憶:像水流一樣從左到右流動** 1. **從 PC 開始** ➜ Instruction Memory(抓指令) 2. **Instruction Decode** ➜ 根據指令的不同欄位送到 Registers、控制單元、立即數擴展(Sign Extend) 3. **ALU 計算** ➜ 用來運算位址、計算資料結果 4. **Data Memory 存取** ➜ load/store 指令會用到 5. **寫回 Register(Write Back)** 可用口訣: 👉 **PC ➜ 指令 ➜ 解碼 ➜ ALU ➜ 記憶體 ➜ 寫回** --- ## 二、區塊化記憶(可拆成 5 個模組) | 區塊名稱 | 功能 | 關鍵元件 | | -------------------- | ----------- | ------------------------------- | | PC 與 Instruction | 取指(fetch) | PC, Instruction Memory | | 解碼(Decode) | 解讀指令與寄存器資料 | Registers, Control, Sign Extend | | ALU 計算 | 位址運算 / 數值運算 | ALU, ALU control, MUX | | 存取記憶體 | 記憶體讀取或寫入 | Data Memory, MemRead, MemWrite | | 寫回(Write Back) | 把結果存回暫存器 | MUX, RegWrite | --- ## 一、主要控制點(Mux Control Points) 1. **寫入暫存器選擇(RegDst)** * **功能**:決定要寫回的暫存器是 `rt`(I-type)還是 `rd`(R-type)。 * **Mux 位置**:選擇 `Instruction[20–16]`(rt)或 `Instruction[15–11]`(rd)作為 `Write Register` 。 2. **ALU 第二輸入選擇(ALUSrc)** * **功能**:決定 ALU 第二個操作數是暫存器 `R[rt]`(RegB)還是延伸後的立即數(Imm)。 * **Mux 位置**:在 ALU 輸入 B 與 Immediate 之間切換 。 3. **寫回資料來源選擇(MemtoReg)** * **功能**:決定寫入暫存器的資料來源是 ALU 運算結果還是 Data Memory 讀出值。 * **Mux 位置**:在 ALUout 與 Memory Data 之間切換 。 4. **下一個 PC 選擇(PCSrc / Branch / Jump)** * **功能**: * `PC+4`(順序執行) * `PC + 4 + (sign_ext(Imm16)≪2)`(分支成功) * `{PC[31:28], Imm26≪2}`(Jump) * **Mux 位置**:Branch Mux(判斷是否分支)與 Jump Mux(分支結果 vs. Jump)。 --- ## 二、主要控制訊號(Control Signals) | 訊號名稱 | 說明 | 作用元件 | | --------------- | ------------------------------------------------------------------ | --------------------------- | | **RegDst** | 0 = 寫入 `rt`;1 = 寫入 `rd` | RegDst Mux | | **ALUSrc** | 0 = ALU B 輸入取自 R\[rt];1 = 取 Immediate | ALUSrc Mux | | **MemtoReg** | 0 = 寫回 ALU 結果;1 = 寫回 Memory 讀出值 | MemtoReg Mux | | **RegWrite** | 1 = 啟用暫存器寫入;0 = 禁用 | Register File write-enable | | **MemRead** | 1 = 啟用記憶體讀取(`lw`);0 = 禁用 | Data Memory read-enable | | **MemWrite** | 1 = 啟用記憶體寫入(`sw`);0 = 禁用 | Data Memory write-enable | | **Branch** | 1 = 如果 Zero=1 則分支;0 = 不分支 | Branch 比較邏輯 & PCSrc Mux | | **Jump** | 1 = 跳至 Jump 目標位址;0 = 走分支/順序路徑 | Jump Mux | | **ALUOp\[1:0]** | 00 = 加法(`lw`/`sw`)<br>01 = 減法(`beq`)<br>10 = R-type(由 `funct` 再解碼) | ALU Control 解碼器 | --- ## 三、控制訊號的產生:主控制器 & ALU 控制器 1. **主控制器(Main Control)** * 根據指令的 Opcode(`Instruction[31:26]`)產生上述所有主控制訊號(除了 ALU Control),通常用一張真值表或小 PLA 實現 。 2. **ALU 控制器(ALU Control)** * 接收 `ALUOp[1:0]` 及 R-type 指令的 `funct[5:0]` 欄位,產生 4-bit 的 ALU 操作碼(AND, OR, ADD, SUB, SLT…) 。 --- ### 1. R-type: `add $t0, $t1, $t2` * RegDst = 1(寫入 rd) * ALUSrc = 0(用暫存器) * MemtoReg = 0(用 ALU 結果) * RegWrite = 1 * MemRead = 0, MemWrite = 0, PCSrc = 0 ### 2. I-type Load: `lw $t0, 4($t1)` * RegDst = 0(寫入 rt) * ALUSrc = 1(立即數) * MemtoReg = 1(來自記憶體) * RegWrite = 1 * MemRead = 1, MemWrite = 0, PCSrc = 0 ### 3. I-type Store: `sw $t0, 4($t1)` * RegWrite = 0 * ALUSrc = 1 * MemWrite = 1 * MemRead = 0, PCSrc = 0 | ALUOp | funct | 操作 | ALUctr (二進制) | 說明 | | :---: | :----: | :--------------: | :----------: | :----------------- | | 00 | — | add | 0010 | 用於 `lw`/`sw` 的位址計算 | | 01 | — | sub | 0110 | 用於 `beq` 的相減比較 | | 10 | 100000 | add | 0010 | `add` | | 10 | 100010 | sub | 0110 | `sub` | | 10 | 100100 | AND | 0000 | `and` | | 10 | 100101 | OR | 0001 | `or` | | 10 | 101010 | set-on-less-than | 0111 | `slt` | ## ALUctr ```text ALUctr<3> = 0 ``` * ALUctr<3> 在所有操作中皆為 0 。 ```text ALUctr<2> = ALUop0 + ALUop1 · ¬func2 · func1 · ¬func0 ``` * 當 ALUOp=01(beq)或 ALUOp=00(lw/sw)時,ALUctr<2>=1;對於 R-type 時,僅在 funct={100100 (AND),100101 (OR),100000 (ADD),100010 (SUB),101010 (SLT)} 中對應子集使能。 ```text ALUctr<1> = ¬ALUop1 + ALUop1 · ¬func2 · ¬func0 ``` * ALUOp=00 (lw/sw) 時,¬ALUop1=1,故 ALUctr<1>=1;對於 R-type,僅在 funct=100000 (ADD) 或 100010 (SUB) 時使能。 ```text ALUctr<0> = ALUop1·¬func3·func2·¬func1·func0 + ALUop1· func3·¬func2· func1·¬func0 ``` * 這兩項分別對應 R-type 中的 `slt` (funct=101010) 及 `or` (funct=100101) 操作。 ## Truth Table of Control Signal | 指令類型 | opcode (6-bit) | RegDst | ALUSrc | MemtoReg | RegWrite | MemRead | MemWrite | Branch | ALUOp | | ------ | -------------- | :----: | :----: | :------: | :------: | :-----: | :------: | :----: | :---: | | R-type | 000000 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 10 | | lw | 100011 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 00 | | sw | 101011 | x | 1 | x | 0 | 0 | 1 | 0 | 00 | | beq | 000100 | x | 0 | x | 0 | 0 | 0 | 1 | 01 | | addi | 001000 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 00 | 若要支援 `j`(jump),需額外一個 Jump 控制訊號,由 opcode = `000010` 解出。 ## PLA $$ \text{RegWrite} = (\overline{op_5}\,\overline{op_4}\,\overline{op_3}\,\overline{op_2}\,\overline{op_1}\,\overline{op_0}) \;+\; (op_5\,\overline{op_4}\,\overline{op_3}\,\overline{op_2}\,op_1\,op_0) $$ ## Chapter - 4 ### Datapath 1. **程式計數器 PC**:儲存下一筆要執行的指令位址。 2. **指令快取/記憶體 Instruction Memory**:取出指令。 3. **暫存器檔 Register File**:執行 ID 時讀取,WB 時寫回。 4. **ALU**:執行 EX 階段的算術/邏輯運算。 5. **資料記憶體 Data Memory**:執行 MEM 階段的讀/寫存取。 6. **立即值延伸器 Sign-Extender**:將立即數從 16-bit 擴展到 32-bit。 7. **多路選擇器(Mux)**:根據控制訊號選擇 ALU 或寫回資料來源。 8. **控制單元 Control Unit**:產生各階段所需的控制訊號,可分為主控(Main Control)和 ALU 控制(ALU Control)。 --- ### 典型五態 FSM 1. **IF(Instruction Fetch)** 2. **ID(Instruction Decode / Register Fetch)** 3. **EX(Execute / Address Calculation)** 4. **MEM(Memory Access)** 5. **WB(Write Back)** --- ### 各階段詳細動作 | 階段 | 動作要點 | | --- | ------------------------------------------------------------------------------------------------------------ | | IF | - 從 `PC` 讀出指令:`Instr ← IM[PC]`<br/>- `PC ← PC + 4` | | ID | - 解析指令欄位:`opcode`、`rs`、`rt`、`rd`、`imm`<br/>- 從暫存器檔讀取:`A ← R[rs]`, `B ← R[rt]`<br/>- 對立即數做 sign-extend | | EX | - R-type:`ALUResult ← A op B`<br/>- Memory:`ALUResult ← A + sign_ext(imm)`<br/>- Branch:計算 `PC+4 + (imm<<2)` | | MEM | - Load:`MDR ← DM[ALUResult]`<br/>- Store:`DM[ALUResult] ← B`<br/>- 其他指令跳過此階段 | | WB | - R-type:`R[rd] ← ALUResult`<br/>- Load:`R[rt] ← MDR`<br/>- 不同指令對應不同寫回來源 | ### 範例流程 ### 5.1 Load Word (`lw`) 1. IF → 取得指令 2. ID → 讀寄存器 `rs, rt`;立即數 sign-extend 3. EX → 計算目標地址 `A + imm` 4. MEM → 讀取記憶體 `MDR ← DM[Address]` 5. WB → `R[rt] ← MDR` ### 5.2 Store Word (`sw`) 1. IF → 取得指令 2. ID → 讀 `rs, rt`;立即數 sign-extend 3. EX → 計算位址 `A + imm` 4. MEM → `DM[Address] ← B` 5. WB → 無 ### 5.3 R-type(算術/邏輯) 1. IF → 取得指令 2. ID → 讀 `rs, rt` 3. EX → `ALUResult ← A op B` 4. MEM → 無 5. WB → `R[rd] ← ALUResult` ### 5.4 Branch (`beq`) 1. IF → 取得指令 2. ID → 讀 `rs, rt`;立即數 sign-extend 3. EX → 比較 `A−B`,且計算分支目標 `PC+4+(imm<<2)` 4. MEM → 無 5. WB → 如果 `A==B`,則 `PC ← target`;否則保留 `PC+4` ### 控制有限狀態機(FSM) ![image](https://hackmd.io/_uploads/rkF8C4VMxe.png) ### Pipeline #### Strutural Hazard 不同指令使用到同一個元件,解決方法:固定所有指令長度(idle) ![image](https://hackmd.io/_uploads/HkgHsVSMll.png) #### Pipeline Hazards ##### 結構衝突 (Structural Hazard) * **定義**:當兩條或多條指令同一個週期需使用相同硬體資源,而該資源在此週期僅能一次性服務時,就會發生結構衝突。 * **範例**:IF 階段要從記憶體讀取指令 (Instruction Fetch),同一週期 MEM 階段又要做資料存取 (`lw`/`sw`),若只配置單一埠記憶體即會衝突。 * **解法**: 1. **哈佛架構** (Harvard):指令與資料分別使用不同記憶體/快取,讓 IF 與 MEM 可並行存取。 2. **多埠記憶體**:設計多埠快取或複製關鍵硬體單元,支援多重存取。 3. **插入停頓** (Stall):硬體偵測到衝突時,凍結後續階段,直到資源釋放為止,雖保證正確,卻犧牲效能。 --- ##### 資料衝突 (Data Hazard) * **定義**:指令之間存在資料相依,其中一條尚未完成寫回,後一條就嘗試讀取該結果,導致讀到「舊值」或無效資料。 * **主要類型**: * **RAW (Read After Write)**:後一指令讀取的操作數,尚未被前一指令寫回寄存器。 * **WAR (Write After Read)** 與 **WAW (Write After Write)**,在五階段 MIPS 流水線中較少發生。 * **解法**: 1. **前推 (Forwarding / Bypassing)**:直接將 EX/MEM 或 MEM/WB 階段的 ALU 結果,經 bypass network 送回 EX 階段輸入,繞過寫回→讀取延遲。 2. **插入停頓 (Stall / Bubble)**:對於 load–use hazard(LW 後立即使用其讀出資料之情況),即使有 forwarding 也無法解決,須由 Hazard Detection Unit 插入 NOP 週期,延後後續指令執行。 --- ##### 控制衝突 (Control Hazard) * **定義**:分支或跳轉指令 (如 BEQ、J) 需要先判斷分支結果或目標位址,才能正確決定下一條要取的指令,否則可能在錯誤路徑上繼續抓指。 * **解法**: 1. **Always-Not-Taken**:預設不跳轉,若分支成立才 flush(清空)錯路指令。 2. **Early Branch**:在 ID 階段就完成比較 (compare),減少必須 flush 的指令數量。 3. **分支預測 (Branch Prediction)**:採用靜態或動態預測(如 1-bit / 2-bit predictor),在硬體層面決定是否跳轉,並在預測錯誤時 flush 錯誤指令,減少停頓開銷。 4. **延遲分支 (Delayed Branch)**:編譯器將有效指令插入分支延遲槽 (delay slot),利用這些槽位填滿流水線空閒。 ![image](https://hackmd.io/_uploads/rkJqkHBMxe.png) ![image](https://hackmd.io/_uploads/r1LGjasGlg.png) ## Chapter - 5 ### Memory Hierarchy #### 基礎知識 | 技術 | 存取時間 | 成本 | | --- | ------- | --- | | SRAM | 0.5 ~ 5 ns | $2000 ~ $5000 | | DRAM | 50 ~ 70 ns | $20 ~ $75 | | Magnetic Disk | 5 ~ 20 ms | $0.2 ~ $2 | - 根據時代的演進 - Size 巨大成長 - Access Time 小幅度成長 為了解決存取時間問題而發明了 Memory Hierarchy 技術 * 記憶體分級 * Upper Level - 空間小 速度快 * Lower Level - 空間大 速度慢 #### 記憶體架構 | 階層 | 技術 | 容量範圍 | 讀取延遲 | 單位容量成本 | 位置 | | -------------- | ---------- | ---------- | ----------- | ------ | ---------- | | **寄存器** | Register | 幾十至上百字元 | \~0.1ns | 最高 | CPU 內部 | | **快取 (L1)** | SRAM Cache | 16–64 KB | \~1–4 ns | 高 | CPU 內部 | | **快取 (L2/L3)** | SRAM Cache | 幾百 KB–幾 MB | \~5–20 ns | 中 | CPU 晶片或封裝中 | | **主記憶體** | DRAM | 幾 GB–數十 GB | \~50–100 ns | 低 | 記憶體插槽 | | **次級儲存** | SSD / HDD | 幾百 GB–數 TB | \~0.1–10 ms | 非常低 | 機殼內儲存裝置 | | **離線儲存** | 磁帶、光碟 | TB 以上 | 秒級以上 | 最低 | 外部、備份 | --- ### 效能衡量與最佳化 * **命中率 (Hit Rate)**:在快取中找到所需資料的比例; * **缺失率 (Miss Rate)**:未命中時需往下層存取,導致額外延遲; * **有效平均存取時間 (AMAT)**: $$ \text{AMAT} = T_\text{hit} + R_\text{miss} \times T_\text{miss\_penalty} $$ 其中 $T_\text{hit}$ 為快取命中延遲,$R_\text{miss}$ 為缺失率,$T_\text{miss\_penalty}$ 為未命中處理的懲罰時間(包括向下層存取與填回快取所需時間)。 透過增加快取容量、提升關聯度 (associativity)、調整區塊大小 (block size) 及採用先進的替換策略,可有效提升命中率並降低 AMAT。 --- 從 Access Time 從上到下排列 / 越上面空間越小 - Registers - Cache - Memory - Disk - Tape 可以透過 Cache Controller (Hardware) 把資料從 Memory 搬到 Cache 來加快 Performance - 只在**相鄰兩層之間**搬資料 - Ex : Cache -> Memory * Principle of Locality(局部性原理) * **Temporal Locality**:近期使用的資料將再次被使用 * **Spatial Locality**:附近的記憶體位址將很快被使用 ----- ### Cache 機制 如果 Cache 裡面沒有 CPU 要的資料就會去 Memory 撈,這種情況稱為 Cache Miss - Hit Rate - 資料 Access 到的機率是多少 - Miss Rate - $1 - \text{Hit Rate}$ - Hit Time - Access Cache 的時間 - Miss Penalty - Cache Miss 以後去撈的時間 - Hit Time < Miss Penalty #### Hierarchy 基本問題 把記憶單位區分成一塊一塊的 Block 分別存於 Cache 和 Memory 1. Block Placement - 要把 Memory Block 放在更上層的哪裡 2. Block Identification - 要怎麼識別這個 Block 是誰 3. Block Replacement - 要怎麼替換掉沒在用的 Block 4. Write Strategy - 如果資料被更新要怎麼寫入 #### Block Placement 像是 Hash Table 一樣用 Mod 的方式來找到放到更上層的位置 ![image](https://hackmd.io/_uploads/HymVFRTZxg.png) #### Block Identification & Block Replacement - Block Identification 可以使用 Tag 來標示,除了用來識別以外也可標示其他資訊如 Valid Bit ( 1 才是有效的 ) - Block Replacement 在 Tag 重複的時候把舊的資料重新 Overwrite 成新的 Block ![image](https://hackmd.io/_uploads/S1Eq9RaZex.png) ![image](https://hackmd.io/_uploads/ryBLqATbgl.png) #### Write Strategy - 如果 Write Block 是 Hit - Write Through 在更新到 Cache 的時候順便寫到 Memory,但 Performance 差 - Write Buffer 在更新 Cache 的同時寫到 Write Buffer 來慢慢寫入,但 Buffer 滿了會會慢 ![image](https://hackmd.io/_uploads/r1-520TWgg.png) - Write Back 先更新 Cache 並標示 Dirty Bit,1 為更改過 / 0 為沒被更改 如果 Dirty Block 要被 Replace 的時候再使用 Write Buffer - 如果 Write Block 是 Miss - Allocate on Miss - 把 Memory 撈進 Cache 再進行後續操作 (適合 Write Back) - Write Around - 直接寫回 Memory (適合 Write Through) | | **Write-Allocate** | **No-Write-Allocate** | | ----------------------- | ------------------------------------------------------------------- | -------------------------------- | | **Write-Back** | 先將寫入地址的資料行(cache line)載入快取,再在快取中更新;最終只在該行被汙損(dirty)且被逐出時,一次性寫回主記憶體。 | 寫入不先載入快取;直接在主記憶體執行寫入操作(通常搭配寫直通)。 | | **Write-Through** | 先將寫入地址的資料行載入快取,再在快取與主記憶體同時更新;每次快取更新都同步寫入主記憶體。 | 不載入快取,寫入時直接送往主記憶體;快取中不保持該資料行。 | --- ### Cache Performance - 增強 Cache Performance - 設計更好的 Cache - 增加 Clock Rate - 減少 Base CPI - 減少 Miss Rate - 減少 Hit Time - 減少 Miss Penalty #### Memory Stall Cycle = $\frac{\text{Memory Access}}{\text{Program}} \times \text{Miss Rate} \times \text{Miss Penalty}$ = $\frac{\text{Instructions}}{\text{Program}} \times \frac{\text{Misses}}{\text{Instruction}} \times \text{Miss Penalty}$ **Example** - Given - I-Cache (Instruction) Miss Rate = 2% - D-Cache (Data) Miss Rate = 4% - Miss Penalty = 100 Cycles - Base CPI = 2 - Load & Stores are 36% of Instructions - Miss Cycles Per Instruction - I-Cache 1 **(All Instructions)** $\times$ 0.02 **(Miss Rate)** $\times$ 100 **(Miss Penalty)** = 2 - D-Cache 0.36 **(36% Instructions)** $\times$ 0.04 **(Miss Rate)** $\times$ 100 **(Miss Penalty)** = 1.44 - Actual CPI = 2 **(Base)** + 2 **(I-Cache)** + 1.44 **(D-Cache)** = 5.44 #### AMAT - Average Memory Access Time - **Hit Time + Miss Rate $\times$ Miss Penalty** **Example** - Given - CPU Clock = 1ns - Hit Time = 1 Cycle - Miss Penalty = 20 Cycles - I-Cache Miss Rate = 5% - AMAT - 1 + 0.05 $\times$ 20 = 2ns - 2 Cycles Per Instruction #### 改善 Cache Performance - Block Size 變大 把 Cache 的 Block Size 增加可以減少 Miss Rate 但加太大會爛 - Block 數量減少 - 可能增加沒用的資料 (Cache Pollution) - 增加 Miss Penalty - Direct Mapped (One-Way Set Associative) 一個位置 Hash 完對一個,沒有任何解釋空間 ![image](https://hackmd.io/_uploads/BJVT2x0-ge.png) - Fully Associative 把所有的 Block 可以的位置都放 - Performance 會炸開,因為要把所有 Block 都檢查過一次 ![image](https://hackmd.io/_uploads/HJ3kaeCble.png) - n-way Set Associative 把資料擺在 n 個位置,如果一個不行就算下一個直到 n - 不需要翻過所有全部的 Block ![image](https://hackmd.io/_uploads/rkwA2gA-xx.png) ![image](https://hackmd.io/_uploads/HkQ9olCZeg.png) Direct Mapped Cache 的時候在拿到 Tag 的同時也拿到值,可以直接先做事再做 Tag Compare 來增強 Performance,但 n-way Set Associative 不行 - Replacement 技術 Random - 隨機踢掉一個資料來拿空間 LRU - 把最舊沒被用到的資料踢掉 Pseudo LRU - 俄羅斯輪盤 有人進來就指下一個人,滿了就把指到的人送去古拉格💀 #### Multi Level Cache 分為不同 Level 的 Cache 來增強 Cache 總體的 Performance - Primary Cache - 讓 Hit Time 越短越好 - L-2 Cache - 容量更大 讓 Primary Cache 能更快在這邊找到東西 **Example** * Given * CPI Base CPI = 1 * Clock Rate = 4GHz * Miss Rate = 2% * Main Memory Access Time = 100ns Compute - Primary Cache Only - Miss Penalty = 100ns / 0.25 ns (4GHz 的倒數) = 400 Cycles - **Effect CPI = 1 + 0.02 $\times$ 400 = 9** - Primary & L-2 Cache - Access Time = 5ns - Global Miss Rate to Main Memory = 0.5% - Primary Miss with L-2 Hit - Penalty = 5ns / 0.25ns = 20 Cycles - Primary Miss with L-2 Miss - Extra Penalty = 400 Cycles - CPI = 1 + 0.02 $\times$ 20 + 0.005 $\times$ 400 = 3.4 - Performance Ration = 9/3.4 = **2.6** #### 發生 Cache Miss 的原因 - Compulsory 絕對不可避免的情況,在開機時不可能有資料的那種 - Conflict 一堆人在搶同一個位置的情況 - 增加 Cache Size - 增加 Associativity - Capacity Cache 容量不夠的問題 ----- ### Virtual Memory #### 基本知識 - 每個程式都有自己的獨立記憶體空間 - 提供比實體記憶體更大的 Virtual Space - 一個 Block 被稱為 Page (Virtual) 或是 Frame (Physical) - Page Fault - Virtual Address 無對應 Physical Page - Page Fault 會透過 Trap 來對 OS 發出 IRQ - 用查表方式來拿對應的 Physical Address - 前 31~12 bit 是 Virtual Page Number - 經過 Translation 變成 Physical Frame Number - Physical Frame Number + offset = Physical Address ![image](https://hackmd.io/_uploads/ByvZUHszle.png) - Virtual Address 會透過 Translation 的機制取得 Physical Address - CPU 會先用 Physical Address 從 Cache 撈資料,Page Fault 再去更深的地方撈 ![image](https://hackmd.io/_uploads/B1yxXUCZle.png) - Page Table - Dirty Bit / 最後一次被 Access 的時間 / ... 等資訊 ### Address Tanslation Example - Virtual Memory Size = $2^{20}$ Bytes - Page Size = $2^{10}$ Bytes - Virtual Address = 7414 - 把 Virtual Address 轉成 Index - 從 Page Table 裡面撈 Frame Address - 取 Page Offset -> 7414 % 1024 ($2^{10}$) = 246 - Physical Address = Page Offset + Frame Address ![image](https://hackmd.io/_uploads/ryrijUC-gl.png) ### Two-Level Page Tables - 前面切一半分成兩個 Table 的 Index ![image](https://hackmd.io/_uploads/rJM1C8iMxx.png) ### TLB TLB = Page Table Cache jalr func main(): jalr meow() func meow(): mov $ra, [rsp] jr $ra return -> PC <- $ra