4、门级仿真环境搭建:综合后网表、SDF文件、库文件设置、门级仿真脚本示例

好,咱们进入正题。门级仿真,说白了就是拿综合后的网表来跑仿真。这跟RTL仿真完全是两码事。RTL仿真你关心的是功能对不对,门级仿真你关心的是时序能不能满足、有没有竞争冒险。

我个人习惯把门级仿真环境拆成四个部分:网表、SDF、库文件、仿真脚本。一个一个来聊。

4.1 综合后网表——你的设计变成了什么样子

综合后的网表,就是工具把你的RTL代码翻译成了标准单元库里的门电路。你想想看,原来写的 always @(posedge clk) 变成了什么?变成了一个D触发器加上一堆组合逻辑。

网表文件一般长这样:

// 综合后的Verilog网表
module top (
    input  clk,
    input  rst_n,
    input  [7:0] data_in,
    output [7:0] data_out
);

// 一个D触发器的实例化
DFFRX1 \data_out_reg[0]  (
    .D(n1),
    .CK(clk),
    .RN(rst_n),
    .Q(data_out[0]),
    .QN()
);

// 组合逻辑
AND2X1 U3 (
    .A(data_in[0]),
    .B(data_in[1]),
    .Y(n1)
);

endmodule

嗯,这里要注意。网表里全是标准单元的名字,比如 DFFRX1AND2X1。这些名字来自你用的工艺库。不同工艺库,名字不一样。我在项目中遇到过,换了个工艺库,网表里所有单元名字都得重新映射,那叫一个头疼。

4.2 SDF文件——时序的灵魂

SDF(Standard Delay Format)文件,就是门级仿真的核心。它记录了每个门、每根连线的延迟信息。没有SDF,门级仿真跟RTL仿真没区别。

SDF文件长这样:

(CELL
  (CELLTYPE "DFFRX1")
  (INSTANCE \data_out_reg[0])
  (DELAY
    (ABSOLUTE
      (IOPATH CK Q (0.12::0.15) (0.10::0.13))
      (IOPATH RN Q (0.08::0.11) (0.07::0.09))
    )
  )
  (TIMINGCHECK
    (SETUP D CK (0.05::0.06))
    (HOLD D CK (0.03::0.04))
  )
)

你看,这里写了CK到Q的延迟是0.12到0.15纳秒,setup time是0.05到0.06纳秒。仿真器就是靠这些数据来判断时序是否违例。

警告:SDF文件里的延迟值,是综合工具根据工艺库和线负载模型估算出来的。实际流片后的延迟可能不一样。所以门级仿真通过了,不代表芯片一定能跑起来。

4.3 库文件设置——仿真器得认识这些门

仿真器不认识 DFFRX1 是什么东西。你得给它提供库文件,告诉它每个标准单元的行为模型。

库文件一般有两种:

  • Verilog库文件:用Verilog写的标准单元行为模型。最常见。
  • VHDL库文件:用VHDL写的,比较少用。

库文件里定义了每个单元的:

  • 端口方向(输入、输出、双向)
  • 功能逻辑(比如D触发器的行为)
  • 时序参数(setup、hold、delay)

我曾经犯过一个低级错误:库文件版本跟综合用的工艺库版本不一致。结果仿真跑出来一堆X态,查了两天才发现是库文件不匹配。嗯,从那以后我每次都要核对库文件的版本号。

4.4 门级仿真脚本示例——把一切串起来

好,现在咱们把网表、SDF、库文件都准备好了。怎么跑仿真?写个脚本。

我用的是Synopsys VCS,但其他仿真器思路一样。给你看个完整的脚本:

// 门级仿真脚本 (vcs_gate_sim.tcl)
// 设置库文件路径
set LIB_PATH "/home/design/libs/tsmc28nm"
set LIB_FILES "$LIB_PATH/verilog/tsmc28nm.v"

// 编译网表和库文件
vlogan -full64 -work work \
    -v $LIB_FILES \
    -v $LIB_PATH/verilog/tsmc28nm_udp.v \
    ../netlist/top_synth.v \
    ../tb/tb_top.v

// 加载SDF文件
vcs -full64 -debug_access+all \
    -sdf min:tb_top.u_top:../sdf/top_synth.sdf \
    -P $LIB_PATH/synopsys/synopsys_sim.db \
    work.tb_top

// 运行仿真
./simv -l sim.log +maxdelays +mindelays +typdelays

这里有几个关键点:

  • -sdf min:tb_top.u_top:../sdf/top_synth.sdf:指定SDF文件,min表示用最小延迟模式。你也可以用maxtyp
  • -P:指定工艺库的仿真模型,用于SDF反标。
  • +maxdelays:仿真时使用最大延迟模式,检查setup是否违例。
小技巧:我建议你跑三种模式:+maxdelays(检查setup)、+mindelays(检查hold)、+typdelays(典型情况)。三种都过了,才算稳。

4.5 避坑指南——我踩过的那些坑

门级仿真环境搭建,坑不少。我列几个常见的:

  1. SDF反标失败:网表里的实例路径跟SDF里的路径不匹配。检查一下顶层模块名和实例名是否一致。
  2. 库文件缺失:有些标准单元在库文件里没定义,仿真器报Unknown module。去工艺库目录下找找,一般都有。
  3. X态传播:门级仿真里X态特别多。别慌,先检查是不是SDF没反标成功,再检查是不是有未初始化的寄存器。
  4. 仿真速度慢:门级仿真比RTL仿真慢10倍以上。我一般只跑几个关键case,不会全量回归。

我曾经遇到一个特别诡异的bug:门级仿真跑出来一个信号一直为X,但RTL仿真完全正常。查了两天,发现是综合工具把某个组合逻辑优化掉了,导致SDF里没有对应的延迟信息。最后我加了dont_touch约束才解决。

4.6 总结一下

门级仿真环境搭建,说白了就是四步:

  • 拿到综合后的网表
  • 准备好SDF文件
  • 配好库文件
  • 写个脚本把上面三个串起来

嗯,听起来简单,但每一步都有坑。我建议你第一次搭环境时,先跑一个最简单的case,比如只检查一个寄存器的setup/hold。确认环境没问题了,再跑全量case。

下一章咱们聊聊门级仿真常见的错误和调试技巧。到时候我会分享一些我调试X态的心得。