数字设计答案精讲:德摩根定律、数制转换与CMOS逻辑门取舍

发布时间:2026/10/28 3:14:37
数字设计答案精讲:德摩根定律、数制转换与CMOS逻辑门取舍

简介:这份《数字设计原理与实践 第四版》习题答案文档面向学习数字逻辑与数字设计课程的学生及备考读者,用于对照课后练习、梳理基础概念与验证解题思路。包内共1个docx文件,约68KB,内容按章节组织,涵盖数字电路、数制转换、编码与控制器等核心模块。具体包括逻辑门与真值表分析(如带反相器的2输入与门等效为或门)、二进制/八进制/十六进制互转、符号-数值/反码/补码表示、补码加法溢出判断,以及状态机编码方式与机械编码盘坏边界等典型题目,并给出对应参考答案与简要推导。已有1220人学习,适合课堂同步练习、期末复习和查漏补缺时使用,也可作为教师布置作业与讲解思路的参考。文档篇幅紧凑,便于按章检索,能帮助读者把抽象原理落到具体题型和计算过程上。

1. 反相器包住与门为什么变成或门:从一道习题看等价变换的底层

把《数字设计原理与实践 第四版 答案》里第 1 章第 1-6 题摊开看:一个 2 输入与门(AND2)的每个输入端、输出端上都各串了一个反相器,题目要求画逻辑图、写真值表,再问能不能简化。真值表排出来,输出和 2 输入或门(OR2)逐行一致——直觉上“反相器加上去怎么也得变成与非门”,结果却是原地反转到或门。这里不是巧合,而是德摩根定律在电路层的具体展开:(A·B)' = A' + B',输入端的两个反相器先把 A、B 各自翻一次,与门内部再运算一次,输出端再翻回来,等价于A + B。这个现象值得拆开看,因为它牵出数字设计里最容易被低估的一类机制:同一种逻辑功能可以映射到完全不同的门级结构,选哪一种不取决于布尔代数本身,而取决于成本、速度、驱动能力和工艺约束。搞懂为什么等价,比记住哪个门接哪个门重要得多。

2. 数制转换:为什么八进制和十六进制能被“一眼看穿”

2.1 分组映射的数学本质:2³ = 8、2⁴ = 16 不是巧合

八进制和二进制之间可以做到“看一位、写三位”,直接靠的是8 = 2³这个等式。每一位八进制数恰好覆盖 3 个二进制位,取值区间 0~7 正好对应 000~111,不需要任何算术运算,分组映射即可。十六进制同理,16 = 2⁴,每一位覆盖 4 个二进制位,取值区间 0~F 正好对应 0000~1111。

<代码片段> 用 Python 模拟数制分组映射(带注释)

# 将八进制字符串按位映射为二进制,无需做除法 def oct_to_bin_group(s: str) -> str: table = {c: format(int(c), "03b") for c in "01234567"} return " ".join(table[ch] for ch in s) # 每一位八进制稳定占 3 个二进制位 print(oct_to_bin_group("1234")) # 1 010 011 100 # 十六进制同理,每一位稳定占 4 位 def hex_to_bin_group(s: str) -> str: table = {c: format(int(c, 16), "04b") for c in "0123456789ABCDEF"} return " ".join(table[ch] for ch in s) print(hex_to_bin_group("29C")) # 0010 1001 1100

这道分组映射关系是第 2 章 2.2、2.3 两题能成立的根本原因:1234₈ = 1 010 011 100₂ = 29C₁₆,中间没有发生过一次十进制算术,走的是“八进制 → 三位二进制 → 四位二进制 → 十六进制”这条链。174637₈转十六进制,是1 111 100 110 011 111₂,再按四位归并就是F99F₁₆;DEAD.BEEF₁₆反向拆回八进制是157255.575674₈,顺序稍不注意就会把“整数部分从右往左三位、小数部分从左往右三位”这一点弄反,这是后面避坑章要单独说的问题。

2.2 十进制与其他进制的双向算法:除基取余与乘基取整

八进制和十六进制在二进制面前是“无算术”的,但碰到十进制就不行。十进制和其他任意进制之间的转换要分整数和小数两条路径:整数部分反复除基取余,余数从低位往高位排;小数部分反复乘基取整,整数部分从高位往低位排。这两种操作本质上都是在做多项式展开:一个十进制数的十进制表示a_n·10^n + ... + a_0·10^0是唯一的,换到 r 进制,就是求一组系数b_k满足Σ b_k·r^k = N。除基取余之所以有效,是因为每次余数恰好是“当前最低位的系数”,商继续向高位收缩。

<代码片段> 完整的双向算法(带注释)

# 整数部分:除基取余,从低位到高位收集,最后反序 def dec_to_base_int(n: int, base: int) -> str: if n == 0: return "0" digits = [] while n > 0: digits.append(n % base) # 当前最低位 n //= base # 商继续收缩 return "".join(str(d) for d in reversed(digits)) # 小数部分:乘基取整,从高位到低位 def dec_to_base_frac(frac: float, base: int, max_bits: int = 16) -> str: out = [] for _ in range(max_bits): frac *= base bit = int(frac) # 取整得到当前位 out.append(str(bit)) frac -= bit # 剩余继续乘 if frac == 0: # 已经精确终止 break return "".join(out) print(dec_to_base_int(209, 2)) # 11010001 print(dec_to_base_int(23851, 16)) # 5D2B print(dec_to_base_frac(0.3125, 2)) # 0101 → 0.3125 精确收敛

第 2 章 2.5 那组十进制转换题里,10100.1101₂ = 20.8125、15C.38₁₆ = 348.21875这类答案之所以“看起来很整”,就是因为它们的二进制/十六进制展开恰好是有限位。反例是十进制 0.1,转成二进制是 0.000110011…,永远除不尽。意识到这一点,后续在设计十进制→BCD 转换、浮点定点转换时,就会自然地对“截断位宽”这个操作持有戒心。

2.3 符号位、补码与反码:为什么八位补码能装下 -128 却装不下 +128

第 2 章 2.11 那组题把三种表示法放在一起,对比很有代表性。以-42为例:符号-数值表示是1010 1010,反码1101 0101,补码1101 0110。以-6为例:符号-数值1000 0110,反码1111 1001,补码1111 1010。补码的构造规则是“按位取反再 +1”,而反码是“按位取反”——两者只差最后一次加 1,但它消掉了补码里的“负零”。8 位二进制能表达 256 种状态,补码划分方式是-128 ~ +127,其中 0 只出现一次(0000 0000),1000 0000被保留给-128,这是符号-数值和反码做不到的——它们的1000 0000和0000 0000会同时映射到 0,白白浪费一个状态,而且给硬件加法的取舍带来额外分支。

补码能把减法并入加法的核心原因是模运算:8 位补码的“加法”本质是模 2⁸ 的同余运算,A - B等价于A + (-B),-B就是B的按位取反加 1。第 2 章 2.12 那组判断溢出的题,考察的不是补码本身,而是补码加法的边界。判定规则可以拆成一句顺口溜:两个符号相同的数相加,如果结果的符号和加数不同,就发生溢出。比如0101 1101 + 0011 0001 = 1000 1110,两个正数加起来结果是负数最高位跳变,判定为溢出;1101 0100 + 1110 1011 = 1011 1111,两个负数相加仍为负数,没有溢出。这套规则的使用要点在后续避坑章单列,因为“只看最高位进位”会翻车。

2.4 状态编码与坏边界:从 8P5 到 2^(n-1)

第 2 章 2.33 那组排列组合题看起来像数论,实际对应的是控制器状态编码的真实工程约束。5 个状态,用 3 位二进制表示,问有多少种编码方式——答案是“从 8 个可用码里取 5 个并按顺序分配”,N = 8×7×6×5×4 = 6720;7 状态是8P7 = 40320;8 状态与 7 状态数量相同,因为 8 个码全用完了,排列数就是8P7。2.34 加了“每个编码字至少含一个 0”的约束,可用的 3 位码从 8 种缩到 7 种(排除全 1 的111),从中选 6 个,N = 7×6×5×4×3×2 = 5040。2.35 和 2.36 是关于机械编码盘的:相邻位置之间的边界如果发生多位同时跳变,就会产生“坏边界”,对 3 位编码盘,坏边界是001/010、011/100、101/110、111/000这四处,n 位盘则是2^(n-1)处。

这几道题为什么放在第 2 章而不是第 5 章,答案其实很清楚:状态编码的设计质量直接关系到状态机的稳定性和功耗,编码分配阶段就决定了后面要不要为坏边界补逻辑。硬件里没有“后悔药”,一个 3 位编码分配错了,后面就得加边沿检测或格雷码转换电路来弥补,成本和延迟都是在校正阶段没法回收的。这也是为什么后续做控制器时会优先考虑格雷码、one-hot 这类“单位跳变”编码,本质上就是在设计阶段主动把坏边界消掉。

3. 逻辑门与电路实现:从 AOI/OAI 到 CMOS 晶体管数

3.1 逻辑图、真值表、卡诺图三者的对应关系

第 3 章 3.11、3.12 两题是 AOI/OAI 结构的反向推导:给一个 AOI 电路图,要求用 AND、OR、INV 重画。3.11 的答案是Z = (A·B + C + D)',3.12 的答案是Z = ((A + B)·C·D)'。从形式上看这两个表达式不过是把 AOI 和 OAI 各自的运算次序颠倒了一下,但在实际电路里它们对应的门级拓扑完全不同——AOI 是先“或”再“非”的与项组合,OAI 是先“与”再“非”的或项组合,CMOS 实现时串并联结构不一样,晶体管数量也可能差一倍。

<代码片段> 用 Python 枚举真值表验证两者是否等价

from itertools import product def aoi(a, b, c, d): return not ((a and b) or c or d) # Z = (A·B + C + D)' def oai(a, b, c, d): return not ((a or b) and c and d) # Z = ((A + B)·C·D)' for inputs in product([0, 1], repeat=4): a, b, c, d = map(bool, inputs) print(inputs, aoi(a, b, c, d), oai(a, b, c, d))

真值表跑出来会发现,AOI 和 OAI 两者并不是同一个函数——这是对的,3.11 和 3.12 分别对应两个不同题目,题目自身并不要求它们等价。这里要提的机制是:真值表和卡诺图是同一种逻辑信息的不同表示,但两者之间有信息转换成本。同一种逻辑,用卡诺图化到最小积之和或最小和之积,得到的门级结构一般不同;前者偏 AND-OR-NOT(或 NAND-NAND),后者偏 OR-AND-NOT(或 NOR-NOR)。第 4 章 4.14、4.58 那几道卡诺图题的本质就是这种“不同表示形态下的优化取舍”。

3.2 噪声容限与 DC 驱动能力:静态电阻负载怎么算

第 3 章 3.21 是整套答案里最接近“真实器件参数”的一道题。反相器的输入低电平阈值 1.5V、高电平阈值 3.5V,从特性曲线上对应的输入值是 2.4V 和 2.5V,于是高态噪声容限3.5 - 2.5 = 1V,低态噪声容限2.4 - 1.5 = 0.9V。这两个数就是电路在“信号已被识别为 0 或 1”前提下还能容忍多少噪声的定量描述。一个常见的误解是噪声容限越大电路越“抗噪”,实际上真正的约束在于接收端和发送端的阈值是否对齐:高态噪声容限由发送端的最小高电平和接收端的最低高电平共同决定,低态同理,两端的阈值一旦不匹配,容限就被压缩到零。

3.27 那组题把驱动能力与负载电阻绑在一起:74HC00 的IOLmax和IOHmax都限制在 4mA,如果外接一个 820Ω 对地电阻,高态输出电流3.84V / 0.82kΩ ≈ 4.683mA > 4mA,不能驱动;如果外接一个 1kΩ 对 Vcc 电阻,低态电流(5 - 0.33) / 1kΩ = 4.67mA > 4mA,同样不能驱动。把 1.2kΩ 接 Vcc、820Ω 接地一起挂上去,就变成两个方向都要分别校验:低态(2.03 - 0.33) / 0.487kΩ ≈ 3.49mA,高态(3.84 - 2.03) / 0.487kΩ ≈ 3.72mA,两端都低于 4mA,才是能驱动的。这里的核心思路是“两条边都校验”,任何一边超了都不算能驱动。

第 3 章 3.26 用两组极端工作点反解 p 沟道和 n 沟道的导通电阻,得到Rp ≈ 151Ω、Rn ≈ 60Ω。这个数量级在 CMOS 器件里并不奇怪——p 管的载流子迁移率本来就比 n 管低一半左右,同尺寸下电阻往往是 n 管的两到三倍。3.65 那道题算的是推挽级里 4kΩ 上拉电阻的浪费电流:(5 - 0.24) / 4 ≈ 1.19mA,浪费功率P = 4.2kΩ × (1.19mA)² ≈ 5.95mW。看起来不大,但那是单个输出级的静态浪费,一整块板上几十上百个输出级一起看,数字很可观,这是 CMOS 逻辑在 5V 时代后期逐步转向低电压供电的直接动机之一。

3.3 门级设计中的晶体管用量:NOR3、OR2 的成本账

第 3 章 3.13 和 3.15 是 CMOS 门级设计里非常经典的对照题。NOR3 的 CMake 结构是“三个 p 管串联、三个 n 管并联”,而 OR2 的结构是“NOR2 后面级联一个 INV”。为什么 OR2 不直接做“或门”,而是先做或非再取反?真实原因是 CMOS 工艺天然擅长做 NAND 和 NOR,而不擅长做 AND 和 OR。NAND2 是“两个 p 管并联、两个 n 管串联”,从速度角度看,p 管本来迁移率就低,串联会进一步拖慢上升时间;NOR2 则反过来,p 管串联、n 管并联,下降时间比上升时间长。一旦要做 OR2,本质上就是“NOR2 + INV”,而 INV 的成本是两个晶体管的面积和一级门延迟。

到了 6.32 那道 BUT 门的 CMOS 优化题,这个“门级结构影响晶体管数”的机制被推到了极致。6.31 给出的最小积之和是Y1 = A1·B1·A2' + A1·B1·B2'、Y2 = A1'·A2·B2 + B1'·A2·B2,按常规二级 AND-OR 结构实现,晶体管用量是 40 只。6.32 要求改用圈-圈逻辑并复用子表达式,最终得到的是Y1 = ((A1·B1)' + (A2·B2)')'、Y2 = ((A2·B2)' + (A1·B1)')',晶体管用量降到 20 只——恰好减半。这里省下来的不是面积这么简单,delay 也降了,因为路径上少了一级反相器。这个结论在后续做全加器、比较器这类复合逻辑时非常有指导意义:先看能不能把子表达式抽出来共用,再决定要不要走最小积之和。

<代码片段> 用 Python 验证 BUT 门优化前后逻辑等价

def but_naive(a1, b1, a2, b2): y1 = (a1 and b1 and not a2) or (a1 and b1 and not b2) y2 = (not a1 and a2 and b2) or (not b1 and a2 and b2) return y1, y2 def but_optimized(a1, b1, a2, b2): t1 = not (a1 and b1) # (A1·B1)' t2 = not (a2 and b2) # (A2·B2)' y1 = not (t1 or t2) # ((A1·B1)' + (A2·B2)')' y2 = not (t2 or t1) # ((A2·B2)' + (A1·B1)')' return y1, y2 for a1 in (0, 1): for b1 in (0, 1): for a2 in (0, 1): for b2 in (0, 1): assert but_naive(a1, b1, a2, b2) == but_optimized(a1, b1, a2, b2) print("all equivalent")

3.4 传输延迟与三要素法:上升时间、下降时间和 RC 时间常数

第 3 章 3.33、3.68、3.69 三题把“RC 一阶电路 + 阈值穿越”这条分析路线串了起来。3.33 只是算时间常数τ = RC,把负载电容和等效电阻乘起来。3.68 分析下降时间,输出从高态 4.45V 切换到低态 0.2V,等效电阻 90Ω,τ = 9ns,从 3.5V 到 1.5V 的下降时间由指数衰减三要素法给出,约 9.1ns。3.69 分析上升时间,输出从 0.2V 到 4.45V,等效电阻 164Ω,τ = 16.4ns,从 1.5V 到 3.5V 的上升时间约 19ns。

<代码片段> 用三要素法复算升降时间(带注释)

import math def fall_time(vh, vl, r, c, vt_high, vt_low): tau = r * c # V(t) = VL + (VH - VL)·exp(-t/τ),解出穿越 VTH 和 VTL 的时间差 t_high = tau * math.log((vh - vl) / (vt_high - vl)) t_low = tau * math.log((vh - vl) / (vt_low - vl)) return t_low - t_high def rise_time(vl, vh, r, c, vt_low, vt_high): tau = r * c # V(t) = VL + (VH - VL)·(1 - exp(-t/τ)) t_low = tau * math.log((vh - vl) / (vh - vt_low)) t_high = tau * math.log((vh - vl) / (vh - vt_high)) return t_high - t_low print(fall_time(4.45, 0.2, 900, 10e-12, 3.5, 1.5)) # ~9.1ns print(rise_time(0.2, 4.45, 1640, 10e-12, 1.5, 3.5)) # ~19ns

这个结果在工程上的意义远不止“两个数字”。上升时间和下降时间的不对称性来自等效电阻的不对称性——上拉级用的是 p 管,导通电阻大,下拉级用 n 管,电阻小,所以上升沿通常比下降沿慢。这就是为什么 3.34 那道题在问“电源电压 +5% 和负载电容 +5% 哪个功率涨更多”时,答案选“电压”而不是“电容”:CMOS 动态功耗是P = C·V²·f,电压是二次方关系,电容是一次方关系,电源端那一点波动被平方放大了。

4. 避坑与排查:数字设计答案里最容易翻车的几个坑

4.1 布尔代数与编码环节的误判

提示:这一节的坑都有一个共同特征——看起来只是代数变形,但改完之后真值表就变脸。排查时优先把真值表跑一遍,不要靠肉眼对表达式。

坑 1:德摩根展开时丢掉运算优先级

现象:第 4 章 4.5 题里,X + Y·Z的补函数,很容易写成X' · Y' + Z',写成之后还会觉得“每项都翻了,看起来没问题”。真实情况是,(X + Y·Z)'的正确展开是X' · (Y·Z)' = X' · (Y' + Z') = X'·Y' + X'·Z'。对比一下X'·Y' + Z',两者在X=0、Y=1、Z=0处的值分别是0 + 1 = 1和1·0 + 1 = 1,看起来一样;放到X=1、Y=0、Z=1处,前者是0 + 1 = 1,后者是0 + 1 = 1,看起来还是一样——这种“大部分场景碰巧一样”的误判是最危险的,因为它过了样例测试但漏了边界。原因在于德摩根定律只对“括号内那个最外层的运算”取反,X + (Y·Z)这个式子里 Y 和 Z 之间的“与”是最内层运算,先被翻掉之后剩下的X'和(Y·Z)'才是并列的“与”关系。正确做法是先在心里给表达式补上完整括号,再逐层翻。

坑 2:判断补码溢出只看最高位进位

现象:第 2 章 2.12 题里,1101 0100 + 1110 1011 = 1011 1111,最高位有进位,但题目答案写“不存在溢出”。同样0101 1101 + 0011 0001 = 1000 1110,最高位也有进位,这次写“存在溢出”。两次看起来行为一样,结论却相反。原因在于溢出本质是“符号位被运算结果篡权”,两个正数相加得到负数、或两个负数相加得到正数,才叫溢出;进位到符号位外面本身不构成溢出,反而可能是正常的(比如两个负数相加,进位丢到模 2⁸ 外面,剩下的符号仍为 1)。正确的判断要三点同时看:加数符号是否相同、结果符号是否与加数不同、最高位进位与次高位进位是否一致。三点对齐才下结论。

坑 3:状态编码用格雷码就不再数坏边界

现象:第 2 章 2.35 那组题里,题目问的是“普通 3 位编码盘”的坏边界,答案是2^(3-1) = 4处。有同学套在格雷码上,得到“0 处坏边界”,于是认为格雷码彻底解决了编码盘问题。原因在于格雷码确实每步只翻一位,坏边界数为零,但它把问题从“多位同时跳变”转移到了“物理位置对齐”上:编码盘上相邻传感器的机械安装误差仍然会让两个位置同时读到 0 或 1,只不过出错形态从“错位跳变”变成“刻线遮挡”。所以机械编码盘本身在格雷码之外还叠加了“刻线对齐 + 传感器间隙”这层物理约束,编码方案解决不了。

4.2 卡诺图与冒险环节的误判

注意:这一节的坑在“写出最小表达式”时不会暴露,跑静态时序仿真或者上电实测时才会冒出来,属于典型的“纸上没问题、板上出问题”。

坑 4:卡诺图无关项(don't-care)被当成“1”用

现象:第 4 章 4.18 那组带 don't-care 的卡诺图题,F = Σ(0,1,3,5,14) + d(8,15),若把 8 和 15 当成 1 全圈进去,化简出来的积之和与正确答案不一致。原因在于无关项之所以“无关”,是因为这些输入组合在实际电路里根本不会出现,或者出现时输出无所谓;一旦把它们强制当 1 圈进卡诺圈,化简结果会在某些“实际可达”的边界条件上产生冒险——表达式是更小了,但它引入了一个原本不存在的路径。正确做法是在画圈前把 don't-care 标记为独立的符号(x 或 -),只在“圈更大更省门”时借用,绝不在逻辑上固定为 1 或 0。

坑 5:静态冒险被当成“表达式已经最小化,不会再出问题”

现象:第 4 章 4.19 题里,F = W·X + W'·Y'已经是最小积之和,但题目又问“找出所有静态冒险并设计无冒险电路”。很多人的第一反应是“卡诺图已经化到最简,怎么还会有冒险”。原因在于最小化追求的是门数最少,牺牲的是“变量在切换时是否会出现瞬间的 0-1 跳变”。W·X和W'·Y'这两个圈在卡诺图上不相交,当X=Y'=1不变、W翻转时,理论上输出应保持 1,但两个与门各自有延迟,W从 1 变 0 时W·X先撤、W'·Y'后立,中间出现一段“两个与门都输出 0”的窗口,或门输出就掉一次 0,这就是静态 1 冒险。消除办法是补一个共识项X·Y',把这条缝隙堵上。

坑 6:估算 CMOS 晶体管数只数逻辑门不数反相器

现象:第 6 章 6.32 的 BUT 门优化题里,很多人写出优化表达式之后数了数“NAND2 用 4 个晶体管、INV 用 2 个晶体管”,得到 20 只,然后和原方案 40 只对比觉得很合理。但代到 3.13 的 NOR3 题里,如果按“NOR3 = OR3 + INV”来数,会得到 6 + 2 = 8 只,而实际 NOR3 只需要 6 只。原因在于 CMOS 门级设计里“NOR3 用 6 只晶体管”是标准结构,3 个 p 管串联、3 个 n 管并联,每个输入端只需要一对 p/n 管;如果先做 OR3 再取反,反而多搭了一级。所以晶体管数估算的前提是“按目标逻辑函数直接构造复合门”,不能按“先拆成基本门再拼”的思路估。

5. 设计取舍:完全集合、静态冒险与“够用就好”的代价

第 4 章 4.39 和 4.41 是两道非常经典的“完全集合判定”题,答案对照值得单独拿出来咀嚼。NAND(2) 是完全集合,证明路径是把它反向搭出 AND、OR、INV 这三种基本运算:两个输入并接得到 INV;INV 后接 NAND2 得到 AND2;输入端各挂一个 INV 再进 NAND2 得到 OR2。三条都搭出来之后,NAND(2) 就自动等于完全集合。XNOR 则相反,它不能通过“接线”保留一个乘积项、消掉另一个乘积项,所以做不出 AND 和 OR,因此不是完全集合。这个结论的直接推论是:如果整块板只允许一种门,选 NAND;如果只允许一种复合门,那选 XNOR 就是死路。

但这套判定在真实工程里的价值,得打一些折扣。用 NAND 一种门搭出整块电路当然能做到“完全集合”的理论完美,代价是门级数变多、路径变长、布线变绕,反而会吞掉当初选型时省下来的那点面积。这也是为什么 4.50 题里“反相门延迟 5ns、非反相门延迟 8ns” 这三条电路速度对比(a 是 16ns、c 是 18ns、d 是 10ns)值得看:不同的门级拓扑对应不同的延迟数字,把“完全集合”推到极致,延迟往往不是最优的。

真正需要拿捏的是“够用就好”这条线。静态冒险这条线尤其典型——按 4.19 的答案,F = W·X + W'·Y'补上共识项X·Y'之后,静态 1 冒险消失,代价是电路多一个与门和一个或门,面积和延迟都涨。问题回到:这条电路的实际输入变量组合里,X=Y'=1且W翻转的情况会不会真的出现?如果这个输入组合在系统状态机里根本到不了,补共识项就是纯浪费;如果它会出现在某个开机自检边界上,那补上就是物有所值。4.47 那道“自对偶函数”判定也是同一个逻辑:F(X,Y,Z) = Σ(1,2,5,7)和它的对偶函数逐项比对,卡诺图不同就不是自对偶;F = X'·Y·Z' + X·Y'·Z' + X·Y和它的对偶比对,卡诺图相同,就是自对偶。用“如果真的要用,会不会用这套性质”来筛选,比理论判定本身更接近工程实际。

我自己在这类取舍上翻过车。早些年做一个 4 位质数检测器的小模块,按 4.11 和 4.16 的路径先化简成最小积之和,发现表达式已经很短,就默认“没问题”直接上了板。结果在N3N2N1N0 = 1101(即十进制 13)这个边界上出现了输出抖动。回头排查,才发现卡诺图化简时把某些 don't-care 当 1 圈进了共享项,表达式虽然是等价的,但在N3N2N1N0从 1100 切到 1101 的瞬间出现了一段静态 1 冒险,正好被后级采样到了。从那以后我每次用卡诺图化简这类小电路,都会强制走一遍“先写最小积之和 → 标出所有共识项 → 在目标输入域上跑状态切换仿真”这条路,即使是几行逻辑的模块也不省这步。省下来的不只是 debug 时间,还有一种“看着简单其实有坑”的认知偏差。

这份《数字设计原理与实践 第四版 答案》里从第 1 章到第 6 章,其实一直在做同一件事:把“结论看起来是什么”和“为什么能这么实现”这两层拉开,读者能自己算出等价变换、自己推导编码方案、自己评估晶体管成本,这比背下 6720 种编码方式或者 20 只晶体管的答案有意义得多。希望帮到你。

本文还有配套的精品资源,点击获取