TDM 模块技术手册
本文面向需要理解、接入或调试 src/tdm 的开发者,流程、数据结构、模式选择和输出均以当前模块实现为准。
目录
1. TDM 在完整流程中的位置
Partition 确定逻辑节点所属的 FPGA/Die,Routing 生成跨分区布线树,TDM 再在已经选定的物理连接上分配 GIO/MGT 类型、离散复用比和实际通道位置。
| 数据 | 含义 | 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 六阶段分配闭环
默认流程使用连续时序优化确定分配方向,再由离散合法化保证结果可实现。
时序 DAG 与容量组
连续 ratio 优化
合法化和全局验收
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=false | FPGA ID | src != dst | board_capacity,为空时使用 cutweights_assignment |
has_die=true | fpga * dies_per_fpga + die | is_diff_fpga(src,dst) | die_graph、die_gio_links、die_mgt_links |
executeTDM() 从 die_parts 生成 node_to_route_part。存在非零 Die ID 或 has_die=true 时使用展平 Die 编号,否则保留 FPGA ID。
2.3 多层级拓扑如何参与 TDM
| 处理位置 | 多层级处理方式 |
|---|---|
| 自动启用 | 延迟库加载成功、包含 scopes 且 fpga_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 时,delay_scope_for_parts() 当前按 part / 4 还原 FPGA ID,即层级范围识别假设每个 FPGA 有 4 个 Die。若 dies_per_fpga 不是 4,需要同步该映射逻辑。
3. 核心数据结构
3.1 timing_edge
timing_edge 表示内部时序 DAG 的一条有向边,真实物理布线段和连接路径使用的 dummy 边共用该结构。
| 字段组 | 主要字段 | 含义 |
|---|---|---|
| 物理定位 | src、dst、port_name | 物理 FPGA/Die 分区与端口 |
| DAG 定位 | src_node、dst_node、net_id | 内部节点与局部 cut-net ID |
| 优化状态 | tdm_ratio、tdm_ratio_assign、lib_choice | 连续 ratio、离散 ratio 与 GIO/MGT 类型 |
| 时序状态 | delay、tdm_delay、die_delay、cut_delay | 当前延迟及模型分量 |
| 层级状态 | delay_scope | 层级延迟范围 |
timing_edge::net_id 是 cut_nets 的局部索引,不是原始超图 net ID。导出原始编号时使用 cut_nets[net_id].net_id。
3.2 tdm_group
tdm_group 聚合同一无向物理分区对之间的全部 TDM 边。规范化后 left_part < right_part;edges[0, backward_id) 为正向,余下为反向。组内保存双向容量、socket/root/lane、连续容量、乘子及关联边。
3.3 bidir_map 与内部 DAG
| 映射 | key | value |
|---|---|---|
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() 创建真实边
- 把原始 net ID 映射为局部 cut-net 索引;
- 为布线树物理段创建或复用 DAG 节点;
- 区分片内固定延迟与跨 FPGA TDM 边;
- 将跨 FPGA 边加入规范化容量组;
- 记录方向、容量、端口、clock 和
delay_scope; - 用
backward_id分隔两个方向。
4.3 buildGraphEdges() 串接时序路径
按 cut_timing_paths 串接相邻 cut-net,将路径段映射到真实布线树父节点链;同时加入超级源、超级汇 dummy 边,生成入边、出边和拓扑顺序,使 arrival、slack 与关键权重能在完整 DAG 上传播。
5. 阶段 2:ratio 初始化与连续优化
5.1 ratio 的容量含义
ratio 表示一条物理通道时分承载的逻辑信号数。ratio 越小通常延迟越低,但消耗的物理容量份额越大。
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, ε),容量组子问题为:
为容量约束引入非负乘子 λ_g:
对未触及边界的 ratio 应用 KKT 条件,可直接得到:
μ_e·s_e 越大,边越关键,ratio 越小;容量越紧张,λ_g 越大,组内 ratio 整体增大。因此 λ_g 可以理解为容量组的“拥塞价格”。ratio 触及上下界时,投影固定越界边、扣除其占用,再为自由边重算乘子和 ratio;有方向容量时 forward/backward 分别处理。
5.5 当前实现的连续迭代
- 建立初始权重:在 DAG 上计算 arrival,由超级汇沿逆拓扑传播 μ,并结合 preceding TDM edge 延迟形成初始关键度。
- 计算容量价格:每组汇总 √(μese),按容量计算 λg。
- 更新 ratio:使用闭式关系生成浮点候选,对零权重和数值异常使用安全上界。
- 恢复容量可行性:限制 ratio 范围,再按
cap_cont缩放或投影,并检查方向容量。 - 刷新时序:更新 delay,沿拓扑顺序重算 edge/node arrival 与 arrival gap。
- 重新分配关键权重:增加临界入边的 μ、减少非关键入边的 μ,并通过逆拓扑传播保持权重流量一致。
- 保存最优连续解:仅在目的节点 arrival 改善时更新 ratio 快照。
- 判断收敛:改善小于阈值或连续达到稳定次数时提前结束,否则至多执行
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 delay7. 阶段 4:物理合法化、评价与后处理
连续容量可行并不等于实际通道可装箱。离散合法化还检查双向 GIO/MGT 容量、真实 channel 桶、socket 与 GIO root、MGT lane 与 clock domain、bypass 语义,以及 signal slot 重复或越界。
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]。根对象必须同时包含可用的 gio 和 mgt,每类至少提供两个不同 ratio 的采样点。
8.2 buildFromJson() 的自动推导
min_ratio和max_ratio缺省时使用首尾采样 ratio;choices缺省时使用全部采样 ratio;- GIO
bypass缺省时使用最小采样点[2, 4.57]; - 采样点会按 ratio 排序、合并重复值并构造 PWL 与回退模型;
delay_detail.json的对象形式采样点和根级bypass同样可解析,但当前不读取其中的fit、fallback_linear和low_range_linear字段。
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_ns、delay_offset_ns 或 add_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.out | contest | net 路径和权重 |
design.tdm.out | contest | Die 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 == 4 | Synergistic 实验对照 | 否,分析后返回 |
test == 5 | Near-optimal 实验对照 | 否,分析后返回 |
contest | contest 优化器 | 是,并额外导出比赛文件 |
avg_only | 负载/容量均分 | 是 |
test == 1 | 早期优化器 | 是 |
test == 3 | 基础实验对照 | 否,分析后返回 |
其他值(含 test=2) | 当前默认第三版优化器 | 是 |
10.2 TDMParams
| 参数 | 默认值 | 作用 |
|---|---|---|
max_iters | 100 | 连续优化迭代上限 |
convergence_threshold | 1e-3 | 目标变化提前终止阈值 |
convergence_num | 10 | 连续满足阈值的计数 |
stable_num | 3 | 解稳定判定次数 |
decay_factor | 0.7 | 乘子更新衰减因子 |
initial_learning_rate | 0.2 | 初始乘子步长 |
avg_only | false | 按负载/容量均分 |
tdm_fast_mode | false | 精简离散策略集合 |
use_worst_slack_objective | false | 使用最差 slack 目标 |
allow_mgt_on_non_timing_edges | false | 允许非时序边使用 MGT |
tdm_opt_mode | legacy | 选择 root 方向拆分、最终打磨和 winner 后处理组合 |
delay_lib_path | 空 | 延迟库路径 |
tdm_topk | 1 | 日志最差路径数 |
gio_channel_grouping_capacity | 23 | 单根 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 前置状态
- 布线树、割线网与原始 net ID 可互相对应;
cut_mat覆盖全部展平节点;- 时序模式下割时序路径与
route_graph完整; - Die 模式下
die_parts、die_graph与dies_per_fpga一致; - 延迟库可解析,或明确接受 fallback;
- 输入对象生命周期覆盖求解过程。
11.2 流程调用
DelayLibrary delayLib;
// routing_flow::routingFlow(...) 已填充 router。
if (para.route) {
tdm::executeTDM(router, fpgas, para, delayLib);
}executeTDM() 完成延迟库复用/加载、Tdm 构造、模式选择和标准结果导出。
12. 输出检查与调试顺序
- 编号一致:局部 net ID 正确映射到
cut_nets; - DAG 闭合:拓扑排序覆盖有效节点,source 到 sink 可达;
- 组方向正确:
backward_id准确分隔方向; - 连续容量可行:每方向、每类型的 Σ1/r 不超过容量;
- ratio 可实现:最终 ratio 属于对应 choices;
- 物理装箱可行:socket/root、方向桶、lane/clock 满足约束;
- 延迟已刷新:离散化或切换类型后重新查询;
- 全局评分一致:离散 WNS 与 winner 日志一致;
- 输出完整:JSON slot 无重复,文本报告覆盖所有 sink。
新离散策略必须同时通过数值容量、真实通道装箱和离散时序检查;修改 ratio 或 lib_choice 后必须同步刷新 delay。
13. 算法环节与代码位置
| 处理环节 | 当前行为 | 主要代码或状态 |
|---|---|---|
| 输入组装 | 复用延迟库、生成展平编号并构造分配器 | tdm::executeTDM() |
| 真实边构建 | 从布线树创建时序边和容量组 | build_tdm() |
| 路径串接 | 生成时序 DAG、dummy 边和拓扑关系 | buildGraphEdges() |
| 连续分配 | 时序权重、ratio 更新和容量投影 | 当前默认第三版优化器 |
| 类型选择 | 按关键性、scope delay 与装箱状态选择 | lib_choice、混合离散策略 |
| 离散合法化 | 检查 choices、方向、socket/root、lane/clock | legalize_*()、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 和物理通道会形成很大的组合搜索;平均分配又无法区分关键路径。拉格朗日松弛提供了更合适的连续层:
- 容量组可分解:固定时序权重后,各
tdm_group独立计算容量价格和 ratio,核心更新具有闭式形式,单轮代价主要随边数增长; - 时序与拥塞统一:
μ_e·s_e表示减少 ratio 的时序收益,λ_g表示共享容量的稀缺程度; - 关注关键路径:关键边获得较小 ratio,非关键边主动让出容量;
- 连续方向更平滑:浮点解避免频繁档位跳变,并为后续离散策略缩小搜索范围;
- 适配多层级延迟:替换不同 scope 的局部 slope 后,模型与求解形式保持不变。
该方法只解决连续容量分配,不替代离散合法化;最终仍须经过 choices、GIO/MGT、socket/root/lane/clock 装箱和离散全局 WNS 验收。
调优优先级
- 先确保延迟库、方向容量和真实装箱可行;
- 校验 Die 展平编号、层级路径和 scope;
- 用
avg_only建立容量与导出基线; - 对比 root split、final polish 和
v2; - 再调整迭代上限、收敛阈值和学习率;
- 同时观察离散 WNS、利用率、GIO/MGT 使用和可装箱性。