第四讲:组合逻辑还原基础——从网表中提取与或非逻辑表达式

各位同学,今天我们来聊聊组合逻辑还原中最基础、也是最核心的一步——从网表里把与或非表达式给“挖”出来。

说实话,我刚入行那会儿,拿到一个综合后的门级网表,看着满屏的AND、OR、INV、NAND、NOR,脑袋都是大的。后来带我的老工程师跟我说了一句话,我到现在还记得:“网表就是一堆门,门就是布尔代数,布尔代数就是与或非。你只要会看门,就会写表达式。”

嗯,今天我就把这句话拆开揉碎了讲给你听。

4.1 网表里的“门”到底长什么样?

我们先看一个最简单的例子。假设你拿到了一段这样的网表描述(Verilog格式):

// 网表片段
AND2 U1 (.A(a), .B(b), .Y(n1));
OR2  U2 (.A(n1), .B(c), .Y(n2));
INV  U3 (.A(n2), .Y(z));

这玩意儿怎么读?

  • U1 是一个两输入与门(AND2),输入是 a 和 b,输出是内部节点 n1。
  • U2 是一个两输入或门(OR2),输入是 n1 和 c,输出是 n2。
  • U3 是一个反相器(INV),输入是 n2,输出是 z。

所以,从输入到输出的逻辑关系就是:

n1 = a & b
n2 = n1 | c = (a & b) | c
z = ~n2 = ~((a & b) | c)

你看,这不就是最基础的与或非表达式吗?

核心思想:网表里的每一个标准单元(Standard Cell),本质上就是一个布尔运算器。你只需要顺着连线,把门的输出表达式代入到下一级的输入里,就能得到最终的逻辑表达式。

4.2 提取表达式的“三步法”

我在项目中总结了一套方法,叫“三步法”。你照着做,基本不会出错。

  1. 第一步:找输出端口
    先找到网表的顶层输出端口(比如 z、y、data_out 等)。这是你的起点。
  2. 第二步:反向追踪
    从输出端口出发,沿着连线往回找。遇到一个门,就把它的输入表达式写出来。遇到内部节点,就继续往前追。
  3. 第三步:代入化简
    把所有中间节点的表达式,逐级代入到输出端口的表达式里。最后得到的就是一个只包含原始输入端口(比如 a、b、c、clk 等)的布尔表达式。

举个例子,我们看一个稍微复杂一点的网表:

NAND2 U4 (.A(d), .B(e), .Y(n3));
NOR2  U5 (.A(n3), .B(f), .Y(n4));
AOI21 U6 (.A(n4), .B(g), .C(h), .Y(z));

这里出现了一个 AOI21(与或非门),它内部结构是:Y = ~( (A & B) | C )

好,我们按三步法来:

  • 输出端口是 z,由 U6 驱动。
  • U6 的输入是 n4、g、h。所以 z = ~( (n4 & g) | h )
  • n4 由 U5 驱动,U5 是 NOR2:n4 = ~(n3 | f)
  • n3 由 U4 驱动,U4 是 NAND2:n3 = ~(d & e)

现在代入:

n4 = ~( ~(d & e) | f )
z = ~( ( ~( ~(d & e) | f ) & g ) | h )

嗯,这个表达式看起来有点长。但别急,后面我们会讲怎么化简。今天你只要学会“提取”这一步就行。

我的小技巧:在纸上画一个简单的逻辑图。把每个门画成一个符号,连线标上节点名。这样反向追踪的时候,眼睛跟着线走,比盯着文本网表舒服多了。我早期做项目时,网表动辄几十万行,不画图根本理不清。

4.3 常见门单元的表达式对照表

为了方便你快速提取,我把最常用的门单元及其布尔表达式整理成了一张表。建议你收藏一下,工作中随时翻看。

单元名称 功能描述 布尔表达式(Y = ...)
AND2 两输入与门 A & B
OR2 两输入或门 A | B
INV 反相器 ~A
NAND2 两输入与非门 ~(A & B)
NOR2 两输入或非门 ~(A | B)
XOR2 两输入异或门 A ^ B
XNOR2 两输入同或门 ~(A ^ B)
AOI21 与或非门(2输入与 + 1输入或 + 反相) ~( (A & B) | C )
OAI21 或与非门(2输入或 + 1输入与 + 反相) ~( (A | B) & C )
AOI22 与或非门(两组两输入与 + 反相) ~( (A & B) | (C & D) )

注意:AOI 和 OAI 这类复合门,在网表里非常常见。它们本质上就是把“与或”和“非”打包成了一个单元。提取表达式时,千万不要把它拆成多个门来写,直接套用上表中的公式即可。我曾经见过有同事把 AOI21 拆成 AND + OR + INV 三步写,结果表达式越写越乱,最后对不上功能。

4.4 一个完整的实战例子

我们来看一个真实项目中遇到的场景。假设网表如下:

// 模块:data_check
// 输入:a, b, c, d, en
// 输出:valid

INV  U1 (.A(en), .Y(n_en));
NAND2 U2 (.A(a), .B(b), .Y(n1));
NOR2  U3 (.A(c), .B(d), .Y(n2));
AOI21 U4 (.A(n1), .B(n2), .C(n_en), .Y(valid));

我们来提取 valid 的表达式。

第一步,输出是 valid,由 U4 驱动。
U4 是 AOI21:valid = ~( (n1 & n2) | n_en )

第二步,追 n1、n2、n_en:

  • n1 = ~(a & b)
  • n2 = ~(c | d)
  • n_en = ~en

第三步,代入:

valid = ~( ( ~(a & b) & ~(c | d) ) | ~en )

嗯,这个表达式看起来有点复杂。但没关系,我们后面会讲怎么用布尔代数化简它。今天你只要能做到这一步,就已经掌握了组合逻辑还原的“基本功”。

总结一下:从网表提取与或非表达式,说白了就是“看门、写式、代入”六个字。你只要认识常见的门单元,会写布尔表达式,再顺着连线反向追踪,就能把任何组合逻辑网表翻译成数学公式。

4.5 本章知识体系图

下面我用一张 SVG 图来总结本章的核心逻辑。你可以把它当作一个“思维导图”来用。

组合逻辑还原基础:从网表提取与或非表达式 网表 → 布尔表达式 ① 找输出端口 ② 反向追踪 ③ 代入化简 从顶层输出端口开始 顺着连线往回找门单元 逐级代入中间节点表达式 核心:每个门 = 一个布尔运算 反向追踪 + 代入 = 完整表达式 图:从网表到布尔表达式的三步流程

4.6 避坑指南

最后,我分享几个自己踩过的坑,希望能帮你少走弯路。

  • 不要忽略反相器。 网表里经常有大量的 INV 单元,它们会改变信号的极性。如果你漏掉一个反相器,整个表达式可能就反了。我曾经在调试一个数据通路时,因为漏掉了一个 INV,导致表达式结果跟仿真对不上,查了整整两天才找到问题。
  • 注意多扇出节点。 如果一个内部节点(比如 n1)被多个门使用,你要确保在代入时,这个节点的表达式只写一次,不要重复计算。否则表达式会变得非常冗余。
  • 复合门不要拆开写。 像 AOI、OAI 这种门,直接套用上表的公式。拆开写不仅容易出错,而且会让表达式变得冗长,不利于后续化简。

好了,今天的内容就到这里。你只要把“三步法”练熟,以后看到任何组合逻辑网表,都能像看数学公式一样清晰。


公众号:蓝海资料掘金营,微信deep3321