【深圳 IO 攻略】番外篇:介绍一下 PGA33X6 这个冷门元件

本文首发于 B 站《深圳 IO》文集(https://www.bilibili.com/read/readlist/rl569860)。原创不易,转载请注明出处。
我们的谜题里有这么一个常驻元件:PGA33X6,它是常驻元件里的最后一个元件,而且我们至今的所有谜题里都没有用到过这个元件。

它是专门用来解决复杂逻辑的一个元件,它有三个输入和三个输出量,以及一个内置的 data 量。
逻辑运算里,与运算又称【逻辑乘】,或运算又称【逻辑加】。而我们观察数据手册发现:

这个元件里的列叫做【乘列】,而行叫做【加法行】。因此这个 PGA33X6 的每一列里的逻辑构成了【与】关系,而列与列之间的逻辑构成了【或】关系。所以这是个实现各种“标准与或式”逻辑的元件。
设左侧三个输入量为 A、B、C,对应输入 <50 时视为 false,对应输入 ≥50 时视为 true。那么,我们用 C 语言风格的代码来解释,在某一列里:
如果你点亮了第 1 行的小方块,相当于 if (A);
如果你点亮了第 2 行的小方块,相当于 if (!A);
如果你点亮了第 3 行的小方块,相当于 if (B);
如果你点亮了第 4 行的小方块,相当于 if (!B);
如果你点亮了第 7 行的小方块,相当于 if (C);
如果你点亮了第 8 行的小方块,相当于 if (!C);
如果你同时点亮了多个小方块,相当于把这些逻辑混合在一起做【与】运算。
因为 A 和 !A 不能同时成立,所以在每个单独的列里,你不能同时点亮第 1、2 行的小方块。同理可得,B 和 !B,以及 C 和 !C 都是不能同时成立的,所以第 3、4 行的小方块,第 7、8 行的小方块也不能同时点亮。
设右侧三个输出量为 X、Y、Z,那么,在某一列里:
如果你点亮了第 9 行的小方块,相当于满足该列条件时令 X = 100;
如果你点亮了第 10 行的小方块,相当于满足该列条件时令 Y = 100;
如果你点亮了第 11 行的小方块,相当于满足该列条件时令 Z = 100。
我们现在打开邮件,找到标题为【为新创意建模】的邮件,然后在空白的电路板上画上这样的电路图:

本例中,左侧有三个开关量,分别接在 A、B、C 的输入口上;右侧有一个电灯,接在 X 的输出口上。
然后我们观察 PGA33X6,发现第一列的第 1、3、7、9 行小方格被点亮了。如果用 C 语言描述的话,相当于这样的代码:
仅当 A、B、C 都为 true 时才给 X 输出 100。那么,我们点击下方的【模拟】运行程序,并尝试切换三个开关的状态,会发现只要不是三个开关都打开,灯就永远不亮。只有三个开关同时打开时,灯才点亮:


然后我们再进一步:我们要求当且仅当两个开关处于打开状态时,灯才点亮;当打开的开关数是 0、1、3 时,灯都处于熄灭状态。
任意两个开关打开,一共有三种可能性:AB 开 C 关,AC 开 B 关,BC 开 A 关。因此我们的逻辑用 C 语言代码描述应该是这样的:
那么,现在你应该很容易把 PGA33X6 的点灯状态改成下面的这个样子:

第一列里我们点亮了第 1、3、8、9 行的小方块,相当于:if (A && B && !C) X = 100;
第二列里我们点亮了第 1、4、7、9 行的小方块,相当于:if (A && !B && C) X = 100;
第三列里我们点亮了第 2、3、8、9 行的小方块,相当于:if (!A && B && C) X = 100;
运行程序,不断切换三个灯的开关状态,你会发现当且仅当两个开关打开时,灯才点亮:




进阶练习 (1)
我们从创意工坊里订阅【PGA33X6 FOR BEGINNERS】这道谜题,然后点击游戏主页上的【概念 SPEC】按钮,找到刚才订阅的【PGA33X6 FOR BEGINNERS】,打开这道由网友分享的 PGA 练习题。

这道题我们需要使用两个 PGA33X6 来实现【与门】、【与非门】、【或门】、【或非门】、【异或门】、【同或门】六大逻辑。
我们先看怎么用 PGA33X6 实现上方的【与门】、【与非门】、【或门】。

两个输入量接在 A、B 上,【与门】输出接在 X 口上,【与非门】输出接在 Y 口上,【或门】输出接在 Z 口上。
当 A、B 均为 true 时,【与门】为 100;
当 A、B 中任意一个为 false 时,【与非门】为 100;
当 A、B 中任意一个为 true 时,【或门】为 100。
将以上逻辑转写成 C 语言代码和 PGA33X6 点灯规则,得:
可以看到我们推理出的要点亮的小方块和上方截图完全一致。
现在我们再来看看怎么用另一块 PGA33X6 实现下方的【或非门】、【异或门】、【同或门】。

两个输入量接在 A、B 上,【或非门】输出接在 X 口上,【异或门】输出接在 Y 口上,【同或门】输出接在 Z 口上。
当 A、B 均为 false 时,【或非门】为 100;
当 A、B 的逻辑值不一致时,【异或门】为 100;
当 A、B 的逻辑值完全一致时,【同或门】为 100。
将以上逻辑转写成 C 语言代码和 PGA33X6 点灯规则,得:
点击左下角的【模拟】,稍等片刻,便会弹出结算界面:

进阶练习 (2)
我们再来尝试通过 PGA33X6 实现一个加法器。我们从创意工坊里订阅【3-BIT FPGA ADDER】这道谜题,然后点击游戏主页上的【概念 SPEC】按钮,找到刚才订阅的【3-BIT FPGA ADDER】,打开这道由网友分享的加法器练习题。

这道题一共有三个二进制位的输入量。二进制加法规则如下:
0 + 0 + 0 = 0
0 + 0 + 1 = 1
0 + 1 + 0 = 1
0 + 1 + 1 = 10(算术位为 0,进位位为 1)
1 + 0 + 0 = 1
1 + 0 + 1 = 10
1 + 1 + 0 = 10
1 + 1 + 1 = 11(算术位为 1,进位位为 1)
算术位连接着 Y 输出,进位位连接着 Z 输出。还是跟之前一样,将以上逻辑改写成 C 语言代码:
很遗憾,PGA33X6 的 6 列逻辑是不够用的,因为三路输入一种有 8 种排列组合,其中 7 种都是至少要激活 Y/Z 中的一路输出的。
但是我们可以借助非门,减少逻辑数量。假如我们将进位位输出 Y 接上一个非门,让其变成 notY,那么我们就可以把表示逻辑的 C 语言代码减少到下面五行:
将 Y 变成 notY 后,和为 10 的三种排列组合不需要激活 notY 和 Z 的任何一路信号。这时候 6 列逻辑的 PGA33X6 就能放得下了。相信看到了这里的你,已经可以根据以上 C 语言代码设计出如下的电路图和点灯信号了:

点击左下角的【模拟】,稍等片刻,便会弹出结算界面:

PGA33X6 的高级用法:状态锁存
我们注意到,PGA33X6 中有一个不起眼的 data 寄存器。它和 X、Y、Z 三路输出不一样,X、Y、Z 都是【满足条件后打开,不满足条件时关闭】,但 data 不一样。data 是这样变化的:
当 X = 100 时,data 会被置为 1;但是当 X = 0 时,data 会维持之前的状态不变;
当 Y = 100 时,data 会被置为 0;但是当 Y = 0 时,data 会维持之前的状态不变。
也就是说,data 的状态是跟随着 X、Y 的激活状态而改变的,单纯地撤掉 X 或 Y 的激活信号,并不会改变 data 的值。
然后在 data 寄存器的右上方有两个小方块,默认选择的是右边的小方块,表示“第一路的输出信号和 X 相同”;如果你改为选择左边的小方块,那么就变成了“第一路的输出信号和 data 相同”。
光听我说这些,你可能还比较懵,不知道我说的是什么意思。那我举个例子,我们要设计一个用按钮控制开关的电灯。你现在通过 PGA 设计了这样一个电路:

然后你点亮了第一列的第 1、9 行的小方块,相当于
A 路输入有信号时,点亮 X 路的输出。

当你按下按钮时,灯点亮了。但是当你放开按钮时……

你发现灯灭了。这个初版设计方案一点都不实用,人还得站在按钮前一直把按钮按着才能让灯一直点亮。
然后你注意到 data 寄存器,按下按钮把灯点亮的时候,它的值是 1;但是现在我松开了按钮,灯灭了,data 的值居然还是 1,没有回到 0。这时候,右上角的两个小方块,你选择了左边那个,告诉 PGA33X6:第一路不要输出 X,给我输出锁存的 data 值!然后,奇迹出现了:

按下按钮的时候,灯跟之前一样会点亮。然后你松开了按钮,惊奇地发现:

这次灯并没有灭掉!右上角两个小方块里,如果我们选择了左边的那个,第一路就不再输出 X 信号了,改为输出 data 信号了。当按钮抬起后,A 输入变成 0 了,我们根据 PGA 里的逻辑推导出 X 也会跟着变成 0。然而我们第一路已经不再输出 X 了,改为输出 data 了。data 这时候仍然是 1,所以我们的灯在按钮抬起后依然会保持亮着的状态。
现在,我们离最终的成品已经只剩一步之遥了。灯不能就一直这么开着,得想办法关掉。于是你在第二路又加了个按钮,用来关灯。电路图变成了下面的样子:

第二列里我们点亮了第 3、10 行的小方块,相当于
我们说过,当把 Y 信号置为 100 时,会同时强制将 data 信号清零。而 data 信号清零后,第一路自然就没有信号输出了。所以,当我们按下第二个按钮后,灯会被强制熄灭。

首先我们按下并松开第一个按钮,把灯点亮。然后我们按下第二个按钮:

data 由 1 变成了 0,灯灭了。这时候我们松开第二个按钮:

发现灯不会重新亮起,data 也会保持在 0 的状态。
相比于我们最早说到的“实现复杂的标准与或式”的功能,PGA33X6 真正强大的地方其实在于我们刚才所说的“状态锁存”这一点,这是普通的逻辑门所做不到的事。
将锁存状态作为输入量,设计更复杂的逻辑电路
之前我只说了逻辑阵列里的第 1、2、3、4、7、8、9、10、11 行的小方块,当时我把第 5、6 行的小方块跳了过去。其实,第 5、6 行的小方块是用于判断锁存器 data 的。PGA33X6 里,锁存器 data 既可以作为输出量,也可以作为输入量而存在。在某一列里:
如果你点亮了第 5 行的小方块,相当于 if (data == 1);
如果你点亮了第 6 行的小方块,相当于 if (data == 0)。
锁存量作为输入有什么用呢?在之前的例子里,通过使用 data 锁存器,我们实现了一个用按钮控制开关的灯泡。我们现在在上一个方案的基础上,给灯泡增加一个【紧急开关】:在灯泡开启的状态下,按下紧急按钮,可以点亮紧急指示灯。而在灯泡关闭的状态下按下紧急按钮时,不产生任何效果。我们设计出如下的电路:

第一列我们点亮了第 1、9 行的小方块;第二列我们点亮了第 3、10 行的小方块;第三列我们点亮了 5、7、11 行的小方块;data 上方我们选择了左边的小方块。右边,主灯泡连接着 data 输出口,紧急指示灯连接着 Z 输出口。
现在,我们将我们的逻辑用 C 语言代码来描述:
现在运行程序:

首先我们按下第一个按钮,将主灯泡点亮。

此时我们按住第三个按钮,发现紧急指示灯被点亮了。

此时松开第三个按钮,发现紧急灯泡灭了。PGA 只有一个锁存器,只能锁存一个输出量。所以紧急指示灯的状态是锁不住的,会随着按钮松开而熄灭。

此时我们按下第二个按钮,将主灯泡熄灭。

主灯泡熄灭后,再按下第三个按钮,会发现紧急指示灯不再亮起。因为紧急指示灯亮起的条件是【data 为 1 时按下第三个按钮】。现在 data 为 0,不满足条件,紧急指示灯自然就不会亮起。
进阶练习 (3)
我们来实现一个带开关功能的示波器。我们从创意工坊里订阅【PGA33X6 SR LATCH】这道谜题,然后点击游戏主页上的【概念 SPEC】按钮,找到刚才订阅的【PGA33X6 SR LATCH】,打开这道由网友分享的 PGA 练习题。

本关要求当 set 信号出现时打开示波器(将 latch 信号置为 100);当 reset 信号出现时关闭示波器(将 latch 信号置为 0)。同时,当示波器开启的状态下,在 filter 端口输出 pulse 给出的波形信号;在示波器关闭的情况下,令 filter 端口保持为 0。
这道题其实就是谜题版的“紧急指示灯”,左边的三路输出正好对应开灯、关灯、紧急指示。相信你到了这一步,连点亮哪些小方块都已经会背了:第一列 1、9,第二列 3、10,第三列 5、7、11,data 上方点左边的小方块。一气呵成。


回顾龙腾系列第 10 关《真人 CS》,使用 PGA33X6 锁存器减少代码行数
现在我们回到龙腾系列关卡里的第 10 关《真人 CS》。我们之前的方案是使用 MC6000 里的 dat 寄存器来锁存“活着”状态的。请回到 【深圳 IO 攻略】第 10 关:真人 CS 查看我们之前的设计方案。


受刚才“按钮点灯”案例的启发,我们现在可以将“活着”状态用 PGA33X6 来输出和锁存。要知道使用 PGA33X6 编写逻辑时,即使你的逻辑阵列写得天花乱坠,最终统计时这些通通都是不计入代码行数的。而且“活着”部分的逻辑代码用 PGA33X6 代替后,剩下的部分是不足 9 行的,可以放在一块 MC4000 里。使用 PGA33X6 的电路图和代码如下:



左边的 PGA33X6 共有 3 路输入,第 1 路输入是右侧的【扣扳机】信号,第 2、3 路输入是左侧的【击中】和【复活】信号。接线图里,第 1 路输出信号连接着【活着】端口,输出的是 data 锁存器的值;第 3 路输出信号则连接着右边 MC4000 的 p0 口。我们再观察 PGA33X6 里点亮的小方块,发现本 PGA33X6 需要执行的是如下的逻辑运算:
这里,我们利用 PGA33X6 锁存器,将【扣扳机】信号加工成了【活着时扣扳机】信号,交给右边的 MC4000 处理。这样,触发【射击】信号的条件就由“同时满足【扣扳机】、【有子弹剩余】、【活着】三者”化简成了“同时满足【活着时扣扳机】、【有子弹剩余】两者”。
右边的 MC4000,x0 口接的是【数量】常数芯片;p0 口用于读取 PGA 的第三路输出信号,判断【是否在活着时扣扳机】;x1 口接的是经过 DX-300 转接后的【添弹】输入;p1 用于输出【射击】信号。代码解析如下:
点击左下角的【模拟】,稍等片刻,便会弹出结算界面:

我们成功地利用 PGA33X6 锁存器做出了一道官方谜题,打破了“官方谜题用不着 PGA33X6”的魔咒,并将本题的代码行数压缩到了 6 行。
以上,便是 PGA33X6 这个逻辑元件的全部用法。