TDM 模块技术手册

多 FPGA / 多 Die 时分复用:时序建模、异构链路选择、离散档位与物理合法化
模块路径: src/tdm/  |  文档版本: 2026-08  |  依赖: Routing, Partition, TBB, spdlog, nlohmann/json
阅读说明

本文面向需要理解、接入或调试 src/tdm 的开发者,流程、数据结构、模式选择和输出均以当前模块实现为准。

目录

  1. TDM 在完整流程中的位置
  2. 算法总览
  3. 核心数据结构
  4. 输入投影、构图与容量分组
  5. ratio 初始化与连续优化
  6. GIO/MGT 选择与离散化
  7. 物理合法化、评价与后处理
  8. 延迟库 JSON
  9. 输出文件
  10. 参数速查与流程分支
  11. 输入、构造与流程调用
  12. 输出检查与调试顺序
  13. 算法环节与代码位置
  14. 常见问题与调优

1. TDM 在完整流程中的位置

Partition 确定逻辑节点所属的 FPGA/Die,Routing 生成跨分区布线树,TDM 再在已经选定的物理连接上分配 GIO/MGT 类型、离散复用比和实际通道位置。

TDM 总体流程
图 1:从 Partition 与 Routing 结果到稳定 TDM 输出的六阶段流程。
数据含义TDM 中的用途
route_trees割线网物理布线树创建真实物理时序边
route_trees_edges带端口名的布线边恢复端口并生成逐 net 报告
cut_nets局部割线网到原始 net ID 的映射关联时序边、源端和汇端
cut_timing_paths压缩后的跨分区时序路径建立 DAG 并计算 arrival/slack
cut_mat有向逻辑连接负载ratio 初始化与容量下界
board_capacity / die_graph物理方向容量构造双向共享容量组

2. 算法总览

2.1 六阶段分配闭环

默认流程使用连续时序优化确定分配方向,再由离散合法化保证结果可实现。

STEP 1–2布线结果投影
时序 DAG 与容量组
STEP 3–4arrival/slack 权重
连续 ratio 优化
STEP 5–6类型与离散档位
合法化和全局验收
build_tdm() + buildGraphEdges()
        ↓
initialize ratio / lib choice / capacity
        ↓
arrival + slack → edge criticality μ
        ↓
continuous capacity projection
        ↓
GIO/MGT selection + discrete choices
        ↓
direction/socket/root/lane/clock legalization
        ↓
discrete global score → commit or rollback

2.2 板级与 Die 级编号

模式物理节点跨 FPGA 判定容量来源
has_die=falseFPGA IDsrc != dstboard_capacity,为空时使用 cutweights_assignment
has_die=truefpga * dies_per_fpga + dieis_diff_fpga(src,dst)die_graphdie_gio_linksdie_mgt_links

executeTDM()die_parts 生成 node_to_route_part。存在非零 Die ID 或 has_die=true 时使用展平 Die 编号,否则保留 FPGA ID。

2.3 多层级拓扑如何参与 TDM

TDM 多层级延迟范围
图 2:层级路径决定 delay scope,并贯穿边状态、连续斜率和离散延迟查询。
处理位置多层级处理方式
自动启用延迟库加载成功、包含 scopesfpga_hierarchy_paths 非空时设置 use_hierarchy_delay=true
范围识别delay_scope_for_parts() 比较两端路径的首个不同层级,得到 Board、InterBoard、InterCluster 或 InterRack
边状态build_tdm() 写入 timing_edge::delay_scope
连续优化scoped_lib_slope() 使用对应范围的 PWL 斜率
离散评价scoped_lib_delay() 查询对应范围和链路档位的延迟
输出tdmDelayReport.json 保存可用的 rack/cluster/board 层级路径

四级路径可区分 Rack、Cluster、Board;三级路径可区分 Cluster、Board;二级路径可区分 Board。缺少层级路径、缺少分范围模型或处于 contest 模式时使用基础延迟语义。

Die 编号约束

有 Die 时,delay_scope_for_parts() 当前按 part / 4 还原 FPGA ID,即层级范围识别假设每个 FPGA 有 4 个 Die。若 dies_per_fpga 不是 4,需要同步该映射逻辑。

3. 核心数据结构

3.1 timing_edge

timing_edge 表示内部时序 DAG 的一条有向边,真实物理布线段和连接路径使用的 dummy 边共用该结构。

字段组主要字段含义
物理定位srcdstport_name物理 FPGA/Die 分区与端口
DAG 定位src_nodedst_nodenet_id内部节点与局部 cut-net ID
优化状态tdm_ratiotdm_ratio_assignlib_choice连续 ratio、离散 ratio 与 GIO/MGT 类型
时序状态delaytdm_delaydie_delaycut_delay当前延迟及模型分量
层级状态delay_scope层级延迟范围
注意:编号不应混用

timing_edge::net_idcut_nets 的局部索引,不是原始超图 net ID。导出原始编号时使用 cut_nets[net_id].net_id

3.2 tdm_group

tdm_group 聚合同一无向物理分区对之间的全部 TDM 边。规范化后 left_part < right_partedges[0, backward_id) 为正向,余下为反向。组内保存双向容量、socket/root/lane、连续容量、乘子及关联边。

双向 TDM 容量组
图 3:同一物理分区对的双向边共享容量组,但两个方向分别受约束。

3.3 bidir_map 与内部 DAG

映射keyvalue
tdm_group_map{min(part),max(part)}容量组 ID
node_maps{local_net_id,physical_part}DAG 节点 ID
edge_map{src_node,dst_node}时序边 ID

bidir_map::operator[] 具有插入语义;只读检查使用 contains()try_get()

3.4 DelayLibrary

统一提供 PWL delay(ratio)slope(ratio)、ratio 边界、离散 choices、bypass 语义及多层级 scope 覆盖。

4. 阶段 1:输入投影、构图与容量分组

4.1 executeTDM() 组装输入

先复用 Routing 已加载的延迟库;若尚未加载且配置了 delay_lib_path,再读取 JSON。随后生成 node_to_route_part,把布线树、割线网、割时序路径、容量矩阵、时钟信息和求解参数传入 Tdm

4.2 build_tdm() 创建真实边

  1. 把原始 net ID 映射为局部 cut-net 索引;
  2. 为布线树物理段创建或复用 DAG 节点;
  3. 区分片内固定延迟与跨 FPGA TDM 边;
  4. 将跨 FPGA 边加入规范化容量组;
  5. 记录方向、容量、端口、clock 和 delay_scope
  6. backward_id 分隔两个方向。

4.3 buildGraphEdges() 串接时序路径

cut_timing_paths 串接相邻 cut-net,将路径段映射到真实布线树父节点链;同时加入超级源、超级汇 dummy 边,生成入边、出边和拓扑顺序,使 arrival、slack 与关键权重能在完整 DAG 上传播。

5. 阶段 2:ratio 初始化与连续优化

5.1 ratio 的容量含义

ratio 表示一条物理通道时分承载的逻辑信号数。ratio 越小通常延迟越低,但消耗的物理容量份额越大。

Σe∈E 1 / re ≤ C

r_e 是连续 ratio,C 是对应方向的 GIO channel 或 MGT lane 容量。

5.2 初始化

初值由方向负载、容量、延迟库边界和 bypass 约束决定,默认优先 GIO;无 GIO 但有 MGT 时直接选 MGT。

5.3 时序权重

arrival/slack 分析把物理边延迟传播到完整路径,并为关键边产生权重 μ。关键程度越高、delay slope 越大,连续优化越倾向于分配更小 ratio。

5.4 拉格朗日松弛模型

连续阶段暂时移除离散档位和物理装箱,只保留时序代价与共享容量。令 μ_e 为关键权重、s_e 为当前延迟曲线局部斜率,并定义 w_e=max(μ_e·s_e, ε),容量组子问题为:

min Σe∈Eg were    s.t.   Σe∈Eg1/re ≤ Cg,   remin ≤ re ≤ remax

为容量约束引入非负乘子 λ_g

Lg(r,λg) = Σ were + λg(Σ1/re − Cg)

对未触及边界的 ratio 应用 KKT 条件,可直接得到:

re = √(λg/we),    √λg = (Σ√we)/Cg

μ_e·s_e 越大,边越关键,ratio 越小;容量越紧张,λ_g 越大,组内 ratio 整体增大。因此 λ_g 可以理解为容量组的“拥塞价格”。ratio 触及上下界时,投影固定越界边、扣除其占用,再为自由边重算乘子和 ratio;有方向容量时 forward/backward 分别处理。

5.5 当前实现的连续迭代

  1. 建立初始权重:在 DAG 上计算 arrival,由超级汇沿逆拓扑传播 μ,并结合 preceding TDM edge 延迟形成初始关键度。
  2. 计算容量价格:每组汇总 √(μese),按容量计算 λg
  3. 更新 ratio:使用闭式关系生成浮点候选,对零权重和数值异常使用安全上界。
  4. 恢复容量可行性:限制 ratio 范围,再按 cap_cont 缩放或投影,并检查方向容量。
  5. 刷新时序:更新 delay,沿拓扑顺序重算 edge/node arrival 与 arrival gap。
  6. 重新分配关键权重:增加临界入边的 μ、减少非关键入边的 μ,并通过逆拓扑传播保持权重流量一致。
  7. 保存最优连续解:仅在目的节点 arrival 改善时更新 ratio 快照。
  8. 判断收敛:改善小于阈值或连续达到稳定次数时提前结束,否则至多执行 max_iters 次。

代码中的 lambda_velocity 保留容量惩罚的动量状态,但当前 ratio 主更新采用 KKT 闭式容量价格。浮点解只作为 GIO/MGT 选择和离散合法化的高质量起点,不直接导出。

6. 阶段 3:GIO/MGT 选择与离散化

6.1 类型选择

混合分配综合关键边权重与 slack 收益、scope ratio-delay 曲线、双向容量、GIO root/socket、MGT link/lane 及 clock domain 可行性选择 GIO 或 MGT。

默认不允许未出现在 cut_timing_path 上的边占用 MGT;allow_mgt_on_non_timing_edges=true 可放宽限制。

6.2 离散 choices

连续 ratio 必须映射到所选类型的离散 choices。类型或 ratio 改变后立即用 scoped_lib_delay() 刷新延迟。

continuous ratio 37.4
        │ GIO choices: 32, 64, 128
        │ MGT choices: 62, 124, 248
        ↓
select lib + feasible discrete ratio + scope delay

7. 阶段 4:物理合法化、评价与后处理

连续容量可行并不等于实际通道可装箱。离散合法化还检查双向 GIO/MGT 容量、真实 channel 桶、socket 与 GIO root、MGT lane 与 clock domain、bypass 语义,以及 signal slot 重复或越界。

TDM 离散化与物理合法化
图 4:连续解经离散档位、物理装箱和全局时序评分后提交或回滚。

7.1 离散全局评价

候选合法化后,在完整 DAG 上重算离散 delay、arrival 与 slack,并检查方向容量和装箱。只有可行且评分不退化的候选才保留,否则恢复快照。

7.2 后处理

tdm_opt_mode 可控制 GIO root 方向拆分、final/score polish、worst-path rescue、winner 后处理和容量补齐。局部后处理按策略上限执行,没有改善或候选不可行时提前结束。

8. 延迟库 JSON

8.1 data/delay.json 的标准结构

{
  "gio": {
    "points": [
      [2, 4.57], [8, 58], [16, 62], [24, 67], [32, 72],
      [40, 77], [48, 81], [56, 86], [64, 90], [72, 95],
      [80, 100], [88, 105], [96, 110], [104, 115],
      [112, 120], [120, 125], [128, 130], [136, 135],
      [144, 140], [152, 145], [160, 150], [168, 155],
      [176, 160], [184, 165], [192, 170], [200, 175],
      [208, 180], [216, 185], [224, 190], [232, 195],
      [240, 200], [248, 205], [256, 210]
    ]
  },
  "mgt": {
    "points": [
      [62, 96], [124, 104], [248, 124], [496, 154], [992, 216]
    ]
  }
}

points 中每项依次为 [ratio, delay_ns]。根对象必须同时包含可用的 giomgt,每类至少提供两个不同 ratio 的采样点。

8.2 buildFromJson() 的自动推导

8.3 scope 覆盖

"scopes": {
  "board":         { "mgt": { "offset_ns": 0.0 } },
  "inter_board":   { "mgt": { "offset_ns": 38.79 } },
  "inter_cluster": { "offset_ns": 128.007 },
  "inter_rack":    { "offset_ns": 160.0 }
}

scope 可提供独立 points,也可继承基础模型并叠加 offset_nsdelay_offset_nsadd_delay_ns。只覆盖一种链路时,另一类型回退到基础模型。

加载失败语义

buildFromJson() 失败时记录告警并使用 fallback 候选和延迟;生产运行应将该告警视为配置错误。

9. 输出文件

9.1 tdmDelayReport.json

按物理分区 pair 输出两端 FPGA/Die 与层级路径、双向容量、socket pair、物理 channel 的类型/ratio/delay/signal slots,以及原始 net_id、端口和 ratio_index

9.2 routing.out

逐 hyperedge 输出拓扑规模、类型数量,以及各 net 的 source、sinks、物理路径、ratio、delay、类型和端口布线段。

9.3 条件输出

文件条件内容
design.route.outcontestnet 路径和权重
design.tdm.outcontestDie pair、方向、net 和 ratio
tdm.txt部分算法且 debug_mode迭代和 ratio 日志
tdm_ratio_compare.txt部分算法连续与离散 ratio 对比
pair_tdm_stats.txt优化统计路径pair 级容量、类型和关键性
cutTimingPathInfo.txt最差路径分析TDM 前后 slack、ratio 和路径延迟

固定相对路径的文件多为覆盖写入;多实例并发运行应使用不同工作目录。

10. 参数速查与流程分支

10.1 调度优先级

条件流程标准导出
test == 4Synergistic 实验对照否,分析后返回
test == 5Near-optimal 实验对照否,分析后返回
contestcontest 优化器是,并额外导出比赛文件
avg_only负载/容量均分
test == 1早期优化器
test == 3基础实验对照否,分析后返回
其他值(含 test=2当前默认第三版优化器

10.2 TDMParams

参数默认值作用
max_iters100连续优化迭代上限
convergence_threshold1e-3目标变化提前终止阈值
convergence_num10连续满足阈值的计数
stable_num3解稳定判定次数
decay_factor0.7乘子更新衰减因子
initial_learning_rate0.2初始乘子步长
avg_onlyfalse按负载/容量均分
tdm_fast_modefalse精简离散策略集合
use_worst_slack_objectivefalse使用最差 slack 目标
allow_mgt_on_non_timing_edgesfalse允许非时序边使用 MGT
tdm_opt_modelegacy选择 root 方向拆分、最终打磨和 winner 后处理组合
delay_lib_path延迟库路径
tdm_topk1日志最差路径数
gio_channel_grouping_capacity23单根 GIO 折算通道数

10.3 tdm_opt_mode

参数值直接作用主要处理内容
legacy使用基础后处理流程不重新划分双向 GIO root,也不执行扩展打磨;离散候选完成局部搜索后,执行 worst-path rescue、合法性检查和无退化回滚。
root_split在基础流程上优化双向 GIO root 分配枚举或调整同一 tdm_group 中 forward/backward 可用的 GIO root 数量,结合时序收益与装箱可行性选择方向容量划分;其余最终处理与 legacy 相同。
final_polish在基础流程上启用完整的最终打磨不调整 GIO root 方向划分;对每个离散候选执行 final polish、批量重分配、score polish 和剩余 GIO 容量补齐,并在 WNS 退化或装箱失败时回滚。
v2同时启用 root split 和完整最终打磨先优化双向 GIO root,再执行 final polish、批量重分配、评分打磨和容量补齐;当 use_worst_slack_objective=true 时,连续阶段同时使用 worst-slack 驱动的 μ 更新。
no_postprocess跳过 winner 的最终救援与打磨保留连续优化、离散化和离散局部搜索的结果,但不执行最终 rescue、polish、批量重分配及容量补齐,用于直接观察后处理前的解。

contest 模式优先于该参数:无论配置哪个值,都会关闭普通流程的 root split、final polish、winner 后处理和 slack μ 特性,使用 contest 自身的处理语义。

11. 输入、构造与流程调用

11.1 前置状态

11.2 流程调用

DelayLibrary delayLib;

// routing_flow::routingFlow(...) 已填充 router。
if (para.route) {
    tdm::executeTDM(router, fpgas, para, delayLib);
}

executeTDM() 完成延迟库复用/加载、Tdm 构造、模式选择和标准结果导出。

12. 输出检查与调试顺序

  1. 编号一致:局部 net ID 正确映射到 cut_nets
  2. DAG 闭合:拓扑排序覆盖有效节点,source 到 sink 可达;
  3. 组方向正确:backward_id 准确分隔方向;
  4. 连续容量可行:每方向、每类型的 Σ1/r 不超过容量;
  5. ratio 可实现:最终 ratio 属于对应 choices;
  6. 物理装箱可行:socket/root、方向桶、lane/clock 满足约束;
  7. 延迟已刷新:离散化或切换类型后重新查询;
  8. 全局评分一致:离散 WNS 与 winner 日志一致;
  9. 输出完整:JSON slot 无重复,文本报告覆盖所有 sink。
策略开发约束

新离散策略必须同时通过数值容量、真实通道装箱和离散时序检查;修改 ratio 或 lib_choice 后必须同步刷新 delay。

13. 算法环节与代码位置

处理环节当前行为主要代码或状态
输入组装复用延迟库、生成展平编号并构造分配器tdm::executeTDM()
真实边构建从布线树创建时序边和容量组build_tdm()
路径串接生成时序 DAG、dummy 边和拓扑关系buildGraphEdges()
连续分配时序权重、ratio 更新和容量投影当前默认第三版优化器
类型选择按关键性、scope delay 与装箱状态选择lib_choice、混合离散策略
离散合法化检查 choices、方向、socket/root、lane/clocklegalize_*()tdm_ratio_assign
全局验收重算离散 WNS 并回滚退化候选状态快照、winner score
结果导出标准 JSON、文本与条件报告export_tdm_solution()

14. 常见问题与调优

Q1:为什么 delay library 加载失败后仍继续?

流程记录可降级告警并使用 fallback ratio 和线性延迟。生产运行应将告警视为配置错误。

Q2:为什么离散 ratio 不在预期集合?

choices 缺省时从 PWL 横坐标推断;加载失败也会进入 fallback。检查加载日志、delayLibReady 和 scope choices。

Q3:为什么某个方向仍然容量溢出?

Σ1/r 可行不保证真实 channel/socket 可装箱。检查方向容量、GIO root、socket、MGT lane/clock 和合法化告警。

Q4:为什么 MGT 分配很少?

默认保护非时序边不使用 MGT;lane、clock domain 或离散 ratio 也可能使候选不可装箱。

Q5:为什么 test=3/4/5 没有标准 JSON?

这些分析型分支完成分析后直接返回。需要标准输出时使用默认流程、avg_only 或 contest 流程。

Q6:为什么层次 scope 没有启用?

需要延迟库加载成功、JSON 含 scopes、层级路径非空,并且当前不是 contest 基础延迟语义。

Q7:为什么连续优化采用拉格朗日松弛法?

直接同时枚举全部边的 GIO/MGT、离散 ratio 和物理通道会形成很大的组合搜索;平均分配又无法区分关键路径。拉格朗日松弛提供了更合适的连续层:

该方法只解决连续容量分配,不替代离散合法化;最终仍须经过 choices、GIO/MGT、socket/root/lane/clock 装箱和离散全局 WNS 验收。

调优优先级

  1. 先确保延迟库、方向容量和真实装箱可行;
  2. 校验 Die 展平编号、层级路径和 scope;
  3. avg_only 建立容量与导出基线;
  4. 对比 root split、final polish 和 v2
  5. 再调整迭代上限、收敛阈值和学习率;
  6. 同时观察离散 WNS、利用率、GIO/MGT 使用和可装箱性。