Python代碼模擬CPU工作原理
理解 CPU 工作原理,重要的是理解 pc 不停地自增地址,順序執(zhí)行程序指令。當(dāng)遇到跳轉(zhuǎn)指令時(shí),會(huì)將 pc 重置為新地址。在順序執(zhí)行程序指令的過(guò)程中,每一步都是解析程序指令、產(chǎn)生控制信號(hào),進(jìn)而控制所有 CPU 相關(guān)器件的工作狀態(tài),產(chǎn)生程序計(jì)算結(jié)果,保存進(jìn)各寄存器或者RAM 中。
本文使用四十行 Python 代碼來(lái)實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的 CPU。使它可編程,支持加減法運(yùn)算、讀寫(xiě)內(nèi)存、無(wú)條件跳轉(zhuǎn)、條件跳轉(zhuǎn)的功能。
Python層面的實(shí)驗(yàn)和書(shū)面上的概念,不足以支撐去論證從Python代碼到CPU執(zhí)行的復(fù)雜過(guò)程,也不足以支撐從CPU讀的內(nèi)存地址到Python內(nèi)存管理上的內(nèi)存地址之間復(fù)雜的關(guān)系,但算是針對(duì)此淺層現(xiàn)象的一個(gè)合理的猜想。
一、引言
從宏觀上,CPU 工作原理是讀取內(nèi)存數(shù)據(jù),在 ALU 中完成計(jì)算,然后保存進(jìn)內(nèi)存,輸入輸出系統(tǒng)完成了同其他外設(shè)交互;從中觀上看,CPU 工作原理就是本文講述的 pc 從 0 開(kāi)始,讀取程序指令寄存器,然后解析指令,控制各部件工作的具體過(guò)程;從微觀上看,pc 程序計(jì)數(shù)器、ALU 數(shù)字邏輯運(yùn)算單元,RAM 存儲(chǔ)器在內(nèi)的所有 CPU 相關(guān)部件,其實(shí)都是一個(gè)個(gè)三極管,這些三極管在電流作用下導(dǎo)通或者截止,完成了數(shù)字邏輯運(yùn)算、保持記憶狀態(tài)、產(chǎn)生脈沖信號(hào)的所有功能。
二、CPU工作原理
讓我們分別介紹各部件工作原理,之后介紹它們?cè)趺磪f(xié)同工作。
1 各部件工作原理
在真實(shí) CPU 中,都有相應(yīng)物理電路與其對(duì)應(yīng),它們的功能分別是:
pc 計(jì)數(shù)器,從 0 開(kāi)始產(chǎn)生 0,1,2,……計(jì)數(shù)可以清零,也可以從外部輸入一個(gè)數(shù),從這個(gè)數(shù)從新開(kāi)始計(jì)數(shù),這被稱(chēng)為置位。用于指示程序和數(shù)據(jù)存取位置。
RAM,存儲(chǔ)數(shù)據(jù)的隨機(jī)存儲(chǔ)器,支持根據(jù)地址(0x01 這種整形)讀取數(shù)據(jù),根據(jù)地址和寫(xiě)入信號(hào) w 寫(xiě)入數(shù)據(jù)。用于存儲(chǔ)程序和數(shù)據(jù)。
寄存器,存儲(chǔ) 8 bit 信息的存儲(chǔ)器,根據(jù) w 信號(hào)為 1 寫(xiě)入當(dāng)前數(shù)據(jù),w 為 0 表示讀取。類(lèi)似 RAM,但只能存儲(chǔ) 8 bit 信息。常用于存儲(chǔ)指令、地址和計(jì)算中間量。
加法器,完成兩數(shù)加減法運(yùn)算,sub 為 1 時(shí)表示減法,ci 為 1 時(shí)表示進(jìn)位。這個(gè)器件是核心器件,用于構(gòu)成 ALU(算數(shù)邏輯單元)。真實(shí) CPU 是采用邏輯門(mén)搭建,還有乘法器、邏輯運(yùn)算單元,等等。
21選擇器,相當(dāng)于單刀雙擲開(kāi)關(guān),根據(jù) s21 信號(hào),決定 8 bit 輸出來(lái)自或左或右 8 bit 輸入端。
2 協(xié)同工作原理
整個(gè)數(shù)據(jù)通路從程序計(jì)數(shù)器 pc 開(kāi)始,計(jì)數(shù)器從 0 開(kāi)始輸出數(shù)字 0,1,2,3,4……。指令 RAM 和數(shù)據(jù) RAM 中分別存儲(chǔ)程序代碼和數(shù)據(jù)。RAM 采用數(shù)字表示的位置訪問(wèn)、存儲(chǔ)數(shù)據(jù)。根據(jù)計(jì)數(shù)器地址 0,1,2之類(lèi),將 RAM 中的數(shù)據(jù)分別放入指令寄存器 IR 和數(shù)據(jù)寄存器 DR。寄存器相當(dāng)于容器、變量,存儲(chǔ)了 RAM 給它的數(shù)據(jù)。
指令寄存器中的指令碼解碼產(chǎn)生 CPU 控制指令,這些 0 和 1 分別表示低電平和高電平信號(hào),而電平信號(hào)則控制諸如加法器進(jìn)位與否,是否打開(kāi)減法,是否使能寄存器寫(xiě)入,選擇 21選擇器哪一個(gè)輸入作輸出,是否重置計(jì)數(shù)器,等等。所以,指令其實(shí)是控制 CPU 各部件協(xié)同工作的電信號(hào)。
數(shù)據(jù)寄存器中的數(shù)據(jù)分別走向加法器 adder 來(lái)進(jìn)行加法、減法運(yùn)算后流向 21選擇器,也可能直接流向 21選擇器等待選擇。21選擇器選擇后,數(shù)據(jù)進(jìn)入累加寄存器 AC 。累加器的數(shù)據(jù)根據(jù) ac 信號(hào)是否為高電平 1 ,來(lái)決定寫(xiě)入與否。AC累加器的數(shù)據(jù)會(huì)參與下次計(jì)算或者根據(jù) w 信號(hào)存入數(shù)據(jù) RAM 中。
至此,我們完成了一次計(jì)算,程序計(jì)數(shù)器加 1,然后執(zhí)行下一次計(jì)算。如果本條指令是跳轉(zhuǎn)指令的話(huà),將跳轉(zhuǎn)目的地址直接賦值給程序計(jì)數(shù)器,程序重新地址開(kāi)始執(zhí)行。
三、 Python 實(shí)現(xiàn) CPU 各組成部分
1 RAM 存儲(chǔ)器
我們用 list 來(lái)存儲(chǔ)數(shù)據(jù)。這是一個(gè)很簡(jiǎn)單和直接的設(shè)計(jì)。
ramc = [0x18, 0x19, 0x1d, 0x02, 0x31, 0x30, 0x00]
對(duì)存儲(chǔ)器的讀寫(xiě),根據(jù) pc 指針來(lái),ramc[pc]=data 表示寫(xiě)入內(nèi)存,讀就是 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
真正的加法器使用邏輯門(mén),相當(dāng)于一堆開(kāi)關(guān)按某種關(guān)系堆疊在一起,這里我們用高級(jí)語(yǔ)言模擬,極大簡(jiǎn)化了實(shí)現(xiàn)。這個(gè)加法器實(shí)現(xiàn)了 a 和 b 的加法,同時(shí) ci 表示進(jìn)位,sub 表示減法。
3 Register 寄存器
寄存器采用 Python 的閉包概念來(lái)設(shè)計(jì),這是為了用自由變量記住寄存器上次的狀態(tài)。當(dāng)我們用 AC = register() 調(diào)用時(shí),AC 相相當(dāng)于返回的內(nèi)部函數(shù) register_inner,此時(shí) temp 作為自由變量和 register_inner 同屬一個(gè)閉包。所以此后對(duì) temp 變量讀、寫(xiě)都是一個(gè)持久的變量。相當(dāng)于維持住了狀態(tài)。
w 信號(hào)為 1 時(shí)寫(xiě)入,相當(dāng)于寄存器使能端 w。
def register(): temp = 0 def register_inner(data=0, w=0): nonlocal temp if w == 1: temp = data return temp return register_inner
真實(shí) CPU 設(shè)計(jì)當(dāng)中,如何設(shè)計(jì)寄存器是一門(mén)大學(xué)問(wèn)。即使在微機(jī)原理課程粗淺的 CPU 模型學(xué)習(xí)中,理解繼電器和 三極管能記憶,也需要費(fèi)一番心思。本文用高級(jí)語(yǔ)言模擬底層硬件,我們只能再兜兜轉(zhuǎn)轉(zhuǎn)一次,所以此處需要深刻理解閉包概念。
4 8bit 21選擇器
21選擇器是在 sel 端為 1 時(shí),返回 b 。當(dāng) sel 為零時(shí),返回 a。也就是兩個(gè)輸入端選擇一個(gè)作為輸出。
def b8_21selector(a=0, b=0, sel=0): return a if sel == 0 else b
四、集成 CPU
當(dāng)我們集成 CPU 各部件時(shí),首先將各部件新建出來(lái),然后進(jìn)行初始化,最后將 pc 置零,開(kāi)始無(wú)限循環(huán)。
循環(huán)過(guò)程中,首先將程序指令 RAM 中的數(shù)據(jù)寫(xiě)入指令寄存器,根據(jù)指令寄存器解碼各控制信號(hào),此后操作都是在指令控制信號(hào)控制下進(jìn)行。
首先如果 IR 指令寄存器中是 HLt 停機(jī)指令的話(huà),那么系統(tǒng) Break。否則根據(jù) dr 決定是否將數(shù)據(jù)信號(hào)寫(xiě)入 DR 數(shù)據(jù)寄存器。
對(duì)加法器的操作,是自動(dòng)的,它的一個(gè)輸入是 AC 累加器存器,另一個(gè)輸入是 DR 數(shù)據(jù)寄存器,同時(shí)受到 sub 減法控制信號(hào)的控制。
加法運(yùn)算器運(yùn)算后,結(jié)果傳到 21選擇器,同數(shù)據(jù)總線(xiàn)上直接過(guò)來(lái)的數(shù)據(jù)一塊,等待 s21 信號(hào)選擇,再根據(jù) ac 信號(hào)存進(jìn) AC 累加寄存器,以備下一計(jì)算。
zf 作為零標(biāo)志位寄存器,如果 AC 累加器存起結(jié)果為零的話(huà),則 zf 為 1。此時(shí)如果 pre 為 1 的話(huà),那么就可以將 pc 設(shè)置為 DR 數(shù)據(jù)寄存器的值,實(shí)現(xiàn)了運(yùn)算結(jié)果為零跳轉(zhuǎn)功能。否則繼續(xù)向下執(zhí)行。
總體集成后,代碼如下:
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): #輔助函數(shù),整數(shù)轉(zhuǎn)換為二進(jìn)制字符串或者元祖。 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 計(jì)數(shù)器從 0 開(kāi)始,無(wú)限循環(huán)。 IR, DR, AC = register(), register(), register() # 新建寄存器 ramc = [0x18, 0x19, 0x1d, 0x02, 0x31, 0x30, 0x00] # 初始化代碼 ramd = [10, 2, 3, 0xff, 0x06, 0x02] # 初始化數(shù)據(jù) IR(0, w=1) # 初始化寄存器 DR(0, w=1) AC(0, w=1) while True: IR(ramc[pc], w=1) # 指令讀寫(xiě) *_, pre, dr, ac, sub, w, s21 = int2bin(IR(), humanOrder=1) # 指令解碼 if IR() == 0: break # HLT信號(hào) DR(ramd[pc], w=dr) # 數(shù)據(jù)讀寫(xiě) r = adder(AC(), DR(), sub=sub) # 加法器自動(dòng)加法 AC(b8_21selector(DR(), r, s21), w=ac) # 選擇器選擇后,累加寄存器讀寫(xiě) ramd[pc] = AC() if w else ramd[pc] # 根據(jù) w 信號(hào),數(shù)據(jù)寫(xiě)入 RAM zf = (AC() == 0) # 零標(biāo)志位寄存器 pc = DR() if (pre == 1 and zf == True and s21 == 1) else pc + 1 # Jz 指令跳轉(zhuǎn) pc = DR() if (pre == 1 and s21 == 0) else pc # 無(wú)條件跳轉(zhuǎn) Jmp print(AC()) if __name__ == '__main__': cpu()
到此這篇關(guān)于Python代碼模擬CPU工作原理的文章就介紹到這了,更多相關(guān)Python模擬CPU內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- python實(shí)現(xiàn)監(jiān)控指定進(jìn)程的cpu和內(nèi)存使用率
- Python限制內(nèi)存和CPU使用量的方法(Unix系統(tǒng)適用)
- python實(shí)現(xiàn)可視化動(dòng)態(tài)CPU性能監(jiān)控
- python獲取當(dāng)前計(jì)算機(jī)cpu數(shù)量的方法
- 10種檢測(cè)Python程序運(yùn)行時(shí)間、CPU和內(nèi)存占用的方法
- 使用Python獲取CPU、內(nèi)存和硬盤(pán)等windowns系統(tǒng)信息的2個(gè)例子
- python計(jì)算程序開(kāi)始到程序結(jié)束的運(yùn)行時(shí)間和程序運(yùn)行的CPU時(shí)間
相關(guān)文章
flask后端request獲取參數(shù)的幾種方式整理
這篇文章主要為大家介紹了flask后端request獲取參數(shù)的幾種方式整理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Python3 獲取一大段文本之間兩個(gè)關(guān)鍵字之間的內(nèi)容方法
今天小編就為大家分享一篇Python3 獲取一大段文本之間兩個(gè)關(guān)鍵字之間的內(nèi)容方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-10-10Python中easy_install 和 pip 的安裝及使用
本篇文章主要介紹了Python中easy_install 和 pip 的安裝及使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06python中使用urllib2偽造HTTP報(bào)頭的2個(gè)方法
這篇文章主要介紹了python中使用urllib2偽造HTTP報(bào)頭的2個(gè)方法,即偽造http頭信息,需要的朋友可以參考下2014-07-07