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