B3: 时序分析和优化

一、阶段目标

B3 阶段的核心目标是使用开源 EDA 工具对 NPC 处理器进行逻辑综合和静态时序分析 (STA),评估设计的时序表现并进行优化。完成后你将:

  1. 理解逻辑综合的基本概念(RTL → 门级网表)
  2. 理解静态时序分析 (STA) 的原理和方法
  3. 掌握 SDC 时序约束文件的编写
  4. 使用 Yosys + iEDA 工具链完成综合和 STA
  5. 识别关键路径并掌握常见优化手段
  6. 理解面积/时序/功耗三者的 trade-off
  7. 评估处理器设计在目标工艺下的最高工作频率

二、B3 与 B2 的承接关系

2.1 B2 完成了什么?

B2 阶段完成了完整的 SoC 集成:

  • CPU 核通过 AXI4 接口对接 ysyxSoC 框架
  • 多种存储器(Flash/PSRAM/SDRAM)和外设(UART16550/SPI)
  • 两级 Bootloader 启动流程
  • 功能验证通过(cpu-tests + DiffTest)

2.2 B3 要解决什么问题?

问题 说明
设计能跑多快? 之前只在仿真中验证功能正确性,不知道真实硬件频率
有没有时序违例? 某些组合逻辑路径可能太长,导致无法满足时钟约束
关键路径在哪? 需要知道哪些模块/信号是性能瓶颈
如何优化? 插入流水寄存器、优化组合逻辑深度
面积多大? 综合后可以评估芯片面积(等效门数)

2.3 为什么在 B2 之后做 B3?

功能正确 (B1/B2) → 时序评估 (B3) → 性能优化 (B4/B5)
                          ↓
              发现瓶颈 → 指导 B4 的 Cache 设计和 B5 的流水线优化

B3 的时序分析结果会反馈到后续阶段:

  • 如果 IFU 到 IDU 的组合逻辑太长 → B5 需要在此插入流水级
  • 如果 AXI 互联是瓶颈 → 需要增加 Spill Register
  • 如果 ALU 延迟大 → 可能需要多周期执行

三、逻辑综合基础

3.1 什么是逻辑综合?

逻辑综合是将 RTL 描述(Verilog/SystemVerilog)转换为由标准单元库中的门级元件组成的网表的过程。

RTL (Verilog)                    门级网表 (Netlist)
┌────────────────┐              ┌────────────────────────────┐
│ always @(posedge clk)│          │ DFF_X1 u1 (.D(n1), .Q(q));│
│   q <= a & b;       │   ──→    │ AND2_X1 u2 (.A(a),       │
│                     │  (综合)   │            .B(b), .Z(n1));│
└────────────────┘              └────────────────────────────┘

3.2 综合的输入和输出

输入 说明
RTL 源文件 Verilog/SystemVerilog 设计文件
标准单元库 (.lib) 描述每个门的延迟、面积、功耗
时序约束 (.sdc) 时钟频率、输入/输出延迟
输出 说明
门级网表 (.v) 由标准单元组成的 Verilog
面积报告 等效门数、单元类型统计
时序报告 关键路径、WNS、TNS

3.3 标准单元库 (nangate45)

本项目使用开源 NanGate 45nm 工艺库:

单元类型 举例 说明
组合逻辑 AND2_X1, OR2_X1, INV_X1 与门、或门、反相器
时序逻辑 DFF_X1, DFFR_X1 D 触发器 (带/不带复位)
缓冲器 BUF_X1, BUF_X2, BUF_X4 不同驱动强度的缓冲
特殊单元 LOGIC0_X1, LOGIC1_X1 常量 0/1 驱动
时钟门控 CLKGATE_X1 时钟门控单元

X1/X2/X4 后缀: 表示驱动强度。X1 面积最小但驱动弱,X4 面积大但驱动强。综合器根据负载自动选择。

3.4 综合流程(本项目的 Yosys 脚本)

# yosys-sta/scripts/yosys.tcl 的核心步骤:

# 1. 读入 RTL
read_verilog -sv $RTL_FILES

# 2. 通用综合 (与工艺无关的优化)
synth -top $DESIGN           # 推断寄存器、合并逻辑

# 3. 优化
opt -purge                   # 删除冗余逻辑

# 4. 技术映射 — 触发器
dfflibmap -liberty $LIB      # DFF → 标准单元库的触发器

# 5. 技术映射 — 组合逻辑
abc -D $CLK_PERIOD_PS \      # 延迟约束下的逻辑优化
    -liberty $LIB \
    -script $ABC_SCRIPT      # ABC 优化脚本 (retime, map, size)

# 6. 插入 tie 单元
hilomap -hicell LOGIC1_X1 Z \
        -locell LOGIC0_X1 Z

# 7. 输出
write_verilog $NETLIST        # 写出网表
stat -liberty $LIB            # 面积统计报告

四、静态时序分析 (STA)

4.1 什么是 STA?

静态时序分析 不执行仿真,而是通过分析电路中所有路径的延迟,判断设计是否能在给定时钟频率下正确工作。

时钟周期 T = 1/频率

Setup 约束: 数据必须在时钟沿之前 t_setup 时间到达
  Data Arrival Time + t_setup ≤ T

Hold 约束: 数据必须在时钟沿之后 t_hold 时间内保持稳定
  Data Arrival Time ≥ t_hold

4.2 关键术语

术语 全称 含义
WNS Worst Negative Slack 最差负时序裕量(<0 表示违例)
TNS Total Negative Slack 所有违例路径的裕量之和
Slack 时序裕量 = 要求的时间 - 实际到达时间
关键路径 Critical Path 延迟最长的路径(决定最高频率)
Fmax Maximum Frequency 设计能工作的最高频率
Setup Time 数据在时钟沿前必须稳定的最小时间
Hold Time 数据在时钟沿后必须保持的最小时间

4.3 路径类型

┌─────────┐              ┌─────────┐
│  Reg A  │──组合逻辑──▶│  Reg B  │
│ (源)    │    (延迟)    │ (目标)  │
└─────────┘              └─────────┘

路径延迟 = t_clk→Q(A) + t_组合逻辑 + t_setup(B)

满足约束条件: 路径延迟 ≤ T (时钟周期)

三类路径:

  1. Reg-to-Reg: 寄存器输出 → 组合逻辑 → 寄存器输入(最常见)
  2. Input-to-Reg: 外部输入端口 → 组合逻辑 → 寄存器输入
  3. Reg-to-Output: 寄存器输出 → 组合逻辑 → 外部输出端口

4.4 时序分析报告解读

典型的 STA 报告格式:

========================================
 Timing Report (Setup Check)
========================================
Clock: core_clock, Period: 10.00 ns (100 MHz)

Startpoint: u_top/u_idu/o_exu_opt_reg[3]
Endpoint:   u_top/u_exu/u_alu/result_reg[31]
Path Type:  max (setup check)

Data Path:
  Pin                          Delay    Arrival
  ─────────────────────────────────────────────
  u_idu/o_exu_opt_reg[3]/Q    0.12     0.12      (源寄存器 clk→Q)
  u_idu/mux_gate_1/Z          0.35     0.47      (组合逻辑)
  u_exu/u_alu/add_gate/CO     0.89     1.36      (ALU 加法进位链)
  u_exu/u_alu/result_reg[31]/D 0.05    1.41      (目标寄存器 D 端)
  ─────────────────────────────────────────────
  Data Required Time:          10.00
  Data Arrival Time:            1.41
  ─────────────────────────────────────────────
  Slack:                        8.59 ns (PASS)

WNS = +2.86 ns (最差路径也满足约束)
TNS = 0.00 ns (无违例)
Estimated Fmax = 1000 / (10.00 - 2.86) = 140 MHz

4.5 Fmax 计算

Fmax = 1 / (T - WNS)

其中:
  T = 时钟约束周期
  WNS = 最差负裕量 (正值表示有余量)

例如:
  约束 100 MHz (T = 10ns), WNS = +2.86ns
  → 最长路径实际延迟 = 10 - 2.86 = 7.14ns
  → Fmax = 1/7.14ns ≈ 140 MHz

  约束 500 MHz (T = 2ns), WNS = +0.14ns
  → 最长路径 = 2 - 0.14 = 1.86ns
  → Fmax = 1/1.86ns ≈ 538 MHz (nangate45 理想值)

五、SDC 时序约束文件

5.1 什么是 SDC?

SDC (Synopsys Design Constraints) 是工业标准的时序约束文件格式,用于告诉综合器和 STA 工具设计的时序要求。

5.2 项目的 SDC 文件 (top.sdc)

# npc/top.sdc — NPC 处理器的时序约束

# 定义时钟端口名称
set clk_port_name clock

# 读取目标频率 (由 Makefile 传入)
set CLK_FREQ_MHZ 100
if {[info exists env(CLK_FREQ_MHZ)]} {
  set CLK_FREQ_MHZ $::env(CLK_FREQ_MHZ)
}

# I/O 延迟占比 (输入输出延迟占时钟周期的比例)
set clk_io_pct 0.2

# 获取时钟端口
set clk_port [get_ports $clk_port_name]

# 创建时钟约束
create_clock -name core_clock \
             -period [expr 1000.0 / $CLK_FREQ_MHZ] \
             $clk_port
# 100 MHz → period = 10.0 ns
# 200 MHz → period = 5.0 ns
# 500 MHz → period = 2.0 ns

5.3 SDC 命令详解

命令 说明 示例
create_clock 定义时钟信号 -period 10.0 (100MHz)
set_input_delay 输入端口到达时间约束 20% 时钟周期
set_output_delay 输出端口建立时间约束 20% 时钟周期
set_max_fanout 最大扇出约束 限制单个信号的负载
set_max_transition 最大转换时间约束 限制信号上升/下降沿

5.4 时钟端口名称的重要性

SDC 中的 clk_port_name 必须与 RTL 中的端口名一致

// top.v 中:
module top (
  input clock,    // ← SDC 中必须用 "clock"
  input reset
);

如果 SDC 写成 set clk_port_name clk 而 RTL 用 clock,STA 工具找不到时钟端口,分析结果会错误。


六、项目的综合和 STA 工具链

6.1 工具链架构

                    ┌─────────────────────────────────────────┐
                    │              yosys-sta 工具链            │
                    │                                         │
RTL (.v/.sv) ──────▶│  ┌───────┐     ┌─────┐     ┌──────┐   │
                    │  │ Yosys │────▶│ iNO │────▶│ iSTA │   │
SDC (.sdc) ────────▶│  │(综合) │     │(优化)│     │(分析)│   │
                    │  └───────┘     └─────┘     └──────┘   │
nangate45 (.lib) ──▶│       │             │           │       │
                    │       ▼             ▼           ▼       │
                    │  网表.syn.v    网表.fixed.v   报告.rpt  │
                    │  面积报告      扇出优化      时序路径    │
                    └─────────────────────────────────────────┘

6.2 三个阶段

阶段 工具 输入 输出 说明
综合 Yosys RTL + .lib 网表 + 面积报告 RTL → 门级
扇出优化 iNO (iEDA) 网表 + SDC 优化网表 插入缓冲器修复高扇出
时序分析 iSTA (iEDA) 优化网表 + SDC + .lib 时序报告 计算所有路径延迟

6.3 运行命令

# 在项目根目录运行:
cd npc
make syn

# 等价于:
cd yosys-sta
make sta DESIGN=top \
         SDC_FILE=$NPC_HOME/top.sdc \
         RTL_FILES="$(find $NPC_HOME/vsrc -path '*/deprecated' -prune -o -name '*.v')" \
         CLK_FREQ_MHZ=100 \
         INCLUDE_PATH=$NPC_HOME/vsrc/include

6.4 输出文件

综合完成后,在 yosys-sta/result/top-100MHz/ 目录下会生成:

文件 说明
top.netlist.syn.v Yosys 综合网表
synth_stat.txt 面积统计报告(单元数/面积)
synth_check.txt 综合检查报告(需要人工审核警告)
yosys.log Yosys 完整日志
top.netlist.fixed.v iNO 扇出优化后的网表
fix-fanout.log 扇出优化日志
top.rpt iSTA 时序分析报告(WNS/TNS/路径)
top.cap 电容违例报告
top.fanout 扇出违例报告
top.trans 转换时间违例报告
top_hold.skew Hold 模式时钟偏斜报告
top_setup.skew Setup 模式时钟偏斜报告
sta.log iSTA 完整日志

6.5 安装依赖

# Ubuntu/Debian:
apt install yosys                              # 开源综合器
apt install libunwind-dev libyaml-cpp-dev \
            libgomp1 libtcl8.6                 # iEDA 依赖库

# 下载 iEDA 二进制和工艺库:
cd yosys-sta
make init    # 从 ysyx 官网下载

七、NPC 中的关键路径分析

7.1 典型关键路径

在处理器设计中,以下路径通常是时序瓶颈:

┌────────────────────────────────────────────────────────────────────┐
│ 路径 1: ALU 运算路径 (最常见瓶颈)                                   │
│                                                                    │
│   IDU译码 → 操作数选择MUX → ALU运算(加法进位链) → 结果输出          │
│   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     │
│   约 2-4ns (取决于 ALU 实现和操作数宽度)                            │
├────────────────────────────────────────────────────────────────────┤
│ 路径 2: 分支决策路径                                                │
│                                                                    │
│   EXU结果 → Zero判断 → BRU分支决策 → PC更新                        │
│   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                     │
│   约 2-3ns                                                         │
├────────────────────────────────────────────────────────────────────┤
│ 路径 3: AXI 地址解码路径                                            │
│                                                                    │
│   请求地址 → addr_decode匹配 → Demux选择 → Slave端口               │
│   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                         │
│   约 1-2ns (组合逻辑地址比较)                                       │
├────────────────────────────────────────────────────────────────────┤
│ 路径 4: LSU 数据对齐路径                                            │
│                                                                    │
│   内存返回数据 → 字节选择/符号扩展 → WBU写回寄存器堆                 │
│   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                       │
│   约 1-2ns                                                         │
└────────────────────────────────────────────────────────────────────┘

7.2 项目实测结果

根据 Git 记录 (4637c62):

  • 目标约束: 100 MHz (T = 10ns)
  • 综合结果: Fmax ≈ 350 MHz (理想,nangate45)
  • 最长路径约 2.86ns → WNS ≈ +7.14ns @100MHz

注意: nangate45 是学术工艺库,实际流片工艺的延迟会不同。350MHz 是该工艺下的理论值,实际 FPGA 频率通常低得多(50-200MHz)。

7.3 面积报告解读

=== top ===
   Number of wires:              12345
   Number of wire bits:          45678
   Number of cells:               8901    ← 等效门数
   
   Chip area for module '\top':  23456.78  ← 面积 (μm²)
   
   Cell type usage:
     DFF_X1:       512    ← 触发器数量 (≈寄存器位数)
     AND2_X1:      234
     OR2_X1:       189
     INV_X1:       456
     MUX2_X1:      123
     NAND2_X1:     678
     ...

解读要点:

  • DFF 数量 ≈ 设计中所有寄存器的总位数
  • 面积受 ALU、寄存器堆、控制逻辑影响
  • RV32E (16 reg × 32 bit = 512 bit RegFile) 面积小于 RV32I (32 reg)

八、时序优化手段

8.1 Spill Register(流水线寄存器)

原理: 在长组合路径中间插入寄存器,将一段路径拆分为两段,每段延迟更短。

优化前 (一段长路径):
  Reg_A ──── 长组合逻辑 (8ns) ────── Reg_B
  最高频率: 1/8ns = 125 MHz

优化后 (插入 Spill Register):
  Reg_A ── 组合逻辑1 (4ns) ── Spill_Reg ── 组合逻辑2 (4ns) ── Reg_B
  最高频率: 1/4ns = 250 MHz
  代价: 增加 1 个时钟周期的延迟 (latency +1)

8.2 项目中的 Spill Register 实现

// npc/vsrc/libs/spill_register.sv (PULP Platform)
module spill_register #(
  parameter type T      = logic,
  parameter bit  Bypass = 1'b0     // Bypass=1 时透明(不插入寄存器)
) (
  input  logic clk_i,
  input  logic rst_ni,
  input  logic valid_i,
  output logic ready_o,
  input  T     data_i,
  output logic valid_o,
  input  logic ready_i,
  output T     data_o
);

工作原理:

  • 内部有 A、B 两个缓冲寄存器
  • 完全切断输入和输出之间的组合逻辑路径
  • 维护 valid/ready 握手语义(不丢数据、不重复数据)
  • Bypass=1 时退化为直连(调试用)

8.3 AXI Demux 中的 Spill Register 应用

// top.v 中 AXI Demux 的实例化参数:
axi_demux_intf #(
  // ...
  .SPILL_AW(1),    // AW 通道插入 Spill Register ← 切断地址解码路径
  .SPILL_W (0),    // W 通道不插入 (数据通道延迟不大)
  .SPILL_B (0),    // B 通道不插入
  .SPILL_AR(1),    // AR 通道插入 Spill Register ← 切断地址解码路径
  .SPILL_R (0)     // R 通道不插入
) u_axi_demux (...);

为什么只在 AW/AR 通道使能 Spill?

  • AW/AR 通道包含地址信号,需要通过 addr_decode 做地址比较(组合逻辑较深)
  • W/R/B 通道只是数据/响应传递,组合逻辑浅
  • Spill 增加延迟,只在必要处使用

8.4 其他优化手段

手段 原理 代价 适用场景
插入 Spill Register 切断长路径 +1 cycle latency AXI 通道、流水线间
逻辑重组 平衡组合逻辑树深度 可能增加面积 ALU 加法器
寄存器重定时 移动寄存器位置均衡路径 改变时序语义 流水线
增大驱动强度 使用 X2/X4 单元 增加面积/功耗 高扇出信号
减少扇出 复制驱动源 增加面积 时钟/复位树
多周期路径声明 允许路径跨多个周期 需要正确约束 低速接口

8.5 Valid/Ready 握手的时序优势

本项目的 Valid/Ready 弹性流水线天然支持 Spill Register 插入:

之前 (全局 stall):
  Reg → 组合逻辑 → Reg     (所有级都绑在一起, 优化困难)

之后 (Valid/Ready):
  Reg → 组合逻辑 → Spill → 组合逻辑 → Reg
                      ↑
            可以在任意点插入 Spill Register
            而不影响功能正确性 (握手协议保证)

这是 B1 阶段选择 Valid/Ready 而非全局 Stall 的重要原因之一。


九、面积/时序/功耗 Trade-off

9.1 铁三角

                  时序 (速度)
                    /\
                   /  \
                  /    \
                 / 选择  \
                /   空间  \
               /          \
              /____________\
          面积            功耗

核心矛盾:

  • 提升速度 → 使用更大的门(X2/X4)、插入缓冲 → 面积↑ 功耗↑
  • 减小面积 → 使用更小的门(X1)、共享逻辑 → 速度↓
  • 降低功耗 → 时钟门控、电压降低 → 速度↓ 或设计复杂度↑

9.2 在 NPC 设计中的体现

设计决策 选择 面积影响 时序影响
RV32E (16 reg) vs RV32I (32 reg) RV32E 面积小 无影响
软件乘除法 vs 硬件乘除法 软件 面积极小 性能差
Spill Register (AW/AR) 使能 面积+少量 时序改善
ICache (B4) 组相联 面积大 性能好
单发射 vs 多发射 单发射 面积小 IPC≤1

9.3 综合约束如何影响结果

# 约束 100 MHz (宽松):
make syn CLK_FREQ_MHZ=100
# 结果: 面积小, 综合器选择小单元, 不需要激进优化

# 约束 500 MHz (激进):
make syn CLK_FREQ_MHZ=500
# 结果: 面积大, 综合器选择大单元, 可能仍有违例

# 约束 0 (不约束, 最小面积):
make syn CLK_FREQ_MHZ=0
# 结果: 面积最小, 但时序无保证

十、综合中的常见问题

10.1 DPI-C 和仿真代码的处理

综合时 DPI-C 调用和 $write/$display 等仿真语句无法映射到硬件。需要通过条件编译排除:

// 项目中的处理方式 (defines.vh):
`ifdef SYNTHESIS
  `define YSYXSOC         // 综合时强制使用 SoC 模式
`endif

// 在外设中:
`ifndef SYNTHESIS
  import "DPI-C" function void rtl_pmem_read(...);
  always @(*) rtl_pmem_read(addr, data, en);
`else
  // 综合时不包含 DPI-C 调用
`endif

10.2 综合检查报告 (synth_check.txt)

Yosys 的 check 命令会报告潜在问题:

警告类型 含义 是否需要关注
Warning: multiple drivers 信号有多个驱动源 ⚠️ 需要修复
Warning: undriven wire 信号无驱动 ⚠️ 检查是否遗漏
Warning: unused module port 模块端口未连接 ℹ️ 通常无害
Warning: loop detected 组合逻辑环 ❌ 必须修复

10.3 综合不可综合的构造

构造 可综合? 替代方案
initial 使用复位信号
$display / $write 条件编译排除
DPI-C import 条件编译排除
real 类型 不使用浮点
delay (#10) 不使用延迟
for 循环 (可展开) 综合器自动展开
generate 参数化设计
always @(posedge clk) 推断为寄存器
always @(*) 推断为组合逻辑

10.4 Latch 推断警告

如果 always @(*) 块中有些分支没有赋值,综合器会推断出 Latch(锁存器)。这通常是 bug:

// 错误: 缺少 default → 推断 Latch
always @(*) begin
  case (sel)
    2'b00: out = a;
    2'b01: out = b;
    // 缺少 default! 当 sel=2'b10 或 2'b11 时 out 保持 → Latch
  endcase
end

// 正确: 添加 default
always @(*) begin
  case (sel)
    2'b00:   out = a;
    2'b01:   out = b;
    default: out = 0;  // ← 消除 Latch
  endcase
end

十一、Git 版本代码演进

11.1 B3 相关 Commits

Commit 说明 核心变化
4637c62 feat: yosys synthesis Fmax=350MHz 完成首次综合,评估最高频率
fab3c15 yosys-sta 完善 STA 流程,添加 iEDA 时序分析

11.2 4637c62 — 首次综合

主要工作:

  1. 集成 yosys-sta 工具链
  2. 编写 npc/top.sdc 时序约束文件
  3. NPC Makefile 添加 make syn 目标
  4. 处理综合报错(排除 DPI-C 等不可综合代码)
  5. 获得 Fmax ≈ 350MHz 的评估结果

关键修改:

# npc/Makefile 中新增:
SYN_TOP = top

syn:
	@echo "Synthesis NPC using yosys-sta"
	cd $(NPC_HOME)/../yosys-sta && make sta \
	  DESIGN=$(SYN_TOP) \
	  SDC_FILE=$(NPC_HOME)/top.sdc \
	  RTL_FILES="..." \
	  CLK_FREQ_MHZ=100 \
	  INCLUDE_PATH=$(NPC_HOME)/vsrc/include

11.3 fab3c15 — STA 完善

  • 添加 iEDA 扇出优化步骤
  • 获得更准确的时序分析结果
  • 生成完整的时序报告(WNS/TNS/路径信息)

十二、Yosys 综合脚本详解

12.1 ABC 优化脚本

Yosys 内部使用 ABC (A System for Sequential Synthesis and Verification) 做技术映射:

# yosys.tcl 中的 ABC 优化脚本:
set abc_script "+strash;ifraig;retime,-D,{D},-M,6;strash;dch,-f;map,-p,-M,1,{D},-f;topo;dnsize;buffer,-p;upsize;"

各步骤含义:

命令 功能
strash 结构化哈希 (AND-Inverter Graph 规范化)
ifraig 功能等价对消减
retime,-D,{D},-M,6 寄存器重定时 (时钟周期=D, 最多移动6级)
dch,-f AIG 选择 (DAG-aware rewriting)
map,-p,-M,1,{D},-f 技术映射 (延迟优化, 时钟约束=D)
topo 拓扑排序
dnsize 缩小不必要的大单元
buffer,-p 插入缓冲修复时序
upsize 增大关键路径上的单元

12.2 关键综合命令

# 推断寄存器和高层结构
synth -top $DESIGN

# 触发器映射: 将 always @(posedge clk) 中的寄存器映射到库中的 DFF
dfflibmap -liberty $MERGED_LIB_FILE

# 组合逻辑映射: 在延迟约束下将逻辑映射到标准单元
abc -D [expr $CLK_PERIOD_NS * 1000] -liberty $MERGED_LIB_FILE

# -D 参数: 目标延迟 (皮秒), ABC 会尝试让所有路径满足此约束
# 100 MHz: -D 10000 (10ns = 10000ps)

十三、iEDA 工具详解

13.1 iNO — 网表优化

iNO (iEDA Network Optimization) 的主要功能是修复扇出违例

问题: 某个信号驱动太多负载 (扇出过高)
  clk_gate_out ──┬──▶ DFF_1
                 ├──▶ DFF_2
                 ├──▶ DFF_3
                 ├──▶ ...
                 └──▶ DFF_100   ← 扇出=100, 转换时间太慢!

解决: 插入缓冲树
  clk_gate_out ──▶ BUF ──┬──▶ DFF_1~DFF_50
                          └──▶ BUF ──┬──▶ DFF_51~DFF_100

13.2 iSTA — 静态时序分析

iSTA (iEDA Static Timing Analysis) 的工作流程:

# sta.tcl 脚本:
set_design_workspace $RESULT_DIR     # 设置工作目录
read_netlist $NETLIST_V               # 读入优化后的网表
read_liberty $LIB_FILES               # 读入单元库 (.lib)
link_design $DESIGN                   # 链接设计
read_sdc $SDC_FILE                    # 读入时序约束
report_timing                          # 生成时序报告

输出报告内容:

  • Setup 检查: 数据到达时间 vs 时钟约束
  • Hold 检查: 数据保持时间 vs 最短路径
  • WNS/TNS: 全局时序质量指标
  • 关键路径: 延迟最长的 N 条路径的详细信息

13.3 iSTA 报告解读参考

iSTA 报告解读可参考 B站视频 BV1a14y1B7uz

报告中的关键信息:

  1. WNS (Worst Negative Slack): 负值 = 时序违例,正值 = 有余量
  2. TNS (Total Negative Slack): 所有违例路径的 slack 之和
  3. 关键路径: 显示从哪个寄存器到哪个寄存器,经过了哪些组合逻辑
  4. 电容/扇出/转换时间违例: 物理实现层面的约束检查

十四、从时序分析到设计优化的闭环

14.1 迭代优化流程

┌─────────────────────────────────────────────────────┐
│                                                     │
│  RTL 修改 ──▶ make syn ──▶ 查看报告 ──▶ 分析瓶颈  │
│     ↑                                       │       │
│     └───────────── 优化设计 ◀───────────────┘       │
│                                                     │
└─────────────────────────────────────────────────────┘

14.2 优化决策树

时序违例 (WNS < 0)?
├── YES: 查看关键路径
│   ├── ALU 运算路径过长?
│   │   └── 选项: 多周期 ALU / 流水线化 ALU / 换用更快的加法器结构
│   ├── AXI 地址解码路径过长?
│   │   └── 选项: 使能 SPILL_AW/SPILL_AR (项目已使能)
│   ├── 寄存器堆读取 + 组合逻辑?
│   │   └── 选项: 在 IDU→EXU 之间插入流水级
│   └── 控制逻辑路径?
│       └── 选项: 简化控制逻辑 / 使用更少的 MUX 层级
└── NO (有余量): 
    ├── 余量很大? → 可以尝试更高频率约束
    └── 余量适中? → 当前设计可以接受

14.3 本项目的优化点

优化位置 手段 效果
AXI Demux AW/AR Spill Register 切断地址解码关键路径
Valid/Ready 握手 弹性流水线 支持后续插入流水级
IFU→ICache Demux 分路 ICache 命中时绕过长路径
defines.vhSYNTHESIS 排除不可综合代码 综合能正确完成

十五、与其他阶段的关系

15.1 B3 的分析结果如何指导 B4 (Cache)

  • STA 告诉你 IFU 取指路径有多长 → ICache 命中时应该在 1 cycle 内返回
  • 如果 ICache 的 tag 比较 + 数据读取路径太长 → 需要拆分为 2 cycle(先比较 tag,再读数据)
  • ICache 的面积预估(通过综合看 SRAM/DFF 数量)

15.2 B3 的分析结果如何指导 B5 (流水线)

  • 识别出最长路径 → 确定在哪里切割流水级
  • 如果 IDU→EXU 路径长 → 在中间加流水寄存器
  • 如果 EXU→BRU (分支) 路径长 → 分支预测才有意义

15.3 B3 为流片准备了什么

  • 验证设计在目标工艺下可行
  • 确认没有 Latch 推断、组合环等致命问题
  • 获得面积估算(决定能否放入目标芯片)
  • 为后端布局布线提供约束基础

十六、PA 讲义相关思考题

Q: 为什么仿真正确的设计综合后可能有时序违例?

回答:
仿真只验证功能正确性(逻辑行为),不关心延迟。仿真中所有组合逻辑都在"零时间"完成。但真实硬件中每个门都有传播延迟,如果一条路径上的门太多,信号无法在一个时钟周期内从源寄存器传到目标寄存器,就会产生时序违例。

类比:逻辑上你的代码是对的(单元测试通过),但在规定时间内跑不完(性能不达标)。


Q: Spill Register 如何在不改变功能的前提下改善时序?

回答:
Spill Register 本质是在 Valid/Ready 握手路径中插入一个 2-entry 缓冲:

  1. 上游发数据时,Spill Register 接收并存储(ready_o=1 表示可接收)
  2. 下游取数据时,Spill Register 输出存储的数据(valid_o=1 表示有数据)
  3. 组合路径被完全切断:上游的 valid/data 到下游的 valid/data 中间有寄存器

功能上等价于直连(只增加 1 cycle 延迟),但时序上路径长度减半。


Q: 为什么选择 nangate45 工艺而不是真实工艺?

回答:

  • nangate45 是开源免费的学术工艺库,任何人都可以使用
  • 真实工艺库(如 TSMC 28nm)受 NDA 保护,不能公开
  • 一生一芯项目需要开源可复现的评估流程
  • nangate45 的绝对数字(延迟/面积)不代表流片结果,但相对比较有意义
  • 例如:优化后 Fmax 从 200MHz 提升到 350MHz → 说明优化有效,即使实际工艺下数字不同

Q: 100 MHz 的时钟约束是如何选择的?

回答:

  • 100 MHz 是一生一芯项目的基础目标频率
  • 对于流片验证,100MHz 已经足够运行演示程序
  • 约束太松(10MHz)→ 综合器不会优化,看不出问题
  • 约束太紧(1GHz)→ 大量违例,综合结果不可用
  • 先用 100MHz 验证可行性,再逐步提高约束看极限

十七、常见问题与解决

Q1: make syn 报错: 找不到 yosys

解决: 安装 Yosys

sudo apt install yosys

Q2: make syn 报错: 找不到 iEDA

解决: 初始化 yosys-sta 环境

cd yosys-sta
make init    # 下载 iEDA 二进制和 nangate45 工艺库

Q3: 综合报错: 不认识 DPI-C 语法

原因: 综合器无法处理仿真专用的 DPI-C 调用。
解决: 使用条件编译 ifndef SYNTHESIS 包围所有 DPI-C 代码。

Q4: 综合报告大量 Latch 警告

原因: always @(*) 块中的 case/if 缺少完整覆盖。
解决: 添加 default 分支,确保所有信号在所有分支都有赋值。

Q5: WNS 为负值(时序违例)

分析步骤:

  1. 查看 top.rpt 中的关键路径
  2. 确定瓶颈在哪个模块(ALU? 地址解码? 控制逻辑?)
  3. 考虑插入 Spill Register 或简化组合逻辑
  4. 如果违例很大(如 -5ns),可能需要架构级修改

Q6: 面积过大

原因可能:

  • 大型 MUX 树(如 LSU 的字节选择逻辑)
  • 冗余逻辑未优化
  • Yosys 的 opt 步骤没有充分运行

解决: 检查 synth_stat.txt 中哪类单元占比最大。


十八、学习建议

  1. 先跑通 GCD 样例 — 在 yosys-sta/example/ 目录下验证工具链正常
  2. 再综合 NPCmake syn 获得自己设计的报告
  3. 仔细阅读 synth_check.txt — 确认没有致命警告
  4. 理解时序报告 — 找到关键路径,画出路径图
  5. 尝试不同约束 — 改变 CLK_FREQ_MHZ 看 WNS 如何变化
  6. 对比优化前后 — 使能/禁用 Spill Register 看面积和时序的变化
  7. 参考 iSTA 视频B站 BV1a14y1B7uz

十九、RTL 源文件清单

19.1 B3 阶段涉及的文件

yosys-sta/                             # 综合 + STA 工具链
├── Makefile                            # 综合流程管理
├── scripts/
│   ├── yosys.tcl                      # Yosys 综合脚本
│   ├── sta.tcl                        # iSTA 时序分析脚本
│   ├── fix-fanout.tcl                 # iNO 扇出优化脚本
│   └── fix-fanout.json                # 扇出优化配置
├── nangate45/                          # 工艺库 (make init 下载)
│   ├── lib/merged.lib                 # 标准单元延迟/功耗模型
│   └── verilog/                       # 标准单元行为模型
└── result/top-100MHz/                  # 综合结果 (运行后生成)
    ├── top.netlist.syn.v              # 综合网表
    ├── top.netlist.fixed.v            # 优化网表
    ├── top.rpt                        # 时序报告
    ├── synth_stat.txt                 # 面积报告
    └── synth_check.txt                # 综合检查

npc/
├── Makefile                            # syn 目标
├── top.sdc                            # 时序约束文件
└── vsrc/
    ├── include/defines.vh             # SYNTHESIS 宏定义
    └── libs/
        ├── spill_register.sv          # Spill Register (时序优化)
        └── spill_register_flushable.sv # Flushable 版本

参考资料:

posted @ 2026-06-24 15:31  mo686  阅读(24)  评论(0)    收藏  举报