第3章 数据路径提取:从比特流中提取组合逻辑路径与寄存器路径的方法
说实话,比特流逆向里最磨人的一步,就是数据路径提取。
你拿到了比特流,也知道了配置位大概对应什么资源。但怎么把这些孤立的位,拼成一条完整的逻辑路径?
这就像你手里有一堆乐高零件,图纸却丢了。你得自己琢磨出,哪个零件该插在哪。
我个人习惯,把路径提取分成两大部分:组合逻辑路径和寄存器路径。两条路走通了,整个设计就活了。
3.1 组合逻辑路径:从LUT到LUT的连线
组合逻辑路径,说白了就是信号从一组LUT出来,经过布线,再进到下一组LUT。中间没有寄存器打拍。
我在项目中遇到过最头疼的情况:明明LUT配置位都读对了,但输出就是不对。后来发现,是路径上的一个MUX配置位被我忽略了。
3.1.1 LUT配置位的提取
每个LUT本质上是一个小RAM。它的真值表就写在比特流里。
举个例子,一个4输入LUT,有16个配置位。你找到这16个位,就能还原出它的逻辑函数。
// 伪代码:提取LUT配置
for (int i = 0; i < 16; i++) {
lut_init[i] = bitstream[ lut_base_addr + i ];
}
// 然后根据输入顺序,重建真值表
3.1.2 布线资源的追踪
组合逻辑路径的难点,在于追踪布线。
比特流里,每个可编程互联点(PIP)对应一个位。你要做的,就是把这些PIP串起来。
我建议你这样做:
- 找到源端:比如一个LUT的输出引脚。
- 找到目标端:比如下一个LUT的输入引脚。
- 遍历中间的所有PIP:从源端出发,沿着布线通道,一路查下去。
嗯,这里要注意。布线通道里有很多“死胡同”。有些PIP虽然配置了,但连到的是空脚。我曾经因为这个,浪费了整整两天。
3.2 寄存器路径:时钟与数据的握手
寄存器路径,比组合逻辑路径多了一个维度:时钟。
你不仅要看数据从哪来,还要看时钟从哪来。时钟错了,数据全白搭。
3.2.1 寄存器的配置位
每个寄存器(FF)在比特流里,通常有这几个关键位:
| 配置项 | 说明 | 常见陷阱 |
|---|---|---|
| 时钟选择 | 选择全局时钟还是局部时钟 | 局部时钟的配置位可能被复用 |
| 复位/置位 | 同步还是异步,高有效还是低有效 | 有些芯片复位极性是固定的 |
| 时钟使能 | CE信号的选择 | CE信号可能来自LUT输出 |
| 初始值 | 上电后寄存器的初始状态 | 这个位很容易被忽略 |
你看,光一个寄存器,就有这么多门道。我刚开始做的时候,只关注了数据输入,结果复位极性搞反了,仿真结果全是反的。
3.2.2 时钟树的追踪
时钟路径的提取,和普通数据路径不太一样。
时钟信号通常走专门的全局布线资源。这些资源在比特流里,有自己独立的配置区域。
我个人习惯,先找到寄存器的时钟输入引脚,然后反向追踪:
- 这个引脚连到了哪个时钟缓冲器(BUFG)?
- 这个BUFG的输入来自哪个时钟输入引脚(PAD)?
- 中间有没有经过MMCM或PLL?
你想想看,如果时钟路径上有一个MMCM,它的分频倍频配置位,往往藏在比特流的另一个角落。不找到它,你永远不知道时钟频率是多少。
3.3 路径提取的自动化脚本
手工提取太慢了。我建议你写脚本。
下面是我常用的一个提取流程:
# 伪代码:自动提取数据路径
def extract_path(bitstream, start_pin, end_pin):
path = []
current_pin = start_pin
while current_pin != end_pin:
# 1. 查找当前引脚的所有扇出
fanouts = find_fanouts(bitstream, current_pin)
# 2. 根据布线资源表,找到有效的连接
next_pin = find_valid_connection(fanouts, end_pin)
if next_pin is None:
# 路径中断,需要回溯
print("路径中断在:", current_pin)
break
path.append((current_pin, next_pin))
current_pin = next_pin
return path
3.4 实战:提取一条完整的寄存器到寄存器路径
我们来走一遍完整的流程。
假设我们要提取从寄存器FF_A到寄存器FF_B的路径。
- 提取FF_A的输出:找到FF_A的Q引脚,以及它对应的输出MUX配置。
- 追踪布线:从Q引脚出发,沿着PIP一路走到FF_B的D引脚。
- 提取FF_B的配置:读取FF_B的时钟选择、复位极性等配置位。
- 提取时钟路径:找到FF_B的时钟源,追踪回全局时钟输入。
- 验证:用提取到的配置,重建一个小的仿真模型,看看数据能不能正确传递。
最后一步验证,特别重要。我遇到过好几次,路径提取看起来没问题,但仿真就是不对。后来发现,是路径上有一个“透明锁存器”被当成了普通寄存器。锁存器的行为,和寄存器完全不同。
3.5 本章小结
数据路径提取,是比特流逆向的核心技能。
说白了,就是两件事:找到元件,找到连线。
组合逻辑路径,关注LUT和布线。寄存器路径,多了一个时钟维度。
我给你的建议是:别急着自动化。先手工提取一两条路径,把芯片的“脾气”摸清楚。等你熟悉了,再写脚本批量处理。
嗯,路径提取这块,水很深。但只要你把基础打牢了,后面分析复杂设计,就会轻松很多。