【深圳 IO 攻略】番外篇:介紹一下 PGA33X6 這個(gè)冷門(mén)元件

本文首發(fā)于 B 站《深圳 IO》文集(https://www.bilibili.com/read/readlist/rl569860)。原創(chuàng)不易,轉(zhuǎn)載請(qǐng)注明出處。
我們的謎題里有這么一個(gè)常駐元件:PGA33X6,它是常駐元件里的最后一個(gè)元件,而且我們至今的所有謎題里都沒(méi)有用到過(guò)這個(gè)元件。

它是專門(mén)用來(lái)解決復(fù)雜邏輯的一個(gè)元件,它有三個(gè)輸入和三個(gè)輸出量,以及一個(gè)內(nèi)置的 data 量。
邏輯運(yùn)算里,與運(yùn)算又稱【邏輯乘】,或運(yùn)算又稱【邏輯加】。而我們觀察數(shù)據(jù)手冊(cè)發(fā)現(xiàn):

這個(gè)元件里的列叫做【乘列】,而行叫做【加法行】。因此這個(gè) PGA33X6 的每一列里的邏輯構(gòu)成了【與】關(guān)系,而列與列之間的邏輯構(gòu)成了【或】關(guān)系。所以這是個(gè)實(shí)現(xiàn)各種“標(biāo)準(zhǔn)與或式”邏輯的元件。
設(shè)左側(cè)三個(gè)輸入量為 A、B、C,對(duì)應(yīng)輸入 <50?時(shí)視為 false,對(duì)應(yīng)輸入 ≥50?時(shí)視為 true。那么,我們用 C 語(yǔ)言風(fēng)格的代碼來(lái)解釋,在某一列里:
如果你點(diǎn)亮了第 1 行的小方塊,相當(dāng)于 if (A);
如果你點(diǎn)亮了第 2 行的小方塊,相當(dāng)于 if (!A);
如果你點(diǎn)亮了第 3 行的小方塊,相當(dāng)于 if (B);
如果你點(diǎn)亮了第 4 行的小方塊,相當(dāng)于 if (!B);
如果你點(diǎn)亮了第 7 行的小方塊,相當(dāng)于 if (C);
如果你點(diǎn)亮了第 8?行的小方塊,相當(dāng)于 if (!C);
如果你同時(shí)點(diǎn)亮了多個(gè)小方塊,相當(dāng)于把這些邏輯混合在一起做【與】運(yùn)算。
因?yàn)?A 和 !A 不能同時(shí)成立,所以在每個(gè)單獨(dú)的列里,你不能同時(shí)點(diǎn)亮第 1、2 行的小方塊。同理可得,B 和 !B,以及 C 和 !C 都是不能同時(shí)成立的,所以第 3、4 行的小方塊,第 7、8 行的小方塊也不能同時(shí)點(diǎn)亮。
設(shè)右側(cè)三個(gè)輸出量為 X、Y、Z,那么,在某一列里:
如果你點(diǎn)亮了第 9 行的小方塊,相當(dāng)于滿足該列條件時(shí)令 X = 100;
如果你點(diǎn)亮了第 10 行的小方塊,相當(dāng)于滿足該列條件時(shí)令 Y = 100;
如果你點(diǎn)亮了第 11 行的小方塊,相當(dāng)于滿足該列條件時(shí)令 Z = 100。
我們現(xiàn)在打開(kāi)郵件,找到標(biāo)題為【為新創(chuàng)意建模】的郵件,然后在空白的電路板上畫(huà)上這樣的電路圖:

本例中,左側(cè)有三個(gè)開(kāi)關(guān)量,分別接在 A、B、C 的輸入口上;右側(cè)有一個(gè)電燈,接在 X 的輸出口上。
然后我們觀察 PGA33X6,發(fā)現(xiàn)第一列的第 1、3、7、9 行小方格被點(diǎn)亮了。如果用 C 語(yǔ)言描述的話,相當(dāng)于這樣的代碼:
僅當(dāng) A、B、C 都為 true 時(shí)才給 X 輸出 100。那么,我們點(diǎn)擊下方的【模擬】運(yùn)行程序,并嘗試切換三個(gè)開(kāi)關(guān)的狀態(tài),會(huì)發(fā)現(xiàn)只要不是三個(gè)開(kāi)關(guān)都打開(kāi),燈就永遠(yuǎn)不亮。只有三個(gè)開(kāi)關(guān)同時(shí)打開(kāi)時(shí),燈才點(diǎn)亮:


然后我們?cè)龠M(jìn)一步:我們要求當(dāng)且僅當(dāng)兩個(gè)開(kāi)關(guān)處于打開(kāi)狀態(tài)時(shí),燈才點(diǎn)亮;當(dāng)打開(kāi)的開(kāi)關(guān)數(shù)是 0、1、3 時(shí),燈都處于熄滅狀態(tài)。
任意兩個(gè)開(kāi)關(guān)打開(kāi),一共有三種可能性:AB 開(kāi) C 關(guān),AC 開(kāi) B 關(guān),BC 開(kāi) A 關(guān)。因此我們的邏輯用 C 語(yǔ)言代碼描述應(yīng)該是這樣的:
那么,現(xiàn)在你應(yīng)該很容易把 PGA33X6 的點(diǎn)燈狀態(tài)改成下面的這個(gè)樣子:

第一列里我們點(diǎn)亮了第 1、3、8、9 行的小方塊,相當(dāng)于:if (A && B && !C) X = 100;
第二列里我們點(diǎn)亮了第 1、4、7、9 行的小方塊,相當(dāng)于:if (A && !B && C) X = 100;
第三列里我們點(diǎn)亮了第 2、3、8、9 行的小方塊,相當(dāng)于:if (!A && B && C) X = 100;
運(yùn)行程序,不斷切換三個(gè)燈的開(kāi)關(guān)狀態(tài),你會(huì)發(fā)現(xiàn)當(dāng)且僅當(dāng)兩個(gè)開(kāi)關(guān)打開(kāi)時(shí),燈才點(diǎn)亮:




進(jìn)階練習(xí) (1)
我們從創(chuàng)意工坊里訂閱【PGA33X6 FOR BEGINNERS】這道謎題,然后點(diǎn)擊游戲主頁(yè)上的【概念 SPEC】按鈕,找到剛才訂閱的【PGA33X6 FOR BEGINNERS】,打開(kāi)這道由網(wǎng)友分享的 PGA 練習(xí)題。

這道題我們需要使用兩個(gè) PGA33X6 來(lái)實(shí)現(xiàn)【與門(mén)】、【與非門(mén)】、【或門(mén)】、【或非門(mén)】、【異或門(mén)】、【同或門(mén)】六大邏輯。
我們先看怎么用 PGA33X6 實(shí)現(xiàn)上方的【與門(mén)】、【與非門(mén)】、【或門(mén)】。

兩個(gè)輸入量接在 A、B 上,【與門(mén)】輸出接在 X 口上,【與非門(mén)】輸出接在 Y 口上,【或門(mén)】輸出接在 Z 口上。
當(dāng) A、B 均為 true 時(shí),【與門(mén)】為 100;
當(dāng) A、B 中任意一個(gè)為 false 時(shí),【與非門(mén)】為 100;
當(dāng) A、B 中任意一個(gè)為 true 時(shí),【或門(mén)】為 100。
將以上邏輯轉(zhuǎn)寫(xiě)成 C 語(yǔ)言代碼和 PGA33X6?點(diǎn)燈規(guī)則,得:
可以看到我們推理出的要點(diǎn)亮的小方塊和上方截圖完全一致。
現(xiàn)在我們?cè)賮?lái)看看怎么用另一塊 PGA33X6 實(shí)現(xiàn)下方的【或非門(mén)】、【異或門(mén)】、【同或門(mén)】。

兩個(gè)輸入量接在 A、B 上,【或非門(mén)】輸出接在 X 口上,【異或門(mén)】輸出接在 Y?口上,【同或門(mén)】輸出接在 Z 口上。
當(dāng) A、B 均為 false 時(shí),【或非門(mén)】為 100;
當(dāng) A、B 的邏輯值不一致時(shí),【異或門(mén)】為 100;
當(dāng) A、B 的邏輯值完全一致時(shí),【同或門(mén)】為 100。
將以上邏輯轉(zhuǎn)寫(xiě)成 C 語(yǔ)言代碼和 PGA33X6?點(diǎn)燈規(guī)則,得:
點(diǎn)擊左下角的【模擬】,稍等片刻,便會(huì)彈出結(jié)算界面:

進(jìn)階練習(xí) (2)
我們?cè)賮?lái)嘗試通過(guò) PGA33X6 實(shí)現(xiàn)一個(gè)加法器。我們從創(chuàng)意工坊里訂閱【3-BIT FPGA ADDER】這道謎題,然后點(diǎn)擊游戲主頁(yè)上的【概念 SPEC】按鈕,找到剛才訂閱的【3-BIT FPGA ADDER】,打開(kāi)這道由網(wǎng)友分享的加法器練習(xí)題。

這道題一共有三個(gè)二進(jìn)制位的輸入量。二進(jìn)制加法規(guī)則如下:
0 + 0 + 0 = 0
0 + 0 + 1 = 1
0 + 1 + 0 = 1
0 + 1 + 1 = 10(算術(shù)位為 0,進(jìn)位位為 1)
1 + 0 + 0 = 1
1 + 0 + 1 = 10
1 + 1 + 0 = 10
1 + 1 + 1 = 11(算術(shù)位為 1,進(jìn)位位為 1)
算術(shù)位連接著 Y 輸出,進(jìn)位位連接著 Z 輸出。還是跟之前一樣,將以上邏輯改寫(xiě)成 C 語(yǔ)言代碼:
很遺憾,PGA33X6 的 6 列邏輯是不夠用的,因?yàn)槿份斎胍环N有 8 種排列組合,其中 7 種都是至少要激活 Y/Z 中的一路輸出的。
但是我們可以借助非門(mén),減少邏輯數(shù)量。假如我們將進(jìn)位位輸出 Y 接上一個(gè)非門(mén),讓其變成 notY,那么我們就可以把表示邏輯的 C 語(yǔ)言代碼減少到下面五行:
將 Y 變成 notY 后,和為 10 的三種排列組合不需要激活 notY 和 Z 的任何一路信號(hào)。這時(shí)候 6 列邏輯的 PGA33X6 就能放得下了。相信看到了這里的你,已經(jīng)可以根據(jù)以上 C 語(yǔ)言代碼設(shè)計(jì)出如下的電路圖和點(diǎn)燈信號(hào)了:

點(diǎn)擊左下角的【模擬】,稍等片刻,便會(huì)彈出結(jié)算界面:

PGA33X6 的高級(jí)用法:狀態(tài)鎖存
我們注意到,PGA33X6 中有一個(gè)不起眼的 data 寄存器。它和 X、Y、Z 三路輸出不一樣,X、Y、Z 都是【滿足條件后打開(kāi),不滿足條件時(shí)關(guān)閉】,但 data 不一樣。data 是這樣變化的:
當(dāng)?X = 100 時(shí),data 會(huì)被置為 1;但是當(dāng) X = 0 時(shí),data 會(huì)維持之前的狀態(tài)不變;
當(dāng) Y = 100 時(shí),data 會(huì)被置為 0;但是當(dāng) Y = 0 時(shí),data 會(huì)維持之前的狀態(tài)不變。
也就是說(shuō),data 的狀態(tài)是跟隨著 X、Y 的激活狀態(tài)而改變的,單純地撤掉 X 或 Y 的激活信號(hào),并不會(huì)改變 data 的值。
然后在 data 寄存器的右上方有兩個(gè)小方塊,默認(rèn)選擇的是右邊的小方塊,表示“第一路的輸出信號(hào)和 X 相同”;如果你改為選擇左邊的小方塊,那么就變成了“第一路的輸出信號(hào)和 data 相同”。
光聽(tīng)我說(shuō)這些,你可能還比較懵,不知道我說(shuō)的是什么意思。那我舉個(gè)例子,我們要設(shè)計(jì)一個(gè)用按鈕控制開(kāi)關(guān)的電燈。你現(xiàn)在通過(guò) PGA 設(shè)計(jì)了這樣一個(gè)電路:

然后你點(diǎn)亮了第一列的第 1、9 行的小方塊,相當(dāng)于
A 路輸入有信號(hào)時(shí),點(diǎn)亮 X 路的輸出。

當(dāng)你按下按鈕時(shí),燈點(diǎn)亮了。但是當(dāng)你放開(kāi)按鈕時(shí)……

你發(fā)現(xiàn)燈滅了。這個(gè)初版設(shè)計(jì)方案一點(diǎn)都不實(shí)用,人還得站在按鈕前一直把按鈕按著才能讓燈一直點(diǎn)亮。
然后你注意到 data 寄存器,按下按鈕把燈點(diǎn)亮的時(shí)候,它的值是 1;但是現(xiàn)在我松開(kāi)了按鈕,燈滅了,data 的值居然還是 1,沒(méi)有回到 0。這時(shí)候,右上角的兩個(gè)小方塊,你選擇了左邊那個(gè),告訴 PGA33X6:第一路不要輸出 X,給我輸出鎖存的 data 值!然后,奇跡出現(xiàn)了:

按下按鈕的時(shí)候,燈跟之前一樣會(huì)點(diǎn)亮。然后你松開(kāi)了按鈕,驚奇地發(fā)現(xiàn):

這次燈并沒(méi)有滅掉!右上角兩個(gè)小方塊里,如果我們選擇了左邊的那個(gè),第一路就不再輸出 X 信號(hào)了,改為輸出 data 信號(hào)了。當(dāng)按鈕抬起后,A 輸入變成 0 了,我們根據(jù) PGA 里的邏輯推導(dǎo)出 X 也會(huì)跟著變成?0。然而我們第一路已經(jīng)不再輸出 X 了,改為輸出 data 了。data 這時(shí)候仍然是 1,所以我們的燈在按鈕抬起后依然會(huì)保持亮著的狀態(tài)。
現(xiàn)在,我們離最終的成品已經(jīng)只剩一步之遙了。燈不能就一直這么開(kāi)著,得想辦法關(guān)掉。于是你在第二路又加了個(gè)按鈕,用來(lái)關(guān)燈。電路圖變成了下面的樣子:

第二列里我們點(diǎn)亮了第 3、10 行的小方塊,相當(dāng)于
我們說(shuō)過(guò),當(dāng)把 Y 信號(hào)置為 100 時(shí),會(huì)同時(shí)強(qiáng)制將 data 信號(hào)清零。而 data 信號(hào)清零后,第一路自然就沒(méi)有信號(hào)輸出了。所以,當(dāng)我們按下第二個(gè)按鈕后,燈會(huì)被強(qiáng)制熄滅。

首先我們按下并松開(kāi)第一個(gè)按鈕,把燈點(diǎn)亮。然后我們按下第二個(gè)按鈕:

data 由 1 變成了 0,燈滅了。這時(shí)候我們松開(kāi)第二個(gè)按鈕:

發(fā)現(xiàn)燈不會(huì)重新亮起,data 也會(huì)保持在 0 的狀態(tài)。
相比于我們最早說(shuō)到的“實(shí)現(xiàn)復(fù)雜的標(biāo)準(zhǔn)與或式”的功能,PGA33X6 真正強(qiáng)大的地方其實(shí)在于我們剛才所說(shuō)的“狀態(tài)鎖存”這一點(diǎn),這是普通的邏輯門(mén)所做不到的事。
將鎖存狀態(tài)作為輸入量,設(shè)計(jì)更復(fù)雜的邏輯電路
之前我只說(shuō)了邏輯陣列里的第 1、2、3、4、7、8、9、10、11 行的小方塊,當(dāng)時(shí)我把第 5、6 行的小方塊跳了過(guò)去。其實(shí),第 5、6 行的小方塊是用于判斷鎖存器 data 的。PGA33X6 里,鎖存器 data 既可以作為輸出量,也可以作為輸入量而存在。在某一列里:
如果你點(diǎn)亮了第 5 行的小方塊,相當(dāng)于 if (data == 1);
如果你點(diǎn)亮了第 6 行的小方塊,相當(dāng)于 if (data == 0)。
鎖存量作為輸入有什么用呢?在之前的例子里,通過(guò)使用 data 鎖存器,我們實(shí)現(xiàn)了一個(gè)用按鈕控制開(kāi)關(guān)的燈泡。我們現(xiàn)在在上一個(gè)方案的基礎(chǔ)上,給燈泡增加一個(gè)【緊急開(kāi)關(guān)】:在燈泡開(kāi)啟的狀態(tài)下,按下緊急按鈕,可以點(diǎn)亮緊急指示燈。而在燈泡關(guān)閉的狀態(tài)下按下緊急按鈕時(shí),不產(chǎn)生任何效果。我們?cè)O(shè)計(jì)出如下的電路:

第一列我們點(diǎn)亮了第 1、9 行的小方塊;第二列我們點(diǎn)亮了第 3、10 行的小方塊;第三列我們點(diǎn)亮了 5、7、11 行的小方塊;data 上方我們選擇了左邊的小方塊。右邊,主燈泡連接著 data 輸出口,緊急指示燈連接著 Z 輸出口。
現(xiàn)在,我們將我們的邏輯用 C 語(yǔ)言代碼來(lái)描述:
現(xiàn)在運(yùn)行程序:

首先我們按下第一個(gè)按鈕,將主燈泡點(diǎn)亮。

此時(shí)我們按住第三個(gè)按鈕,發(fā)現(xiàn)緊急指示燈被點(diǎn)亮了。

此時(shí)松開(kāi)第三個(gè)按鈕,發(fā)現(xiàn)緊急燈泡滅了。PGA 只有一個(gè)鎖存器,只能鎖存一個(gè)輸出量。所以緊急指示燈的狀態(tài)是鎖不住的,會(huì)隨著按鈕松開(kāi)而熄滅。

此時(shí)我們按下第二個(gè)按鈕,將主燈泡熄滅。

主燈泡熄滅后,再按下第三個(gè)按鈕,會(huì)發(fā)現(xiàn)緊急指示燈不再亮起。因?yàn)榫o急指示燈亮起的條件是【data 為 1 時(shí)按下第三個(gè)按鈕】?,F(xiàn)在 data 為 0,不滿足條件,緊急指示燈自然就不會(huì)亮起。
進(jìn)階練習(xí) (3)
我們來(lái)實(shí)現(xiàn)一個(gè)帶開(kāi)關(guān)功能的示波器。我們從創(chuàng)意工坊里訂閱【PGA33X6 SR LATCH】這道謎題,然后點(diǎn)擊游戲主頁(yè)上的【概念 SPEC】按鈕,找到剛才訂閱的【PGA33X6 SR LATCH】,打開(kāi)這道由網(wǎng)友分享的 PGA 練習(xí)題。

本關(guān)要求當(dāng) set 信號(hào)出現(xiàn)時(shí)打開(kāi)示波器(將 latch 信號(hào)置為 100);當(dāng) reset 信號(hào)出現(xiàn)時(shí)關(guān)閉示波器(將 latch 信號(hào)置為 0)。同時(shí),當(dāng)示波器開(kāi)啟的狀態(tài)下,在 filter 端口輸出 pulse 給出的波形信號(hào);在示波器關(guān)閉的情況下,令 filter 端口保持為 0。
這道題其實(shí)就是謎題版的“緊急指示燈”,左邊的三路輸出正好對(duì)應(yīng)開(kāi)燈、關(guān)燈、緊急指示。相信你到了這一步,連點(diǎn)亮哪些小方塊都已經(jīng)會(huì)背了:第一列 1、9,第二列 3、10,第三列 5、7、11,data 上方點(diǎn)左邊的小方塊。一氣呵成。


回顧龍騰系列第 10 關(guān)《真人 CS》,使用 PGA33X6 鎖存器減少代碼行數(shù)
現(xiàn)在我們回到龍騰系列關(guān)卡里的第 10 關(guān)《真人 CS》。我們之前的方案是使用 MC6000 里的 dat 寄存器來(lái)鎖存“活著”狀態(tài)的。請(qǐng)回到?【深圳 IO 攻略】第 10 關(guān):真人 CS?查看我們之前的設(shè)計(jì)方案。


受剛才“按鈕點(diǎn)燈”案例的啟發(fā),我們現(xiàn)在可以將“活著”狀態(tài)用 PGA33X6 來(lái)輸出和鎖存。要知道使用 PGA33X6 編寫(xiě)邏輯時(shí),即使你的邏輯陣列寫(xiě)得天花亂墜,最終統(tǒng)計(jì)時(shí)這些通通都是不計(jì)入代碼行數(shù)的。而且“活著”部分的邏輯代碼用 PGA33X6 代替后,剩下的部分是不足 9 行的,可以放在一塊 MC4000 里。使用 PGA33X6 的電路圖和代碼如下:



左邊的 PGA33X6 共有 3 路輸入,第 1 路輸入是右側(cè)的【扣扳機(jī)】信號(hào),第 2、3 路輸入是左側(cè)的【擊中】和【復(fù)活】信號(hào)。接線圖里,第 1 路輸出信號(hào)連接著【活著】端口,輸出的是 data 鎖存器的值;第 3 路輸出信號(hào)則連接著右邊 MC4000 的 p0 口。我們?cè)儆^察?PGA33X6 里點(diǎn)亮的小方塊,發(fā)現(xiàn)本 PGA33X6 需要執(zhí)行的是如下的邏輯運(yùn)算:
這里,我們利用 PGA33X6 鎖存器,將【扣扳機(jī)】信號(hào)加工成了【活著時(shí)扣扳機(jī)】信號(hào),交給右邊的 MC4000 處理。這樣,觸發(fā)【射擊】信號(hào)的條件就由“同時(shí)滿足【扣扳機(jī)】、【有子彈剩余】、【活著】三者”化簡(jiǎn)成了“同時(shí)滿足【活著時(shí)扣扳機(jī)】、【有子彈剩余】?jī)烧摺薄?/p>
右邊的 MC4000,x0 口接的是【數(shù)量】常數(shù)芯片;p0 口用于讀取 PGA 的第三路輸出信號(hào),判斷【是否在活著時(shí)扣扳機(jī)】;x1 口接的是經(jīng)過(guò) DX-300 轉(zhuǎn)接后的【添彈】輸入;p1 用于輸出【射擊】信號(hào)。代碼解析如下:
點(diǎn)擊左下角的【模擬】,稍等片刻,便會(huì)彈出結(jié)算界面:

我們成功地利用 PGA33X6 鎖存器做出了一道官方謎題,打破了“官方謎題用不著 PGA33X6”的魔咒,并將本題的代碼行數(shù)壓縮到了 6 行。
以上,便是 PGA33X6 這個(gè)邏輯元件的全部用法。