2014年8月25日更新,更新內(nèi)容為超越函數(shù)計(jì)算器和CPU指令實(shí)現(xiàn)部分
斷斷續(xù)續(xù)終于做好了視頻和介紹,就用日志一起發(fā)出來了。工程還未完工,視頻展示計(jì)算器功能,電子表和字符顯示器時(shí)序控制。
視頻建議看的時(shí)候點(diǎn)右下角設(shè)置選超清+全屏觀看,選標(biāo)清既不清楚有時(shí)候還會(huì)卡,高清也看不清細(xì)節(jié),就算是超清也比我上傳的原視頻縮了。優(yōu)酷60秒的廣告還要縮畫質(zhì)滿地節(jié)操真心抱歉。。。
本工程基于一個(gè)叫Minecraft的游戲,我使用的版本是1.4.7。之所以使用一個(gè)游戲作為平臺(tái),是因?yàn)檫@個(gè)游戲可以做到實(shí)時(shí)運(yùn)行超大規(guī)模集成電路模型(大于10000個(gè)邏輯門)并且提供壯觀的可視化效果(三維數(shù)字電路)。
半年前我剛接觸這個(gè)游戲的時(shí)候,想做一個(gè)簡(jiǎn)單的計(jì)算器。國(guó)外玩家兩年前已經(jīng)有人做到了,基于整數(shù)ALU和直連總線的機(jī)器。我開始規(guī)劃做一個(gè)16bit的計(jì)算器,輸入輸出線路一樣是直連的,也就是說這個(gè)計(jì)算器完全是專用的芯片,連單片機(jī)的等級(jí)都不到。后來我發(fā)現(xiàn)這個(gè)游戲可以實(shí)現(xiàn)更加復(fù)雜的東西。原因很簡(jiǎn)單,游戲只提供了“或”“非”邏輯電路,但理論上“或”“非”門可以表達(dá)一切邏輯。同時(shí)游戲提供的基于活塞機(jī)械的斷路,繼電器的延時(shí)時(shí)序特性以及繼電器的鎖存特性會(huì)讓很多高級(jí)觸發(fā)器成為可能。換句話說,F(xiàn)PGA能實(shí)現(xiàn)的東西這個(gè)游戲基本都能實(shí)現(xiàn),區(qū)別在于這個(gè)游戲提供的是一個(gè)純粹數(shù)學(xué)模型化的信號(hào)系統(tǒng),元器件是簡(jiǎn)化的模型而不是現(xiàn)實(shí)中根據(jù)半導(dǎo)體材料設(shè)計(jì)的具有一定特性的電子元件,在線路連接的拓?fù)浣Y(jié)構(gòu)上也和現(xiàn)實(shí)中的電路不同。
在造計(jì)算器到一半的時(shí)候我打算改單片機(jī),也就是具有“圖靈完備性”的簡(jiǎn)單計(jì)算機(jī),他可以執(zhí)行一切計(jì)算機(jī)程序。我規(guī)劃了指令集架構(gòu),儲(chǔ)存器架構(gòu)和指令發(fā)射方式等。隨著除法器,可讀寫儲(chǔ)存器,緩沖隊(duì)列等重要電路結(jié)構(gòu)的設(shè)計(jì)成功,我開始有了一個(gè)大膽的設(shè)想,嘗試實(shí)現(xiàn)一個(gè)具有流水線結(jié)構(gòu),總線結(jié)構(gòu),溢出中斷,堆棧,標(biāo)志位寄存器,基本的分支預(yù)測(cè)和亂序執(zhí)行等現(xiàn)代高級(jí)計(jì)算機(jī)技術(shù)的16bit RISC CPU以及一個(gè)附屬的包含超越函數(shù)的單精度浮點(diǎn)處理器32bit FPU(目前只規(guī)劃作為計(jì)算器使用)。
工程現(xiàn)在進(jìn)展順利,只是因?yàn)楣こ塘烤薮筮M(jìn)度較慢。我已經(jīng)將16bit整數(shù)計(jì)算器改成了完全時(shí)序邏輯電路控制,并且有溢出判斷的計(jì)算器。這在全世界Minecraft紅石電路玩家里應(yīng)該是首次。這個(gè)計(jì)算器作為片外系統(tǒng)借用CPU的ALU部分進(jìn)行運(yùn)算并經(jīng)過總線傳輸數(shù)據(jù)。目前CPU的ALU,主儲(chǔ)存器,和寄存器等EU部分已經(jīng)完工,內(nèi)部環(huán)狀總線已經(jīng)完工,CU部分,也就是最繁瑣的部分正在建設(shè)中。而FPU部分已經(jīng)完成了加法器,乘法器,三角函數(shù)運(yùn)算單元,開方運(yùn)算單元?,F(xiàn)在整個(gè)工程大約有10萬門以上的電路。
目前不可逾越的困難是游戲的基準(zhǔn)單位延時(shí)t是0.1秒,加載地圖最大范圍是長(zhǎng)寬1024m,高256m的范圍,這就限制了計(jì)算機(jī)的運(yùn)算速度以及造出來的硬件規(guī)模。特別是儲(chǔ)存器,我的片上程序儲(chǔ)存器只有1kb,這對(duì)于現(xiàn)實(shí)中的儲(chǔ)存器容量而言太小了。所以想利用這有限的空間做一個(gè)匯編編譯器,簡(jiǎn)易的操作系統(tǒng)實(shí)在是太困難。
對(duì)于工程的介紹我分為6部分:信號(hào)系統(tǒng),硬件單元和硬件算法,儲(chǔ)存器架構(gòu)和流水線,指令集架構(gòu),總線和時(shí)鐘,圖形顯示原理。我盡量用非專業(yè)的語言來介紹,不可避免會(huì)用一些術(shù)語。
本工程需要的專業(yè)知識(shí)基本就是微機(jī)原理,數(shù)字電路,少許編譯原理和計(jì)算機(jī)圖形學(xué)。
先貼一張CPU架構(gòu)圖
其中每一個(gè)方框都代表一個(gè)或若干個(gè)硬件單元,小一點(diǎn)的大約一兩百個(gè)門電路,大的有幾千個(gè)門電路。架構(gòu)圖基本是按照實(shí)際距離做的,在工程上方俯視看到的結(jié)構(gòu)和架構(gòu)圖可以一一對(duì)應(yīng)。下面的俯視圖對(duì)應(yīng)架構(gòu)圖的右半部分(Data Bus以及其圍住的右下部分)
信號(hào)系統(tǒng)
構(gòu)成超大規(guī)模信號(hào)系統(tǒng)的邏輯等級(jí)基本如下:
基本信號(hào)元件→基本邏輯門→復(fù)雜邏輯門→簡(jiǎn)單功能結(jié)構(gòu):組合電路,時(shí)序電路,觸發(fā)器→復(fù)合功能結(jié)構(gòu)→硬件功能單元→硬件功能模塊→計(jì)算機(jī)
舉例如:
或門,非門→與門,異或門→全加器,信號(hào)長(zhǎng)度轉(zhuǎn)換器,多態(tài)選擇器,儲(chǔ)存器單元,譯碼器單元,求補(bǔ)碼單元,移位器單元→可讀寫儲(chǔ)存器,譯碼器,加法器,移位器,時(shí)鐘發(fā)生器→加減法器,乘法器,除法器,可讀寫儲(chǔ)存器陣列,寄存器,程序計(jì)數(shù)器→總線,ALU,CU→計(jì)算機(jī)
信號(hào)元件
先從邏輯底層開始介紹。這個(gè)游戲用于傳輸信號(hào)的原件稱為“紅石電路”,是在游戲地下的礦藏里挖出來的紅石礦物加上各種材料合成出來的東西。最主要的原件只有四個(gè),如下圖:
從左到右依次為:1.繼電器/二極管/鎖存器/延時(shí)器(同時(shí)兼有四個(gè)功能)2.紅石火把(高電平信號(hào)源)3.紅石粉(紅石導(dǎo)線)4.粘性活塞(可推拉的開/通路元件)
這些元件可以被放置在其他實(shí)體方塊上,方塊是這個(gè)游戲所有東西占據(jù)的空間結(jié)構(gòu),每一個(gè)物品占據(jù)一個(gè)正方體空間,將一個(gè)方塊空間占滿的是實(shí)體方塊,像下面幾個(gè)圖中藍(lán)色的,紫色的都是實(shí)體方塊。長(zhǎng)度的計(jì)量單位游戲中每一個(gè)方塊的邊長(zhǎng)是1米,玩家身高大約1.7米。本工程占地大約600x600x200米
紅石火把和繼電器:紅石火把給鄰近的同一高度的方格輸出高電平信號(hào),紅石粉和繼電器都會(huì)被激活并傳遞信號(hào),如下左,而繼電器同時(shí)為二極管,所以是單向?qū)ǖ?,如下右。繼電器亮了表示信號(hào)通過,不亮的那個(gè)是因?yàn)榉较蚍催^來所以信號(hào)不通過。
信號(hào)不是無限傳輸下去的,每傳輸15個(gè)方格就需要1個(gè)繼電器延續(xù)信號(hào),如下左,可以看到距離紅石火把越遠(yuǎn)的紅石導(dǎo)線亮度就越暗,當(dāng)超過15格還沒有繼電器的時(shí)候就會(huì)熄滅。同時(shí)每個(gè)繼電用的電路元件會(huì)花0.1秒來反應(yīng),并不是一瞬間就繼電。游戲中0.1秒即為最小的時(shí)間單位,這對(duì)應(yīng)為數(shù)字電路里的一刻時(shí)間“1t”,一切時(shí)序邏輯都是建立在0.1秒這個(gè)最小單位上的,這也正好對(duì)應(yīng)現(xiàn)實(shí)電路中電子傳遞速度導(dǎo)致的信號(hào)傳遞延遲。繼電器有延時(shí)器的特性,可以選擇1,2,3,4四種檔位,分別對(duì)應(yīng)0.1秒,0.2秒,0.3秒,0.4秒的延時(shí)(反應(yīng)時(shí)間),也就是說默認(rèn)的最小0.1秒反應(yīng)時(shí)間可以延長(zhǎng)到0.4秒。如右下,靠左上的繼電器檔位在1,靠右下的檔位在4。這一特性可以用于用盡量少的器件累積長(zhǎng)時(shí)間延時(shí)。比如5個(gè)4檔繼電器串聯(lián)時(shí)信號(hào)輸出就將延遲2秒。
另外一個(gè)重要的特性是,只要信號(hào)輸入時(shí)間夠長(zhǎng),繼電器將累積一定的信號(hào),累積值和輸入時(shí)間相等,最大累積值和檔位延遲時(shí)間相同。比如4檔繼電器輸入端輸入信號(hào)0.1秒,則0.4秒后繼電器輸出信號(hào),長(zhǎng)度為0.1秒,當(dāng)輸入端輸入0.3秒信號(hào),則0.4秒后繼電器輸出信號(hào),長(zhǎng)度為0.3秒。當(dāng)輸入0.4秒信號(hào)及以上,輸入端關(guān)閉后,繼電器輸出端將輸出0.4秒信號(hào)。
像下圖那樣,紅石火把發(fā)出信號(hào),之后藍(lán)色方塊上每15格繼電一次。第二個(gè)繼電器到第一個(gè)橙色方塊正好是第16格,此時(shí)沒再加繼電器,所以橙色兩個(gè)方塊上的紅石導(dǎo)線熄滅了。每15格的傳輸線需要一個(gè)繼電器,所以一個(gè)單位傳輸線路最長(zhǎng)距離是15格的線+1格的繼電器=16格,16格正好是二進(jìn)制數(shù),游戲開發(fā)者選這個(gè)數(shù)肯定是為了方便編程。
紅石粉不僅僅可以在同一個(gè)高度上傳輸,不同高度的紅石導(dǎo)線只要可以連在一起即可:紅石粉只能鋪在方塊表面,不同方塊表面的紅石粉可以和前后左右高一格或低一格的方塊表面的紅石粉相連,如左下。而當(dāng)紅石粉和繼電器組成環(huán)路時(shí),一旦通了信號(hào),只要不切斷環(huán)路,環(huán)路信號(hào)就一直存在。如右下圖中右邊一個(gè)環(huán)路沒有紅石火把輸出也可以保持高電平,這同時(shí)表現(xiàn)了繼電器的儲(chǔ)存特性。而如果環(huán)路里沒有繼電器則不能保存信號(hào)。
而紅石火把不僅可以放置在方塊上面,也可以放置在側(cè)面,如下左,等效于側(cè)面空氣方塊的位置被一個(gè)火把占據(jù)。輸出的信號(hào)同樣是相鄰的方塊表面。而繼電器連接信號(hào)的方塊表面與高度有關(guān),如下右,左邊一個(gè)繼電器比紅石導(dǎo)線低一格,可以繼電,而右邊一個(gè)繼電器比紅石導(dǎo)線高一格,不能繼電,這和充能原理有關(guān)系,下面再講。
充能特性:導(dǎo)電方塊(游戲提供的大部分方塊都是,除了玻璃等)都會(huì)被周圍的一些信號(hào)(如紅石導(dǎo)線和繼電器)充能?!俺淠堋钡囊馑际沁@個(gè)方塊變成了一個(gè)信號(hào)源,可以像一個(gè)紅石火把一樣工作,即這個(gè)方塊等效于同一個(gè)地方放了一個(gè)火把,并且好處是這個(gè)方塊上仍然可以鋪上電路元件,而且可以對(duì)其下方的方塊輸出信號(hào)。這是一個(gè)很重要的紅石信號(hào)特性,有了它可以使一些空間錯(cuò)位的信號(hào)傳輸變?yōu)榭赡堋?div style="height:15px;">
非門特性:這個(gè)游戲用于構(gòu)建非門的方式是,當(dāng)一個(gè)方塊被充能時(shí),其前后左右和上方的紅石火把會(huì)滅掉(變成低電平輸出)。
如下左,靠近屏幕的上下兩個(gè)方塊上各有一個(gè)火把,而下面的火把正對(duì)著上面的方塊,上面的方塊被充能,所以上面方塊上的火把滅了。遠(yuǎn)離屏幕的三個(gè)方塊,最右邊方塊上面和側(cè)面有三個(gè)火把,左邊低一格的方塊表面有導(dǎo)線直連,方塊被充能,所以三個(gè)火把全滅。右下圖里方塊被繼電器充能,上面的紅石粉被激活,下方方塊表面的紅石粉也被激活,而側(cè)面的火把被熄滅。
再來兩個(gè)例子,左下上面的紫色方塊被充能,側(cè)面火把滅,上方紅石導(dǎo)線亮,側(cè)面活塞伸出。右下最左邊紫色方塊表面的紅石導(dǎo)線將該紫色方塊充能,所以側(cè)面火把滅。
最后兩個(gè)例子,下左圖是一個(gè)不斷變化的信號(hào)通路,即火把通過一個(gè)半環(huán)路給自己的所在的方塊充能,但被充能的方塊會(huì)讓火把熄滅,火把熄滅后紅石導(dǎo)線上又沒了信號(hào),方塊不被充能,這樣火把又會(huì)亮,所以左下的火把會(huì)不斷地亮滅亮滅循環(huán),每0.1秒循環(huán)一次。游戲里如果不斷有這種信號(hào)通路出現(xiàn)勢(shì)必會(huì)給電腦運(yùn)行造成很大負(fù)擔(dān),所以游戲規(guī)定一個(gè)紅石火把在1秒鐘內(nèi)在兩個(gè)狀態(tài)里循環(huán)8次即會(huì)永久熄滅,只能通過玩家修改線路讓其再次激活。右下利用充能原理說明繼電器的不同高度繼電效應(yīng)。左邊繼電器低一格,可以繼電,是因?yàn)猷徑咭桓竦姆綁K被充能向四周從輸出信號(hào)。而右邊繼電器高一格不繼電,是因?yàn)殡m然下方方塊被充能,但是繼電器的一個(gè)重要特性是接受前后左右鄰接的信號(hào),不接受其所在的下方方塊的信號(hào)。
開路與粘性活塞:粘性活塞是一種可以黏住方塊的活塞,游戲里還有普通活塞,沒有粘性,將物品推出去就收不回來。而粘性活塞推出去可以拉回來,推出去和拉回來正好對(duì)應(yīng)了兩種狀態(tài)。下圖左右均為被激活的活塞,活塞的推拉可以朝向上下左右任意方向。
下左為活塞黏住方塊將其推出。
下右為粘性活塞的開路特性??壳暗木€路凹下去的那一個(gè)方塊上的紅石導(dǎo)線和兩邊方塊表面的紅石導(dǎo)線正常相連,可以讓信號(hào)通過。而后面的那個(gè)線路,凹下去方塊的紅石導(dǎo)線與兩邊方塊的紅石導(dǎo)線被上方的方塊阻隔。這時(shí)就構(gòu)成了開路。而粘性活塞可以推拉方塊,就可以利用該特性讓特定的線路選擇通還是斷。斷路或者說開路的特性就意味著粘性活塞和普通線路可以構(gòu)成三態(tài)門結(jié)構(gòu),這種開路可以看做是現(xiàn)實(shí)中電路的高阻抗?fàn)顟B(tài)。
下左仍然是活塞開路特性,只不過活塞從水平方向轉(zhuǎn)移到豎直方向。左邊活塞收回是通路,右邊活塞推出是開路。
下右是利用充能特性和活塞開路特性的結(jié)合電路。紅色方塊作為可推拉方塊當(dāng)其收回的時(shí)候前面的繼電器和后面的紅石導(dǎo)線沒有連接,所以是開路,而當(dāng)活塞將紅色方塊推出后繼電器會(huì)給紅色方塊充能,這時(shí)被充能的紅色方塊不僅會(huì)像后方的導(dǎo)線輸出信號(hào),也會(huì)激活下方的活塞。即此時(shí)活塞就算不被其他信號(hào)激活也會(huì)被紅色方塊永久激活,只要繼電器信號(hào)不撤去,無論活塞下方的輸入端(比如最下層的那個(gè)藍(lán)色方塊)是什么信號(hào),都不會(huì)改變活塞推出的狀態(tài)。這種特性的直接意義就是只要一個(gè)短信號(hào)激活活塞,就會(huì)輸出一個(gè)永久信號(hào)。
繼電器鎖存特性:繼電器的最后一個(gè)特性是鎖存器。如下左,上下兩個(gè)橫著放的繼電器通電,右邊兩個(gè)繼電器上就會(huì)加上一條黑色的橫條,這就表示右邊兩個(gè)繼電器被鎖住了,他們將保持原來的信號(hào)一直不變,不管輸入端是高電平還是低電平。而當(dāng)左邊的兩個(gè)繼電器不通信號(hào)的時(shí)候,右邊兩個(gè)繼電器就不再鎖存。鎖存器的出現(xiàn)使得大規(guī)模儲(chǔ)存器縮小體積變成了可能(雖然仍很難在游戲可加載范圍內(nèi)放上足夠多的儲(chǔ)存器)
利用這個(gè)特性我設(shè)計(jì)了一種可同時(shí)讀寫的儲(chǔ)存器單元如下右,是一個(gè)1byte的儲(chǔ)存器。
其他類型的信號(hào)單元
下左方塊上有一個(gè)按鈕,這個(gè)按鈕的功能是輸出一個(gè)1秒(10t)的信號(hào),按一下會(huì)給其所在的方塊充能1秒。這一特性可以用于操作者對(duì)機(jī)器的操控。下右為拉桿,拉桿可以置于兩種狀態(tài),打開的狀態(tài)會(huì)輸出永久信號(hào),關(guān)閉的狀態(tài)不輸出信號(hào)。
視覺信號(hào)與顯示器
游戲本身沒有顯示屏這種東西,但是玩家可以通過各種方式實(shí)現(xiàn)視覺上的信息傳遞。
第一種是紅石燈。如下左,紅石燈被充能時(shí)會(huì)亮,不充能時(shí)不亮,這兩種狀態(tài)即可組成圖形,和計(jì)算機(jī)的bitmap一致。
第二種是陰影成像。即游戲中白天光照條件下淺顏色的方塊凹陷處的陰影會(huì)和周圍的方塊形成反差,也構(gòu)成了兩態(tài)信號(hào)的圖像。如下右的七段顯示器。
而實(shí)現(xiàn)方塊凹陷的方式就是粘性活塞,如下圖,活塞推拉分別對(duì)應(yīng)填平和凹陷。
向上傳輸和BUD
向上傳輸是游戲提供的一種信號(hào)單向向上傳輸?shù)姆绞?,可以用兩種方塊實(shí)現(xiàn)。如下左,左邊花紋方塊是螢石,本身有自然發(fā)光的作用,同時(shí)可以用圖中方式向上疊放。正常的方塊這樣疊放肯定會(huì)擋住信號(hào),所以正常方塊向上向下傳輸必須螺旋盤疊,這樣會(huì)占據(jù)更大的空間,于是游戲提供了單向向上傳輸節(jié)約空間。但是可惜游戲沒有提供單向向下傳輸(至少我使用的1.4.7版本沒有提供),可以看到如圖中左邊的螢石信號(hào)通路輸入端在上方,下方方塊的紅石導(dǎo)線沒有亮,而右邊的螢石通路輸入端在下方,上方方塊的紅石導(dǎo)線亮了。另一種單向向上傳輸?shù)姆綁K是“半磚”,即只占一般空間的磚頭,如下左圖中右邊灰色的磚塊。因?yàn)橹挥幸话敫叨?,所以這樣盤疊不會(huì)擋住各自導(dǎo)線的連接。半磚同樣只實(shí)現(xiàn)單向向上傳輸。
BUD是游戲中一類類似BUG的信號(hào)特性。但是又不能叫做BUG,因?yàn)檫@些特性也可以看做是信號(hào)系統(tǒng)的組成部分。由于游戲編程中對(duì)于方塊更新的檢測(cè)機(jī)制存在一定局限性,所以一些方塊會(huì)被非正常激活。只舉一個(gè)例子,如下右圖不斷升高的信號(hào)線路,綠色方塊活塞推出是正常被充能的情況,紅色方塊活塞抽回未被激活也是正常的。但是中間紫色方塊活塞沒有鄰接任何被充能方塊,但是處于推出狀態(tài),這種情況是反常的,稱之為BUD。出現(xiàn)這種情況很多時(shí)候會(huì)對(duì)設(shè)計(jì)造成困難,有一次我調(diào)試線路出現(xiàn)了很奇怪的錯(cuò)誤,排查了半天才發(fā)現(xiàn)是BUD問題。有些時(shí)候也可以利用BUD的特性做成特定功能的線路。
實(shí)際上游戲中還是有BUG的,有一次我排查了一個(gè)多小時(shí)竟然發(fā)現(xiàn)某個(gè)錯(cuò)誤的原因是這樣的:兩個(gè)相隔100多米毫無功能關(guān)聯(lián)的繼電器,當(dāng)一個(gè)置于2檔的時(shí)候,另一個(gè)會(huì)工作不正常。這屬于游戲難免會(huì)有的BUG,但是有時(shí)候一個(gè)小BUG會(huì)導(dǎo)致整個(gè)計(jì)算機(jī)癱瘓。
邏輯門
信號(hào)元件基本就全部介紹完畢了,然后正式介紹數(shù)字電路的部分。
游戲提供的二態(tài)信號(hào)正好對(duì)應(yīng)于二進(jìn)制0和1,也對(duì)應(yīng)于數(shù)字電路里用高低電平表示的信號(hào)。所以二態(tài)信號(hào)系統(tǒng)無論其實(shí)現(xiàn)的載體和方式如何,規(guī)律必定都是一樣的。所以可以用相同的組合和算法構(gòu)造更復(fù)雜的結(jié)構(gòu)。
有了四種信號(hào)元件如何進(jìn)一步做成邏輯門呢?非門前面已經(jīng)給出了,即利用紅石火把被充能方塊熄滅的特性。
或門更簡(jiǎn)單,“或”在邏輯上就是只要任意一個(gè)輸入端(不僅僅是一共2個(gè)輸入端的情況)輸入信號(hào),輸出端就一定輸出信號(hào)。如下左,兩個(gè)橙色的方塊為輸入端,只要有一個(gè)放上火把,綠色的輸出端就會(huì)輸出信號(hào)。下右為簡(jiǎn)單的組合邏輯,4個(gè)輸入端組成的或門加上輸出端的非門組成的或非門。這種電路一般用于“0判斷”,即輸入端全為0,輸出就有信號(hào),只要有一個(gè)輸入是1,輸出端的紅石火把就會(huì)滅。
可以證明只用或門和非門就能實(shí)現(xiàn)一切邏輯,游戲的設(shè)計(jì)者也只設(shè)計(jì)了這兩種能直接實(shí)現(xiàn)的邏輯門,這一點(diǎn)和現(xiàn)實(shí)的晶體管電路也很符合。通過在空間上對(duì)或門和非門的組合排布就能實(shí)現(xiàn)更加復(fù)雜的邏輯門。
與非門如下左,紫色為輸入端,橙色為輸出端,可以看出輸入端連著兩個(gè)紅石火把是兩個(gè)非門,火把中間通著導(dǎo)線是一個(gè)或門,真值表我就不寫了,簡(jiǎn)單計(jì)算即可知這是一個(gè)與非門。常見的與非門應(yīng)用也就是RS觸發(fā)器了,比如下右這個(gè)基本RS觸發(fā)器,低電平有效,紫色輸入,橙色輸出,RSQQ非就隨便怎么分配了,此時(shí)圖中輸入端均有效,輸出端無效,當(dāng)輸入端從01或10置為00(高電平)時(shí)會(huì)鎖存。而當(dāng)輸入端同時(shí)從00變?yōu)?1時(shí)游戲的方塊刷新機(jī)制會(huì)默認(rèn)選擇其中一個(gè)輸出端輸出1,另一個(gè)輸出端輸出0,當(dāng)然本身就不用考慮會(huì)使用這種情況。所以用與非門構(gòu)造的RS觸發(fā)器和現(xiàn)實(shí)中基本一致。
與門比與非門復(fù)雜一點(diǎn),只要在與非門基礎(chǔ)上加個(gè)非門的紅石火把就可以了。如下圖,下左為標(biāo)準(zhǔn)的與門,兩個(gè)紅色的輸入端,紫色為輸出端,可以看出是3個(gè)非門和一個(gè)或門組成的邏輯電路??赡茏x者仍然不便理解,我就將其轉(zhuǎn)化為框圖,如下中圖。簡(jiǎn)單的計(jì)算可得只有當(dāng)兩個(gè)輸入端同時(shí)輸入1時(shí),輸出端為1,和與邏輯相同。下右兩個(gè)同樣為與門,只不過線路排布稍微變化即可變?yōu)榭臻g構(gòu)造不同的與門,可以用于各種不同的布線情況。
活塞斷路其實(shí)也是與邏輯。廣義上的“與”可以看做同時(shí)滿足各自條件的若干個(gè)輸入端才能使輸出端輸出特定信號(hào)。比如下左上面的紫色輸入端輸入0,下面的紫色輸入端輸入1才能使綠色輸出端輸出1,而下右活塞原本擋住橙色線路,當(dāng)活塞被激活將藍(lán)色方塊推出時(shí),會(huì)使凹下的橙色方塊線路與兩邊聯(lián)通,這時(shí)右邊的紫色輸入1,左邊的綠色才會(huì)輸出1。即這是輸入端必須全為1的標(biāo)準(zhǔn)與門。
之后的復(fù)雜信號(hào)結(jié)構(gòu)的介紹我都盡量簡(jiǎn)略,如果真要從頭到尾講清楚,要寫一本書。其中涉及到的專業(yè)知識(shí)太多了,很難讓所有讀者都能理解,見諒。關(guān)于數(shù)字電路和微機(jī)原理的各種基礎(chǔ)知識(shí)介紹我都從略。
異或門是數(shù)字電路里非常重要的一類復(fù)雜邏輯門,是構(gòu)造全加器以及一切具有ALU運(yùn)算器結(jié)構(gòu)單元的基礎(chǔ)。比較簡(jiǎn)單的異或門設(shè)計(jì)如下圖左右兩種,除了紅石導(dǎo)線外,左邊一種用到了活塞,火把和繼電器,右邊一種只用了火把。這兩種都是國(guó)外玩家設(shè)計(jì)的,是目前設(shè)計(jì)出來的體積最小的異或門。我一開始自己設(shè)計(jì)出的異或門比這兩種體積大一點(diǎn)。而基礎(chǔ)邏輯門的體積對(duì)計(jì)算機(jī)建設(shè)至關(guān)重要,基礎(chǔ)邏輯門稍微大一點(diǎn)整體結(jié)構(gòu)就將超過地圖加載范圍。我的工程在設(shè)計(jì)上如果沒有這些高手玩家在基礎(chǔ)結(jié)構(gòu)上的設(shè)計(jì),是不可能實(shí)現(xiàn)的,因?yàn)橛胢inecraft實(shí)現(xiàn)實(shí)時(shí)運(yùn)算超大規(guī)模信號(hào)系統(tǒng)最重要的難題就是體積問題。
這兩種異或門右邊一種較好,因?yàn)橛螒蛑械幕鸢芽梢栽?秒鐘內(nèi)承受8次信號(hào)變化才會(huì)熄滅,而活塞似乎承受不了這么多次的變化,容易在快速的信號(hào)變化中出現(xiàn)差錯(cuò)。所以我的計(jì)算機(jī)中基本都是采用右邊一種異或門。兩個(gè)橙色方塊是輸入端,紫色方塊是輸出端。
其他所有邏輯門都可以通過或,非門的組合得到,就不再詳述。
簡(jiǎn)單功能結(jié)構(gòu)
利用邏輯門的組合就可以設(shè)計(jì)適用于各種功能的信號(hào)結(jié)構(gòu)。
全加器:全加器可以看做是計(jì)算機(jī)最核心的部件,之前的一個(gè)異或門相當(dāng)于一個(gè)半加器,兩個(gè)半加器可以組合成一個(gè)全加器。由第一種異或門組成的全加器 如下左,下右是4個(gè)相同的全加器級(jí)聯(lián)。
但是這種基于活塞的全加器不穩(wěn)定,所以較為好的設(shè)計(jì)是如下圖的基于第二種異或門設(shè)計(jì)的全加器。兩個(gè)紅色為輸入端,藍(lán)色為進(jìn)位端,紫色為本位輸出端。下右為兩個(gè)不同顏色的全加器級(jí)聯(lián)。
其他的組合電路,時(shí)序電路和觸發(fā)器就舉幾個(gè)例子。
前一部分已經(jīng)介紹過RS觸發(fā)器,實(shí)際上并不常用。常用的是一些邊沿觸發(fā)的時(shí)序電路。下左圖為活塞開路的兩種最基本的應(yīng)用,兩個(gè)同樣的藍(lán)色開路線路,作為輸入端的紅石火把左邊在下,右邊在上。左邊的藍(lán)色線路因?yàn)殚_路的節(jié)點(diǎn)(凹下去的地方)比開路輸入端的節(jié)點(diǎn)更靠近火把,而4檔繼電器的延遲為0.4秒,活塞的延遲為0.1秒,所以第0.5秒后活塞會(huì)伸出使線路開路,這時(shí)輸入端信號(hào)就傳不到活塞了。而繼電器里可以存下0.4秒的信號(hào),所以再過0.5秒活塞會(huì)收回,線路又會(huì)通。然后就會(huì)這樣循環(huán)的“開路-通路-開路-通路”下去,每1秒是一個(gè)循環(huán)。實(shí)際的效果就是每1秒鐘內(nèi)可以輸出一個(gè)0.5秒的信號(hào)。右邊那條線路輸入端通往活塞的節(jié)點(diǎn)在開路節(jié)點(diǎn)的前面,所以不受開路影響,只要輸入端有持久信號(hào)就會(huì)在0.1秒后永久開路,使得下方輸出0.1秒的瞬間信號(hào)。必須等待輸入端變?yōu)榈碗娖交钊艜?huì)收回,這等價(jià)于一個(gè)上沿信號(hào)。
下右圖是一個(gè)T觸發(fā)器,左邊紫色為輸入端,接一個(gè)上沿信號(hào)發(fā)生器輸出0.2秒短信號(hào),右上綠色方塊是輸出端,T觸發(fā)器儲(chǔ)存一個(gè)信號(hào),高電平短信號(hào)使觸發(fā)器工作,效果是使原有信號(hào)翻轉(zhuǎn)并儲(chǔ)存輸出。
下左為短信號(hào)轉(zhuǎn)1秒信號(hào)器,實(shí)際上可以做出任意長(zhǎng)度信號(hào)之間的轉(zhuǎn)換,比如0.1秒轉(zhuǎn)4秒,5秒轉(zhuǎn)0.2秒等等。下右為3秒短信號(hào)輪換器,即第0秒輸出短信號(hào)到A端,第3秒輸出短信號(hào)到B端,第6秒輸出到A端……
下圖為移位觸發(fā)器,也是很常用的一種結(jié)構(gòu),可以做成單向或雙向。
下左為時(shí)鐘頻率儲(chǔ)存器,即長(zhǎng)度mt的信號(hào)在長(zhǎng)度nt為一個(gè)周期的環(huán)路中(n>m)作循環(huán)傳遞。時(shí)鐘頻率儲(chǔ)存器和信號(hào)發(fā)生器組合可以變成計(jì)算機(jī)的時(shí)鐘信號(hào)發(fā)生器。下右為短信號(hào)阻斷器(名字值得吐槽,我也不知道該取什么名字= =),可以濾去0.6秒以下的短信號(hào)。
下左藍(lán)色部分為4路選擇觸發(fā)器,發(fā)射信號(hào)選擇其中一路并儲(chǔ)存該狀態(tài),之后發(fā)射信號(hào)選擇其他某一路會(huì)清除之前的選擇并存進(jìn)新的選擇。下右黑色部分為總線信號(hào)清空單元,可以周期性的阻斷總線信號(hào)通路。
復(fù)合功能結(jié)構(gòu)
由簡(jiǎn)單功能結(jié)構(gòu)可以進(jìn)一步組成復(fù)合功能結(jié)構(gòu),從而完整地實(shí)現(xiàn)某一功能。比如全加器級(jí)聯(lián)變成加法器;異或門和加法器串聯(lián),然后級(jí)聯(lián),再加上符號(hào)信號(hào)端變成求補(bǔ)器等等。
舉幾個(gè)例子。
下圖為帶溢出判斷的補(bǔ)碼加法器
下圖為譯碼器
下圖為另一種譯碼器
下圖為顯示器單元
下圖為可讀寫儲(chǔ)存器單元,作為寄存器MAR。
硬件功能單元
復(fù)合功能單元能執(zhí)行某一個(gè)完整的邏輯功能,比如加法器使兩個(gè)補(bǔ)碼相加,求補(bǔ)器使某個(gè)原碼求補(bǔ)碼。而硬件上加減法器的完整功能一般指從求補(bǔ)碼到加減法到求原碼返回寄存器或總線的完整過程。
舉三個(gè)例子
下圖為緩沖隊(duì)列,有兩個(gè)功能信號(hào)端和一個(gè)16bit的輸入接口和一個(gè)16bit的輸出接口。
下圖為乘法器溢出判斷的一部分,是譯碼器,位數(shù)判斷器,加法器構(gòu)成的。
再來兩個(gè)體積較大的。
下圖為16bit除法器,可以輸出商和余數(shù)。
下圖為單精度浮點(diǎn)加法器,符合IEEE754標(biāo)準(zhǔn)。這個(gè)家伙算是結(jié)構(gòu)比較復(fù)雜的了,四種基本元件用掉了34530個(gè),以邏輯門數(shù)量來估算也大概有5000個(gè)左右了。
硬件功能模塊
功能單元足夠多的時(shí)候就會(huì)形成模塊。比如加減法器,乘法器,除法器,移位器,布爾邏輯單元等等組成ALU;指令緩沖隊(duì)列,指令譯碼器,指令發(fā)射端等等組成CU;地址譯碼器,儲(chǔ)存器陣列,寄存器等等組成完整的具有等級(jí)結(jié)構(gòu)的儲(chǔ)存器體系。功能單元的位置,朝向等都會(huì)大大影響布線的困難程度和延時(shí)的長(zhǎng)短,這對(duì)整個(gè)計(jì)算機(jī)的運(yùn)行效率有至關(guān)重要的影響。所以對(duì)功能模塊的放置需要花很多時(shí)間計(jì)算,排列,布置。我花了很多時(shí)間不斷修改,調(diào)整。
舉兩例,第一例最上面那張俯視圖已經(jīng)給出,是ALU和總線的結(jié)構(gòu),再給一例顯示器模塊的背面(還在建設(shè)中),如下圖
當(dāng)所有必要的硬件功能模塊都竣工的時(shí)候,就變成了完整的計(jì)算機(jī)。
硬件單元及硬件算法
硬件單元列表及特性
英文名(縮寫)
中文全稱
特性
Program Counter
程序計(jì)數(shù)器
9bit,最大尋址512單元,支持指令跳轉(zhuǎn),自動(dòng)加1
MAR
儲(chǔ)存器地址寄存器
9bit,最大尋址512單元
MDR
儲(chǔ)存器數(shù)據(jù)寄存器
16bit,支持儲(chǔ)存器讀寫數(shù)據(jù)
General Register
通用寄存器
暫定為12個(gè)
Address Decoder
地址譯碼器
將地址碼譯成儲(chǔ)存器行列信號(hào)并控制儲(chǔ)存器讀寫
Pre-Decoder
預(yù)譯碼器
第一級(jí)指令譯碼機(jī)制用于執(zhí)行指令跳轉(zhuǎn)和偏移
BPU
分支預(yù)測(cè)單元
為了減少流水線冒泡動(dòng)態(tài)預(yù)測(cè)分支指令
Offset Address Unit
偏移地址單元
計(jì)算偏移地址輸出至地址列隊(duì)
Address Queue
地址隊(duì)列
接受跳轉(zhuǎn)地址并輸出至PC
PC STACK
程序計(jì)數(shù)器棧區(qū)
用于部分指令如Call,Return彈壓地址
Clock
時(shí)鐘發(fā)生器
CPU總時(shí)鐘信號(hào)發(fā)生器
Instruction Buffer
指令緩沖隊(duì)列
3單元共6byte,支持指令按同一方向壓入和彈出
Instruction Register
指令寄存器
接受指令緩沖隊(duì)列彈出指令并送往指令譯碼器
Instruction Decoder
指令譯碼器
將指令譯碼并將時(shí)序控制信號(hào)輸出至指令發(fā)射端
Issue Port
指令發(fā)射端
將信號(hào)分發(fā)至EU個(gè)單元的控制單元
Flag Register
標(biāo)志位寄存器
暫時(shí)還未定共有多少標(biāo)志位
Stack
棧
4個(gè)寄存器共8byte,支持彈壓棧
ACC
累加器
儲(chǔ)存X操作數(shù)以及部分ALU運(yùn)算結(jié)果
Y Register
Y寄存器
儲(chǔ)存Y操作數(shù)
Adder
整數(shù)加減法器
支持16bit帶符號(hào)整數(shù)加減法
Multiplier
整數(shù)乘法器
支持16bit帶符號(hào)整數(shù)乘法
Divider
整數(shù)除法器
支持16bit帶符號(hào)整數(shù)除法
Logic Unit
布爾邏輯單元
支持按位與或非異或邏輯運(yùn)算
Overflow Trap
乘法器溢出中斷
有兩個(gè)部分共同執(zhí)行溢出判斷輸出至計(jì)算器和F&I
Sequential Control
除法器時(shí)序控制器
時(shí)序電路控制除法器運(yùn)算推進(jìn)避免電腦壓力過大
INC&DEC
加一減一單元
ALU的一部分,用于加一減一循環(huán)指令
Shift Unit
移位器
可執(zhí)行算數(shù)或邏輯左右移位-15至15位
OPU
亂序執(zhí)行單元
在執(zhí)行除法時(shí)將非數(shù)據(jù)相關(guān)指令并發(fā)
Data Bus
數(shù)據(jù)總線
折線狀環(huán)形3D總線,由總線清存單元控制
BIOS
基本輸入輸出系統(tǒng)
計(jì)算機(jī)開機(jī)后首先控制顯示器并輸出信息的單元
FPU
浮點(diǎn)處理器
協(xié)處理器,目前已完工部分ALU
Faults & Interrupts
異常中斷響應(yīng)
發(fā)生異常時(shí)接管CPU并執(zhí)行保護(hù)指令的單元
Calculator CU
計(jì)算器控制器
完全時(shí)序控制,交互式IO管理
Calculator Keyboard
計(jì)算器鍵盤
輸入端和指令發(fā)射端
Input Register
輸入寄存器
將操作數(shù)輸入EU
Output Register
輸出寄存器
將結(jié)果從EU輸出
Keyboard Decoder
鍵盤譯碼器
接受計(jì)算器鍵盤信號(hào)并譯碼
Screen Control
顯示器控制器
接受鍵盤譯碼器信號(hào)輸出圖像信息
BCD→BIN
BCD轉(zhuǎn)BIN運(yùn)算器
接受輸入譯碼器的BCD碼轉(zhuǎn)化成BIN碼輸出
BIN→BCD
BIN轉(zhuǎn)BCD運(yùn)算器
接受EU的BIN碼轉(zhuǎn)化成BCD碼輸出至顯示器
Instruction RAM
程序儲(chǔ)存器
1kb,按字讀寫
Data RAM
數(shù)據(jù)儲(chǔ)存器
512byte,按字或字節(jié)讀寫
上表共40個(gè)硬件單元是大部分CPU和計(jì)算器部分硬件單元的列表,其中除了指令譯碼器,指令發(fā)射端,異常中斷響應(yīng)沒有做完,其他都竣工了。還有一些小的硬件單元就沒寫上去了。字符顯示器模塊零部件太多也沒加上去,留到最后一部分介紹。
硬件算法
算法對(duì)硬件設(shè)計(jì)是靈魂,好的算法設(shè)計(jì)出來的硬件單元可能要比不好的設(shè)計(jì)節(jié)省10倍的運(yùn)算時(shí)間,10倍的空間,10倍的建造精力??傊惴Q定ALU的一切。
加減法就是常見的補(bǔ)碼加法算法,乘法就是級(jí)聯(lián)串行乘法,都沒什么特別的。重點(diǎn)介紹后面幾個(gè)。
BCD/BIN轉(zhuǎn)換算法
輸入端BCD轉(zhuǎn)BIN算法(這個(gè)自己想出來的):
想法很直接,BCD十進(jìn)制碼轉(zhuǎn)BIN二進(jìn)制碼按照常規(guī)的數(shù)學(xué)運(yùn)算就是十進(jìn)制每一位乘上10的各自位數(shù)-1次方。比如123=1x10^2+2x10^1+3。這個(gè)反映到二進(jìn)制算法上就是將BCD每一位數(shù)的四個(gè)信號(hào)乘以10的n次方的二進(jìn)制值,n為該位數(shù)-1,最后所有位再加起來。重要的是這種算法在硬件上實(shí)現(xiàn)很簡(jiǎn)易,所以我也沒找其他算法,就直接用了。下圖為BCD轉(zhuǎn)BIN單元。
輸出端BIN轉(zhuǎn)BCD算法:
這個(gè)用的是通用的算法,流程如下:
某二進(jìn)制數(shù)一直做左移操作,每一次左移后從第一次移位進(jìn)入的那個(gè)位向左每4個(gè)位切斷成一組(作為BCD數(shù)),然后判斷改組是否大于等于5,如果大于等于5則該組+3處理,小于5不用處理。所有組處理完后繼續(xù)移位,一直移到末尾進(jìn)入第一次移位的那個(gè)位。文字介紹很別扭,可以結(jié)合下面的圖看。
借用MC論壇上的舉例介紹:(
http://rrurl.cn/ilFYn5)
把Units,Tens,Hundreds和他們所處的那一列統(tǒng)稱為1組,另外Binary和它所在的那一列不算一組,表格一行一行看。1組數(shù)據(jù)對(duì)應(yīng)一個(gè)BCD字符,2進(jìn)制數(shù)有多少位就把它往左移多少位。左邊的英文是操作,shift是移位,add是加。Units組的最右邊一位就是上文指的“第一次移位進(jìn)入的那個(gè)位”。
上圖是以255為例,下面再以123為例的流程如下:
123的二進(jìn)制數(shù)是0111 1011
我們先將其左移1位,得到1111 0110
目前還在binary那列里,所以繼續(xù)移位
得到0001 1110 1100
組里的數(shù)字小于5,繼續(xù)移
得到0011 1101 1000
繼續(xù)移位
得到0111 1011 0000
可以看到Units組里的數(shù)字已經(jīng)大于5了,所以把當(dāng)前該組里的數(shù)據(jù)+3處理
得到1010 1011 0000
繼續(xù)移位
得到0001 0101 0110 0000
Units組里的數(shù)字等于5,所以再加3
得到0001 1000 0110 0000
繼續(xù)移位
得到0011 0000 1100 0000
繼續(xù)移位
得到0110 0001 1000 0000
這次是Tens里的數(shù)據(jù)大于5了,所以+3處理
得到1001 0001 1000 0000
因?yàn)樵?進(jìn)制數(shù)前面補(bǔ)了一個(gè)0,所以變成了8位的數(shù)據(jù),現(xiàn)在還差最后一次的移位
得到0001 0010 0011 0000 0000
結(jié)束
最終設(shè)計(jì)出的硬件結(jié)構(gòu)如下圖,是一個(gè)15bit的BIN轉(zhuǎn)BCD轉(zhuǎn)換器。
除法算法
除法用的是恢復(fù)余數(shù)的加減交替算法,流程舉例如下:
整個(gè)串行的除法器利用減法判斷符號(hào)來決定上商和恢復(fù)余數(shù)。由于除法在硬件上的運(yùn)算密度比較高,串行除法器如果讓它完全不受時(shí)序控制直接串行推進(jìn)運(yùn)算會(huì)讓電腦負(fù)擔(dān)太大的運(yùn)算量導(dǎo)致卡頓。這個(gè)的主要原因是信號(hào)在時(shí)間上的重疊,初始信號(hào)一開始就傳送到了最后,每一行的部分積余數(shù)一算完,后面所有的信號(hào)都要全部改變一次,會(huì)導(dǎo)致幾千個(gè)紅石火把每一秒經(jīng)歷若干次變化(還好總數(shù)比8小不至于崩潰)。所以我又設(shè)計(jì)了一個(gè)控制運(yùn)算推進(jìn)的時(shí)序電路,最終卡頓的情況比原來好了許多。
設(shè)計(jì)出來的硬件單元前文已給出,再貼兩張細(xì)節(jié)。
下圖右上灰色部分是最終恢復(fù)的余數(shù)輸出端,右下紅綠相間的加法器是除數(shù)輸入端,被圈出來的藍(lán)色方塊從上到下一共15個(gè)是被除數(shù)輸入端,被圈出來的那個(gè)是最低位,它下方偏右的那個(gè)是最高位。
下圖是換了一側(cè)看除法器,圖中綠色的一共15個(gè)是商輸出端
浮點(diǎn)加減法算法
這個(gè)也是用的通用算法。
按照下面幾個(gè)步驟來:
浮點(diǎn)數(shù)由階碼和尾數(shù)構(gòu)成,可以看做是二進(jìn)制的科學(xué)計(jì)數(shù)法。階碼就相當(dāng)于科學(xué)計(jì)數(shù)法的那個(gè)冪次方數(shù),尾數(shù)就相當(dāng)于有效數(shù)字的具體數(shù)值。比如0.11x2^3,其中0.11是尾數(shù),3是階數(shù)。IEEE754標(biāo)準(zhǔn)的浮點(diǎn)數(shù)規(guī)格如下
這是WIKI上的表格,要不要用偏正值其實(shí)無所謂,只要知道單精度浮點(diǎn)數(shù)(single precision floating point)的位數(shù)是32bit,指數(shù)位數(shù)=8,尾數(shù)位數(shù)為=23,還有一位符號(hào)位。其中指數(shù)位數(shù)中有一位是指數(shù)的符號(hào)位即表示其范圍為-127到127,不算這個(gè)符號(hào)位就是指偏正值0-255,其實(shí)含義是一樣的。而單獨(dú)的那個(gè)符號(hào)位是給整個(gè)浮點(diǎn)數(shù)用的。
設(shè)有兩個(gè)浮點(diǎn)數(shù)x和y,它們分別為
x=2Ex·Mx
y=2Ey·My
其中Ex和Ey分別為數(shù)x和y的階碼,Mx和My為數(shù)x和y的尾數(shù)。
(1) 比較階碼大小并完成對(duì)階
兩浮點(diǎn)數(shù)進(jìn)行加減,首先要看兩數(shù)的階碼是否相同,即小數(shù)點(diǎn)位置是否對(duì)齊。若二數(shù)階碼相同,表示小數(shù)點(diǎn)是對(duì)齊的,就可以進(jìn)行尾數(shù)的加減運(yùn)算。反之,若二數(shù)階碼不同,表示小數(shù)點(diǎn)位置沒有對(duì)齊,此時(shí)必須使二數(shù)階碼相同,這個(gè)過程叫作對(duì)階。要對(duì)階,首先應(yīng)求出兩數(shù)階碼Ex和Ey之差,即△E = Ex-Ey。若△E=0,表示兩數(shù)階碼相等,即Ex=Ey;若△E>0,表示ExEy。當(dāng)Ex≠Ey 時(shí),要通過尾數(shù)的移動(dòng)以改變Ex或Ey,使之相等。原則上,既可以通過Mx移位以改變Ex來達(dá)到Ex=Ey,也可以通過My移位以改變Ey來實(shí)現(xiàn)Ex=Ey。但是,由于浮點(diǎn)表示的數(shù)多是規(guī)格化的,尾數(shù)左移會(huì)引起最高有效位的丟失,造成很大誤差。尾數(shù)右移雖引起最低有效位的丟失,但造成誤差較小。因此,對(duì)階操作規(guī)定使尾數(shù)右移,尾數(shù)右移后階碼作相應(yīng)增加,其數(shù)值保持不變。顯然,一個(gè)增加后的階碼與另一個(gè)階碼相等,增加的階碼的一定是小階。因此在對(duì)階時(shí),總是使小階向大階看齊,即小階的尾數(shù)向右移位(相當(dāng)于小數(shù)點(diǎn)左移)每右移一位,其階碼加1,直到兩數(shù)的階碼相等為止,右移的位數(shù)等于階差△E。
(2) 尾數(shù)求和運(yùn)算
對(duì)階結(jié)束后,即可進(jìn)行尾數(shù)的求和運(yùn)算。不論加法運(yùn)算還是減法運(yùn)算,都按加法進(jìn)行操作,其方法與定點(diǎn)加減法運(yùn)算完全一樣。我這里就照搬常用加減法器。
(3) 結(jié)果規(guī)格化
在浮點(diǎn)加減運(yùn)算時(shí),尾數(shù)求和的結(jié)果也可以得到01.ф…ф或10.ф…ф,即兩符號(hào)位不等,這在定點(diǎn)加減法運(yùn)算中稱為溢出,是不允許的。但在浮點(diǎn)運(yùn)算中,它表明尾數(shù)求和結(jié)果的絕對(duì)值大于1,向左破壞了規(guī)格化。此時(shí)將運(yùn)算結(jié)果右移以實(shí)現(xiàn)規(guī)格化表示,稱為向右規(guī)格化。規(guī)則是:尾數(shù)右移1位,階碼加1。當(dāng)尾數(shù)不是1.M時(shí)需向左規(guī)格化。
(4) 舍入處理
在對(duì)階或向右規(guī)格化時(shí),尾數(shù)要向右移位,這樣,被右移的尾數(shù)的低位部分會(huì)被丟掉,從而造成一定誤差,因此要進(jìn)行舍入處理。簡(jiǎn)單的舍入方法有兩種:一種是"0舍1入"法,即如果右移時(shí)被丟掉數(shù)位的最高位為0則舍去,為1則將尾數(shù)的末位加"1"。另一種是"恒置一"法,即只要數(shù)位被移掉,就在尾數(shù)的末尾恒置"1"。
在IEEE754標(biāo)準(zhǔn)中,舍入處理提供了四種可選方法:
就近舍入 其實(shí)質(zhì)就是通常所說的"四舍五入",這是默認(rèn)的常用方法。例如,尾數(shù)超出規(guī)定的23位的多余位數(shù)字是10010,多余位的值超過規(guī)定的最低有效位值的一半,故最低有效位應(yīng)增1。若多余的5位是01111,則簡(jiǎn)單的截尾即可。對(duì)多余的5位10000這種特殊情況:若最低有效位現(xiàn)為0,則截尾;若最低有效位現(xiàn)為1,則向上進(jìn)一位使其變?yōu)?0。
朝0舍入 即朝數(shù)軸原點(diǎn)方向舍入,就是簡(jiǎn)單的截尾。無論尾數(shù)是正數(shù)還是負(fù)數(shù),截尾都使取值的絕對(duì)值比原值的絕對(duì)值小。這種方法容易導(dǎo)致誤差積累。
朝+∞舍入 對(duì)正數(shù)來說,只要多余位不全為0則向最低有效位進(jìn)1;對(duì)負(fù)數(shù)來說則是簡(jiǎn)單的截尾。
朝-∞舍入 處理方法正好與 朝+∞舍入情況相反。對(duì)正數(shù)來說,只要多余位不全為0則簡(jiǎn)單截尾;對(duì)負(fù)數(shù)來說,向最低有效位進(jìn)1。
本浮點(diǎn)加減法器符合IEEE754單精度fp32浮點(diǎn)標(biāo)準(zhǔn),但由于體積問題沒有使用舍入法,所以精度有一定損失。整體硬件單元的外觀上面已經(jīng)有一個(gè)圖給出了。我再貼兩張圖描述一下具體細(xì)節(jié)。
下圖中間一大塊都是對(duì)階判斷單元,也就是再下一張圖里右下角突出來的那一塊。右上方紫色橙色相間的是結(jié)果規(guī)格化單元。
下圖中呈對(duì)稱狀的三角形一共有三層,都是移位單元用于對(duì)階,其后方,也就是圖中藏在移位模塊下方紅綠相間的是尾數(shù)求和加減法器。
正余弦算法
這個(gè)用的是經(jīng)典的cordic迭代算法中的旋轉(zhuǎn)坐標(biāo)算法。公式推導(dǎo)如下:
將平面坐標(biāo)系中向量(Xi , Yi)旋轉(zhuǎn)角度θ得到新向量(Xj , Yj)
參數(shù)意義如下圖,β是初始角,θ是旋轉(zhuǎn)角,R是圓周半徑
化為矩陣式
可以看出θ如果拆成許多個(gè)小θ,即θ=θ1+θ2+θ3+…+θn,那么作n次旋轉(zhuǎn)即可得到結(jié)果。
為了方便二進(jìn)制硬件運(yùn)算,現(xiàn)構(gòu)造一個(gè)θ序列:
矩陣各項(xiàng)除以θn
先不管cos θn,構(gòu)造θn=arctan(1/2^n),并且滿足
Sn表示θ的正負(fù),也就是說構(gòu)造出的這列θn前面要加正負(fù)號(hào),以反復(fù)偏大偏小的趨勢(shì)逼近θ。每一步旋轉(zhuǎn)的角度Zn滿足如下條件
綜上得
經(jīng)過N次迭代后
這個(gè)K就是一坨cosθn的連乘,定義為增益因子。
取無限次迭代值為
P為K的倒數(shù)。
Cordic算法有幾種模式,這里只取旋轉(zhuǎn)模式。將上述矩陣化為數(shù)列得
N次迭代后
然后就是套三角函數(shù)了,取X0=K,Y0=0,Z0=α,那么N次迭代之后
正余弦就算出來了。沒了。
用在硬件上的優(yōu)勢(shì)是,該算法從矩陣去除cos因子之后就在盡力構(gòu)造簡(jiǎn)易的二進(jìn)制運(yùn)算比如加減和移位。需要預(yù)先算好那個(gè)K的值精確到指定位數(shù),還要算arctan(1/2^n),這些都要放到儲(chǔ)存器里。
其中細(xì)節(jié)不說了,最后我設(shè)計(jì)出的玩意兒就下面這貨。
硬件框圖如下
注:1.由于我懶得去用數(shù)學(xué)軟件打公式,以上數(shù)學(xué)公式的圖片均截取自一篇來自桂林電子科技大學(xué)李全,陳石平和付佃華的論文《基于CORDIC 算法的32 位浮點(diǎn)三角超越函數(shù)之正余弦函數(shù)的FPGA 實(shí)現(xiàn)》
2.我不打算讓三角函數(shù)運(yùn)算單元加入FPU結(jié)構(gòu)了,所以沒做成IEEE754標(biāo)準(zhǔn),只給浮點(diǎn)計(jì)算器用。
開方算法
特殊函數(shù)計(jì)算器除了三角函數(shù)外另一種運(yùn)算是7位操作數(shù)開方運(yùn)算,輸出4位開方結(jié)果和4位余數(shù)。
算法為筆算開方算法(快速平方根算法),流程如下:
圖片來自《基于FPGA快速平方根算法的實(shí)現(xiàn)》- 戢小亮,嵌入式技術(shù),2007年14期
該算法在硬件上實(shí)現(xiàn)很簡(jiǎn)答,只需要用到加法器和移位器即可,所以在本工程中實(shí)現(xiàn)出來的體積不大。最終實(shí)現(xiàn)了一個(gè)23位開方根器,如下圖直角梯形部分。
特殊函數(shù)計(jì)算器的運(yùn)行結(jié)果顯示如下兩圖示例
三角函數(shù)運(yùn)算輸入4位定點(diǎn)有效數(shù)字的角度,輸出sin值和cos值,運(yùn)算+輸出時(shí)間約為130秒輸出sin值,再過10秒輸出cos值,輸入角限制為0-83.88度之間。開方運(yùn)算輸入7位有效數(shù)字,可以選擇小數(shù)點(diǎn)在第三位或整數(shù)形式,輸出結(jié)果為4位開方結(jié)果和4位余數(shù),運(yùn)算+輸出時(shí)間約為50秒輸出開方值,再過10秒輸出余數(shù)。
儲(chǔ)存器架構(gòu)和流水線
因?yàn)檫€沒有做完這一部分,可能還會(huì)有修改,所以就簡(jiǎn)要的介紹一下。
現(xiàn)代計(jì)算機(jī)都是圍繞儲(chǔ)存器為中心,因?yàn)閮?chǔ)存器容量極其巨大,其占用的晶體管數(shù)量遠(yuǎn)遠(yuǎn)超過用于運(yùn)算和指令分配的其他邏輯單元。
比如一顆GPU,拿GK104為例,一共30多億晶體管,片上緩存就占了芯片面積的接近三分之一。這還只是第一級(jí)的讀取機(jī)制,緩存分一二三級(jí),后面還有主儲(chǔ)存器(內(nèi)存),還有硬盤,這些容量每一級(jí)都要比上一級(jí)大了大約兩個(gè)數(shù)量級(jí)。容量大,晶體管多,電流流過的時(shí)間長(zhǎng),最終讀取到數(shù)據(jù)的時(shí)間必然變長(zhǎng)。但是處理器時(shí)時(shí)刻刻都在做運(yùn)算,如果第一時(shí)間不能取到需要的指令和數(shù)據(jù),流水線就會(huì)空置?,F(xiàn)代處理器運(yùn)算速度和儲(chǔ)存器延遲的鴻溝越來越大,計(jì)算機(jī)核心技術(shù)基本都是圍繞儲(chǔ)存器開展的。為了填補(bǔ)這拉開的時(shí)間差所造成的瓶頸,各種流水線結(jié)構(gòu),總線結(jié)構(gòu),硬件算法孕育而生。
流水線技術(shù)使得整個(gè)指令流程前后重疊,能最大限度利用每一個(gè)硬件單元。早期CPU流水線級(jí)數(shù)較小,現(xiàn)在的CPU一般都是十幾級(jí)流水線。流水線級(jí)數(shù)也不是越大越好,因?yàn)榇嬖谝恍┣闆r,比如較晚的分支預(yù)測(cè)錯(cuò)誤,會(huì)導(dǎo)致流水線冒泡。
本工程使用Harvard結(jié)構(gòu),相對(duì)于Neumann結(jié)構(gòu)。程序儲(chǔ)存器和數(shù)據(jù)儲(chǔ)存器分開放置。程序儲(chǔ)存器1kb,數(shù)據(jù)儲(chǔ)存器0.5kb。由于指令是統(tǒng)一的雙字節(jié),所以程序儲(chǔ)存器只按字(雙字節(jié))存取。而數(shù)據(jù)格式可以是單字節(jié)(低8位),也可以是雙字節(jié),所以數(shù)據(jù)儲(chǔ)存器可以按字或按字節(jié)存取。
寄存器方面,ALU用ACC存放X操作數(shù)和運(yùn)算結(jié)果,這里的運(yùn)算結(jié)果都是需要雙操作數(shù)的那些邏輯運(yùn)算指令比如加減乘除與或異或,另一個(gè)操作數(shù)由Y寄存器存取。除法的余數(shù)最終會(huì)輸出到最靠近除法器輸出端的那4個(gè)寄存器的末端一個(gè)。所以如果之后要使用余數(shù),就要避免在轉(zhuǎn)移余數(shù)之前使用該寄存器做其他事情。另外還有8個(gè)通用寄存器供自由使用,一共可自由支配的寄存器有13個(gè),ACC+12個(gè)通用寄存器。再算上棧區(qū)的4個(gè)單元,就一共有17個(gè)。
下圖為簡(jiǎn)易的CPU功能模塊立體位置圖。
下圖為程序儲(chǔ)存器側(cè)面圖,是立體8層疊加式結(jié)構(gòu),每一層128byte。
本工程因?yàn)轶w積和延時(shí)問題的特殊性,主程序儲(chǔ)存器只有1.5kb,訪存速度相對(duì)于真實(shí)的計(jì)算機(jī)快多了,所以這個(gè)并不是瓶頸,可以在相對(duì)較短的時(shí)間內(nèi)取到想要的指令。所以本想借這個(gè)優(yōu)勢(shì)實(shí)現(xiàn)指令全流水,也就是依靠復(fù)雜的分支預(yù)測(cè)機(jī)制將流水線漏洞封死。后來嘗試做了一下,發(fā)現(xiàn)問題還是很多,比如預(yù)譯碼,預(yù)跳轉(zhuǎn)中PC的占用沖突和布線困難,前一條跳轉(zhuǎn)指令和后一條跳轉(zhuǎn)指令的沖突,如果想要做出來,開銷過于巨大,基本上CU的體積要增加0.5倍,這對(duì)我之后的工作影響很大故棄之,改為二位動(dòng)態(tài)分支預(yù)測(cè)器來執(zhí)行分支預(yù)測(cè),這樣流水線會(huì)冒氣泡,但是損失不大。
流水線:取指→預(yù)譯碼,預(yù)執(zhí)行→譯碼(→間指)→執(zhí)行(→寫回)
完整的流水線,一條指令會(huì)經(jīng)歷如下過程:當(dāng)指令緩沖隊(duì)列的指令條數(shù)小于2條時(shí),會(huì)發(fā)出一個(gè)信號(hào)往PC,PC將當(dāng)前儲(chǔ)存的地址傳送至程序儲(chǔ)存器MAR,然后PC+1,地址譯碼器將相應(yīng)地址的指令A(yù)傳送至程序儲(chǔ)存器MDR,然后MDR將A傳送至預(yù)譯碼單元,如果是跳轉(zhuǎn)指令等需要修改PC的指令則預(yù)執(zhí)行指令。意思是將程序指針類指令全部放在另一個(gè)流水線分叉上執(zhí)行。如果不是跳轉(zhuǎn)指令,則MDR將A指令壓入指令緩沖隊(duì)列。如果此時(shí)緩沖隊(duì)列里有ABC三條指令按這個(gè)順序排列,那么等待C先彈出,然后B彈出,最后輪到A彈出至IR,IR將A送往指令譯碼器,控制信號(hào)通完各EU單元前端。等待前面的B指令執(zhí)行完了 ,然后正式執(zhí)行A指令。此時(shí)如果有間指周期,則將數(shù)據(jù)地址輸送至地址譯碼器,從寄存器或數(shù)據(jù)儲(chǔ)存器讀取數(shù)據(jù)送到相應(yīng)單元。然后執(zhí)行指令。執(zhí)行完畢如果有寫回周期則執(zhí)行寫回。一條指令執(zhí)行完畢。期間在指令緩沖隊(duì)列和IR中如果前面有一條指令是條件跳轉(zhuǎn)指令并且分支預(yù)測(cè)錯(cuò)誤,會(huì)使緩沖隊(duì)列和IR清存。PC發(fā)射正確的地址重新取指。
流水線流程圖如下:
藍(lán)線是控制信號(hào),綠線是數(shù)據(jù)信號(hào),紅色字母和橙色箭頭是指令隊(duì)列的順序壓進(jìn)方式。
指令集架構(gòu)
指令格式和取指方式:
ALU Instruction Formats
OP: Operation Code
AM: Addressing Mode
EA: Effective Address
RA: Register Addressing
X: Operation Data
REG: Register
OCID: Off-chip Device ID
Number: Length
N: Null
OP - 5
AM - 2
EA - 9
OP - 5
RA - 2
N
REG src - 4
REG dest - 4
OP- 7
X - 9
OP - 7
REG - 4
X - 5
OP - 7
N
REG src - 4
REG dest - 4
OP - 5
OCID - 3
EA - 8
Addressing Modes
Immediate Addressing
Direct Addressing
Register Addressing
Register Indirect Addressing
暫定指令表:
由于CU還沒做完,指令機(jī)器碼可能還有較大變動(dòng),所以是暫定表,這一部分也不多介紹。其中有一些指令是我特殊設(shè)計(jì)出來為了節(jié)約代碼,所以助記符不一定規(guī)范(有些縮寫就是在瞎編)。
機(jī)器碼
指令名稱
助記符
指令格式
00001
按字節(jié)取數(shù)據(jù)
LDB
5
2
9
00010
按字節(jié)存數(shù)據(jù)
SDB
00011
按字取數(shù)據(jù)
LDW
00100
按字存數(shù)據(jù)
SDW
00101
按字取指令
LIB
00110
按字存指令
SIB
00111
整數(shù)加
ADD
01000
整數(shù)減
SUB
01001
整數(shù)乘
MUL
01010
整數(shù)除
DIV
01011
按位與
ANL
01100
按位或
ORL
01101
按位異或
XRL
01110
按位非
NOT
10000
加1
INC
10001
減1
DEC
1001000
數(shù)據(jù)傳送
MOV
7
N
4
4
1001001
算數(shù)移位
ASH
7
4
5
1001010
邏輯移位
LSH
1001011
標(biāo)志位X判斷
FXJ
7
N
1
3
1001100
無條件跳轉(zhuǎn)
JMP
7
9
1001101
無條件偏移
OFA
1001110
條件跳轉(zhuǎn)
XJMP
1001111
條件偏移
XOFA
1010000
壓棧
PUSH
7
N
4
1010001
彈棧
POP
1010010
暫停
HLT
7
N
10101
累加器輸入
IN
5
3
8
10110
累加器輸出
MOVX
10111
I/O執(zhí)行
IOE
11000
寄存器賦值
RIA
5
3
8
110110
調(diào)用
CALL
5
9
110111
返回
RET
5
9
111000
無操作
NOP
部分指令說明:
1. 數(shù)據(jù)傳送指令:MOV,PUSH,POP,RIA
MOV指令支持除了MAR,MDR,PC和棧區(qū)之外的寄存器之間的數(shù)據(jù)傳送;
PUSH和POP指令為壓棧和彈棧,原地址和目標(biāo)地址均為寄存器,當(dāng)棧滿時(shí)PUSH則無效,原棧區(qū)數(shù)據(jù)不變,以溢出中斷處理;
RIA指令可實(shí)現(xiàn)單字節(jié)的立即數(shù)寫入寄存器,設(shè)計(jì)該指令的目的是為了使寄存器賦初值等操作更靈活,節(jié)約指令周期。
2. 數(shù)據(jù)讀寫指令:LDB,SDB,LDW,SDW,LIB,SIB
LDB,SDB,LDW,SDW均為對(duì)數(shù)據(jù)儲(chǔ)存器的讀寫操作,讀操作均由儲(chǔ)存器傳輸至ACC,寫操作均由ACC至儲(chǔ)存器;
LIB,SIB均為對(duì)程序儲(chǔ)存器的讀寫操作,讀操作均由儲(chǔ)存器傳輸至ACC,寫操作均由ACC至儲(chǔ)存器;
這6種操作均支持4種尋址方式。
3.算術(shù)運(yùn)算指令:ADD,SUB,MUL,DIV,INC,DEC
ADD,SUB,MUL,DIV均為取操作數(shù)于Y寄存器,然后與ACC進(jìn)行算術(shù)運(yùn)算,結(jié)果存于ACC。當(dāng)尋址方式為寄存器尋址時(shí),指令格式為
OP - 5
RA - 2
N
REG src - 4
REG dest - 4
即該格式指令支持目標(biāo)寄存器,結(jié)果由ACC存至目標(biāo)寄存器;
INC和DEC指令只支持寄存器直接尋址。
4.邏輯運(yùn)算指令:ASH,LSH,AND,ORL,XRL,NOTASH和LSH指令只支持寄存器內(nèi)數(shù)據(jù)移位操作,移位數(shù)值為立即數(shù),取值范圍-15到+15;
AND,OR,XOR指令和算術(shù)運(yùn)算指令同格式;
NOT,LSH指令為單操作數(shù)指令。
控制轉(zhuǎn)移類指令參考流水線架構(gòu)。
關(guān)于尋址位數(shù)。因?yàn)閮?chǔ)存器很小,我在16bit的雙字節(jié)指令里正好塞下了5位的基本操作碼,2位的尋址方式和9位的儲(chǔ)存器尋址。9位尋址對(duì)應(yīng)512個(gè)程序儲(chǔ)存器單元共1kb,也正好對(duì)應(yīng)了512個(gè)數(shù)據(jù)儲(chǔ)存器單元512byte,所有可用空間都填滿了。所以不能再擴(kuò)充內(nèi)存,也用不到像8086一樣的造過于復(fù)雜的段式內(nèi)存管理,那樣的MMU會(huì)給系統(tǒng)造成很大的延遲。
總線和時(shí)鐘
把總線單拉出來講是因?yàn)楸竟こ藽PU的延遲瓶頸在總線而不是儲(chǔ)存器。儲(chǔ)存器體積雖然大但是立體結(jié)構(gòu)使得信號(hào)傳輸時(shí)間相對(duì)較短。由于本工程CPU的EU部分接口太多,還都是16bit,還要考慮ALU輸入輸出接口朝向的問題,排了半天也很難將這些接口的距離縮短,最終變成了一個(gè)折線形排布。此時(shí)就需要總線將所有接口貫通起來。而本工程總線的一個(gè)重要特點(diǎn)是環(huán)狀的,因?yàn)橛螒蛴美^電器實(shí)現(xiàn)信號(hào)傳輸具有二極管特性,只能單向?qū)ǎㄗ龀呻p向很麻煩),所以總線如果實(shí)現(xiàn)從任意段輸入任意段輸出只能走環(huán)路。環(huán)路雖然增加了一倍的距離,其延遲還在可以接受的范圍內(nèi),大約5.8秒(每一個(gè)bit位的總線一共58個(gè)繼電器800多米長(zhǎng))。最大的難題是當(dāng)總線在某一周期的任務(wù)完成后,需要進(jìn)行下一輪數(shù)據(jù)傳輸。但是因?yàn)榄h(huán)路的特性,繼電器的儲(chǔ)存器效應(yīng)會(huì)讓環(huán)路保持原有的信號(hào)。此時(shí)必須加一個(gè)開路裝置將原有的信號(hào)阻斷。按照平常的思路在某一個(gè)環(huán)路節(jié)點(diǎn)加一排活塞將線路切斷,信號(hào)會(huì)在總線里拖尾5.8秒(因?yàn)槔^電器會(huì)儲(chǔ)存相同長(zhǎng)度信號(hào),切斷的節(jié)點(diǎn)其一邊的信號(hào)會(huì)沿環(huán)路繞一大圈傳輸?shù)搅硪贿吅蟛艜?huì)最終消失)。這樣的話一個(gè)周期一共要耗費(fèi)11.6-12秒,這長(zhǎng)度實(shí)在是難以接受。
在我本以為實(shí)在沒辦法解決這個(gè)延遲問題準(zhǔn)備向其妥協(xié)的時(shí)候,突然想到了一個(gè)解決方案:總線清存也用時(shí)序邏輯控制。也就是說在總線上找若干個(gè)節(jié)點(diǎn)都放上一排開路活塞,每一次傳輸完畢后所有活塞在同一時(shí)間切斷線路,那么這時(shí)需要考慮的延遲時(shí)間就是相隔最遠(yuǎn)的兩個(gè)節(jié)點(diǎn)之間的距離差。比如最后的成品中相隔最遠(yuǎn)的兩個(gè)節(jié)點(diǎn)是180米,那么就是1.2秒的信號(hào)拖尾,從原來的5.8秒節(jié)省到1.2秒,總的單周期時(shí)間正好是7秒。只需要加一個(gè)總線控制電路讓其和系統(tǒng)時(shí)鐘同步就可以了。而且這樣設(shè)計(jì)的另一個(gè)好處馬上凸顯:在這1.2秒的清存時(shí)間里,指令發(fā)射端正好可以做各種調(diào)整工作,此時(shí)不需要使用總線,打了一個(gè)時(shí)間差,意味著各設(shè)備都充分利用到了時(shí)間間隙,是一個(gè)讓我很驚訝的非常巧合的設(shè)計(jì),感覺就好像有一種內(nèi)在驅(qū)動(dòng)力會(huì)讓這一切看起來就應(yīng)該是這樣契合一樣。
下圖黑色方塊部分為總線清存器(第一部分已經(jīng)提到過一次)。圖中藍(lán)白相間的,橙灰相間的,以及深灰色的線路全是總線,藍(lán)白相間的是高八位,橙灰相間的是低八位,深灰色的是轉(zhuǎn)角處的線路。顏色不同只是為了方便識(shí)別節(jié)點(diǎn)和高低位,沒有功能上的區(qū)別。黑色方塊上有很多繼電器都是時(shí)序控制電路,用于周期性的向活塞輸出開路信號(hào)。每7秒輸出一個(gè)1.2秒的信號(hào)將一段一段的信號(hào)阻隔直到全部消失。
時(shí)鐘暫定為總線周期7秒,取指周期5秒。所有信號(hào)的源頭都是CU的時(shí)鐘信號(hào)發(fā)生器發(fā)出的。一般的指令都是1或2周期,所以一般執(zhí)行一條指令需要7或14秒,乘除類的運(yùn)算指令時(shí)間較長(zhǎng),最長(zhǎng)的除法指令需要6個(gè)周期。所以這個(gè)計(jì)算機(jī)的運(yùn)算速度根本指望不上了,一個(gè)極簡(jiǎn)單的程序就會(huì)運(yùn)行幾分鐘。畢竟是在“計(jì)算機(jī)實(shí)時(shí)模擬計(jì)算機(jī)”,所以速度什么的已經(jīng)盡力做到最快了。
圖形顯示原理
說實(shí)話,顯示器是最難做的東西之一,因?yàn)橥耆菚r(shí)序邏輯在控制還要顧及到和使用者的交互。而且圖形的東西對(duì)面積體積時(shí)間等問題要求極其嚴(yán)格(現(xiàn)實(shí)中的顯示器沒必要考慮那么多,因?yàn)檫@些都不是瓶頸)。而圖形處理器就更是天方夜譚了,有很多玩家會(huì)說要是在Minecraft 里造一臺(tái)計(jì)算機(jī)可以玩Minecraft就吊炸天。這顯然不可能,而且想做一個(gè)純粹靠通用處理器運(yùn)算來玩的小游戲都絕對(duì)不可能。比如貪吃蛇這種圖像刷新率低的游戲,肯定做不出來。
先簡(jiǎn)單介紹一下現(xiàn)實(shí)中的圖形處理器以及顯示器是如何工作的,這對(duì)理解一些設(shè)計(jì)理念有很大幫助,也能解釋為啥MC里如此難以實(shí)現(xiàn)標(biāo)準(zhǔn)意義上的顯卡。這一部分專業(yè)術(shù)語過多,僅供做相關(guān)參考,可以直接跳過看下面本工程的圖形設(shè)備的設(shè)計(jì)。
現(xiàn)實(shí)中的圖形顯示是按照“圖形流水線”(graphic pipeline)來完成的。一般我們玩的3D游戲中,顯卡是圖像處理的設(shè)備。顯卡的核心是GPU,CPU將應(yīng)用程序的圖像請(qǐng)求發(fā)送往GPU,GPU是圖形處理器,作為協(xié)處理器。操作系統(tǒng)將所有的設(shè)備統(tǒng)一編址,并具有各自規(guī)范,所以每一個(gè)操作系統(tǒng)要調(diào)用GPU必須要有相應(yīng)GPU的軟件驅(qū)動(dòng)程序?,F(xiàn)在的GPU較為獨(dú)立,CPU大部分時(shí)間不參與圖形運(yùn)算。GPU直接運(yùn)行的是shader API,驅(qū)動(dòng)程序指導(dǎo)GPU運(yùn)行shader API,GPU的硬件結(jié)構(gòu)將API編譯成一條一條instruction。現(xiàn)在常用的shader API是OpenGL和Direct3D。由于圖形運(yùn)算是密集型并行運(yùn)算,所以GPU內(nèi)部有成百上千的unified shader ALU組成若干模塊如nVIDIA GPU中的SM或者AMD GPU中的CU ,這些模塊是程序員直接面對(duì)的對(duì)象,包含F(xiàn)PU,Load/Store Unit和SFU等等。還有TMU,Tessellator,rasterizer等等流水線上其他的功能單元,我們叫這些東西為:Fixed Function Unit。GPU的底層指令按照warp/wave的模式每一個(gè)指令周期都有成百上千條被發(fā)射,這些指令相關(guān)性小,一般都是頂點(diǎn),像素,幾何或紋理的shader指令。指令列隊(duì)叫thread,每一個(gè)thread都會(huì)對(duì)應(yīng)一個(gè)像素或頂點(diǎn),若干shader ALU組成的vector單元同一時(shí)間用不同數(shù)據(jù)執(zhí)行不同thread中相同的指令(因?yàn)閒ront-end單元稀缺)。 每一個(gè)周期有成百上千個(gè)thread的某些指令被處理完,若干周期后所有thread都處理完,這時(shí)候一張畫面就被初步執(zhí)行完了,一般都是接近百萬個(gè)像素點(diǎn)比如1280x720分辨率的顯示器。之后圖形流水線會(huì)將畫面光柵化-rasterization,經(jīng)過各種紋理,抗鋸齒處理后,完整的具有正確幾何信息和顏色信息的畫面就處理完了。然后該幅畫面就被送往幀緩存-framebuffer,這個(gè)是在顯存中劃出來的模塊,等到合適的時(shí)機(jī),該幅畫面就傳送往顯示器輸出,一張畫面稱為一幀。每秒鐘一個(gè)GPU繪制出幾十張這樣的畫面,人的肉眼就會(huì)看到流暢的畫面。
GPU是典型的SIMD結(jié)構(gòu),單指令多數(shù)據(jù)的大規(guī)模并行運(yùn)算。并且其運(yùn)算的數(shù)據(jù)多為浮點(diǎn)型。GPU耗費(fèi)的晶體管數(shù)量會(huì)大于CPU,需要造一大堆重復(fù)的ALU陣列和寄存器陣列。
下面貼三張GPU架構(gòu)圖,都是AMD(ATI)和nVIDIA這兩年的GPU產(chǎn)品。
可以很明顯的看出GPU一般重復(fù)結(jié)構(gòu)較多,都是SIMD陣列加上少數(shù)dispatch和其他shading pipeline結(jié)構(gòu),所以如果在minecraft中簡(jiǎn)化到極致,比如每一個(gè)模塊只造一個(gè)單元確實(shí)可以造出一個(gè)具有完整結(jié)構(gòu)的顯卡,但是想要做一個(gè)可以持續(xù)輸出流暢畫面的GPU對(duì)于minecraft來說基本是不可能,光是造一個(gè)浮點(diǎn)ALU就要占據(jù)幾百乘以幾百乘以幾十的體積。假若一個(gè)屏幕按照30x30的像素來計(jì)算,并且是bitmap,只有亮暗兩種色彩。就算是2D的圖像程序,一共900個(gè)像素,再放寬條件要求每3秒才出一幀,那么每一秒鐘也要處理300個(gè)像素,按照最簡(jiǎn)單的2D指令,假若平均一個(gè)像素只需要3條指令就能得出其是亮還是暗,那么就需要300個(gè)ALU每秒運(yùn)算3次。到這里也不需要考慮其他什么圖形流水線了,光是ALU團(tuán)簇已經(jīng)這么多,造出標(biāo)準(zhǔn)意義的顯卡基本不可能。很多玩家認(rèn)為在minecraft里面可以造出運(yùn)行minecraft的計(jì)算機(jī),這種宏圖大業(yè)是不可能完成了。就算是常用的顯示程序比如操作系統(tǒng)界面,也沒不可能造出鼠標(biāo)這種東西了,因?yàn)椴豢赡茏龀鳇c(diǎn)控的設(shè)備。
然后回歸正題,既然造標(biāo)準(zhǔn)意義的顯卡不可能,那么就退而求其次,做一些功能弱一些的顯示設(shè)備,比如說只要求顯示器輸出部分字符,并不要求其控制每一個(gè)像素。這樣可行度會(huì)大大提高。
演示視頻中的計(jì)算器和字符顯示器都是可以控制輸出字符的顯示設(shè)備。那么該如何用盡量少的電路來實(shí)現(xiàn)這些結(jié)構(gòu)并且能夠讓其反應(yīng)迅速呢?又如何增加顯示設(shè)備與玩家的交互性呢?
前面已經(jīng)介紹過minecraft中常見的圖像信號(hào)組成方式:紅石燈和陰影。視頻里正好展示了這兩種,計(jì)算器和電子表部分用的是陰影,字符顯示器用的是紅石燈。為什么這樣選擇呢,這和方塊的特性也有一定關(guān)系不過這個(gè)無關(guān)緊要。先來介紹計(jì)算器的數(shù)字顯示。
常接觸單片機(jī)的人很容易看出我用的是七段數(shù)碼顯示器。七段顯示器顧名思義,所有十進(jìn)制數(shù)字信息都可以由七個(gè)部分組成的,3橫4豎。電話機(jī),老式收銀機(jī)上的數(shù)字都是用這種方式顯示的。“8”這個(gè)數(shù)字是最復(fù)雜的,它把7個(gè)段都用到了,如下圖,右邊的“2”顯然是“8”去掉左上和右下兩個(gè)段,其他所有數(shù)字都可以用少于7個(gè)段表示。
那么如何用二進(jìn)制電路表示十進(jìn)制數(shù)呢?編碼的原則是越簡(jiǎn)單越好,顯然10個(gè)十進(jìn)制數(shù)字可以用10個(gè)4位二進(jìn)制數(shù)表示,比如3是0011, 9是1001,這就是BCD碼。計(jì)算機(jī)說到底就是一堆不同種類的碼來回轉(zhuǎn)換的過程。要達(dá)到數(shù)字顯示到屏幕上的過程,需要如下步驟:二進(jìn)制碼發(fā)射到A單元上,A單元將二進(jìn)制碼對(duì)應(yīng)的十進(jìn)制數(shù)連接到各數(shù)字對(duì)應(yīng)的七段信息上,比如0100是數(shù)字4,而4對(duì)應(yīng)的七段信息如上圖是左上,右上,中,右下四個(gè)段,最后這四個(gè)段每段3個(gè)方塊的活塞抽回來,則數(shù)字4就被顯示出來,這整個(gè)過程譯碼了兩次,一次是二進(jìn)制碼BIN轉(zhuǎn)十進(jìn)制碼BCD一次,然后十進(jìn)制碼轉(zhuǎn)對(duì)應(yīng)的七段信息是第二次。字符顯示也一樣是這種原理,具體后面再說。
上面所提的A單元就是譯碼器。計(jì)算機(jī)里充斥了各種譯碼器。下圖為BIN轉(zhuǎn)BCD再轉(zhuǎn)七段信息的譯碼器(橙色條形方塊下面的部分)。這個(gè)譯碼器經(jīng)過極度的體積壓縮保證它占據(jù)的體積是所能實(shí)現(xiàn)的最小的。因?yàn)橐贿B串字符排在一起,如果譯碼器較寬,一個(gè)一個(gè)排在一起會(huì)占據(jù)較大空間使數(shù)字看起來松散。實(shí)際上整個(gè)工程每一個(gè)單元的體積我都盡了最大努力將其壓縮,這耗費(fèi)非常大的腦力和精力。關(guān)于如何在三維結(jié)構(gòu)上壓縮電路也可以單拉出來寫幾千字。
計(jì)算器還需要控制端按鈕轉(zhuǎn)BIN譯碼器,多位BIN轉(zhuǎn)BCD譯碼器和多位BCD譯碼器轉(zhuǎn)BIN用于和CPU溝通,這些比較復(fù)雜就不多說了和顯示設(shè)備無關(guān),上面部分已經(jīng)介紹過算法。
下面介紹字符顯示器。
字符顯示器是點(diǎn)陣式的,即在一個(gè)5x5像素的點(diǎn)陣上顯示一個(gè)字符,如下圖5x5顯示屏單元上的字母N,和七段顯示器一樣,后面一長(zhǎng)串就是BIN轉(zhuǎn)字符轉(zhuǎn)5x5像素譯碼器。
我使用的是自己設(shè)計(jì)的縮減版的ASCII碼,只有不到64個(gè)字符,如下表,我暫時(shí)稱之為ASCII X碼。
上表字符的BIN碼都是一個(gè)字節(jié)的低6位,另外還有一個(gè)字符Enter表示換行,使用01000000表示的。
游戲中能做到的最小像素是2x2個(gè)紅石燈,之所以不能做到1個(gè)紅石燈為一個(gè)像素,是因?yàn)轶w積上不可能做到在那么小的空間里控制每一個(gè)紅石燈的亮滅。而就算2x2的紅石燈為一個(gè)像素,也很難做到點(diǎn)控。關(guān)于這些字符譯碼的具體電路結(jié)構(gòu)不作詳述,下面貼幾張字符顯示器的流水線結(jié)構(gòu),視頻里也有介紹:
下圖為字符顯示器BIOS部分的輸入端,有兩個(gè)寄存器用輪發(fā)射方式發(fā)射字符信息,字符信息來自右邊的只讀儲(chǔ)存器。
下圖為雙線程字符譯碼器,整個(gè)字符顯示器模塊都是時(shí)序控制的。
下圖為字符顯示器的輸出部分,用總線連接一共兩排24個(gè)單元,每個(gè)單元分顯示器,鎖存器和閃屏器。中間的后方是一個(gè)總的移位控制單元。全時(shí)序控制最快速的每3.4秒輸出一個(gè)字符,可換行換頁。
下圖就是雙向移位觸發(fā)器。
做這個(gè)顯示器耗費(fèi)了較長(zhǎng)的時(shí)間,我一開始的設(shè)計(jì)方案體積大概是這個(gè)的三倍,后來突發(fā)奇想解決了不少技術(shù)問題縮小了體積并改為完全的時(shí)序控制?,F(xiàn)在還缺計(jì)算機(jī)鍵盤的交互式控制和其他幾個(gè)模塊的顯示單元。
下面用幾段話回顧視頻里展示的功能。
首先是計(jì)算器的功能:
完全時(shí)序控制 15bit整數(shù)加減乘除,除法輸出商和余數(shù) 三種溢出判斷:輸入溢出,輸出溢出,除數(shù)為0
然后是電子表功能:
可開關(guān) 循環(huán)顯示0點(diǎn)0分0秒到23點(diǎn)59分59秒 可通過按鈕精確調(diào)整時(shí)間
關(guān)于電子表多說幾句。電子表對(duì)于整個(gè)CPU而言只是一個(gè)獨(dú)立的附屬物,我把它當(dāng)做主要的展示品是因?yàn)殡娮颖砜梢暬男Ч容^好。原本我想再錄一些關(guān)于總線技術(shù)和流水線技術(shù)的視頻,但是太抽象了看著都困就作罷。電子表電路原理很簡(jiǎn)單,就是用移位觸發(fā)器循環(huán)一些數(shù)字而已。重點(diǎn)不在原理而在電路體積大小。我花了些精力將電子表的體積壓縮到如下圖這樣,應(yīng)該是非常迷你了。
最后是字符顯示器功能:
可接入任何標(biāo)準(zhǔn)儲(chǔ)存器并輸出儲(chǔ)存器中的字符信息 輸出字符可換行換頁 鍵盤交互式操作,單字符控制(這個(gè)還沒做完所以視頻里沒有)
視頻里有個(gè)字幕寫錯(cuò)了,有一句話里welcome沒有加最后那個(gè)e,不過已經(jīng)費(fèi)勁千辛萬苦把超清視頻傳到優(yōu)酷里,就懶得重新壓視頻再傳上去了。
關(guān)于工程的架構(gòu)名稱:Alpha21016。之所以取這個(gè)名字,是為了紀(jì)念十幾年前DEC(Digital Equipment Corporation)的Alpha架構(gòu),那是一個(gè)處理器時(shí)代的傳奇,可惜商業(yè)上并不成功。Alpha組的人很多后來都去了Intel和AMD,并立下了汗馬功勞。
綜合視頻和日志粗略的介紹了一下工程,題目說是“技術(shù)細(xì)節(jié)”實(shí)際上還有好多沒介紹的,就先不說了,真要寫完估計(jì)要寫一本200多頁的書。具體的規(guī)劃細(xì)節(jié)比如各種重要功能結(jié)構(gòu)的設(shè)計(jì),指令集的設(shè)計(jì),硬件單元接口排布,儲(chǔ)存器空間位置,流水線級(jí)數(shù),動(dòng)態(tài)分支預(yù)測(cè),亂序執(zhí)行,顯示器控制原理等等實(shí)在寫不動(dòng),這篇已經(jīng)寫了2萬多字了,等最終成品做完了再發(fā)完整的技術(shù)文檔。
本文是2013年8月寫的,不知為何2013年12月11日被大家頂上來。首先感謝大家的評(píng)論,分享和贊揚(yáng)。需要看工程新進(jìn)展可以到我的相冊(cè)里找。工程最終大約在2015年完工,會(huì)再發(fā)一篇完整地視頻和文檔出來。
最后再次感謝大家的支持?。?!
2014年8月25日更新
最近仍有很多人關(guān)注我的工程,我非常感動(dòng)。我沒棄坑,只是進(jìn)度緩慢。
最新的進(jìn)度圖
目前CPU已經(jīng)可以執(zhí)行若干種機(jī)器指令(以MOV為主):通用寄存器賦值,按字/字節(jié)+立即數(shù)/間接/直接尋址。
詳細(xì)設(shè)置如下:
指令名稱:數(shù)據(jù)儲(chǔ)存器取數(shù)據(jù)至X寄存器
指令目標(biāo):將數(shù)據(jù)儲(chǔ)存器中的某一字/字節(jié)數(shù)據(jù)傳輸至X寄存器中
指令格式:00001 0/1 0/1 addr(9)
對(duì)應(yīng)含義:指令碼 直接尋址/立即數(shù)尋址 按字/按字節(jié) 數(shù)據(jù)地址
備注:如果地址為奇數(shù)且為按字尋址,則改地址數(shù)據(jù)賦值到目的寄存器高8位
指令名稱:傳輸MOV
指令目標(biāo):通用寄存器之間的數(shù)據(jù)傳輸
指令格式:1000000 x reg(4) reg(4)
對(duì)應(yīng)含義:指令碼 無效 源寄存器地址 目的寄存器地址
指令名稱:加減乘除
指令目標(biāo):將被操作數(shù)取出傳輸至Y寄存器四則運(yùn)算后儲(chǔ)存至X寄存器
指令格式:00100/00101/00110/00111 0/1 0/1 addr(9)
對(duì)應(yīng)含義:加/減/乘/除 直接尋址/立即數(shù)尋址 取數(shù)計(jì)算/直接計(jì)算 數(shù)據(jù)地址
備注:只支持按字讀取數(shù)據(jù)儲(chǔ)存器
指令名稱:寄存器間接尋址
指令目標(biāo):將某寄存器中數(shù)據(jù)作為地址傳輸至MAR,取數(shù)后傳輸?shù)饺我饧拇嫫?div style="height:15px;">