第二章:数字电路基础回顾

说实话,很多做FPGA逆向的朋友,上来就盯着网表看,结果越看越晕。为什么?因为基础没打牢。我个人的习惯是,不管做正向设计还是逆向分析,先把数字电路的几个核心模块理清楚。这一章,我们就来快速过一遍组合逻辑、时序逻辑和状态机的基础。

2.1 组合逻辑:没有记忆的电路

组合逻辑,说白了就是输出只取决于当前输入,跟历史没关系。你给它什么,它立马给你什么,不存任何状态。

2.1.1 基本门电路:与、或、非

这三个门是所有数字电路的基石。我在逆向分析网表时,经常看到一堆门级网表,其实就是与或非的组合。

门类型 逻辑表达式 真值表(2输入)
与门 (AND) Y = A & B 只有A=1且B=1时,Y=1
或门 (OR) Y = A | B 只要A=1或B=1,Y=1
非门 (NOT) Y = ~A 输入取反
我的小经验: 在网表逆向中,看到一堆与非门(NAND)组合,别慌。因为很多工艺库只用NAND门就能搭出所有逻辑。我曾经在一个项目里,花了半天才把一个NAND网络还原成AND-OR结构,其实就是把德摩根定律用熟了。

2.1.2 多路选择器 (MUX)

MUX在FPGA里太常见了。LUT(查找表)本质上就是一个可编程的MUX。2选1 MUX的逻辑是:Y = S ? A : B

// 2选1 MUX的RTL描述
assign Y = sel ? data1 : data0;

我在逆向时,经常看到网表里一堆MUX级联。比如4选1 MUX,其实就是三个2选1串起来。你想想看,如果网表里看到一个MUX树,大概率是某个条件判断逻辑。

2.1.3 加法器

加法器是算术运算的核心。半加器、全加器,再到行波进位加法器。嗯,这里有个坑——行波进位加法器的延迟跟位宽成正比。我在一个高速设计中吃过亏,用了32位行波进位,结果时序跑不过。后来换成超前进位,才解决问题。

// 1位全加器
assign sum   = a ^ b ^ cin;
assign cout  = (a & b) | (a & cin) | (b & cin);
避坑指南: 我曾经在逆向一个DSP模块时,看到网表里加法器结构特别复杂。后来发现是饱和加法器——当结果溢出时,输出最大值而不是回绕。这种细节,光看网表很难猜,必须结合功能验证。

2.2 时序逻辑:有记忆的电路

时序逻辑就不一样了。它的输出不仅看当前输入,还看之前的状态。说白了,它记得过去。

2.2.1 触发器 (Flip-Flop)

触发器是时序逻辑的基本单元。D触发器最常见,边沿触发。我在网表逆向时,找触发器就是找时钟信号和复位信号。

// D触发器RTL
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        q <= 1'b0;
    else
        q <= d;
end

为什么用非阻塞赋值(<=)?因为要模拟硬件并行行为。我刚开始写Verilog时,用阻塞赋值写时序逻辑,仿真结果一塌糊涂。后来才明白,时序逻辑必须用非阻塞赋值。

2.2.2 锁存器 (Latch)

锁存器是电平敏感的。注意,FPGA里尽量别用锁存器!为什么?因为锁存器对时序分析不友好,而且容易产生毛刺。我在一个项目中,因为if-else写得不完整,综合出了锁存器,结果芯片功能异常。查了两天才找到原因。

关键区别: 触发器是边沿触发,锁存器是电平触发。在网表逆向中,看到电平敏感的存储单元,基本就是锁存器。FPGA内部其实没有真正的锁存器,而是用LUT+反馈模拟出来的。

2.2.3 寄存器 (Register)

寄存器其实就是一组触发器。比如32位寄存器,就是32个D触发器并排。在网表里,寄存器通常表现为一组带时钟和复位的触发器链。

我个人的习惯是,在逆向分析时,先找到所有寄存器的时钟域。同一个时钟域的寄存器,大概率属于同一个功能模块。

2.3 状态机基础

状态机是数字电路设计的灵魂。说白了,就是根据当前状态和输入,决定下一个状态和输出。

2.3.1 状态机的分类

  • Moore型: 输出只取决于当前状态。比如一个简单的计数器,输出就是当前计数值。
  • Mealy型: 输出取决于当前状态和输入。比如一个序列检测器,检测到特定序列才输出。

我在逆向时,怎么区分Moore和Mealy?看输出逻辑。如果输出只连到状态寄存器的输出,那就是Moore;如果输出还连到输入信号,那就是Mealy。

2.3.2 状态编码

状态编码方式有三种:二进制编码、格雷码、独热码。FPGA里常用独热码,因为每个状态只有一个触发器为1,译码逻辑简单,速度快。

编码方式 4状态示例 触发器数量 适用场景
二进制 00, 01, 10, 11 2个 状态少,资源紧张
格雷码 00, 01, 11, 10 2个 跨时钟域,减少毛刺
独热码 0001, 0010, 0100, 1000 4个 FPGA常用,速度快
我的经验: 在网表逆向中,看到很多触发器但只有少数几个状态,大概率是独热码。我曾经逆向过一个UART控制器,状态机用了8个独热码状态,一眼就看出来了。

2.3.3 状态机网表逆向要点

逆向状态机,我一般三步走:

  1. 找状态寄存器: 找到所有带反馈的触发器组。
  2. 找状态转移逻辑: 看这些触发器的输入来自哪里。
  3. 找输出逻辑: 看输出是直接来自状态寄存器,还是来自组合逻辑。

你想想看,如果状态转移逻辑里有很多比较器(比如 == 4'b0010),那基本就是独热码状态机。如果转移逻辑是简单的加法器,那可能是二进制计数器。

2.4 本章知识体系

下面这张图,是我自己总结的数字电路基础结构。做逆向时,脑子里要有这张图,看到网表就能快速定位到对应模块。

数字电路基础结构 组合逻辑 输出只取决于当前输入 • 与或非门 • 多路选择器 (MUX) • 加法器 时序逻辑 输出取决于当前输入+历史状态 • 触发器 (DFF) • 锁存器 (Latch) • 寄存器 状态机 (FSM) 组合逻辑 + 时序逻辑 的结合 Moore型 | Mealy型 | 状态编码 状态机 = 组合逻辑(状态转移)+ 时序逻辑(状态存储)

这张图其实就说明了一件事:状态机是组合逻辑和时序逻辑的结合体。做逆向时,你看到反馈回路(输出连回输入),基本就是时序逻辑;看到纯路径(输入到输出无反馈),就是组合逻辑。

核心总结: 数字电路就两样东西——组合逻辑做运算,时序逻辑做存储。状态机把两者结合起来,实现复杂控制。网表逆向,就是把这层关系反过来,从门级网表还原出功能。

好了,这一章的基础就到这里。下一章我们会深入网表结构,看看实际逆向时怎么操作。


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