4、网表结构分析:模块层次、实例化、信号连接、端口映射的识别方法
网表这东西,说白了就是芯片设计的「电路图纸」。你拿到一份网表,就像拿到了一栋大楼的施工蓝图——你得先看懂它的结构,才能知道哪里是承重墙、哪里是水电管线。
我个人习惯,拿到网表第一件事不是看细节,而是先搭框架。就像拼图一样,先把边角拼好,再往里面填。今天我就带你走一遍这个流程。
4.1 模块层次结构——从顶层到底层
一个数字芯片,通常是一个大模块套小模块。顶层模块就是芯片的「外壳」,里面一层层嵌套着各种功能单元。
举个例子,一个简单的 SoC 芯片,顶层可能是 top,里面套着 cpu_core、memory_controller、uart 这些子模块。每个子模块又可能继续往下拆。
我在项目中遇到过一份网表,顶层模块下面直接挂了 200 多个子模块,密密麻麻的。我当时就意识到,如果不先把层次理清楚,后面根本没法干活。
识别层次结构的方法:
- 看模块声明:网表里
module关键字后面跟的就是模块名。顶层模块通常没有外部端口以外的引用。 - 看实例化语句:子模块被调用时,会写成
module_name instance_name (...)的形式。这个instance_name就是实例名。 - 画树状图:我习惯用文本工具把模块名和实例名列出来,画成树状结构。一眼就能看出谁是谁的父模块。
核心要点:模块层次就是芯片的「家族树」。顶层是祖宗,底层是孙子。你只要找到根节点,顺着实例化关系往下捋,整个结构就清晰了。
4.2 实例化——模块的「复制粘贴」
实例化,说白了就是把一个模块「用」起来。就像你写了一个函数,然后在主程序里调用它。网表里每个实例化语句,都代表一个具体的硬件单元。
你想想看,一个芯片里可能有几十个相同的寄存器模块。设计者不会把同一个模块写几十遍,而是写一次,然后实例化几十次。这就是实例化的意义。
实例化语句长什么样?
// 一个典型的实例化语句
reg_file u_reg_file_0 (
.clk (clk),
.rst_n (rst_n),
.wr_en (wr_en_0),
.rd_addr (rd_addr_0),
.rd_data (rd_data_0)
);
reg_file u_reg_file_1 (
.clk (clk),
.rst_n (rst_n),
.wr_en (wr_en_1),
.rd_addr (rd_addr_1),
.rd_data (rd_data_1)
);
你看,reg_file 是模块名,u_reg_file_0 和 u_reg_file_1 是实例名。两个实例用的是同一个模块,但连接的信号不同。
我的小技巧:分析网表时,我会先统计每个模块被实例化了多少次。如果某个模块被实例化了几十次,那它大概率是个基础单元(比如寄存器、加法器)。如果某个模块只出现一次,那它可能是控制核心。
4.3 信号连接——网表的「血管」
信号连接,就是模块之间的「电线」。没有这些连接,模块就是孤岛。网表里,信号连接通过 wire 和 reg 声明,然后在实例化时映射到端口上。
我曾经接手过一个项目,网表里信号命名乱七八糟,什么 sig_1、sig_2、tmp_3 都有。我当时花了整整两天,才把信号连接关系理清楚。嗯,从那以后我养成了一个习惯——先看信号命名规范。
信号连接的识别方法:
- 追踪信号名:一个信号从哪个模块输出,又输入到哪个模块。这就像追踪水流一样,找到源头和终点。
- 看信号位宽:
wire [31:0] data_bus表示这是一个 32 位的数据总线。位宽不匹配的地方,往往是 bug 高发区。 - 注意多驱动:同一个信号被多个模块驱动,这在网表里是严重错误。我见过一次,查了三天才发现是两个模块同时写了一个寄存器。
避坑指南:我曾经在分析一个加密 IP 的网表时,发现一个关键信号被「悬空」了——它没有连接到任何地方。后来才发现,这是设计者故意留的「后门」。所以,信号连接分析不只是技术活,还是安全排查的关键步骤。
4.4 端口映射——模块的「接口协议」
端口映射,就是实例化时把模块的端口和外部信号连起来。格式是 .端口名(信号名)。端口名是模块内部定义的,信号名是外部实际连接的。
举个例子:
// 模块定义
module adder (
input [7:0] a,
input [7:0] b,
output [8:0] sum
);
// 实例化时的端口映射
adder u_adder_0 (
.a (data_in_0),
.b (data_in_1),
.sum (result_out)
);
这里 a、b、sum 是端口名,data_in_0、data_in_1、result_out 是外部信号。端口映射就是把模块的「插头」插到对应的「插座」上。
端口映射的注意事项:
- 端口名不能错:写错端口名,工具会报错。但有些网表里,端口名和信号名一模一样,这反而容易混淆。
- 注意端口方向:
input、output、inout。方向搞反了,信号就传不过去。 - 未连接的端口:有些端口可能被显式地写成
.port()空括号,表示悬空。这在安全分析时要特别留意。
4.5 知识体系总览
下面这张图,是我自己总结的网表结构分析框架。你照着这个思路走,基本不会迷路。
4.6 实战技巧总结
说了这么多,我总结几条实战经验给你:
- 先宏观后微观:先看模块层次,再看实例化,最后看信号连接。别一上来就扎进细节里。
- 善用文本工具:用 grep 搜索模块名、实例名,用 awk 提取端口映射。命令行工具在网表分析时特别好用。
- 画图辅助理解:遇到复杂的连接关系,画个框图。我习惯用 Graphviz 自动生成层次图,省时省力。
- 注意异常模式:悬空端口、多驱动信号、命名不规范的模块——这些往往是安全漏洞或后门的藏身之处。
最后说一句:网表结构分析,说白了就是「拆解」和「重组」的过程。你把芯片拆成一个个模块,再理解它们怎么连起来的,整个芯片的架构就印在你脑子里了。这个能力,是硬件逆向和 IP 保护的基础中的基础。
公众号:蓝海资料掘金营,微信deep3321