# 手搓 CPU - 未來無限可期 微算機專題,用電子元件自製 CPU,並結合 pic18 的應用。 題目:微處理器創新整合應用 [實體 DEMO]:12/18 [上傳書面報告]:12/21 ## 架構: 控制單元、IO 單元、ALU、Register、Memory、指令集、Program counter 以 8bit 為 word ### pic18f4520: 作為我們與 CPU 互動的介面,有一顆負責將指令燒錄進 Program Memory,或者作為直譯器,以 UART 操作 CPU(超他媽炫砲) 另一顆則負責用我們專門設計的 register,與 CPU 互動 ### 實際空間分配 ![截圖 2026-01-22 下午5.11.48](https://hackmd.io/_uploads/B1eycwk8We.png) ### Register 以 DFF 實現 提供 8 個 Reg Reg[0] 是 0x00 Reg[1] 實作特殊功能用,暫且保留(如 BANK) Reg[2:5] 單純 register Reg[6] 對 CPU 來說 Read Only,由外部決定 Reg[7] 其值專門給外部讀取 |WriteToReg|CP|IN|D|Q| |------|------|------|------|------| |0|-|X|X|$Q_{n-1}$| |1|↑|X|IN|D| 控制是否要發出正緣訊號給 DFF ##### latch-based clock gating ### ALU: 8 bit input 提供運算: |id|name|實作方式|包含指令| |------|------|------|------| |0|A and B||| |1|A xor B||| |2|not A||| |3|A or B||| |4|A add B||| |5|A sub B|(not B) add A with carry|NEG (0 - B)| |6|lrotate A||| |7|rrotate A||| ### PC(Program counter) 找到 instruction 的位置並從 program memory 抓進 instruction register jump 等指令會賦值 pc = x,或者一般執行時 pc += 1 ### Data Memory #### sram(program memory 用): - 兩個 - 第一個:RD為I/O,RB為地址線 - 第二個:RC0~5、RE 1, 2(6, 7的位置)為I/O,RB為地址線 - RC6, 7是RX, TX要用來debug所以先不用 - ~~RA 要調設定好麻煩~~ #### IO 問題 如何確保寫的時候能將 B 寫進去,而讀的時候 B 能不影響 output? 把 SN74HC245N 當 tri-state buffer 控制 IO 要不要吃輸入 平時要把 CS 設定好,需要用時再開啟 以 74HC74 做微狀態機 要保證: CPU 給對的訊號在 CS 打開前 在沒有連續 LOAD/STORE 前應該都做得到? - 非 LOAD/STORE 1. SRAM_CS = 0 - LOAD: - STATE 0: SRAM_CS = 1 ; 關閉 SRAM CPU_OE = 1 ; CPU 停止輸出 - STATE 1: SRAM_OE = 0 ; SRAM 驅動資料線 SRAM_CS = 0 ; 開啟 SRAM - STORE - STATE 0: SRAM_CS = 1 ; 關閉 SRAM SRAM_OE = 1 ; SRAM 不驅動資料線 - STATE 1: CPU_OE = 1 ; CPU 開始輸出 SRAM_CS = 0 ; 開啟 SRAM ### pipeline: 範例(by 祭祖簡報): ![截圖 2025-11-30 凌晨3.52.39](https://hackmd.io/_uploads/ryDGyC_W-x.png) 我們的 4 stage pipeline 結構: ![截圖 2025-12-20 晚上8.19.22](https://hackmd.io/_uploads/S1cLEz4XZx.png) 由於我們使用 Bank 而不是 offset 來支援 data memory 的存取,所以 ex 和 mem 可以平行的在同一 stage 處理,也降低了硬體設計的複雜度。 #### 各階段要往後送的資料: | 階段 | 內容 | Register 需求 | | --- | --- | --- | | IF / ID | 指令本身(Instruction):<br>下一階段需要知道指令是什麼,才能解碼。<br>PC(Program Counter):<br>用於計算 branch/jump 目標或更新<br>|3 個| | ID / EX&MEM |A, B 兩個的數值,對 ALU 是兩個運算元。對 MEM 來說 B 是 addr 的目標,A 是低 8 位元的 addrs。<br>A 與 B 的 id,讓 Forwarding 判斷是否要跟 EX&MEM/WB 取值。<br>target id:<br>WB 階段要知道要寫回哪個暫存器,也讓 Forwarding 知道能否使用。<br>B_src:<br>B是否來自 register,若不是則不能 Forwarding(A 一定來自 register 所以不需要)<br>ALU control:<br>(做什麼運算)。<br>MemRead / MemWrite:<br>是否讀寫記憶體<br>RegWrite:<br>是否要寫回暫存器<br>PC:<br>用於 EX 階段做 branch 計算。|6~8 個| | EX&MEM / WB |result:<br>LOAD -> Memory 讀的資料。<br>算數 -> ALU 結果。<br>RegWrite:<br>WB 是否寫回暫存器。<br>target id:<br>WB 回哪。|2 個| #### Forwarding: ALU 要看他的 input 是否是後面 EX&MEM 的結果,要確保他們是要 Write Back 的。 ![截圖 2025-12-02 凌晨3.54.52](https://hackmd.io/_uploads/Bk25zOsWbe.png) if (EX&MEM/WB.RegWrite && A.rs == EX&MEM/WB.rd && A_is_reg) ALU_INPUT = EX&MEM/WB.data else ALU_INPUT = ID/EX.A.data #### Hazard detection: 如果 Branch 會發生,要將原本做的事清理乾淨(前兩個 STAGE 的控制訊號必須清 0) 實作方式:ID/EX&M 控制訊號設為全 0,IF/ID 的指令給一組沒實際作用的 ### 指令集: #### 硬體端 如果是 IMM 類型指令,其 imm 在 Ex/MEM 階段一定是 B 如果是 REG 類型指令,reg_1 為 A,reg_2 為 B 如果有 MEM 操作,mem_addr 必定是 B 如果是 條件分支(B2),由 A 與 B 比較,硬體會另外實作 PC+offset 指令 decoder 概念圖 |類型|opcode|rd|A|B|offset| |-|-|-|-|-|-| |IMM|00xxx[0-4]|5~7|Reg[5-7]|8~F|-| |REG|01xxx,xx[0-4,8-9]|5~7|Reg[A-C]|Reg[D-F]|-| |LOADI|10000[0-4]|5~7|-|8~F|-| |STOREI|10001[0-4]|-|Reg[5-7]|8~F|-| |LOAD|10010[0-4]|5~7|-|Reg[D-F]|-| |STORE|10011[0-4]|-|Reg[5-7]|Reg[D-F]|-| |B1|10100xxx[0-7]|-|-|-|8~F| |B2|11X[0-1,F]|-|Reg[5-7]|Reg[2-4]|sign_ex(8-E)| |IF/ID|值|大小(bit)| |-|-|-| |instruction|`instruction`|16| |PC|`PC`|8| |ID/EX&MEM|值|大小(bit)|產生單元|進度| |-|-|-|-|-| |isBranch|`Conditional_Branch \| Unconditional_Branch`|1|Control|存在| |Unconditional_Branch|`101`|1|IF/ID|存在| |Conditional_Branch|`11`|1|IF/ID|存在| |Conditional_Branch_Type|`[F]`|1|IF/ID|存在| |offset|`[8,E]+(Conditional_Branch&[E] \| !Conditional_Branch&[F])`|8|Sign_ex|正確| |NEW_PC|`PC + offset`|8|PC_adder|正確| |is_WB|`[0]`|1|IF/ID|存在| |WB_rd|`[5~7]`|3|IF/ID|存在| |MemRead|`1x0x0`|1|Control|存在| |MemWrite|`1x0x1`|1|Control|存在| |ALU_op|`[2~4]`|3|IF/ID|存在| |A_isreg|`true`|1|-|存在| |B_isreg|`(x1 \| 10x1)`|1|Control|存在| |A_regid|`(01)&[A-C] \| (1\|0)&[5-7]`|3+1|Control|存在| |B_regid|`(11)&[2-4]\|(0\|0)&[D-F]`|3+1|Control|存在| |A_value|`Reg[A_regid]`|8|register|可存在| |B_value|`((!b_isreg)&[8~F])\|:(b_isreg&Reg[b_regid]`|8|register/imm MUX|可存在| |EX&MEM/WB|值|大小(bit)| |-|-|-| |is_WB|`[0]`|1| |WB_rd|`[5~7]`|3| |WB_VALUE|`WB_VALUE`|8| |WB_rd(8bit)|WB_rd|8| 共同前綴:00 | 指令 | 說明 | opcode | type | |------|------|------|------| | ANDI,breg_rd,imm | `Reg[reg_rd] &= imm` |000|IMM| | XORI,reg_rd,imm | `Reg[reg_rd] ^= imm` |001|IMM| | ORI,reg_rd,imm | `Reg[reg_rd] \|= imm` |011|IMM| | ADDI,reg_rd,imm | `Reg[reg_rd] += imm` |100|IMM| | SUBI,reg_rd,imm | `Reg[reg_rd] -= imm` |101|IMM| 共同前綴:01 | 指令 | 說明 | opcode | type | |------|------|------|------| | NOT,reg_rd,reg | `Reg[reg_rd] = !Reg[reg]` |010|IMM| | LROTATE,reg_rd,reg | `Reg[reg_rd] = Reg[reg]<<1` |110|IMM| | RROTATE,reg_rd,reg | `Reg[reg_rd] = Reg[reg]>>1` |111|IMM| | AND,reg_rd,reg_1,reg_2 | `Reg[reg_rd] = Reg[reg_1]&Reg[reg_2]` |000|REG| | XOR,reg_rd,reg_1,reg_2 | `Reg[reg_rd] = Reg[reg_1]^Reg[reg_2]` |001|REG| | OR,reg_rd,reg_1,reg_2 | `Reg[reg_rd] = Reg[reg_1]\|Reg[reg_2]` |011|REG| | ADD,reg_rd,reg_1,reg_2 | `Reg[reg_rd] = Reg[reg_1]+Reg[reg_2]` |100|REG| | SUB,reg_rd,reg_1,reg_2 | `Reg[reg_rd] = Reg[reg_1]-Reg[reg_2]` |101|REG| 共同前綴:10 | 指令 | 說明 | opcode | type | |------|------|------|------| | LOADI,reg_rd,imm_mem | `Reg[reg_rd] = Mem[bank,imm_mem]` |000|LOADI| | STOREI,reg,imm_mem | `Mem[bank,imm_mem] = Reg[reg]` |001|STOREI| | LOAD,reg,reg_mem | `Reg[reg_rd] = Mem[bank,Reg[reg_mem]]` |010|LOAD| | STORE,reg,reg_mem | `Mem[bank,Reg[reg_mem]] = Reg[reg]` |011|STORE| | JUMP,imm| `PC += imm` |100|B1| 共同前綴:11 | 指令 | 說明 | opcode | type | |------|------|------|------| | JUMP-IF-EQ,reg_1,reg_2,imm2| `if(Reg[reg_1] == Reg[reg_2]) PC += imm2` |0|B2| | JUMP-IF-LESS,reg_1,reg_2,imm2| `if(Reg[reg_1] < Reg[reg_2]) PC += imm2` |1|B2| > 可以用來實作 jump-if-zero、jump-if-greater 等 指令長度 opcode:5 reg_addr:3 imm:8 imm2:7 ### 時序上會有問題的部分? Program Memery 的讀取 Data Memory 的讀寫 Register 的讀 (WriteBack) 寫(ID) 統一有 clock1 與 clock2 clock2 = !clock1 clock 上緣觸發 register 將對的值設定好 clock 下緣觸發 register 的寫入 clock $clock_i$ = clock nor ($write_i$ or $!is\_reg$) ### 工作頻率: 還未算最長 delay 路徑 ### 元件: 74HC00 NAND ![截圖 2025-11-30 清晨5.38.47](https://hackmd.io/_uploads/HJreuJK-Zl.png) 74HC02 NOR ![image](https://hackmd.io/_uploads/SJpcGbGWbx.png) 74HC04 NOT ![截圖 2025-12-15 下午2.46.36](https://hackmd.io/_uploads/H1eJJV6fWe.png) 74HC08 AND ![截圖 2025-12-13 下午1.33.05](https://hackmd.io/_uploads/SyDj9dqfZl.png) 74HC32 OR ![截圖 2025-12-13 下午2.07.26](https://hackmd.io/_uploads/SJ1nGY5M-l.png) 74HC86 XOR ![截圖 2025-12-17 凌晨1.06.00](https://hackmd.io/_uploads/S1BYWfyXbg.png) 74HC573 latch ![截圖 2025-12-14 晚上9.43.27](https://hackmd.io/_uploads/BkobyShG-g.png) ![image](https://hackmd.io/_uploads/rkkAj9bZWe.png) 74HC574 DFF ![截圖 2025-12-13 下午2.43.17](https://hackmd.io/_uploads/By8GjFqGZx.png) ![截圖 2025-11-30 凌晨2.13.49](https://hackmd.io/_uploads/BJnkuh_bbe.png) [74HC139](https://www.ti.com/lit/ds/symlink/sn74hc139-q1.pdf?ts=1763995453072&ref_url=https%253A%252F%252Fwww.google.com%252F) ![截圖 2025-11-25 凌晨2.14.40](https://hackmd.io/_uploads/H1a5xXz--l.png) ![截圖 2025-11-25 凌晨2.15.03](https://hackmd.io/_uploads/S1EhemGbZx.png) HM628128 Sram ![截圖 2025-11-30 晚上9.39.39](https://hackmd.io/_uploads/HkLXFpKZbg.png) ![截圖 2025-11-30 晚上9.40.07](https://hackmd.io/_uploads/rJBrF6K-Wg.png) 74HC74 D-Trigger 74HC245 ![截圖 2025-12-11 下午5.20.17](https://hackmd.io/_uploads/S1C0nb_GWg.png) ![截圖 2025-12-11 下午5.20.45](https://hackmd.io/_uploads/rytxaWOG-x.png) 74HC283 adder ![截圖 2025-12-15 晚上7.35.09](https://hackmd.io/_uploads/BkZtGupfZe.png) ### 有用的網站 [畫方塊圖](https://app.diagrams.net/) [元件圖](https://app.ultralibrarian.com/) ## 專題工作順序: [專題大綱提案繳交]:11/20 [實體 DEMO]:12/18 [上傳書面報告]:12/21 ![截圖 2025-11-02 凌晨1.16.22](https://hackmd.io/_uploads/rJKKl6Q1bg.png)