二十、內(nèi)存
所謂飯前便后要洗手,一個完整的衛(wèi)生間,除了有若干坑位,還得有洗手池配套。類似的,學(xué)習(xí)CPU的結(jié)構(gòu)原理,也得把“內(nèi)存”牽出來溜溜,否則無法說明計算機編程的本質(zhì)。CPU+內(nèi)存才是一個完整的計算機(核心),才能展現(xiàn)出CPU的功能。由此也能聯(lián)系到,將鼠標鍵盤顯示器等稱為“外部”設(shè)備的道理。
最基本的內(nèi)存單元能夠存儲和讀寫一個位(bit),是由一個上升沿D觸發(fā)器和傳輸門組成,如下圖所示。傳輸門電路我沒有找到只有一位的,拿這個四位GAA的湊合看吧。
內(nèi)存單元的符號如下圖所示。(取自《穿》)
把8個bit單元的讀寫端分別連起來,就可以存儲一個字節(jié)(8bit)。下圖是能夠存儲5bit的內(nèi)存單元。(取自《穿》)此圖所示的結(jié)構(gòu),我們稱之為“一層”。
存儲4bit的一層的符號如下圖所示。這時我學(xué)會了multisim12里的“層次塊”這個東西,下圖就是用層次塊畫的,這樣可以將復(fù)雜的電路封裝起來,省地方了,還能復(fù)用。所以說模塊化的思想在硬件設(shè)計里就有了。(“用層次塊替換…”和VS里的“Extract Method…”功能是何其類似!)
用一層一層的內(nèi)存單元,即可構(gòu)成存儲器。對于有8層的存儲器,需要3個bit表示需要讀寫的層數(shù)(23=8)。地址譯碼器的作用是:輸入101時,輸出的第5個(從0開始計數(shù))引腳為1,其余均為0。有了地址譯碼器,存儲器對外只需很少的地址線(例如10根)即可使用很多層(例如1024層)。這也符合了軟件設(shè)計中接口盡可能簡單的原則。本文所用的RAM存儲器的結(jié)構(gòu)如下圖所示。其中左邊的“3-8translator”就是譯碼器。
其層次塊符號如下圖所示。
這個RAM有三條地址線(Addr3、Addr2和Addr1),能夠表示23=8個字(層),每個字的長度是4bit。這個小小的RAM剛好夠存儲(5+1+2+4)這個示例的指令和數(shù)據(jù),下面就用這個RAM繼續(xù)進化CPU。
這里也順便把“3-8translator”譯碼器的電路實現(xiàn)貼出來吧,如下圖所示。
二十一、9位循環(huán)移位寄存器
在version2里用的“2位循環(huán)移動寄存器”只需要一個乒乓觸發(fā)器(詳見上一篇)就可以了,但是后面要做的全自動控制器,需要一個“9位循環(huán)移位寄存器”,且這個寄存器要在加電時自動將第一個輸出管腳置為1,其余為0。具有這樣的功能的寄存器如下圖所示。
如上圖所示,第二行有4個D↑觸發(fā)器,其中最左邊的那個負責(zé)第一個輸出管腳,即應(yīng)該在加電時就置為1的那個管腳。這是通過左下方的電路實現(xiàn)的,其原理大家自己琢磨吧,無非是利用反饋電路實現(xiàn)了只生效一次這個功能而已。“9位循環(huán)移位寄存器”的符號如下圖所示,其中“D0”是第一個輸出管腳。
還有一個3bit的計數(shù)器,在上一篇里已經(jīng)提過計數(shù)器,這里直接上圖。
其層次塊表示如下圖所示。
二十二、自動控制器
有了內(nèi)存,我們就要把指令和數(shù)據(jù)存進去。存儲數(shù)據(jù)很好理解,是多少就寫入多少。存指令之前,我們需要為每條指令分配一個4位的編碼,比如0000表示Load,1111表示Add,然后用這個指令碼控制“KLoad”和“KAdd”的開閉,所以這又是一個轉(zhuǎn)換電路。有了這個轉(zhuǎn)換電路,就可以只用“K”開關(guān)來完成“準備指令、準備數(shù)據(jù)、執(zhí)行”這些操作了。全部自動化的CPU如下圖所示。我們將這個版本的CPU稱為version3的CPU。
這里的“ALU”是用層次塊表示的version1里的電路,因為不這樣的話,電路太大,而且不容易重點突出自動控制器的工作流程。同樣的,“ShiftRegister9bit”、“Translator”、“Counter3bit”、“RAM8F4bit”都是層次塊表示的電路。
“ShiftRegister9bit”會依次將輸出端置為1,這可以從上方的三個數(shù)值顯示器看出來。“Counter3bit”是3bit的計數(shù)器。“RAM8F4bit”是8層(每層4bit)的內(nèi)存。“Translator”實現(xiàn)了控制信號的自動控制,“Translator”的內(nèi)部實現(xiàn)如下圖所示。
Version3所示的下半部分展示了譯碼電路,即把代表指令的4bit信號轉(zhuǎn)換為指令信號的電路。由于我們指定Load和Add指令的代碼(0000和1111)都是很規(guī)則的,所以譯碼電路也比較簡單,如下圖所示。
Version3的CPU用“9位循環(huán)移位寄存器”等器件實現(xiàn)了“取指令、分析指令,取數(shù)、執(zhí)行”的全部自動化,其中取指令、分析指令、取數(shù)、執(zhí)行分別占用了移位寄存器的t0- t2、t3、t4-t6、t7-t8這9個階段。
注:如果用振蕩器替換了“K”,這個CPU會不停地運轉(zhuǎn)下去,這樣就得不到我們想要的結(jié)果0xC(十進制的12)了。所以還需要添加“停機”指令。不過到這里已經(jīng)說清了CPU的控制器是如何一步步實現(xiàn)自動化的,不再繼續(xù)講述如何添加新的指令。
二十三、最原始的機器語言編程
在給出上文的自動控制器里,我們只說了從內(nèi)存里取指令和數(shù)據(jù),而沒有說這些指令和數(shù)據(jù)是如何寫進去的。其實寫進去的過程就是(機器語言)編程的過程。最簡單的,你可以用撥動開關(guān)的方式,調(diào)整好要寫入的位置,再調(diào)整好要寫入的數(shù)值,把指令和數(shù)據(jù)一個字一個字地寫入內(nèi)存。最初的計算機編程就是用類似這樣的方式(打孔紙帶)編程的。(這個過程實在無聊,本文就不展示了,有興趣的話自己拿multisim玩玩就好~)
用助記符和一些宏來代替機器碼,這就是匯編語言。用C語言這種方式封裝了匯編語言的編程方法,就是面向過程編程。用C++\C# \Java這樣的語言封裝了面向過程的語言,就是面向?qū)ο蟮木幊谭椒āS胕f(。。){…}代替JMP指令這種東西比較容易想象,但用“封裝繼承多態(tài)”這種飄渺的概念代替面向過程編程就有點困難了。我在另一篇文章《用C表達面向?qū)ο笳Z言的機制——C#版》中作了分析和總結(jié),現(xiàn)在終于算是從繼電器一路走到面向?qū)ο缶幊塘恕?/p>