從0開始構(gòu)建計(jì)算機(jī)

序言

很多人都聽過(guò)計(jì)算機(jī)是由0、1以及運(yùn)算組成的,或在計(jì)算機(jī)基礎(chǔ)學(xué)習(xí)時(shí)候也都聽說(shuō)過(guò)馮諾依曼設(shè)計(jì)思想:基于二進(jìn)制的存儲(chǔ)和運(yùn)算……我相信大多數(shù)人都會(huì)對(duì)此表示神奇和懷疑:“通過(guò)0和1竟然能實(shí)現(xiàn)這么強(qiáng)大的功能?”對(duì)此大家肯定有過(guò)或多或少的學(xué)習(xí)和了解。隨著知識(shí)深度和廣度的增加:編程語(yǔ)言、計(jì)算機(jī)原理(CPU/匯編)、編譯原理、可計(jì)算理論等等,對(duì)于沒(méi)學(xué)習(xí)數(shù)字邏輯電路的人來(lái)說(shuō),最低層的機(jī)制始終缺少了那么一環(huán)!那么其底層到底是如何從通過(guò)何種形式一步步構(gòu)建為CPU乃至計(jì)算機(jī)的呢?真的是復(fù)雜、很難理解嗎?

一個(gè)偶然的機(jī)會(huì)我在油管上看到一個(gè)牛人用面包板、基礎(chǔ)元件逐步構(gòu)建了一個(gè)可以編程、運(yùn)行的計(jì)算機(jī)雛形,佩服的同時(shí)也打開了我的深入探索之心,在學(xué)習(xí)過(guò)程種時(shí)不時(shí)的就會(huì)有豁然開朗的感覺(jué),很多的干貨。隨后我通過(guò)邏輯電路軟件,嘗試去繪制構(gòu)建,定義指令集、構(gòu)建CPU,最終成功的實(shí)現(xiàn)了可以運(yùn)行的自動(dòng)機(jī)!盡管受限于內(nèi)存規(guī)模、指令集數(shù)量,功能還相對(duì)較弱,但也是五臟俱全,理論上擴(kuò)展下就可以是圖靈完備的。因此我將此整理出來(lái),和大家一起分享這整個(gè)過(guò)程:從0開始,突破認(rèn)知,打開新思路,徒手構(gòu)建一個(gè)可以運(yùn)行的8位計(jì)算機(jī)!這將會(huì)讓大家對(duì)計(jì)算機(jī)、CPU的機(jī)制有深入理解,結(jié)合其他知識(shí),打通從底層到高層的每個(gè)環(huán)節(jié)認(rèn)知!

本文知識(shí)點(diǎn)每一節(jié)難度都不大,不需要很深的知識(shí)儲(chǔ)備,中學(xué)的知識(shí)儲(chǔ)備即可以看懂,不過(guò)需要靜心研讀。理解了本文的思想,你離馮諾依曼、圖靈等大神就更近一步!另外,對(duì)很多事情的認(rèn)知或許也會(huì)有些不一樣的靈感!

作者:吳亞萌
時(shí)間:2022-04

一、基礎(chǔ)知識(shí)

相信愿意讀這篇文章的人對(duì)此都有深入的了解,這里給接觸不多的朋友以簡(jiǎn)單的介紹,熟悉的小伙伴請(qǐng)直接跳過(guò)對(duì)應(yīng)部分。

1. 二進(jìn)制

1.1. 數(shù)的表示

組成數(shù)的字符集中只有2個(gè)符號(hào):0和1,計(jì)算時(shí)每個(gè)位置滿2進(jìn)1。
自然數(shù)的序列就是:0,1,10,11,100,101,110,111,1000,...
在其上亦可進(jìn)行加、減、乘、除的運(yùn)算,這里不做闡述。

1.2. 單位(位、字節(jié))

二進(jìn)制數(shù)中的每一個(gè)0或1符號(hào)叫做1“位”(bit),是最小單位。
8位組成1“字節(jié)”(byte),是最常用的單位。1字節(jié)可以用于表達(dá)1個(gè)英文字符。
當(dāng)然還有更大的單位,如KB,MB,GB,TB,PB等,每級(jí)之間都說(shuō)是2^{10}=1024倍(接近1000)。
每1位有2個(gè)狀態(tài),1字節(jié)(8位)就2^8=256種狀態(tài)。
之所以1字節(jié)選擇8位,是因?yàn)?字節(jié)需要能夠表示:英語(yǔ)字母(大小寫)、數(shù)字、常用符號(hào)等,接近128個(gè),這就是最基礎(chǔ)的ASCII表,再加上一個(gè)擴(kuò)展標(biāo)記或預(yù)留也就是8位。

1.3. 負(fù)數(shù)的表示(補(bǔ)碼)

一個(gè)n位的二進(jìn)制數(shù)有2^n狀態(tài),可以表示0 ~ 2^n-1這些自然數(shù)。假如我們需要表示負(fù)數(shù),可以怎么做呢?
以1字節(jié)為例,想象一下,把這256個(gè)符號(hào)按0 ~ 255順時(shí)針排成時(shí)鐘一樣的圓環(huán),順時(shí)針移動(dòng)1格則加1,逆時(shí)針移動(dòng)1格減小1。因此從0開始逆時(shí)針旋轉(zhuǎn)的一半部分用來(lái)表示負(fù)數(shù)!這就是補(bǔ)碼的思想。
因?yàn)槲覀儗⑤^大的一半的符號(hào)記作負(fù)數(shù),也就剛好最高位是1的數(shù)表示負(fù)數(shù)。
負(fù)數(shù)補(bǔ)碼和其相反數(shù)關(guān)于0是對(duì)稱的,不難得出他們的關(guān)系是:取反再加1。
在1字節(jié)下,最大的有符號(hào)整數(shù)是01111111_b,對(duì)應(yīng)十進(jìn)制127;無(wú)符號(hào)最大值11111111_b表示-1,加1剛好變?yōu)?(最高位進(jìn)位溢出忽略);最小的數(shù)是10000000_b,表示十進(jìn)制的-128。
一般的,n位有符號(hào)二進(jìn)制數(shù)可以表示的有符號(hào)整數(shù)范圍是:-2^{n-1} ~ 2^{n-1}-1。

2. 十六進(jìn)制

二進(jìn)制表示數(shù)時(shí)位數(shù)比較多,書寫和表達(dá)不方便,因此常用十六進(jìn)制來(lái)替代。
十六進(jìn)制是選擇0~9、A-F(大小寫不區(qū)分)這16個(gè)符號(hào)來(lái)計(jì)數(shù),以0x打頭。
1個(gè)十六進(jìn)制位剛好和4位二進(jìn)制數(shù)對(duì)應(yīng),對(duì)應(yīng)表如下:

十進(jìn)制 二進(jìn)制 十六進(jìn)制 十進(jìn)制 二進(jìn)制 十六進(jìn)制
0 0000 0 8 1000 8
1 0001 1 9 1001 9
2 0010 2 10 1010 A
3 0011 3 11 1011 B
4 0100 4 12 1100 C
5 0101 5 13 1101 D
6 0101 6 14 1110 E
7 0111 7 15 1111 F

十六進(jìn)制、十進(jìn)制、二進(jìn)制相互轉(zhuǎn),通過(guò)1,2,4,8(權(quán)重)組合相加即可快速換算獲得。
一個(gè)字節(jié)可以用2位16進(jìn)制字符來(lái)表示。最基礎(chǔ)的ASCII表設(shè)計(jì)時(shí),也是按16進(jìn)制劃段:0x20對(duì)應(yīng)字符空格,隨后是常見符號(hào);0x30對(duì)應(yīng)字符'0',隨后數(shù)字符號(hào);0x40對(duì)應(yīng)字符'A',隨后是大寫字母;0x60對(duì)應(yīng)字符'a',隨后是小寫字母。

3. 布爾邏輯

布爾邏輯相信大家也再熟悉不過(guò)了,只有真True(用1表示)、假False(用0表示)兩種狀態(tài)。
其上的常見運(yùn)算有“與”、“或”、“非”,我們用 &, |, ! 符號(hào)表示。
與運(yùn)算:0&0=0, 0&1=1&0=0, 1&1=1
或運(yùn)算:0|0=0, 0|1=1|0=1, 1|1=1
非運(yùn)算:!0=1, !1=0

除了這3個(gè)運(yùn)算,還有其他的衍生運(yùn)算符,比如異或、與非、或非等。
異或運(yùn)算,輸入相同則為0,不同則為1,正好對(duì)應(yīng)不進(jìn)位加法
“與非”和“或非”是在“與”、“或”的輸出上再取反。

3.1. 等價(jià)性

不難發(fā)現(xiàn)邏輯運(yùn)算由“與”、“非”可以得到“或”,由“或”、“非”可以得到“與”:
a|b=!(!a \& !b)
a\&b=!(!a|!b)

4. 邏輯電路

在現(xiàn)實(shí)世界,我們利用電壓的高低表示0和1,用物理電路來(lái)實(shí)現(xiàn)上述邏輯運(yùn)算,簡(jiǎn)稱邏輯門。
邏輯門的實(shí)現(xiàn)大致上可以簡(jiǎn)單理解為:通過(guò)開關(guān)的串聯(lián)、并聯(lián)、短路方式來(lái)實(shí)現(xiàn)“與”、“或”、“非”。這里僅是為了方便大家理解,實(shí)際過(guò)程有差異,有興趣的同學(xué)自行搜索。而晶體管就是基本的電子開關(guān)元件,也是芯片的主要組成。

在邏輯門電路中,“與非門”(或者“或非門”)這1個(gè)邏輯門是全能的! 因?yàn)閷蓚€(gè)輸入連接在一起就是“非門”,結(jié)合上章節(jié)的等價(jià)性可得。也正因如此,理論上芯片制造時(shí)的電路是可以只采用“與非門”來(lái)實(shí)現(xiàn)!

4.1. 邏輯門符號(hào)

“與、或、非”的邏輯門常用的符號(hào)如下:

與或非.png

“與非”、“或非”門的圖形是在“與”、“或”門的輸出段多一個(gè)圓孔,表示取反:

與非或非.png

4.2. 三態(tài)門(Tri-State)

在實(shí)際的電路中,其實(shí)除了低電壓表示0、高電壓表示1之外,還有一種 “高阻態(tài)(High Impendance)”,高阻態(tài)相當(dāng)于斷開狀態(tài)。
在礎(chǔ)元件中有“三態(tài)門(Tri-State)”組件,有輸入、控制和輸出端,其作用有點(diǎn)像“與門”的開關(guān),區(qū)別在于:控制端輸入0時(shí),輸出和輸入是斷開的;控制端輸入1時(shí),內(nèi)部導(dǎo)通,輸出等于輸入。符號(hào)如下:

Tri-State_e.png

4.3. 總線元件(Bus)

在邏輯電路的設(shè)備連接中,1個(gè)設(shè)備的輸出可以和多個(gè)下游設(shè)備連接,而任何設(shè)備的輸入只能接收1個(gè)明確信號(hào)的輸出,否則多個(gè)信號(hào)可能會(huì)沖突,出現(xiàn)錯(cuò)誤。
為了能讓多個(gè)源設(shè)備輸出到相同的下游設(shè)備,需要對(duì)這些源設(shè)備不同時(shí)輸出信號(hào)進(jìn)行聚合,在邏輯電路中需要使用Bus元件,輸入端可以是2個(gè)或者多個(gè),這里Bus的輸入端就需要同一時(shí)刻只能有一個(gè)有數(shù)據(jù),其他都處于斷開的高阻態(tài)(用三態(tài)門):

BUS-2.png

二、功能模塊構(gòu)建

根據(jù)馮諾依曼的計(jì)算機(jī)體系,計(jì)算機(jī)需要包含5大設(shè)備:輸入、存儲(chǔ)、運(yùn)算、控制、輸出。其中輸入和輸出比較簡(jiǎn)單,針對(duì)其余部分存儲(chǔ)、運(yùn)算、控制,我們將會(huì)在后面的章節(jié)逐步解鎖。

1. 工具選擇

為了做邏輯電路的學(xué)習(xí)和實(shí)踐,我們需要使用相關(guān)軟件支撐。經(jīng)過(guò)我的對(duì)比,最終選出比較理想的工具 logic.ly (https://logic.ly/demo/),其優(yōu)點(diǎn)是界面簡(jiǎn)潔、直觀以及美觀,布線可調(diào)整,最關(guān)鍵的是可以進(jìn)行邏輯封裝,制作和查看各種模塊,進(jìn)而構(gòu)建復(fù)雜系統(tǒng)。同時(shí)該軟件也支持在瀏覽器里使用,非常便捷!

該軟件種的主要輸入是開關(guān),輸出是燈泡。
以最簡(jiǎn)單的直連圖為例,輸出等于輸入,白色表示0,藍(lán)色為1(另外灰色表示未連接狀態(tài),紅色表示錯(cuò)誤):

輸入輸出.gif

在logic.ly中三態(tài)門如下,Control端為0,輸出線是“灰色”表示斷開狀態(tài)(燈泡也是微紅燈絲)。

Tri-State.png

集成封裝:工具中可以通過(guò)選中完整功能的電路圖,通過(guò)“集成”按鈕可以對(duì)模塊進(jìn)行封裝,在此之前需要對(duì)輸入(開關(guān))、輸出(燈泡)設(shè)置名稱,封裝后對(duì)應(yīng)模塊/芯片的引腳。封裝后的模塊可以被其他組件直接引用,該模塊的名稱可以修改,但是內(nèi)部結(jié)構(gòu)不可變更。

2. 常見結(jié)構(gòu)

這些“與、或、非”邏輯門有什么作用呢?我們先從幾個(gè)簡(jiǎn)單的結(jié)構(gòu)說(shuō)起,作為熱身。

2.1. 與門開關(guān)

將“與門”的一個(gè)輸入連接控制信號(hào),另一個(gè)輸入和輸出作為通道??刂菩盘?hào)為0時(shí),輸出恒為0;控制信號(hào)為1時(shí),輸出等于輸入。

2.2. 或門匯聚

“或門”常用于將多個(gè)信號(hào)匯聚(任意一個(gè)輸入為1時(shí)就輸出為1)。

2.3. 選擇器(Selector)

利用“與、或、非”我們可以構(gòu)造“選擇器”,用于兩個(gè)數(shù)據(jù)源時(shí)二選一輸出,通過(guò)“選擇開關(guān)”進(jìn)行切換。思路是:1個(gè)非門將選擇信號(hào)分離出一個(gè)反向信號(hào),然后通過(guò)2個(gè)“與門”分別作篩選開關(guān),將二個(gè)數(shù)據(jù)源二選其一,再利用“或門”將兩路輸出聚合,結(jié)構(gòu)圖如下:

select-1.png

當(dāng)Switch為0是,輸出等于A;Switch為1時(shí),輸出等于B。
將上圖導(dǎo)出為Select-1模塊:

select-1_chip.png

輸出Q通過(guò)選擇開關(guān)S控制來(lái)源:是S=0時(shí),輸出Q是從A獲??;S=1時(shí)從B獲取。
然后利用多個(gè)Select-1并聯(lián)成多位的選擇器,比如8位選擇器封裝后圖形如下(看起來(lái)很大,功能確很簡(jiǎn)單):

select-8封裝.png

3. 存儲(chǔ)的基本結(jié)構(gòu)

存儲(chǔ)是一切的基礎(chǔ),因此我們首先需要設(shè)計(jì)具備存儲(chǔ)的基礎(chǔ)結(jié)構(gòu)或者元件。
一般的簡(jiǎn)單連接(無(wú)環(huán)的樹形結(jié)構(gòu))組網(wǎng)下,所有的輸出是輸入函數(shù),所以是無(wú)法實(shí)現(xiàn)存儲(chǔ)功能的!
因此需要考慮帶環(huán)路、反饋(輸出接到輸入)的結(jié)構(gòu),我們從最簡(jiǎn)單的情況考慮:
采用1個(gè)“或門”的反饋電路如下圖,可以接收1信號(hào),存儲(chǔ)內(nèi)容從0變?yōu)?,但是一旦變?yōu)?后,系統(tǒng)則無(wú)法對(duì)輸入做出反應(yīng),儲(chǔ)存的內(nèi)容將會(huì)不會(huì)改變。演示圖如下:

OR回路.gif

“與門”有類似的作用,但是剛好相反,可以接收0信號(hào),存儲(chǔ)從1變?yōu)?。

3.1. AND-OR鎖存器(AND-OR Latch)

從上面的2種回環(huán)結(jié)構(gòu)圖來(lái)看,“或門”可以存儲(chǔ)1,“與門”可以存儲(chǔ)0,因此考慮將兩者結(jié)合起來(lái):

AND_OR鎖存器.png

通過(guò)Set輸入1將存儲(chǔ)變?yōu)?,隨后Set變?yōu)?也不再影響存儲(chǔ)的內(nèi)容;
通過(guò)Reset輸入1將存儲(chǔ)變?yōu)?,隨后Reset變?yōu)?也不再影響存儲(chǔ)的內(nèi)容。
如此,我們就實(shí)現(xiàn)了存儲(chǔ)的基本單元,我們稱之為鎖存器(Latch),上圖被稱為AND-OR鎖存器。

3.2. SR鎖存器(SR Latch)

還有另外一種結(jié)構(gòu)更簡(jiǎn)單、通過(guò)兩個(gè)“或非”門組成的Set-Reset Latch。“或非”門相當(dāng)于一個(gè)多輸入的非門,只要有一個(gè)輸入是1,輸出則為0。兩個(gè)“或非”門相互循環(huán)對(duì)接,起到了相互取反設(shè)置的作用:

SR鎖存器.png

通過(guò)Set輸入1將存儲(chǔ)Q變?yōu)?;通過(guò)Reset將Q變?yōu)?;圖中Q’和Q始終相反。
從圖中可以看出,若在某時(shí)刻兩個(gè)“或非”門的輸出相同,且兩個(gè)元件“完全一致”,則會(huì)出現(xiàn)兩個(gè)元件同步不斷取反震蕩過(guò)程,直到Set或者Reset輸入1。在logic.ly工具中,該結(jié)構(gòu)也是多個(gè)基礎(chǔ)元件的組成,在工具啟動(dòng)時(shí)或某些狀態(tài)下還真會(huì)出現(xiàn)這種頻閃的不穩(wěn)定狀態(tài),此時(shí)需要對(duì)元件觸發(fā)Reset進(jìn)行重置。而在現(xiàn)實(shí)中因?yàn)樵O(shè)備不可能會(huì)完全一致,所以不會(huì)出現(xiàn)震蕩的情況,通電后Q是隨機(jī)的0或1。

3.3. D鎖存器(D Latch)

在上述的鎖存器基礎(chǔ)上稍微擴(kuò)展,實(shí)現(xiàn)對(duì)單一Data輸入(0或1)的存儲(chǔ)。為了控制輸入時(shí)機(jī),還需要有個(gè)Store開關(guān),用于確認(rèn)存儲(chǔ),此結(jié)構(gòu)稱為Data Latch,結(jié)構(gòu)如下:

D-Latch.png

上圖的右側(cè)是采用AND-OR鎖存器,使用SR鎖存器亦可,并且大多應(yīng)用中主要使用SR結(jié)構(gòu)。此處為了降低logic.ly工具中“頻閃”的不穩(wěn)定概率,我們選擇使用AND-OR結(jié)構(gòu)。
左側(cè)一半是一個(gè)典型的選擇電路:1個(gè)非門將輸入信號(hào)分離出一個(gè)反向信號(hào),然后通過(guò)2個(gè)“與門”分別作為開關(guān),篩選其中之一(另外經(jīng)常下游還會(huì)有個(gè)“或門”將兩路輸出聚合)。
將此結(jié)構(gòu)導(dǎo)出為D Latch模塊(2個(gè)輸入、1個(gè)輸出)。導(dǎo)出的模塊可直接用于其他邏輯電路圖中,封裝后的模塊圖形如下:

D-Latch封裝.png

3.4. D觸發(fā)器(D Flip-Flop)

上述的D鎖存表面上看可以用于1位二進(jìn)制的存儲(chǔ),然而在多系統(tǒng)交互中,為了能讓多個(gè)輸入的時(shí)機(jī)一致且可控,我們需要通過(guò)時(shí)鐘信號(hào)0/1震蕩的矩形波)統(tǒng)一協(xié)調(diào)控制。并且在實(shí)踐中會(huì)發(fā)現(xiàn),需要做成二級(jí)級(jí)聯(lián)結(jié)構(gòu)以實(shí)現(xiàn)“瞬時(shí)的讀取”:由2個(gè)D鎖存器串聯(lián)組成,結(jié)構(gòu)圖如下,當(dāng)外部時(shí)鐘信號(hào)Clock為0時(shí),Data信號(hào)立即存儲(chǔ)在第1個(gè)鎖存器中,并作為第2個(gè)鎖存器的輸入,當(dāng)時(shí)鐘信號(hào)變?yōu)?時(shí),第2個(gè)鎖存器將鎖存器1中存儲(chǔ)的數(shù)據(jù)也存儲(chǔ)在自身中,此結(jié)構(gòu)稱為D觸發(fā)器(Data Flip-Flop),為了能快速清空2個(gè)鎖存器中內(nèi)容,我們加入了Reset輸入(首次看圖理解時(shí)1個(gè)與門、2個(gè)或門是導(dǎo)通的,可以忽略),邏輯電路圖如下:

Flip-Flop.png

D觸發(fā)器存儲(chǔ)過(guò)程需要一個(gè)時(shí)鐘信號(hào)從0變?yōu)?觸發(fā),“觸發(fā)器”因此得名。時(shí)鐘信號(hào)改變時(shí)鎖存器1中的內(nèi)容是穩(wěn)定的,最終兩個(gè)鎖存器中內(nèi)容一致,且都是時(shí)鐘信號(hào)即將改變那個(gè)時(shí)刻的Data值。
將此結(jié)構(gòu)導(dǎo)出為D Flip-Flop模塊,D為數(shù)據(jù)輸入,>表示時(shí)鐘信號(hào),CLR表示重置清空。

D-Flip-Flop封裝1.png

后續(xù)我們將會(huì)發(fā)現(xiàn),Clock、Clear是許多模塊的基礎(chǔ)輸入,除此之外還會(huì)有“允許輸入Enable”、“允許輸出Output Control”等。
在logic.ly中已經(jīng)有了現(xiàn)成的D Flip-Flop模塊,自帶模塊比我們創(chuàng)建的多出一個(gè)PRE’,用來(lái)重置Q’的,給恒定輸入1即可。另外該組件的清除信號(hào)CLR’是Clear的反,使用時(shí)需要注意。在后續(xù)構(gòu)建中可以選用自己構(gòu)建的,或者工具自帶的都可以。

D-Flip-Flop自帶.png

3.5. T觸發(fā)器(T Flip-Flop)

和D觸發(fā)器類似的,我們可以設(shè)計(jì)一個(gè)根據(jù)時(shí)鐘信號(hào)實(shí)現(xiàn)存儲(chǔ)的內(nèi)容0~1來(lái)回切換的元件,效果和生活中常見的“按鈕開關(guān)”一樣:按一次開,再按一次關(guān)閉。
在D觸發(fā)器基礎(chǔ)上調(diào)整,將數(shù)據(jù)輸入D取消,增加允許切換開關(guān)EN:EN為1時(shí),時(shí)鐘信號(hào)觸發(fā)動(dòng)作,將輸出的反Q’作為輸入傳入,從而實(shí)現(xiàn)內(nèi)容0~1切換;EN為0時(shí)將Q作為輸入,從而實(shí)現(xiàn)存儲(chǔ)內(nèi)容不變。這里不再繪制圖像,大家可以自行嘗試?yán)L制,logic.ly工具中提供現(xiàn)成的模塊(其中PRE’也是用來(lái)預(yù)置內(nèi)容,不使用時(shí)給恒定1輸入即可):

T-Flip-Flop原生.png

T觸發(fā)器僅在時(shí)鐘信號(hào)從0變?yōu)?時(shí)觸發(fā)存儲(chǔ)的Q內(nèi)容將取反(Q和Q’互換)。

4. 寄存器(Register)

學(xué)過(guò)匯編語(yǔ)言的同學(xué)知道,中央處理器(CPU)中的基本單元就是寄存器就是存儲(chǔ)固定位數(shù)二進(jìn)制數(shù)的元件),整個(gè)CPU的運(yùn)行都是依托于寄存器的操作,因此我們首先要構(gòu)造寄存器。
上述的D Flip-Flop是具有“1位”存儲(chǔ)功能的,通過(guò)并聯(lián)4(或8)個(gè)即可做成4(或8)位寄存器。為了能夠?qū)ζ鋵懭霑r(shí)機(jī)進(jìn)行控制,我們還需要對(duì)寄存器增加“允許輸入”開關(guān)。
我們使用上面封裝的D Flip-Flop來(lái)構(gòu)建1位寄存器,輸入端我們?cè)黾釉试S輸入Enable開關(guān),采用典型的選擇電路,控制后端D觸發(fā)器的輸入來(lái)來(lái)源:是在外部輸入Data或者自身原本的結(jié)果(保持不變)。結(jié)構(gòu)圖如下:

Register-1N.png

將此封裝為Register-1模塊,然后通過(guò)4個(gè)Register-1并聯(lián)并封裝為Register-4:

Register-4.png

類似的,用2個(gè)Register-4并聯(lián)封裝為Register-8(這樣比直接從8個(gè)Register-1組合連線數(shù)量要少)。
封裝后模塊如下圖:布局中左側(cè)8根輸入,右側(cè)8根輸出,下側(cè)3個(gè)控制輸入(允許寫入、時(shí)鐘信號(hào)、重置清空)。

Register-8f封裝.png

至此,我們構(gòu)建了自己的8位寄存器,然后可以利用開關(guān)作輸入,燈泡或者4-Bit Digit(顯示0~F)作為輸出,檢測(cè)模塊正確性。

5. 運(yùn)算模塊

有了寄存器我們就可以對(duì)數(shù)據(jù)進(jìn)行的保存,下面開始考慮構(gòu)建最基礎(chǔ)的運(yùn)算設(shè)備“加法器”。
現(xiàn)在想象一下二進(jìn)制的加法過(guò)程,和我們十進(jìn)制一樣,從低位到高位逐個(gè)相加,因此需要實(shí)現(xiàn)2個(gè)二進(jìn)制位加法,同時(shí)除第一位外,還需要考慮來(lái)自低位的進(jìn)位,也就是3個(gè)二進(jìn)制位相加,輸出2位(當(dāng)前位和進(jìn)位)。

5.1. 半加器(Half Adder)

我們先來(lái)考慮第一位的相加,也就是2個(gè)輸入,2個(gè)輸出,只有如下4中情況:
0+0=0, 0+1=1, 1+0=1, 1+1=10
看結(jié)果的最低位,剛好和異或運(yùn)算結(jié)果相同;而第二位上,當(dāng)且僅當(dāng)輸入都是1時(shí)才為1,因此可以用如下邏輯電路實(shí)現(xiàn)2個(gè)1位二進(jìn)制數(shù)的加法,該圖被成為半加器,別被名字嚇唬住了,其實(shí)就是實(shí)現(xiàn)的功能很簡(jiǎn)單:

Half_Adder.png

5.2. 全加器(Full Adder)

那對(duì)于第二以及以上的位置上相加,除了計(jì)算當(dāng)前位置2個(gè)數(shù)相加,還需要再加上低位上的進(jìn)位。
仔細(xì)想象這個(gè)過(guò)程,其實(shí)就是通過(guò)了2次上面的半加過(guò)程,就是將半加中的Q0和進(jìn)位值再相加,新的進(jìn)位和Q1取并即可(因?yàn)樽畲笾稻褪?+1+1=11),這被成為全加器,邏輯電路圖為:

Full_Adder1.png

其中HADD是半加器,便于理解。由于HADD也不復(fù)雜,直接用原始元件構(gòu)建亦可。
將此全加器導(dǎo)出為ADD模塊(3個(gè)輸入,2個(gè)輸出)。

5.3. 算術(shù)邏輯單元(ALU)

我們通過(guò)多個(gè)全加器串接,即實(shí)現(xiàn)多位的算術(shù)邏輯單元ALU(Arithmetic&logical Unit),比如4位的ALU-4構(gòu)建圖如下:

ADD-4.png

這里注意第一位我們并沒(méi)有使用半加器,而是采用了全加器(多出了一個(gè)外部輸入C0),一方面我們可以使得ALU-4可以進(jìn)一步級(jí)聯(lián)變成高位的累加器;另一方面,最低位的C0結(jié)合額外的1個(gè)輸入可以直接實(shí)現(xiàn)減法運(yùn)算!構(gòu)建圖如下:

ALU-8.png

當(dāng)SUB標(biāo)記為0時(shí),B和0進(jìn)行異或運(yùn)算保持不變,2組ALU的輸出是A+B;當(dāng)SUB標(biāo)記為1時(shí),一排異或是對(duì)B進(jìn)行了取反操作,同時(shí)又在最低C0上輸入1,也就是對(duì)B取反后又加了1,這正好是-B的補(bǔ)碼(原理見基礎(chǔ)知識(shí)章節(jié)),ALU的輸出結(jié)果就是A-B,非常的巧妙!對(duì)ALU-8封裝后的模塊如下(左側(cè)為A,下側(cè)為B以及SUB標(biāo)記,右側(cè)是A+/-B的結(jié)果輸出,C8是溢出位):

ALU-8封裝.png

5.4. 簡(jiǎn)單應(yīng)用

有了上面的寄存器,可以用來(lái)存變量,又有了算術(shù)邏輯單元,那就可以用來(lái)組合有趣的邏輯電路了:取1個(gè)Register-4寄存器(考慮手工連線的數(shù)量),4個(gè)開關(guān)輸入,分別記作A和B,將他們對(duì)接到ALU-4上,然后將ALU的輸出再接到A的輸入,給B賦值常數(shù)(比如1),通過(guò)數(shù)字顯像管連接ALU的輸出查看數(shù)值,通過(guò)工具自帶的時(shí)鐘驅(qū)動(dòng)設(shè)備運(yùn)行,即做成了一個(gè)循環(huán)計(jì)數(shù)器:

ACL-4樣例.png

給B賦值1111_b=0xF=15時(shí),可以起到循環(huán)遞減的效果。

6. 內(nèi)存模塊(RAM)

上面的D觸發(fā)器(D Flip-Flop)也可以作為內(nèi)存的基本單元(解決方案不唯一),將D觸發(fā)器橫橫豎豎排列方陣,通過(guò)輸入的地址(解析器為方陣中坐標(biāo)X行、Y列)對(duì)應(yīng)到其中的1個(gè)單元,然后進(jìn)行寫入、讀取操作。另外,為了能持續(xù)的集群擴(kuò)展,還需要增加一個(gè)選擇CS(通過(guò)“與門”作為篩選開關(guān)):CS為1時(shí),此塊被選中;CS位0時(shí),此內(nèi)存塊未被選擇。

內(nèi)存模塊中涉及到多個(gè)存儲(chǔ)輸出聚合,因此可以在基本存儲(chǔ)單元加上“允許輸出”控制,和“允許輸入”略有不同,不允許輸出時(shí)需要斷開(高阻態(tài)),因此我們通過(guò)1位寄存器基礎(chǔ)上增加個(gè)1個(gè)“三態(tài)門Tri-State”來(lái)實(shí)現(xiàn),構(gòu)圖如下:

UNIT-1高級(jí)封裝.png

將此封裝為UNIT-1模塊,將成為內(nèi)存基本單元。

6.1. 地址解析(Address Decoder)

以4位地址為例(A0 ~ A3),需要映射位16選1的信號(hào),首先將4位輸入拉分支并取反,然后根據(jù)0~15的的二進(jìn)制分別選取原輸入或者反值,然后通過(guò)4輸入的與門(所有輸入都為1時(shí)才輸出為1)判定。輸入二進(jìn)制數(shù)A,16個(gè)輸出中僅有對(duì)應(yīng)編號(hào)1個(gè)輸出為1,其他15個(gè)為0,起到選擇作用。比如10對(duì)應(yīng)1010_b,那它被選擇的條件就是A3 & !A2 & A1 & !A0。構(gòu)圖如下:

AddressDecoder.png

對(duì)于一個(gè)8位的地址的內(nèi)存塊,對(duì)應(yīng)X、Y分別是4地址,也就是對(duì)應(yīng)16*16=256字節(jié)。

6.2. 內(nèi)存構(gòu)建

本次實(shí)踐,為了方便我們先實(shí)現(xiàn)4位地址(Y=1),也就是16字節(jié)的內(nèi)存。
使用上面地址解析器以及上面基礎(chǔ)單元,使用“與門”將選擇的行與“允許輸入”、“允許輸出”連接,達(dá)到選擇、控制單元的作用。每個(gè)存儲(chǔ)單元的輸出需要用Bus元件匯聚。
16個(gè)“1位”內(nèi)存構(gòu)圖如下:

RAM-1-16N.png

注意:假若將允許輸出放在Bus之后,會(huì)導(dǎo)致不允許輸出時(shí)輸出為0,因?yàn)椋? & 高阻態(tài) = 0,這樣達(dá)不到輸出高阻的預(yù)期。
將上述模塊封裝為RAM 1-16(存儲(chǔ)1位16個(gè))。再使用8組RAM-1-16并聯(lián)即可構(gòu)成16字節(jié)容量的內(nèi)存。

RAM-16N.png

將此封裝為RAM 8-16,模塊如下:

RAM-8-16封裝.png

其中上側(cè)輸入A是內(nèi)存地址,左側(cè)D是數(shù)據(jù)輸入,右側(cè)Q為輸出,下側(cè)是選片CS、允許寫入EN、>時(shí)鐘、允許輸出OC和CLR清空。在地址及數(shù)據(jù)引腳中,編號(hào)小的對(duì)應(yīng)低地址。

7. 計(jì)數(shù)器(Counter)

有了內(nèi)存設(shè)備,我們可以用于存儲(chǔ)程序,而程序的自動(dòng)執(zhí)行,不可避免的需要使用計(jì)數(shù)器組件,因此我們需要構(gòu)建二進(jìn)制計(jì)數(shù)器。
考慮之前的T觸發(fā)器,2個(gè)時(shí)鐘周期時(shí)間觸發(fā)2次存儲(chǔ)變化,存儲(chǔ)的值又變回原值,也就是說(shuō)T觸發(fā)器的輸出Q’(或者Q)變化周期是原時(shí)鐘周期的2倍,利用這個(gè)特性我們可以用來(lái)設(shè)計(jì)二進(jìn)制計(jì)數(shù)器:將多個(gè)T觸發(fā)器串聯(lián),每個(gè)觸發(fā)器的輸出Q’作為下一個(gè)觸發(fā)器的時(shí)鐘信號(hào)輸入,利用周期是2倍的特性(權(quán)重)來(lái)實(shí)現(xiàn)二進(jìn)制計(jì)數(shù)!

計(jì)數(shù)器.png

在允許計(jì)數(shù)Enable=1時(shí)(4個(gè)觸發(fā)器的T輸入都是1),時(shí)鐘信號(hào)將觸發(fā)計(jì)數(shù)器內(nèi)所表達(dá)的二進(jìn)制數(shù)增加1;Enable=0時(shí),存儲(chǔ)內(nèi)容保持不變。

7.1. 計(jì)數(shù)器增強(qiáng)

上述的計(jì)數(shù)器只能自增或被重置清0,而為了更加的靈活的控制和使用,我們可以利用T觸發(fā)器的PRE’/CLR’對(duì)其存儲(chǔ)值進(jìn)行直接修改,構(gòu)件圖如下:

ProgramCounter.png

由于基礎(chǔ)元件T Flip-Flop中預(yù)置和清理都是反信號(hào)PRE’和CLR’,因此上圖種用到了幾個(gè)“與非”門,理解起來(lái)有些繞,實(shí)際原理并不復(fù)雜。
實(shí)現(xiàn)的功能就是:對(duì)于正常狀態(tài)(Jump=0),允許計(jì)數(shù)Count Enable=1時(shí),每個(gè)時(shí)鐘周期,內(nèi)部存儲(chǔ)的值增加1;跳轉(zhuǎn)模式(Jump=1)時(shí),時(shí)鐘信號(hào)觸發(fā)外部輸入數(shù)據(jù)直接存儲(chǔ)。

8. 總線(Bus)

有了上面的基礎(chǔ)元件(寄存器、運(yùn)算器、內(nèi)存)即可構(gòu)建復(fù)雜組網(wǎng),而這些元件之間的數(shù)據(jù)傳輸,兩兩連接不現(xiàn)實(shí),因此我們通過(guò)“公共的導(dǎo)線”進(jìn)行連接,我們稱之為“總線”。

多個(gè)組件都的輸出和輸入都和“總線”相連,用于相互的數(shù)據(jù)傳輸。多設(shè)備可以從總線上獲取數(shù)據(jù),這通過(guò)直接建立多連接即可(總線的輸出和需要的設(shè)備輸入連接);多設(shè)備的輸出也要通過(guò)總線(輸出到總線上)傳輸給其他設(shè)備。

因?yàn)榭偩€是多個(gè)設(shè)備公用的,因此同一時(shí)間只能有一個(gè)設(shè)備允許輸出,其余的需要斷開輸出,因此每個(gè)輸出連接在總線上的設(shè)備都要進(jìn)行“允許輸出”控制。

在現(xiàn)實(shí)的物理電路中,多個(gè)設(shè)備和總線直接導(dǎo)線相連即可,而在邏輯電路中多個(gè)設(shè)備輸出不能直接相連,需要使用Bus元件。使用Bus元件將多個(gè)設(shè)備的輸出進(jìn)行聚合,有一樣的約束“同一時(shí)間上游設(shè)備只能有1個(gè)設(shè)備有輸出,其他設(shè)備處于斷開(高阻態(tài))”,否則將可能出現(xiàn)信號(hào)沖突錯(cuò)誤。

8.1 緩沖器(Buffer)

對(duì)于上述我們創(chuàng)建的內(nèi)存,因?yàn)橐呀?jīng)有了“允許輸出”控制,因此可以直接連接Bus元件,而對(duì)于沒(méi)有輸出控制的組件,就需要控制輸出的組件,簡(jiǎn)稱緩沖器(Buffer),可以通過(guò)多個(gè)三態(tài)門并聯(lián)組成,4位的緩沖器結(jié)構(gòu)如下:

Buffer4.png

8位也類似的,然后導(dǎo)出為Buffer模塊,其中D為輸入,Q為輸出,EN為通斷控制:

Buffer4封裝.png

9. 總體結(jié)構(gòu)

有了上面的基礎(chǔ)組件,即可用于自動(dòng)機(jī)的設(shè)備構(gòu)建。想要設(shè)計(jì)一個(gè)計(jì)算機(jī),此處涉及一些可計(jì)算理論中的自動(dòng)機(jī)模型,限于篇幅不做深入的討論,這里只做最簡(jiǎn)結(jié)構(gòu)的介紹和實(shí)現(xiàn)。

一個(gè)簡(jiǎn)單的自動(dòng)機(jī)模型是:首先有一個(gè)長(zhǎng)長(zhǎng)的紙帶(對(duì)應(yīng)這里的內(nèi)存),然后有個(gè)讀寫頭,可以的操作就是:移動(dòng)讀寫頭、讀取、計(jì)算、寫入操作。

9.1. 組成部分

從最簡(jiǎn)化角度考慮,構(gòu)造一個(gè)自動(dòng)機(jī),需要的必要組件如下:

  1. 為了緩存當(dāng)前操作數(shù),最少需要2個(gè)通用寄存器,記作A、B;
  2. 一個(gè)邏輯運(yùn)算單元ALU(arithmetic&logical unit)用于計(jì)算;
  3. 為了存儲(chǔ)程序指令、計(jì)算中間結(jié)果等,需要內(nèi)存RAM(random access memory);
  4. 為了訪問(wèn)內(nèi)存數(shù)據(jù),需要一個(gè)內(nèi)存地址寄存器MAR(memory address register);
  5. 為了記錄程序執(zhí)行到第幾行,需要一個(gè)程序計(jì)數(shù)器PC(program counter register);
  6. 用于記錄控制指令,需要一個(gè)指令寄存器IR(instruction register);
  7. 將上述各組件連接在一起的總線(Bus);
  8. 輸出設(shè)備:輸出寄存器OUT;
  9. 輸入設(shè)備:按鈕若干,用于直接對(duì)內(nèi)存進(jìn)行寫入進(jìn)行編程。

其中指令可以設(shè)計(jì)為:指令代碼+參數(shù)的形式,也可以稱為:操作碼+操作數(shù),參數(shù)/操作數(shù)是可選的。
根據(jù)上述的組件的構(gòu)成可以值知道各類型元件的控制信號(hào)有:

  1. 寄存器:允許輸入、輸出控制。
  2. 邏輯運(yùn)算單元:運(yùn)算切換(做減法)、結(jié)果輸出、進(jìn)位判斷。
  3. 計(jì)數(shù)器(增強(qiáng)):允許計(jì)數(shù)、輸出、數(shù)據(jù)輸入(跳轉(zhuǎn))控制。

所有組件的上述控制,將會(huì)在1個(gè)時(shí)鐘信號(hào)(從0變成1)時(shí)觸發(fā)。

9.2. 架構(gòu)圖

結(jié)合前述組件的功能,將各組件進(jìn)行連接,組成最簡(jiǎn)單的計(jì)算機(jī)架構(gòu)圖如下:

最簡(jiǎn)計(jì)算機(jī)架構(gòu)1.png

其中:“藍(lán)色”為通過(guò)總線傳輸數(shù)據(jù),“綠色”為線路直連,“橙色”為各組件的控制信號(hào)。

圖中各組件的控制信號(hào)含義:

  1. MI:內(nèi)存地址寄存器MAR輸入
  2. RI:內(nèi)存RAM輸入
  3. RO:內(nèi)存RAM輸出
  4. II:指令寄存器IR輸入
  5. IO:指令寄存器IR輸出
  6. CO:程序計(jì)數(shù)器PC輸出
  7. CE:程序計(jì)數(shù)器PC計(jì)數(shù)
  8. J:程序計(jì)數(shù)器PC輸入(跳轉(zhuǎn))
  9. AI:寄存器A允許輸入
  10. AO:寄存器A輸出
  11. BI:寄存器B輸入
  12. BO:寄存器B輸出
  13. SO:邏輯運(yùn)算單元ALU結(jié)果輸出
  14. SUB:邏輯運(yùn)算單元ALU切換為減法
  15. CY:邏輯運(yùn)算單元ALU結(jié)果發(fā)生進(jìn)位
  16. OI:輸出寄存器OUT輸入

鑒于我們當(dāng)前最小化的設(shè)計(jì),內(nèi)存是16字節(jié),因此地址相關(guān)傳輸是4位的,涉及組件PC、MAR。
指令寄存器IR是8位,高4位對(duì)應(yīng)指令代碼,低4位為參數(shù),參數(shù)常表示地址。
4位數(shù)據(jù)傳輸在圖中是是窄箭頭,寬箭頭表示8位的數(shù)據(jù)傳輸。

10. 邏輯控制器

上面的架構(gòu)圖僅是實(shí)現(xiàn)了各組件的連接,而為了讓系統(tǒng)能夠自動(dòng)執(zhí)行,還需要設(shè)備能按照我們?cè)O(shè)置的指令列表(也就是程序)進(jìn)行逐條獲取、解析、執(zhí)行,程序通過(guò)內(nèi)存模塊存儲(chǔ),解析和執(zhí)行就需要“邏輯控制器”來(lái)實(shí)現(xiàn)。

自動(dòng)機(jī)的操作通過(guò)“指令”執(zhí)行來(lái)完成,指令的執(zhí)行又可以進(jìn)一步分解為一系列的控制動(dòng)作:從哪個(gè)組件輸出就打開對(duì)應(yīng)組件的允許輸出,傳輸?shù)侥膫€(gè)組件就打開對(duì)應(yīng)組件的允許輸入。為了對(duì)細(xì)化步驟控制,我們還需要引入一個(gè)步驟計(jì)數(shù)器(Step Counter),每個(gè)指令的每一步都對(duì)應(yīng)特定的控制信號(hào),這就是邏輯控制器實(shí)現(xiàn)的功能是:將“指令代碼”+“指令步驟”映射為“控制信號(hào)”。

10.1. 指令分析

我們從最簡(jiǎn)單的應(yīng)用開始,比如我們需要計(jì)算兩個(gè)數(shù)x、y的和,然后通過(guò)輸出,我們需要的作用有:

  1. 將x加載到寄存器A
  2. 將y加載到寄存器B
  3. 將ALU計(jì)算A+B的結(jié)果輸出到A
  4. 將寄存器A中的內(nèi)存輸出到OUT寄存器用于顯示

因此我們可以如此設(shè)計(jì)指令:

  1. LDA指令:加載內(nèi)存某地址中數(shù)據(jù)到寄存器A,有1個(gè)地址參數(shù),指令編號(hào)定為0x1;
  2. ADD指令:加載內(nèi)存某地址中數(shù)據(jù)到寄存器B,有1個(gè)地址參數(shù),并將ALU的輸出(A+B)的結(jié)果輸出給寄存器A,指令編號(hào)定位0x2;
  3. OUT指令:將寄存器A中的內(nèi)容輸出到OUT寄存器,無(wú)參數(shù),指令編號(hào)定為0xe(0xf用于停機(jī)HLT)

下面我們會(huì)具體看每個(gè)指令、步驟對(duì)應(yīng)的控制信號(hào)。因?yàn)槌绦蚴谴鎯?chǔ)在內(nèi)存中,因此首先要獲取和解析要執(zhí)行的內(nèi)容,這也是所有指令的公共步驟,因此對(duì)于所有指令指令步驟中的前2步相同:

步驟 內(nèi)容
T0 將程序計(jì)數(shù)器PC輸出到內(nèi)存地址寄存器MAR。
(初始時(shí)PC時(shí)0,也就是指向RAM的0行)
T1 將RAM內(nèi)容(第一句代碼)輸出指令寄存器II中;
同時(shí)打開PC允許計(jì)數(shù),用于下個(gè)時(shí)鐘周期遞增1

對(duì)于LDA指令(指令代碼為0x1)來(lái)說(shuō):

步驟 內(nèi)容
T2 將指令寄存器中的參數(shù)(低4位,也就是x的地址)輸出到內(nèi)存地址寄存器MAR
T3 將RAM中的內(nèi)容(x)輸出到寄存器A

對(duì)于ADD指令(指令代碼為0x2)來(lái)說(shuō):

步驟 內(nèi)容
T2 將指令寄存器中的參數(shù)(低4位,也就是y的地址)輸出到內(nèi)存地址寄存器MAR
T3 將RAM中的內(nèi)容(y)輸出到寄存器B
T4 將ALU的結(jié)果(A+B)輸出到寄存器A

對(duì)于OUT指令(指令代碼為0xe)來(lái)說(shuō):

步驟 內(nèi)容
T3 將寄存器A輸出到OUT寄存器;

整理成指令控制表為:

指令 指令代碼 指令步驟 控制信號(hào) 數(shù)據(jù)流轉(zhuǎn)
Fetch xxxx 000 CO,MI PC->MAR
Fetch xxxx 001 RO,II,CE RAM->IR
PC下個(gè)時(shí)鐘周期加1
LDA 0001 010 IO,MI IR(參數(shù)部分)->MAR
LDA 0001 011 RO,AI RAM->A
ADD 0010 010 IO,MI IR(參數(shù)部分)->MAR
ADD 0010 011 RO,BI RAM->B
ADD 0010 100 SO,AO ALU->A
OUT 1110 010 AO,OI A->OUT

有了這個(gè)控制表,即可對(duì)邏輯寄存器進(jìn)行實(shí)現(xiàn)了,輸入有:指令,指令步驟,輸出為所有的控制信號(hào):

  1. 通過(guò)地址解析器接入解析指令,獲得0~15的編號(hào)
  2. 通過(guò)地址解析器解析指令步驟,獲得0~4的編號(hào)
  3. 將上面2個(gè)輸入利用與門連接識(shí)別(映射),然后輸出作為三態(tài)門的控制信號(hào)(因?yàn)樾枰鄠€(gè)信號(hào)使用Bus聚合),將三態(tài)門的輸出連接到對(duì)應(yīng)的控制即可。

10.2. 條件跳轉(zhuǎn)

程序的執(zhí)行需要分支判定或者循環(huán),也就是對(duì)于自動(dòng)機(jī)來(lái)說(shuō)需要實(shí)現(xiàn)“跳轉(zhuǎn)”功能,無(wú)條件跳轉(zhuǎn)比較簡(jiǎn)單,直接將新地址賦值給PC寄存器即可;而有條件跳轉(zhuǎn)就還需要外部的狀態(tài)識(shí)別,這就需要我們引入“狀態(tài)寄存器(Flag Register)”,用來(lái)存儲(chǔ)外部狀態(tài)標(biāo)記。

這里我們先實(shí)現(xiàn)2個(gè)最基礎(chǔ)的:

標(biāo)記 含義
ZF(Zero Flag) A寄存器是否為0
CF(Carry Flag) ALU是否觸發(fā)了進(jìn)位

將各個(gè)標(biāo)記增加到邏輯控制器的輸入中,即可用于條件跳轉(zhuǎn)指令。

10.3. 指令集設(shè)計(jì)

至此,我們可以自主設(shè)計(jì)指令集了!這里我們一條指令用1個(gè)字節(jié),其中高4位為指令代碼(最多16個(gè)),低4位為參數(shù)(常表示內(nèi)存地址)。

指令設(shè)計(jì)如下表:

指令符號(hào) 指令代碼 功能說(shuō)明 格式說(shuō)明
NOP 0x0 無(wú)操作,讀取下一條指令 NOP無(wú)參數(shù),補(bǔ)齊1字節(jié)
LDA 0x1 從內(nèi)存指定地址讀取1字節(jié)到寄存器A LDA addr(4位地址)
ADD 0x2 從內(nèi)存指定地址讀取1字節(jié)到寄存器B,
通過(guò)ALU計(jì)算A+B,并將結(jié)果賦值到寄存器A中
ADD addr(4位地址);此步驟可能觸發(fā)ZF,CF標(biāo)志
SUB 0x3 從內(nèi)存指定地址讀取1字節(jié)到寄存器B,
通過(guò)ALU計(jì)算A-B,并將結(jié)果賦值到寄存器A中
SUB addr(4位地址);此步驟可能觸發(fā)ZF,CF標(biāo)志
STA 0x4 將寄存器A中內(nèi)容保存到指定地址 STA addr(4位地址)
LDI 0x5 加載4位的立即數(shù)value到寄存器A LDI value(4位立即數(shù))
JMP 0x6 無(wú)條件跳轉(zhuǎn)至指定地址 JMP addr(4位地址)
JZ 0x7 零值標(biāo)志ZF為1時(shí)觸發(fā)跳轉(zhuǎn)到指定地址,否則繼續(xù)執(zhí)行下一條指令 JZ addr(4位地址)
JC 0x8 進(jìn)位標(biāo)志CF為1時(shí)觸發(fā)跳轉(zhuǎn)到指定地址,否則繼續(xù)執(zhí)行下一條指令 JC addr(4位地址)
... ... 其他為預(yù)留,無(wú)操作,讀取下一條指令
OUT 0xe 將寄存器A中內(nèi)容輸出到輸出設(shè)備,用于顯示 OUT無(wú)參數(shù),補(bǔ)齊1字節(jié)
HLT 0xf 停止設(shè)備的控制時(shí)鐘,從而停止運(yùn)行 HLT無(wú)參數(shù),補(bǔ)齊1字節(jié)

根據(jù)上述指令代碼表,擴(kuò)展之前的指令步驟控制表,然后即可對(duì)邏輯控制器進(jìn)行調(diào)整,最終構(gòu)建圖如下:

LogicCtrl.png

左側(cè)是指令代碼解析,左上步驟計(jì)數(shù)器,上部是控制信號(hào)。部分未連接的部分是預(yù)留功能。

中間組網(wǎng)從上到下是各個(gè)指令,從右向左是T0 ~ T4各個(gè)指令步驟。其中個(gè)別步驟多個(gè)指令都會(huì)出現(xiàn),通過(guò)多輸入“或門”直接復(fù)用,比如指令代碼1 ~ 4的T2步驟,都是加載參數(shù)(操作數(shù)地址)到內(nèi)存地址寄存器Instruction->MAR。
將上圖導(dǎo)出成邏輯控制器模塊:

LogicCtrl封裝.png

其中下面數(shù)輸入:OP為指令代碼,S為指令步驟,*F為狀態(tài)標(biāo)志;上方一排為各控制信號(hào)輸出。

10.4. 循環(huán)計(jì)數(shù)器

邏輯控制器的工作,需要額外的指令步驟計(jì)數(shù)器組件配合,鑒于當(dāng)前的我們?cè)O(shè)計(jì)的指令最復(fù)雜的是5步,指令步驟只需要0~4循環(huán)即可。
循環(huán)計(jì)數(shù)通過(guò)計(jì)數(shù)器+地址解析器實(shí)現(xiàn):在計(jì)數(shù)器輸出5時(shí)反過(guò)來(lái)對(duì)計(jì)數(shù)器進(jìn)行重置即可。

StepCounter.png

上圖中Q是二進(jìn)制計(jì)數(shù),而最右側(cè)是T0 ~ T4的指示燈。

邏輯控制器的控制信號(hào)輸出需要早于其他組件的時(shí)鐘信號(hào),因此我們需要將“步驟計(jì)數(shù)器”的時(shí)鐘比“執(zhí)行動(dòng)作”的時(shí)鐘信號(hào)提前半個(gè)時(shí)鐘周期,通過(guò)在全局時(shí)鐘信號(hào)上增加一個(gè)非門取反作為步驟計(jì)數(shù)器的時(shí)鐘即可實(shí)現(xiàn)!

三、組裝自動(dòng)機(jī)

按照前面的架構(gòu)圖將各組件進(jìn)行連接,左下角添加步驟計(jì)數(shù)器和邏輯控制器,邏輯控制的輸出和各組件控制輸入相連,然后再根據(jù)需要做了些輕微的調(diào)整:

  1. 左上角增加時(shí)鐘信號(hào)作為整體驅(qū)動(dòng)。通過(guò)1個(gè)選擇器用于“自動(dòng)執(zhí)行”(Start開關(guān))或“手動(dòng)單步執(zhí)行”(Step按鈕)切換;
  2. 停機(jī)HLT信號(hào)通過(guò)非門、與門與時(shí)鐘輸入相連用于控制運(yùn)行;
  3. 在RAM的左側(cè)增加“地址、數(shù)據(jù)輸入”,通過(guò)“選擇器”和原電路相連。Switch引腳和“模式開關(guān)(Switch Mode)”對(duì)接,控制系統(tǒng)“編程(Program)”還是“運(yùn)行(Run)”模式?!熬幊獭蹦J街苯油ㄟ^(guò)手工輸入代碼到內(nèi)存;輸入完畢后,切換到“運(yùn)行”模式自動(dòng)執(zhí)行。
  4. 在總線Bus、內(nèi)存地址MAR、指令寄存器高4位分別添加“數(shù)字顯示器”用于查看實(shí)時(shí)的“總線傳輸”、“內(nèi)存地址”、“指令代碼”內(nèi)容。
  5. 內(nèi)存的Clear按鈕和其他組件重置按鈕Reset是分開的,防止在重置時(shí)錄入的代碼被清空。

1. 成品展示

整理線路后效果圖如下,這就是我們可以編程、自動(dòng)運(yùn)行的自動(dòng)機(jī)!

cmp2.png

除去內(nèi)存的限制,深入的分析該自動(dòng)機(jī)、指令集,其實(shí)它是“圖靈完備”的,因此也可以稱之為“計(jì)算機(jī)”!

1.1. 使用說(shuō)明

  1. 打開工程文件后,需要執(zhí)行l(wèi)ogic.ly界面左下角的“Reset Simulation”,然后再“Start Simulation”,便于清空邏輯控制器中的元件狀態(tài)。
  2. 在執(zhí)行程序前單機(jī)自動(dòng)機(jī)左下角的“Reset”按鈕對(duì)系統(tǒng)中各組件清空(內(nèi)存不會(huì)被清空)。
  3. 想要編程前通過(guò)單擊一次左上角“Step”按鈕使得步驟指示燈處于第2個(gè)燈(T1)上,此時(shí)總線上是RAM的內(nèi)容,便于查看。
  4. 通過(guò)“Switch Mode”開關(guān)將系統(tǒng)切換“編程”模式。
  5. 編程模式下,在“Set Address”關(guān)閉時(shí),地址可以通過(guò)點(diǎn)Next按鈕自動(dòng)加1,便于逐行輸入代碼;打開Set Address開關(guān),可以直接手動(dòng)輸出地址,通過(guò)Goto按鈕(和Next是同一個(gè)按鈕)直接跳轉(zhuǎn)到目標(biāo)地址。
  6. 通過(guò)Data按鈕錄入代碼,點(diǎn)擊Store存儲(chǔ)到RAM當(dāng)前地址中。
  7. 代碼輸入完畢后,切換系統(tǒng)為“Run”模式,即可通過(guò)Step單步執(zhí)行,或者Start自動(dòng)執(zhí)行,任何時(shí)候都可以隨時(shí)切換。

1.2. 程序編寫

想要使用系統(tǒng),需要先將您的問(wèn)題編寫程序,并根據(jù)前面的指令代碼表轉(zhuǎn)成對(duì)應(yīng)的二進(jìn)制。
根據(jù)1.1中的說(shuō)明將程序錄入RAM中……程序代碼是從RAM中0行開始的,因此高地址段可以用于存儲(chǔ)變量。

2. 應(yīng)用樣例

樣例1. 加減混合運(yùn)算

比如我們要計(jì)算x+y-z,先將3個(gè)數(shù)字轉(zhuǎn)成二進(jìn)制(我們都用十六進(jìn)制展示,和二進(jìn)制也可以口算轉(zhuǎn)換),然后分別存儲(chǔ)內(nèi)存的f、e、d地址,比如128+76-88,偽代碼如下:

地址 偽代碼 機(jī)器碼 解釋
0 LDA f 1f 加載內(nèi)存地址f中內(nèi)容到寄存器A
1 ADD e 2e 將內(nèi)容地址為e中內(nèi)容累加到A
2 SUB d 3d 將A減去內(nèi)存地址為d中內(nèi)容
3 OUT e0 輸出寄存器A中結(jié)果
4 HLT f0 停機(jī)
...
d 88 58 z
e 76 4c y
f 128 80 x

預(yù)期結(jié)果:128+76-88=116=0x74,執(zhí)行結(jié)果如下:

sample1.png

修改f、e、d中的值,然后點(diǎn)擊Reset按鈕,可以重新執(zhí)行新的計(jì)算f+e-d的結(jié)果。

樣例2. 計(jì)算2個(gè)數(shù)乘法

計(jì)算2個(gè)數(shù)x和y的乘法,將x和y分別存入f、e,我們用循環(huán)來(lái)實(shí)現(xiàn),使用d累加器,每次累加y,循環(huán)x次。因此d初始要賦值0;給c賦值常量1,用于f的遞減循環(huán)次數(shù)控制。比如要計(jì)算3*76,則代碼如下:

地址 偽代碼 機(jī)器碼 解釋
0 LDA f 1f 加載內(nèi)存地址f中內(nèi)容到寄存器A
1 SUB c 3c A減少1
2 JC 6 86 若A減1之前不為0,都會(huì)觸發(fā)進(jìn)位跳轉(zhuǎn)
3 LDA d 1d 加載d中結(jié)果,用于輸出
4 OUT e0 輸出結(jié)果
5 HLT f0 停機(jī)
6 STA f 4f 將f-1保存回f
7 LDA d 1d 加載中間結(jié)果d
8 ADD e 2e 將e累加到d
9 STA d 4d 將d+e更新回d
a JMP 0 60 跳轉(zhuǎn)會(huì)程序開始,進(jìn)行下一個(gè)循環(huán)
...
c 1 01 常量c
d 0 00 累加中間值d,初始是0,中間過(guò)程是y的倍數(shù)
e 76 4c 存儲(chǔ)y
f 3 03 存儲(chǔ)x,執(zhí)行過(guò)程會(huì)被遞減修改

上述代碼中地址為2的代碼利用了溢出標(biāo)記來(lái)識(shí)別減1之前寄存器A是否0,因?yàn)锳LU做減1運(yùn)算,相當(dāng)于加0xff,當(dāng)且僅當(dāng)計(jì)算前A為0才不會(huì)觸發(fā)進(jìn)位
預(yù)期結(jié)果:3*76=228=0xe4,執(zhí)行結(jié)果如下:

sample2.png

圖中數(shù)字顯示風(fēng)格和之前不同是軟件版本所致,此為2.0beta版本風(fēng)格。

樣例3. 斐波那契數(shù)列

斐波那契數(shù)列,又稱兔子序列,前兩項(xiàng)為0、1,從第3項(xiàng)開始數(shù)值等于前2項(xiàng)值和。
因此序列如下:0,1,1,2,3,5,8,13,21,34,89,144,233,……
編程實(shí)現(xiàn)該序列的動(dòng)態(tài)展示,直到溢出(超過(guò)255)為止。

程序設(shè)計(jì)思路:通過(guò)2個(gè)地址e和f存儲(chǔ)初始的0和1,計(jì)算e+f的和,并將結(jié)果更新到較早的地址,循環(huán)執(zhí)行即可。

地址 偽代碼 機(jī)器碼 解釋
0 LDA f 1f 加載內(nèi)存地址f中內(nèi)容到寄存器A
1 ADD e 2e 將內(nèi)存地址e中內(nèi)存累加到寄存器A
2 JC a 8a 對(duì)上面ADD指令做溢出判斷,如果溢出則退出循環(huán)
3 OUT e0 輸出當(dāng)前的和(寄存器A)到顯示設(shè)備
4 STA e 4e 將當(dāng)前的和保存在地址e中
5 ADD f 2f 將內(nèi)存地址為f
6 JC a 8a 對(duì)上面ADD指令做溢出判斷,如果溢出則退出循環(huán)
7 OUT e0 輸出當(dāng)前的和(寄存器A)到顯示設(shè)備
8 STA f 4f 將當(dāng)前的和保存在地址f中
9 JMP 1 61 跳轉(zhuǎn)到程序1行,進(jìn)行下一個(gè)循環(huán)
a HLT f0 停機(jī)
...
e 0 00 初始第1項(xiàng)
f 1 01 初始第2項(xiàng)

預(yù)期結(jié)果:每一輪循環(huán)OUT端輸出2次中間結(jié)果,最后第一個(gè)顯示的數(shù)值是233=0xe9,實(shí)際運(yùn)行效果一致:

sample3.png

結(jié)束語(yǔ)

上面的自動(dòng)機(jī),經(jīng)過(guò)擴(kuò)展位數(shù)、增加指令集、增加專用計(jì)算模塊等,以及進(jìn)一步的高層架構(gòu)設(shè)計(jì)即可逐步拓展為強(qiáng)大的計(jì)算機(jī),而現(xiàn)在我們也知道了其內(nèi)部核心是只由“與非門”一個(gè)元件構(gòu)成的,大道至簡(jiǎn)

眾多簡(jiǎn)單的邏輯門通過(guò)不同的組網(wǎng)結(jié)構(gòu)實(shí)現(xiàn)各種的功能:存儲(chǔ)(記憶)、運(yùn)算、邏輯控制等,其中回環(huán)網(wǎng)狀的連接形成了記憶,樹形的組網(wǎng)可以組成復(fù)雜的邏輯運(yùn)算功能。

很自然的讓我們聯(lián)想到人或動(dòng)物的,其內(nèi)部也是主要由無(wú)數(shù)的神經(jīng)元組成!每個(gè)神經(jīng)元的功能也是相對(duì)是簡(jiǎn)單的,通過(guò)樹突接收信號(hào),神經(jīng)元簡(jiǎn)單的判定然后決定是否將信號(hào)繼續(xù)向下傳遞,通過(guò)眾多神經(jīng)元的組網(wǎng)連接,形成了記憶、邏輯思維、各種特定功能區(qū)等等,這二者是多么的相似!

也讓我突然想起關(guān)于人類大腦的一個(gè)常識(shí):兒童的記憶很好,隨著年齡的增長(zhǎng),記憶會(huì)減弱,但邏輯思維會(huì)變強(qiáng)。兒童大腦的神經(jīng)元網(wǎng)絡(luò)交織繁多,像一張白紙,對(duì)應(yīng)著記憶好,富有天馬行空的想象力。隨著年齡的增長(zhǎng),這種網(wǎng)狀結(jié)構(gòu)逐漸被“學(xué)習(xí)”的過(guò)程“雕琢”,形成相對(duì)穩(wěn)定、更加高效的邏輯思維功能區(qū)……也和計(jì)算機(jī)的模型吻合。

當(dāng)然這種相似只是的籠統(tǒng)的類比,比如在“存儲(chǔ)/記憶”方面,并不是只有回環(huán)結(jié)構(gòu)才可以,比如通過(guò)“電容”等類似有狀態(tài)的組件亦可以實(shí)現(xiàn),腦的真正的機(jī)制還需要科學(xué)研究去探索。

其他方面,比如腦的學(xué)習(xí)、認(rèn)知的原理是什么,和當(dāng)今熱門的人工智能、機(jī)器學(xué)習(xí)是否也有相通之處呢?這些關(guān)于腦機(jī)制的都是我感興趣的方面,有機(jī)會(huì)再單獨(dú)撰文寫下我的感悟。不管如何說(shuō),持續(xù)學(xué)習(xí)打開思路,讓我們向著追尋的方向,更進(jìn)一步!

文章撰寫倉(cāng)促,并受限于個(gè)人水平,未表達(dá)清楚或者表述不準(zhǔn)確之處,盡請(qǐng)諒解!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容