【邏輯門的奇妙冒險(xiǎn)】第5篇 時(shí)序邏輯——帶上時(shí)間軸,我們?nèi)ノ磥?lái)
本篇目標(biāo):
1.搭建并分析SR鎖存器;
2.深入理解SR鎖存器的原理;
3.基于SR鎖存器,搭建D鎖存器;
4.理解時(shí)鐘與波形;
5.基于D鎖存器,搭建D觸發(fā)器;
6.基于D觸發(fā)器,搭建寄存器;
7.利用寄存器,實(shí)現(xiàn)一個(gè)計(jì)數(shù)器;
8.搭建寄存器文件
?
上一篇我們介紹了組合邏輯電路,它們都由與或非三種邏輯門搭建,然后層層封裝層層套娃,最終搭建出來(lái)一個(gè)功能相對(duì)豐富的算術(shù)邏輯單元ALU。ALU可以根據(jù)輸入的選擇信號(hào)S,來(lái)決定給出倆輸入AB的不同運(yùn)算結(jié)果。這個(gè)模塊可以說(shuō)很有意思了,但是包括這個(gè)模塊在內(nèi)的所有組合電路,都有一個(gè)特點(diǎn):就是此刻的輸出信號(hào)完完全全只與此刻的輸入信號(hào)有關(guān),與過(guò)往的輸入不相關(guān),也就是說(shuō),組合邏輯電路,沒(méi)有歷史信息。某某器件被輸入0101,就得到1010——這一對(duì)輸入輸出就會(huì)永遠(yuǎn)成立,不論這個(gè)器件之前被輸入什么,完全無(wú)所謂,但凡是0101來(lái)了,永遠(yuǎn)得到1010——它沒(méi)有“記憶”。
?
但是細(xì)細(xì)琢磨也沒(méi)毛病呀,它們都是用與或非邏輯門搭建起來(lái)的。邏輯門這么簡(jiǎn)潔的東西,本來(lái)就沒(méi)有記憶呀(攤手.jpg)。嘿嘿,閑話不多說(shuō),開(kāi)始本篇的奇妙冒險(xiǎn)吧~
?
1.搭建并分析SR鎖存器
開(kāi)門見(jiàn)山,關(guān)于這個(gè)器件,我先隱藏一下需求和設(shè)計(jì)思路,直接上這個(gè)電路,馬上開(kāi)始分析。試圖為讀者復(fù)現(xiàn)作者當(dāng)初學(xué)習(xí)這部分知識(shí)的那種“哇好有意思”和“哦原來(lái)如此”的感覺(jué)。這個(gè)電路很簡(jiǎn)單,只用或門、非門各兩個(gè),長(zhǎng)這樣:

誒,這個(gè)電路和我們以前見(jiàn)過(guò)的不一樣哦,怎么有一個(gè)回環(huán)呢?有意思哦。我們先分析一下試試,如果倆輸入SR都是11,那么A點(diǎn)就直接是1了,B點(diǎn)是0,B點(diǎn)同時(shí)輸入到下面那個(gè)或門,得到C點(diǎn)是1,D點(diǎn)就是0,同時(shí)給到上面那個(gè)或門,使得A點(diǎn)是1,然后B點(diǎn)是0,C是1,D是0……雖然層層轉(zhuǎn)圈分析不盡,略有小奇怪,但是確實(shí)也沒(méi)啥問(wèn)題吧?我們可以說(shuō),輸入SR是11,輸出Output就是0。如果要給它畫(huà)一個(gè)真值表的話,那么這一項(xiàng)有咯。
?
我們繼續(xù)看不同的輸入是怎么樣:

左上角:輸入SR分別是01,那么首先C點(diǎn)是1,D點(diǎn)是0,進(jìn)而A點(diǎn)就是0,B點(diǎn)是0,C點(diǎn)是1……OK,穩(wěn)定循環(huán)了,我們可以表達(dá)為:SR=01,Output=1。
右上角:輸入SR分別是10,那么首先A點(diǎn)是1,B點(diǎn)是0,進(jìn)而C點(diǎn)就是0,D點(diǎn)是1,A點(diǎn)是1……OK,穩(wěn)定循環(huán)了,我們可以表達(dá)為:
SR=10,Output=0。
左下角:輸入SR分別是00,那么假設(shè)D點(diǎn)是1,A點(diǎn)就得到1,B點(diǎn)是0,進(jìn)而C點(diǎn)是0,D點(diǎn)是1,A點(diǎn)是1……OK,穩(wěn)定循環(huán)了,也即:SR=00,Output=0。
右下角:輸入SR分別是00,那么假設(shè)D點(diǎn)是0,A點(diǎn)就得到0,B點(diǎn)是1,進(jìn)而C點(diǎn)是1,D點(diǎn)是0,A點(diǎn)是0……OK,穩(wěn)定循環(huán)了,也即:SR=00,Output=1。
?
等等等等?!左下角和右下角的圖,輸入SR都是00,輸出卻不一樣?!再好好看看分析過(guò)程,好像也沒(méi)錯(cuò)呀。我們?cè)倏匆谎郏龠M(jìn)一步整理一下:

左下角的SR=00可以看做是從左上角的SR=01,將R變成0得到,同時(shí)內(nèi)部的ABCD點(diǎn)狀態(tài)還是那個(gè)樣子不變;
左下角的SR=00可以看做是從右上角的SR=10,將S變成0得到,同時(shí)內(nèi)部的ABCD點(diǎn)狀態(tài)還是那個(gè)樣子不變。
?
再進(jìn)一步,我們可以得到這樣一個(gè)圖:

我們知道電路上的輸入要從SR=11變到SR=00,有兩條路徑,要么先讓S等于0,然后R,要么就是先讓R等于0,然后S。有意思的是,兩種路徑最后的輸出不一樣誒——輸出Output和以往的路徑信息有關(guān)誒,這個(gè)神奇的Output仿佛記得自己是從哪條路下來(lái)的——誒,這個(gè)電路有“記憶”!

2.深入理解SR鎖存器的原理
為啥SR鎖存器這個(gè)電路結(jié)果會(huì)有記憶的神奇功能呢?它的原理到底是啥呢?好怪哦,再看一眼:

我們發(fā)現(xiàn)倆或門好像有點(diǎn)摸魚(yú),如果我們大膽一點(diǎn),化簡(jiǎn)一下電路,將AD兩點(diǎn)直連,將BC兩點(diǎn)直連,同時(shí)把或門去掉。誒嘿,好像沒(méi)毛病誒:

電路也是穩(wěn)定的!其實(shí)呢,這個(gè)結(jié)構(gòu)就是“雙穩(wěn)態(tài)電路”,說(shuō)白了就是兩個(gè)非門頭尾相接,我們?cè)偕蠈?dǎo)線捋一下就看得更清楚了:

所謂的記憶功能,其實(shí)就是這個(gè)結(jié)構(gòu)在發(fā)揮作用,這也是上一篇的組合邏輯從未有過(guò)的結(jié)構(gòu),很有意思吧。
?
進(jìn)一步地,我們不難發(fā)現(xiàn),這個(gè)結(jié)構(gòu)下的AD點(diǎn)和BC點(diǎn)必然是相反的,畢竟是通過(guò)非門連接嘛。于是我們就可以利用這一點(diǎn),來(lái)定義這個(gè)結(jié)構(gòu)存儲(chǔ)的信息了,AD點(diǎn)和BC都往外輸出,并且改名Q和~Q:

但是呢,聰明的小伙伴發(fā)現(xiàn)了一個(gè)問(wèn)題,Q點(diǎn)和~Q點(diǎn)對(duì)應(yīng)回原來(lái)的結(jié)構(gòu)分別是B點(diǎn)和D點(diǎn),未必是相反的關(guān)系呀,例如:

SR=11的時(shí)候,BD都是0,Q和~Q都是0,這就有點(diǎn)不對(duì)勁了。Q和~Q的正確狀態(tài)應(yīng)該是這樣的才對(duì):

事實(shí)上,S的意思是Set,R的意思的Reset,也就是說(shuō):SR=10,就是要設(shè)置Q為1;SR=01,就是要重置Q為0,SR=00;就是不設(shè)置不重置,Q點(diǎn)保持原值;SR=11,既要設(shè)置也要重置,Q點(diǎn)表示有點(diǎn)矛盾……于是我們整理出SR鎖存器的真值表:

3.基于SR鎖存器,搭建D鎖存器
我們現(xiàn)在知道了SR鎖存器的結(jié)構(gòu)、特性和原理了,但是這玩意多少差點(diǎn)意思——因?yàn)镾R同時(shí)是1的時(shí)候,Q和~Q都是0,又因?yàn)镼和~Q原本是非門環(huán)的兩端,理應(yīng)相反才對(duì),現(xiàn)在被破壞了,不成環(huán)了,就很尷尬。我們有沒(méi)有辦法把這個(gè)狀態(tài)取消掉呢?
?
誒,或許我們套娃一層,整一個(gè)新的殼,把殼的SR輸入映射到真SR鎖存器輸入,主要目標(biāo)是避免SR=11的非法狀態(tài):

看起來(lái)可以哦,來(lái),上套路,填真值表(順手給NewS和NewR這倆草率的信號(hào)名換一個(gè)雅稱):

寫表達(dá)式:
S = E & D
R = E & ~D
然后上電路:

效果怎么樣呢?我們來(lái)測(cè)試一下:

SR鎖存器的輸入永遠(yuǎn)不會(huì)是11,并且我們觀察電路可以發(fā)現(xiàn):
當(dāng)E=1時(shí),若D=1,則SR=10,Set狀態(tài),Q為1,即Q=D;
當(dāng)E=1時(shí),若D=0,則SR=01,Reset狀態(tài),Q為0,即Q=D;
當(dāng)E=0時(shí),無(wú)論D何值,SR都是00,Lock狀態(tài),Q保持原值
?
誒這個(gè)性質(zhì)真漂亮誒,電路的“記憶”功能看起來(lái)有點(diǎn)樣子了。順便解密一下小彩蛋,E是Enable,D是Data。哦吼,難怪E=1,Q=D,因?yàn)閑nable嘛,而E=0,Q不變呢,因?yàn)閐isable嘛~
4.理解波形與時(shí)鐘
邏輯門的奇妙冒險(xiǎn)走到了這里,就已經(jīng)有了神奇的“記憶”功能。也就是說(shuō),從現(xiàn)在開(kāi)始,邏輯門有了“時(shí)間軸”,有了過(guò)去和未來(lái)。于是,很自然的,時(shí)序邏輯電路就比較少用真值表了,畢竟真值表是靜態(tài)的,不帶時(shí)間信息,卻而代之的是帶有時(shí)間信息的波形圖,例如剛剛介紹的D鎖存器,它的波形圖就是這樣的:

我們一般約定,波形上凸的表示1,下凹的表示0(如果是多比特信號(hào),就直接把數(shù)值標(biāo)在臉上)。我們分析就時(shí)候就像上圖的標(biāo)記那樣:紅框里面就表示E=1的時(shí)候,Q就等于D,紅款之外,就是E=0,Q等于先前的Q,保持不變。
?
再進(jìn)一步地,既然有了波形,有了過(guò)去未來(lái),那是不是應(yīng)該有一個(gè)時(shí)鐘信號(hào)呀,它每10ns變化一輪,告訴所有時(shí)序電路:“嘿嘿,過(guò)了10ns了,干活干活咯”。

實(shí)際上,這個(gè)信號(hào)是由一個(gè)叫做晶振的裝置產(chǎn)生的,它規(guī)律地反復(fù)橫跳,給所有時(shí)序電路做同步。如果讀者對(duì)晶振的原理感興趣,可以自己去搜索相關(guān)資料(我不會(huì),頂鍋蓋跑)。這里手動(dòng)畫(huà)一個(gè)重點(diǎn),時(shí)鐘信號(hào)clock是0還1,其實(shí)并不重要,重要的是clock從0到1的變化,叫做“上升沿”(posedge)。同樣的也有從1到0的變化,下降沿,negedge。另外,clock有時(shí)候也簡(jiǎn)寫為clk。

就在我們的小軟件Logisim里面,有一個(gè)方便的時(shí)鐘信號(hào),在選項(xiàng)Simulate這里可以啟用時(shí)鐘,調(diào)整時(shí)鐘頻率等,大家可以先玩玩:

我們接下來(lái)針對(duì)時(shí)序邏輯電路的分析就要看波形,關(guān)注時(shí)鐘了~
?
5.基于D鎖存器,搭建D觸發(fā)器
上文已經(jīng)介紹過(guò),從SR鎖存器開(kāi)始,基于非門環(huán),或者專業(yè)點(diǎn)說(shuō)“雙穩(wěn)態(tài)電路”,我們的電路開(kāi)始有了“記憶”功能。但是SR鎖存器可能存在一個(gè)非法的、可能破壞非門環(huán)的狀態(tài),我們通過(guò)對(duì)SR鎖存器套一層封裝,成功地避開(kāi)了這個(gè)狀態(tài)。事實(shí)上,到了D鎖存器這里,我們就很好地實(shí)現(xiàn)了電路的“記憶”功能。
?
但是呢,D鎖存器還是差點(diǎn)意思:我們希望用clk信號(hào)來(lái)控制輸入,如果直接將clk接到D鎖存器的輸入E,然后輸入D接到一個(gè)數(shù)據(jù)源上,我們期待鎖存器可以保持上一次數(shù)據(jù)源的信息。然而,這個(gè)數(shù)據(jù)源也是聽(tīng)clk信號(hào)指揮的,也就是每一個(gè)周期給一次新值。這樣一來(lái),D鎖存器的波形就是這樣了:

啊這,Q和source完全相等的話,不就跟導(dǎo)線一樣了,這個(gè)鎖存器就廢了呀。我們的期待是鎖存器可以保持上一次數(shù)據(jù)源的信息,波形應(yīng)該是這樣的才對(duì):

都說(shuō)要記憶呀,要?dú)v史呀,那這樣的波形才對(duì)勁嘛。這就要求我們的器件必須在時(shí)鐘從0到1的上升沿這個(gè)跳變的瞬間,完成存儲(chǔ),因?yàn)閟ource也馬上就要變了,如果存儲(chǔ)慢了,source的歷史信息就丟了,所以必須要快。
?
那具體要怎么做呢?一個(gè)可行的方案是這樣的:

有兩個(gè)D鎖存器,其中一個(gè)的輸入D是另一個(gè)的輸出Q,而且,其中一個(gè)的E端口和clk信號(hào)直連,另一個(gè)的E端口和clk信號(hào)的取反連接。這樣一來(lái),兩個(gè)D鎖存器總是處于一開(kāi)一關(guān)的狀態(tài),不會(huì)同時(shí)打開(kāi)也不會(huì)同時(shí)關(guān)閉。這樣的電路會(huì)怎么工作呢?不妨直接試一試:

我們注意到,當(dāng)clk等于0的時(shí)候,前一個(gè)鎖存器打開(kāi),D直達(dá)temp處,后一個(gè)鎖存器關(guān)閉,不管;當(dāng)clk跳變成1的瞬間,前一個(gè)鎖存器關(guān)閉,temp的值被鎖住,同時(shí)后一個(gè)鎖存器打開(kāi),temp的值被第二個(gè)鎖存器讀入,完成存儲(chǔ);當(dāng)clk回到0的時(shí)候,前一個(gè)器件打開(kāi),后一個(gè)關(guān)閉,于是,上一個(gè)D的值就被記下來(lái),放在第二個(gè)鎖存器里面了。很有意思呀,這個(gè)簡(jiǎn)單巧妙的結(jié)構(gòu)實(shí)現(xiàn)了我們的預(yù)期,我們封裝一下,用接上真正周期性變化的clk信號(hào):

這個(gè)東西就叫做D 觸發(fā)器,它可以記錄D信息上一個(gè)時(shí)鐘周期的信息。誒,我們是不是再帶勁一點(diǎn),來(lái)個(gè)套娃,既然可以記錄上一個(gè)時(shí)鐘的信息,也就可以記錄上上個(gè)時(shí)鐘的,還可以記錄上上上個(gè)時(shí)鐘的……于是我們就得到了這樣的東西:

它的波形也很有意思,一層一層套娃:

6.基于D觸發(fā)器,搭建寄存器
這個(gè)D觸發(fā)器呀,就兩個(gè)輸入,一個(gè)數(shù)據(jù)源D輸入,一個(gè)時(shí)鐘輸入。這樣它的功能就稍微有些不完備,根據(jù)我們的現(xiàn)實(shí)生活經(jīng)驗(yàn),我們至少需要一個(gè)開(kāi)關(guān),一個(gè)復(fù)位信號(hào)。開(kāi)關(guān)信息我們叫做enable,復(fù)位就是reset。注意這里的enable和D鎖存器的enable不是同一個(gè)哦。如果能加上這兩個(gè)信號(hào),那么我們的記憶器件就更加完備了,到了這一步,一般我們稱之為寄存器(register)。
?
那么寄存器的enable信號(hào)要怎么實(shí)現(xiàn)呢?我們注意到,D觸發(fā)器的時(shí)鐘如果不變,那么整個(gè)器件就也就不會(huì)變化了,畢竟奧義之時(shí)間暫停嘛(這句劃掉)。所以,如果我們要讓enable信號(hào)來(lái)控制這個(gè)器件,我們可以讓enable信號(hào)來(lái)決定是否授予時(shí)鐘信號(hào),電路實(shí)現(xiàn)起來(lái)也簡(jiǎn)單,也就是將enable信號(hào)與時(shí)鐘進(jìn)行與運(yùn)算:

顯然,當(dāng)enable等于0的時(shí)候,D觸發(fā)器就收不到時(shí)鐘了,enable等于1才可以。OK,簡(jiǎn)單地實(shí)現(xiàn)了。那reset呢?我們希望reset信號(hào)為1的時(shí)候,D觸發(fā)器被重置為0。一個(gè)可行的方案是這樣的:

簡(jiǎn)單分析一下可以發(fā)現(xiàn),reset等于1時(shí),clk可以被正常連通,同時(shí)輸入的源被改成了0,也就成功完成了預(yù)期的目標(biāo)。我們最后封裝一下,就是長(zhǎng)這樣啦:
進(jìn)一步地,現(xiàn)在這個(gè)器件只能存儲(chǔ)1比特的信息誒,如果要存儲(chǔ)多比特呢?一個(gè)方案是直接并行:

這就是一個(gè)4比特的寄存器了,要多少比特都可以這樣并行。甚至可以相似結(jié)構(gòu)再套一層:

這就是16比特的寄存器了,下面是封裝后的樣子:

(另外,我原本想著所有器件都由自己用與或非三門搭建出來(lái),理論上確實(shí)是可以的,沒(méi)有問(wèn)題。但是我們用的這個(gè)小軟件Logisim,如果全部用自己的模塊,它有點(diǎn)扛不住,會(huì)卡死,所以后續(xù)我們一些規(guī)模比較大的寄存器組,就用Logisim軟件提供的模塊吧,這樣軟件會(huì)比較流暢。)
7.利用寄存器,實(shí)現(xiàn)一個(gè)計(jì)數(shù)器
到了這里,我們就有了一個(gè)具有記憶能力的,同時(shí)功能也比較完備的器件,我們可以把它和上一篇的組合邏輯電路結(jié)合一下,整點(diǎn)小活。首先,最直觀的應(yīng)用就是用寄存器和加法器搭建一個(gè)計(jì)數(shù)器:

寄存器的輸入會(huì)進(jìn)入一個(gè)加法器,和常量1相加的結(jié)果再輸入回寄存器,這樣一來(lái),寄存器就可以不停地累加1了。很自然地,我們就會(huì)想到,如果加法器的另一個(gè)輸入是常量2,那就可以不停地累加2了。輸入常量n,那就不停累加n。
?
但是現(xiàn)在的這個(gè)累加,也就只能是0大15循環(huán),假如我們想要0到9循環(huán)呢?我們注意到reset信號(hào)可以用起來(lái),如果我們能在寄存器的值等于9的時(shí)候,令reset等于1就可以了,寄存器就會(huì)重置。好勒,思路有了,馬上開(kāi)工:

那如果我們想要倒計(jì)時(shí)呢?也簡(jiǎn)單,那加法器換成減法器,每次減掉1,并且相等判斷檢測(cè)到0的時(shí)候,就不管減法器的運(yùn)算結(jié)果,將寄存器的輸入重新設(shè)置為9就可以了,reset暫時(shí)不好用,那我們就直接在輸入端用復(fù)用器好了:

那如果我們更飄逸一點(diǎn)呢,要求電路按順序給出一個(gè)任意序列呢,例如2、3、5、7、11、13……一個(gè)簡(jiǎn)單的方案是直接用計(jì)數(shù)器的輸入,當(dāng)做復(fù)用器的選擇信號(hào),復(fù)用器的輸入,我們以前被要輸出的序列準(zhǔn)備好即可:

使用這個(gè)電路結(jié)構(gòu),我們就可以讓電路給出任何序列了~
?
8.搭建寄存器文件
最后一關(guān),我們希望讓寄存器們合作一下,組合出一個(gè)功能更加完備的模塊。最直觀的要求就是寄存器有多個(gè),我們可以把某一個(gè)特定的值寫入其中任意一個(gè)寄存器,我們也可以讀出任意一個(gè)寄存器的值。
?
首先,我們先實(shí)現(xiàn)讀的功能。先帶大伙琢磨一下:假設(shè)寄存器有8個(gè),我們要讀出其中任意一個(gè)寄存器的值,就應(yīng)該給一個(gè)3比特的讀地址,告訴我們的電路,到底要讀出哪一個(gè)寄存器,也就是說(shuō),這個(gè)功能涉及兩個(gè)端口,輸入raddr和輸出rdata,分別是讀地址和讀數(shù)據(jù)。
?
具體的實(shí)現(xiàn)也比較簡(jiǎn)單,直接把所有的寄存器的值都接入到一個(gè)大復(fù)用器的輸入,然后讀地址raddr作為復(fù)用器的選擇信號(hào),復(fù)用器的輸出就是讀數(shù)據(jù)rdata。再進(jìn)一步地,我們可以要求讀端口有兩套,也就是兩個(gè)raddr和兩個(gè)rdata,分別用兩個(gè)大復(fù)用器實(shí)現(xiàn):

接下來(lái)我們要實(shí)現(xiàn)一下寫的功能,再帶大伙琢磨一下:總不能每次寫入都全部寫進(jìn)去吧,那所有寄存器都沒(méi)有區(qū)別了,寄存器堆就退化成一個(gè)寄存器了……所以,我們寫入的時(shí)候,需要多一個(gè)信號(hào),寫地址waddr,告訴我們的電路,哪一個(gè)寄存器要被寫入,其他的寄存器保持不變。同時(shí)我們也不能時(shí)時(shí)刻刻都往里面寫東西吧,總得休息一會(huì)吧。所以我們還需要一個(gè)寫使能信號(hào)wen,當(dāng)且僅當(dāng)wen等于1的時(shí)候,電路才會(huì)寫入,否則就不變。最后很自然地,我們還需要告訴電路要寫入什么數(shù)據(jù),也就是寫數(shù)據(jù)wdata。OK,小結(jié)一下,我們有仨輸入,一個(gè)總使能wen,一個(gè)寫地址waddr,一個(gè)寫數(shù)據(jù)wdata。
?
具體的實(shí)現(xiàn)呢?這仨輸入的電路,要怎么連呢?我們可以這樣操作:waddr所指向哪一個(gè)寄存器,我們就把那個(gè)寄存器的wen連上,其他的寄存器都把使能信號(hào)都關(guān)掉,電路可以是這樣的:

這八個(gè)輸入分別接到八個(gè)寄存器的使能信號(hào)即可,我們知道waddr
等于何值,對(duì)應(yīng)的行就會(huì)點(diǎn)亮,允許wen通過(guò),實(shí)現(xiàn)了我們的預(yù)期。而寫入功能的第三個(gè)信號(hào)wdata就比較簡(jiǎn)單了,有了wen和wadd的控制,wdata可以直接連接到所有寄存器的輸入端。最后我們的寄存器文件就是這樣的:

這里有一個(gè)小彩蛋:我們直接將0號(hào)寄存器鎖定成0,它不可寫入,永遠(yuǎn)都只會(huì)讀出0,電路上可以直接把0號(hào)寄存器換成常量:

至于為什么要這樣做呢?因?yàn)樗拇嬖跁?huì)方便很多偽指令。誒這又是啥意思呢?這里先留個(gè)坑,把它說(shuō)明白需要一些前置知識(shí),咱們以后再細(xì)說(shuō)。最后我們拉出八個(gè)調(diào)試接口,方便我們?cè)谕饷鏅z查寄存器的值是否符合預(yù)期,最后封裝好就是這樣。一般我們管它叫寄存器文件,RegFile:

擴(kuò)展閱讀:
(建立時(shí)間與保持時(shí)間,鴿了)