網站首頁 編程語言 正文
理解 CPU 工作原理,重要的是理解 pc 不停地自增地址,順序執行程序指令。當遇到跳轉指令時,會將 pc 重置為新地址。在順序執行程序指令的過程中,每一步都是解析程序指令、產生控制信號,進而控制所有 CPU 相關器件的工作狀態,產生程序計算結果,保存進各寄存器或者RAM 中。
本文使用四十行 Python 代碼來實現一個最簡單的 CPU。使它可編程,支持加減法運算、讀寫內存、無條件跳轉、條件跳轉的功能。
Python層面的實驗和書面上的概念,不足以支撐去論證從Python代碼到CPU執行的復雜過程,也不足以支撐從CPU讀的內存地址到Python內存管理上的內存地址之間復雜的關系,但算是針對此淺層現象的一個合理的猜想。
一、引言
從宏觀上,CPU 工作原理是讀取內存數據,在 ALU 中完成計算,然后保存進內存,輸入輸出系統完成了同其他外設交互;從中觀上看,CPU 工作原理就是本文講述的 pc 從 0 開始,讀取程序指令寄存器,然后解析指令,控制各部件工作的具體過程;從微觀上看,pc 程序計數器、ALU 數字邏輯運算單元,RAM 存儲器在內的所有 CPU 相關部件,其實都是一個個三極管,這些三極管在電流作用下導通或者截止,完成了數字邏輯運算、保持記憶狀態、產生脈沖信號的所有功能。
二、CPU工作原理
讓我們分別介紹各部件工作原理,之后介紹它們怎么協同工作。
1 各部件工作原理
在真實 CPU 中,都有相應物理電路與其對應,它們的功能分別是:
? ? pc 計數器,從 0 開始產生 0,1,2,……計數可以清零,也可以從外部輸入一個數,從這個數從新開始計數,這被稱為置位。用于指示程序和數據存取位置。
? ? RAM,存儲數據的隨機存儲器,支持根據地址(0x01 這種整形)讀取數據,根據地址和寫入信號 w 寫入數據。用于存儲程序和數據。
? ? 寄存器,存儲 8 bit 信息的存儲器,根據 w 信號為 1 寫入當前數據,w 為 0 表示讀取。類似 RAM,但只能存儲 8 bit 信息。常用于存儲指令、地址和計算中間量。
? ? 加法器,完成兩數加減法運算,sub 為 1 時表示減法,ci 為 1 時表示進位。這個器件是核心器件,用于構成 ALU(算數邏輯單元)。真實 CPU 是采用邏輯門搭建,還有乘法器、邏輯運算單元,等等。
? ? 21選擇器,相當于單刀雙擲開關,根據 s21 信號,決定 8 bit 輸出來自或左或右 8 bit 輸入端。
2 協同工作原理
整個數據通路從程序計數器 pc 開始,計數器從 0 開始輸出數字 0,1,2,3,4……。指令 RAM 和數據 RAM 中分別存儲程序代碼和數據。RAM 采用數字表示的位置訪問、存儲數據。根據計數器地址 0,1,2之類,將 RAM 中的數據分別放入指令寄存器 IR 和數據寄存器 DR。寄存器相當于容器、變量,存儲了 RAM 給它的數據。
指令寄存器中的指令碼解碼產生 CPU 控制指令,這些 0 和 1 分別表示低電平和高電平信號,而電平信號則控制諸如加法器進位與否,是否打開減法,是否使能寄存器寫入,選擇 21選擇器哪一個輸入作輸出,是否重置計數器,等等。所以,指令其實是控制 CPU 各部件協同工作的電信號。
數據寄存器中的數據分別走向加法器 adder 來進行加法、減法運算后流向 21選擇器,也可能直接流向 21選擇器等待選擇。21選擇器選擇后,數據進入累加寄存器 AC 。累加器的數據根據 ac 信號是否為高電平 1 ,來決定寫入與否。AC累加器的數據會參與下次計算或者根據 w 信號存入數據 RAM 中。
至此,我們完成了一次計算,程序計數器加 1,然后執行下一次計算。如果本條指令是跳轉指令的話,將跳轉目的地址直接賦值給程序計數器,程序重新地址開始執行。
三、 Python 實現 CPU 各組成部分
1 RAM 存儲器
我們用 list 來存儲數據。這是一個很簡單和直接的設計。
ramc = [0x18, 0x19, 0x1d, 0x02, 0x31, 0x30, 0x00]
對存儲器的讀寫,根據 pc 指針來,ramc[pc]=data 表示寫入內存,讀就是 ramc[pc]。
2 Adder 加法器
def adder(a=0, b=0, ci=0, sub=0):
return a-b+ci if sub == 1 else a+b+ci
真正的加法器使用邏輯門,相當于一堆開關按某種關系堆疊在一起,這里我們用高級語言模擬,極大簡化了實現。這個加法器實現了 a 和 b 的加法,同時 ci 表示進位,sub 表示減法。
3 Register 寄存器
寄存器采用 Python 的閉包概念來設計,這是為了用自由變量記住寄存器上次的狀態。當我們用 AC = register() 調用時,AC 相相當于返回的內部函數 register_inner,此時 temp 作為自由變量和 register_inner 同屬一個閉包。所以此后對 temp 變量讀、寫都是一個持久的變量。相當于維持住了狀態。
w 信號為 1 時寫入,相當于寄存器使能端 w。
def register():
temp = 0
def register_inner(data=0, w=0):
nonlocal temp
if w == 1:
temp = data
return temp
return register_inner
? ? 真實 CPU 設計當中,如何設計寄存器是一門大學問。即使在微機原理課程粗淺的 CPU 模型學習中,理解繼電器和 三極管能記憶,也需要費一番心思。本文用高級語言模擬底層硬件,我們只能再兜兜轉轉一次,所以此處需要深刻理解閉包概念。
4 8bit 21選擇器
21選擇器是在 sel 端為 1 時,返回 b 。當 sel 為零時,返回 a。也就是兩個輸入端選擇一個作為輸出。
def b8_21selector(a=0, b=0, sel=0):
return a if sel == 0 else b
四、集成 CPU
當我們集成 CPU 各部件時,首先將各部件新建出來,然后進行初始化,最后將 pc 置零,開始無限循環。
循環過程中,首先將程序指令 RAM 中的數據寫入指令寄存器,根據指令寄存器解碼各控制信號,此后操作都是在指令控制信號控制下進行。
首先如果 IR 指令寄存器中是 HLt 停機指令的話,那么系統 Break。否則根據 dr 決定是否將數據信號寫入 DR 數據寄存器。
對加法器的操作,是自動的,它的一個輸入是 AC 累加器存器,另一個輸入是 DR 數據寄存器,同時受到 sub 減法控制信號的控制。
加法運算器運算后,結果傳到 21選擇器,同數據總線上直接過來的數據一塊,等待 s21 信號選擇,再根據 ac 信號存進 AC 累加寄存器,以備下一計算。
zf 作為零標志位寄存器,如果 AC 累加器存起結果為零的話,則 zf 為 1。此時如果 pre 為 1 的話,那么就可以將 pc 設置為 DR 數據寄存器的值,實現了運算結果為零跳轉功能。否則繼續向下執行。
總體集成后,代碼如下:
def adder(a=0, b=0, ci=0, sub=0):
return a-b+ci if sub == 1 else a+b+ci
def b8_21selector(a=0, b=0, sel=0):
return a if sel == 0 else b
def register():
temp = 0
def register_inner(data=0, w=0):
nonlocal temp
if w == 1:
temp = data
return temp
return register_inner
def int2bin(data=0, length=8, tuple_=1, string=0, humanOrder=0):
#輔助函數,整數轉換為二進制字符串或者元祖。
r = bin(data)[2:].zfill(length)
r = r[::-1] if humanOrder == 0 else r
return r if string == 1 else tuple(int(c) for c in r)
def cpu():
pc = 0 # pc 計數器從 0 開始,無限循環。
IR, DR, AC = register(), register(), register() # 新建寄存器
ramc = [0x18, 0x19, 0x1d, 0x02, 0x31, 0x30, 0x00] # 初始化代碼
ramd = [10, 2, 3, 0xff, 0x06, 0x02] # 初始化數據
IR(0, w=1) # 初始化寄存器
DR(0, w=1)
AC(0, w=1)
while True:
IR(ramc[pc], w=1) # 指令讀寫
*_, pre, dr, ac, sub, w, s21 = int2bin(IR(), humanOrder=1) # 指令解碼
if IR() == 0:
break # HLT信號
DR(ramd[pc], w=dr) # 數據讀寫
r = adder(AC(), DR(), sub=sub) # 加法器自動加法
AC(b8_21selector(DR(), r, s21), w=ac) # 選擇器選擇后,累加寄存器讀寫
ramd[pc] = AC() if w else ramd[pc] # 根據 w 信號,數據寫入 RAM
zf = (AC() == 0) # 零標志位寄存器
pc = DR() if (pre == 1 and zf == True and s21 == 1) else pc + 1 # Jz 指令跳轉
pc = DR() if (pre == 1 and s21 == 0) else pc # 無條件跳轉 Jmp
print(AC())
if __name__ == '__main__':
cpu()
相關推薦
- 2022-08-22 PyCharm安裝庫numpy失敗問題的詳細解決方法_python
- 2023-02-27 一文帶你學習C/C++中的<Windows.h>庫_C 語言
- 2022-06-12 Python利用subplots_adjust方法解決圖表與畫布的間距問題_python
- 2022-03-24 樹莓派搭建nas服務器的詳細過程_Linux
- 2022-06-06 typescript中abstractClass(抽象類)、extends、abstract
- 2022-10-23 React日期時間顯示組件的封裝方法_React
- 2022-01-03 踩坑解決mongoose對已經存在的集合查詢,查詢條件不起限制作用的問題
- 2022-07-15 C#中Timer定時器類的簡單使用_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支