现在位置: 首页 > 计算机组成原理 > 正文

逻辑门电路搭建

上一讲我们认识了四种基本逻辑门。这一讲,我们把它们连接起来,搭出能解决实际问题的电路。

单个逻辑门就像一颗螺丝钉——本身功能有限。但把它们组合起来,就能构建出任意复杂的功能。


生活化类比:乐高积木搭建城堡

想象你面前有四种颜色的乐高积木:

  • 绿色积木 = AND 门:只有两端都连接时才能通电
  • 橙色积木 = OR 门:任意一端连接就能通电
  • 红色积木 = NOT 门:通电变断电,断电变通电
  • 紫色积木 = XOR 门:两端状态不同时通电

用这几块积木,你可以搭出什么?

一个投票器:三个人投票,少数服从多数。这是一个典型的组合逻辑电路——把 AND/OR 门连起来就能实现。

一个密码锁:需要同时按下正确的几个键才能开锁。这本质上是 AND 门的级联——每个键对应一个输入,只有所有输入都为 1 时才输出 1。

一个报警器:任意一个传感器触发就报警。这本质上是 OR 门的应用——只要有一个传感器输出 1,报警器就响。


组合逻辑 vs 时序逻辑

在深入电路搭建之前,先区分两个重要概念:

类型特点输出取决于有记忆吗?例子
组合逻辑输出仅由当前输入决定当前输入值没有加法器、多数表决器、译码器
时序逻辑输出由当前输入 + 历史状态决定当前输入 + 之前存的值计数器、寄存器、状态机

本讲关注组合逻辑。时序逻辑将在第 9 讲详细介绍。

简单区分方法:组合逻辑是「你输入什么,我就输出什么」(像数学函数);时序逻辑是「我还记得你之前输入过什么」(像人的记忆)。


电路搭建方法论:三步法

用逻辑门搭建任意组合逻辑电路,有一个通用的三步流程:

  1. 列出真值表:把输入的所有组合列出来,写出每种情况下期望的输出。
  2. 写出逻辑表达式:对于输出为 1 的每一行,写出对应的「与项」(各输入的与组合),再用 OR 连接所有与项。这叫做「积之和」(Sum of Products)形式。
  3. 画电路图 / 写代码:把逻辑表达式翻译成门电路连接。

下面我们通过三个由浅入深的例子来实践这个方法。


案例一:两输入密码锁

需求:设计一个简单的密码检查电路。有两个输入 A 和 B,只有当 A=1 且 B=1 时输出 1(解锁),其他情况输出 0(锁定)。

这个需求其实就是一个 AND 门。但我们可以用它来演示三步法:

第一步:真值表

AB期望输出 Y
000(锁)
010(锁)
100(锁)
111(解锁)

第二步:逻辑表达式

只有最后一行输出为 1。对应的与项是:A AND B。

所以:Y = A AND B

第三步:电路实现

只需要一个 AND 门,A 和 B 作为输入,输出就是 Y。

太简单了?下面来看一个有实际意义的例子。


案例二:多数表决器(核心案例)

需求:三个人(A、B、C)对提案进行投票表决(1 表示赞成,0 表示反对)。当至少两人赞成时,提案通过(输出 1)。

这是一个经典的三输入多数表决器,也是数字电路教材中的必学案例。

第一步:列出真值表

三输入共有 23 = 8 种组合:

ABC赞成人数是否通过 Y说明
00000无人赞成
00110仅 C 赞成
01010仅 B 赞成
01121B 和 C 赞成
10010仅 A 赞成
10121A 和 C 赞成
11021A 和 B 赞成
11131全票通过

第二步:写出逻辑表达式

找出输出为 1 的行(第 4、6、7、8 行),每行写一个与项:

  • 第 4 行:NOT(A) AND B AND C
  • 第 6 行:A AND NOT(B) AND C
  • 第 7 行:A AND B AND NOT(C)
  • 第 8 行:A AND B AND C

用 OR 连接:Y = (NOT(A) AND B AND C) OR (A AND NOT(B) AND C) OR (A AND B AND NOT(C)) OR (A AND B AND C)

这个表达式虽然看起来有点长,但逻辑上非常清晰:Y 为 1 当且仅当任意两个或三个输入为 1

第三步:用 SVG 画出电路图

A B C AND AND AND OR Y 图:三输入多数表决器电路结构

三个 AND 门分别检测「AB」、「BC」、「AC」三对组合是否为 11,然后 OR 门汇总结果。


交互演示:多数表决器的 Python 实现

实例

# 三输入多数表决器(runoob 演示)
# 需求:三个人投票,至少两人赞成(1)时才通过(输出 1)

# ----- 复用第 6 讲的基本逻辑门 -----
def AND(a, b):
    return 1 if a == 1 and b == 1 else 0

def OR(a, b):
    return 1 if a == 1 or b == 1 else 0

def NOT(a):
    return 1 if a == 0 else 0


# ----- 方法一:积之和(SOP)标准形式 -----
def majority_voter_sop(a, b, c):
    """
    多数表决器的「积之和」实现。
    直接从真值表推导:找出所有输出为 1 的行,
    每行写一个与项,再用 OR 连接。
    """

    term1 = AND(AND(NOT(a), b), c)       # ~A·B·C
    term2 = AND(AND(a, NOT(b)), c)       # A·~B·C
    term3 = AND(AND(a, b), NOT(c))       # A·B·~C
    term4 = AND(AND(a, b), c)            # A·B·C
    return OR(OR(term1, term2), OR(term3, term4))


# ----- 方法二:优化简化形式 -----
def majority_voter_optimized(a, b, c):
    """
    多数表决器的优化实现:Y = AB + BC + AC。
    这种形式需要的门更少,但逻辑等价于方法一。

    为什么等价?
    方法一的 SOP 形式展开后,利用布尔代数吸收律可以化简:
    ~A·B·C + A·~B·C + A·B·~C + A·B·C
    = (~A·B·C + A·B·C) + (A·~B·C + A·B·C) + (A·B·~C + A·B·C)
    = B·C·(~A+A) + A·C·(~B+B) + A·B·(~C+C)
    = B·C + A·C + A·B
    """

    return OR(OR(AND(a, b), AND(b, c)), AND(a, c))


# ============================================
# 打印完整真值表,对比两种方法
# ============================================
print("=" * 60)
print("  三输入多数表决器真值表(RUNOOB 演示)")
print("=" * 60)
print()
print("┌───┬───┬───┬────────┬──────────┬──────────┐")
print("│ A │ B │ C │ 通过? │ SOP 方法 │ 优化方法 │")
print("├───┼───┼───┼────────┼──────────┼──────────┤")
for a in [0, 1]:
    for b in [0, 1]:
        for c in [0, 1]:
            count = a + b + c
            passed = "通过" if count >= 2 else "未通过"
            sop = majority_voter_sop(a, b, c)
            opt = majority_voter_optimized(a, b, c)
            print(f"│ {a} │ {b} │ {c} │  {passed}  │    {sop}     │    {opt}     │")
print("└───┴───┴───┴────────┴──────────┴──────────┘")

# ============================================
# 门电路使用统计
# ============================================
print("\n" + "=" * 60)
print("  两种方法使用的门电路数量对比")
print("=" * 60)
print()
print("方法一(SOP 完整形式):")
print("  NOT 门: 3 个(~A, ~B, ~C)")
print("  AND 门: 4 个(四个与项,每个需要 2 个 2 输入 AND)")
print("  OR 门:  3 个(合并四个与项)")
print("  总计:   约 14 个基本门调用")
print()
print("方法二(优化形式 Y = AB + BC + AC):")
print("  AND 门: 3 个(AB, BC, AC)")
print("  OR 门:  2 个(合并三个与项)")
print("  总计:   5 个基本门调用")
print()
print("优化后减少了约 65% 的门电路使用量!")
print("这就是布尔代数化简的工程价值。")

注意真值表到逻辑表达式的转换过程。这是数字电路设计中最核心的技能之一:看到真值表,就能写出表达式;看到表达式,就能画出电路图。三者一一对应。


案例三:一位比较器

需求:比较两个 1 位二进制数 A 和 B。输出三个信号:A>B、A=B、A

这是一个非常重要的基础电路——多位比较器和 CPU 中的条件判断,都是从这里扩展出去的。

第一步:真值表

ABA > BA = BA < B
00010
01001
10100
11010

第二步:逻辑表达式

观察真值表:

  • A > B 为 1 当且仅当:A=1 AND B=0。所以:Greater = A AND NOT(B)
  • A = B 为 1 当且仅当:A 和 B 相同。所以:Equal = NOT(A XOR B),或者说 Equal = (A AND B) OR (NOT(A) AND NOT(B))
  • A < B 为 1 当且仅当:A=0 AND B=1。所以:Less = NOT(A) AND B

注意 A = B 的检测,和 XOR 门关系密切。XOR 输出为 1 意味着「不同」,对其取 NOT 就得到了「相等」。

第三步:代码实现

实例

# 一位比较器(runoob 演示)
# 比较两个 1 位二进制数 A 和 B

def AND(a, b):
    return 1 if a == 1 and b == 1 else 0

def OR(a, b):
    return 1 if a == 1 or b == 1 else 0

def NOT(a):
    return 1 if a == 0 else 0


def one_bit_comparator(a, b):
    """
    一位比较器:比较 A 和 B。
    返回三元组 (Greater, Equal, Less)
    """

    greater = AND(a, NOT(b))   # A > B: A=1 且 B=0
    less = AND(NOT(a), b)      # A < B: A=0 且 B=1
    # A = B: 既不是 greater 也不是 less
    # 等价于 NOT(A XOR B),这里用另一种实现
    equal = AND(NOT(greater), NOT(less))
    return (greater, equal, less)


# 打印真值表
print("=" * 50)
print("  一位比较器真值表(RUNOOB 演示)")
print("=" * 50)
print()
print("┌───┬───┬────────┬────────┬────────┐")
print("│ A │ B │ A > B  │ A = B  │ A < B  │")
print("├───┼───┼────────┼────────┼────────┤")
for a in [0, 1]:
    for b in [0, 1]:
        gt, eq, lt = one_bit_comparator(a, b)
        print(f"│ {a} │ {b} │   {gt}    │   {eq}    │   {lt}    │")
print("└───┴───┴────────┴────────┴────────┘")

# 测试一些有用的场景
print("\n测试场景:")
a_test, b_test = 1, 0
gt, eq, lt = one_bit_comparator(a_test, b_test)
print(f"A={a_test}, B={b_test}: A>B={gt}, A=B={eq}, A<B={lt}")
print(f"解读:A 大于 B" if gt else ("A 等于 B" if eq else "A 小于 B"))

案例四:2-4 译码器

需求:输入 2 位二进制数(00、01、10、11),让对应的 4 条输出线中的一条为 1,其余为 0。

译码器是 CPU 中的核心部件——当 CPU 要选择某个寄存器时,实质上就是在用译码器。

真值表

A1A0Y0Y1Y2Y3
001000
010100
100010
110001

逻辑表达式

  • Y0 = NOT(A1) AND NOT(A0)
  • Y1 = NOT(A1) AND A0
  • Y2 = A1 AND NOT(A0)
  • Y3 = A1 AND A0

实例

# 2-4 译码器(runoob 演示)
# 输入:2 位二进制数 A1 A0
# 输出:4 条线中恰好一条为 1

def AND(a, b):
    return 1 if a == 1 and b == 1 else 0

def NOT(a):
    return 1 if a == 0 else 0


def decoder_2to4(a1, a0):
    """
    2 线到 4 线译码器。
    返回列表 [Y0, Y1, Y2, Y3],
    其中恰好有一个为 1,其余为 0。
    """

    Y0 = AND(NOT(a1), NOT(a0))  # 00 → Y0
    Y1 = AND(NOT(a1), a0)       # 01 → Y1
    Y2 = AND(a1, NOT(a0))       # 10 → Y2
    Y3 = AND(a1, a0)            # 11 → Y3
    return [Y0, Y1, Y2, Y3]


# 打印真值表
print("=" * 50)
print("  2-4 译码器真值表(RUNOOB 演示)")
print("=" * 50)
print()
print("┌────┬────┬────┬────┬────┬────┐")
print("│ A1 │ A0 │ Y0 │ Y1 │ Y2 │ Y3 │")
print("├────┼────┼────┼────┼────┼────┤")
for a1 in [0, 1]:
    for a0 in [0, 1]:
        Y = decoder_2to4(a1, a0)
        print(f"│ {a1}  │ {a0}  │ {Y[0]}  │ {Y[1]}  │ {Y[2]}  │ {Y[3]}  │")
print("└────┴────┴────┴────┴────┴────┘")

# 展示一个实际使用场景
print("\n实际应用场景:用译码器选择寄存器")
registers = ["R0", "R1", "R2", "R3"]
for a1 in [0, 1]:
    for a0 in [0, 1]:
        Y = decoder_2to4(a1, a0)
        idx = Y.index(1)          # 找到哪条输出线为 1
        print(f"  地址 A1A0={a1}{a0} → 选择寄存器 {registers[idx]}")

译码器是「一选多」的经典电路。与之相对的是多路选择器(MUX),它是「多选一」。这两者在 CPU 的数据通路设计中非常常见。译码器用于「选中某个目标」(如选择写哪个寄存器),多路选择器用于「选中某个来源」(如选择 ALU 从哪个寄存器读数据)。


通用门:NAND 和 NOR 的特别之处

实际芯片制造中,NAND 和 NOR 门比 AND 和 OR 门更「底层」。

NAND 门 = AND + NOT,即先做 AND 再取反。NAND 和 NOR 被称为「通用门」(Universal Gates),因为它们各自单独就能实现所有布尔函数。

为什么芯片偏爱 NAND/NOR?因为在 CMOS 工艺中:

  • NAND 门只需要 4 个晶体管,而 AND 门需要 6 个(AND = NAND + NOT)
  • NAND 门的开关速度更快
  • 因此实际芯片中的 AND 功能,底层都是用 NAND + NOT 实现的

下面验证 NAND 门的「通用性」:

实例

# 证明 NAND 门的通用性(runoob 演示)
# NAND 门单独就可以实现 NOT、AND、OR 三种基本功能

def NAND(a, b):
    """NAND 门:AND 的结果取反。
    Y = 0 当且仅当 a=1 且 b=1"""

    return 0 if a == 1 and b == 1 else 1


# 用 NAND 实现 NOT:NOT(A) = NAND(A, A)
def NOT_from_NAND(a):
    return NAND(a, a)


# 用 NAND 实现 AND:AND(A,B) = NOT(NAND(A,B)) = NAND(NAND(A,B), NAND(A,B))
def AND_from_NAND(a, b):
    nand_ab = NAND(a, b)
    return NAND(nand_ab, nand_ab)


# 用 NAND 实现 OR:OR(A,B) = NAND(NOT(A), NOT(B))
def OR_from_NAND(a, b):
    return NAND(NOT_from_NAND(a), NOT_from_NAND(b))


# ============================================
# 验证
# ============================================
print("=" * 60)
print("  证明 NAND 门的通用性(RUNOOB 演示)")
print("=" * 60)

print("\n[验证:NAND 实现的 NOT]")
for a in [0, 1]:
    expected = 1 if a == 0 else 0
    actual = NOT_from_NAND(a)
    print(f"  A={a}: NOT_from_NAND = {actual}, 期望 = {expected}, {'通过' if actual == expected else '失败'}")

print("\n[验证:NAND 实现的 AND]")
for a in [0, 1]:
    for b in [0, 1]:
        expected = 1 if a == 1 and b == 1 else 0
        actual = AND_from_NAND(a, b)
        print(f"  A={a}, B={b}: AND_from_NAND = {actual}, 期望 = {expected}, {'通过' if actual == expected else '失败'}")

print("\n[验证:NAND 实现的 OR]")
for a in [0, 1]:
    for b in [0, 1]:
        expected = 1 if a == 1 or b == 1 else 0
        actual = OR_from_NAND(a, b)
        print(f"  A={a}, B={b}: OR_from_NAND = {actual}, 期望 = {expected}, {'通过' if actual == expected else '失败'}")

print("\n结论:只需 NAND 一种门,就能构造出 ANY 布尔函数。")
print("这就是为什么 nand2tetris 课程从 NAND 门开始就能搭建整台计算机。")

nand2tetris(从与非门到俄罗斯方块)是一个著名的计算机课程。它挑战你只用 NAND 门这一种元件,逐步搭建出 CPU、内存、操作系统,最终运行一个俄罗斯方块游戏。如果你对计算机底层感兴趣,强烈推荐。


交互演示:多数表决器电路搭建器(runoob vis-network 演示)

下方使用 vis-network 可视化了一个三输入多数表决器电路(Y = AB + BC + AC)。点击输入节点 A、B、C 切换 0/1,电路会自动求值并更新所有连线颜色——红色表示信号为 1,灰色表示信号为 0。

三输入多数表决器(点击 A/B/C 节点切换)

A=0, B=0, C=0 → 未通过(至少需要 2 票赞成)
输入节点 A 输入节点 B 输入节点 C AND 门 OR 门 输出 Y