3. 设计输入:理解 Verilog 网表、SDC 约束、Liberty 库文件

做后端实现,说白了就是三样东西喂给工具:网表、约束、库。缺一个,工具就罢工。我见过不少新手上来就跑 flow,结果跑出来的 timing 一塌糊涂,回头一看——约束没写对,或者 Liberty 库选错了 PVT 条件。嗯,这章咱们就把这三样东西掰开揉碎讲清楚。

3.1 一个简单的计数器设计

先准备一个 8 位计数器,带同步复位和使能。代码很简单,但足够说明问题。

// counter.v
module counter (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       en,
    output reg  [7:0] count
);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            count <= 8'b0;
        else if (en)
            count <= count + 1'b1;
    end
endmodule

我个人习惯,写 RTL 时就想着后端。比如复位用异步低有效,这样综合时工具能自动映射到带复位端的 flip-flop,省掉额外的复位树。你想想看,要是写成同步复位,综合出来要多一堆逻辑门,面积和功耗都不划算。

3.2 Verilog 网表:从 RTL 到门级

综合之后,RTL 变成了门级网表。网表里全是标准单元——与门、或门、触发器、MUX……不再有 always 块了。

网表长什么样?

说白了就是一堆 module 实例化,把标准单元连起来。工具读的就是这个。

// counter_synth.v (综合后网表示例)
module counter_synth (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       en,
    output wire [7:0] count
);
    wire [7:0] count_int;
    wire       en_sync;

    // 一个 D 触发器实例
    DFF_X1 count_reg_0_ (
        .D   (count_int[0]),
        .CK  (clk),
        .Q   (count[0]),
        .QN  ()
    );
    // ... 其余 7 位类似
endmodule

我在项目中遇到过一件事:综合出来的网表里,有些信号名字被工具改了,比如 _reg_0_ 这种后缀。如果你在 SDC 里用 RTL 里的信号名去约束,工具会报错。所以,约束一定要对着网表写,或者用 get_pins 配合通配符。

避坑指南

我曾经因为网表里一个信号被优化掉了,导致 STA 报了一堆 violation。后来养成习惯:综合完先看一眼网表,确认关键路径上的信号还在不在。

3.3 SDC 约束:告诉工具你的目标

SDC 是 Synopsys Design Constraints 的缩写。工具不知道你的芯片要跑多快,你得告诉它。

最基本的约束就这几条:

约束类型 命令示例 说明
时钟定义 create_clock -period 10 [get_ports clk] 周期 10ns,对应 100MHz
输入延迟 set_input_delay -clock clk 2 [get_ports en] 数据在时钟沿后 2ns 到达
输出延迟 set_output_delay -clock clk 3 [get_ports count] 数据在时钟沿前 3ns 必须稳定
伪路径 set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b] 跨时钟域路径不做时序检查

对于我们的计数器,SDC 文件大概这样:

# counter.sdc
create_clock -name clk -period 10 [get_ports clk]
set_clock_uncertainty -setup 0.2 [get_clocks clk]
set_clock_transition -rise 0.1 [get_clocks clk]
set_clock_transition -fall 0.1 [get_clocks clk]

set_input_delay -clock clk -max 2 [get_ports en]
set_input_delay -clock clk -min 0.5 [get_ports en]

set_output_delay -clock clk -max 3 [get_ports count]
set_output_delay -clock clk -min 1 [get_ports count]

我的习惯

我会在 SDC 里把 set_clock_uncertainty 设得比实际大一点,比如多 0.1ns。这样留点余量,流片回来万一频率差点,还能救。但别太大,否则面积会炸。

3.4 Liberty 库文件:标准单元的“身份证”

Liberty 库(.lib)描述了每个标准单元的时序、功耗、面积信息。工具靠它来计算延迟。

一个典型的 Liberty 条目长这样:

cell (DFF_X1) {
  area : 5.6;
  pin (CK) {
    direction : input;
    capacitance : 0.002;
  }
  pin (Q) {
    direction : output;
    timing () {
      related_pin : "CK";
      timing_type : rising_edge;
      cell_rise (delay_template_7x7) {
        index_1 ("0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0");
        index_2 ("0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0");
        values ( \
          "0.05, 0.06, 0.08, 0.12, 0.18, 0.30, 0.55", \
          "0.06, 0.07, 0.09, 0.13, 0.19, 0.31, 0.56", \
          ...
        );
      }
    }
  }
}

你看,cell_rise 是一个 7x7 的查找表,横轴是输入转换时间,纵轴是输出负载电容。工具根据实际值查表、插值,算出延迟。

关键点

  • 每个 PVT 条件(工艺角、电压、温度)对应一个 .lib 文件
  • 典型组合:WC(最差情况,慢速)、BC(最好情况,快速)、TC(典型情况)
  • OpenROAD 里用 read_liberty 读入

我刚开始做后端时,总觉得 .lib 是工艺厂给的,不用管。直到有一次,我用了 WC 的库做 setup 分析,结果 timing 过了,但流片回来芯片在低温下跑不动。为什么?因为低温下管子变快,hold 反而出问题了。后来我才明白:setup 看最慢的库,hold 看最快的库。

避坑指南

我曾经在项目中同时用了两个不同 PVT 的 .lib,结果工具报了一堆不匹配的 warning。后来发现是 .lib 里 nom_process、nom_voltage、nom_temperature 字段不一致。所以,多个 .lib 一定要来自同一个工艺版本。

3.5 三者的关系:一张图说清楚

下面这张图展示了网表、SDC、Liberty 库如何协同工作:

设计输入三要素关系图 设计输入三要素关系图 Verilog 网表 门级连接关系 SDC 约束 时序目标与例外 Liberty 库 单元时序/功耗/面积 综合 (synth) 布局布线 (PD) 三者缺一不可 网表提供结构,SDC 定义目标,Liberty 提供延迟数据

你看,网表、SDC、Liberty 库三者缺一不可。网表告诉工具“你有哪些单元”,SDC 告诉工具“你要达到什么目标”,Liberty 库告诉工具“每个单元有多快”。工具把这三样东西揉在一起,才能算出 timing、面积、功耗。

3.6 准备我们的计数器设计

好了,理论讲完了,咱们动手准备一下。对于 OpenROAD,你需要三个文件:

  1. counter.v — 综合后的门级网表(或者 RTL,让 OpenROAD 自己综合)
  2. counter.sdc — 时序约束文件
  3. Nangate45.lib — 工艺库文件(OpenROAD 自带示例库)

小提示

OpenROAD 的 flow 脚本里,默认会从 designs/src 读 RTL,然后调用 yosys 综合。你也可以自己提前综合好,把网表放到 designs/<design_name>/results 下,然后在 config.mk 里设置 VERILOG_NETLIST 指向它。

我个人习惯用 OpenROAD 自带的综合流程,省事。但如果你有特殊的综合选项(比如 retiming、资源 sharing),那就自己跑 yosys,再喂给 OpenROAD。