【agent辅助pcb routing coding学习】实践7 length matching 算法学习

Length Matching 算法设计文档

1. 算法概述

1.1 目的

Length Matching 算法用于在PCB布线中添加trombone(长号)风格的蛇形走线结构,以实现等长匹配。这对于高速信号(如DDR、USB、PCIe等)的时序对齐至关重要。

1.2 核心思想

通过在走线路径中插入垂直的锯齿状蛇形结构,在不显著增加走线面积的情况下延长走线长度。算法自动寻找最长的直线段,并在该位置插入蛇形结构,同时进行严格的碰撞检测以确保不违反设计规则。

2. 算法输入与输出

2.1 输入参数

参数 类型 说明
net_results Dict[str, dict] 网络名称到路由结果的映射字典
net_names List[str] 需要进行等长匹配的网络名称列表
config GridRouteConfig 布线配置对象
pcb_data PCBData PCB数据,包含所有线段、过孔、焊盘
prev_group_segments List[Segment] 前一组已处理网络的线段(用于组间碰撞检测)
prev_group_vias List[Via] 前一组已处理网络的过孔(用于组间碰撞检测)

2.2 输出结果

返回值 类型 说明
net_results Dict[str, dict] 修改后的网络路由结果,包含添加蛇形后的新线段
每个网络结果包含:
- new_segments List[Segment] 蛇形化后的走线段列表
- new_vias List[Via] 过孔列表
- length_matched bool 是否成功完成等长匹配

3. 算法流程

3.1 主流程 apply_length_matching_to_group()

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    apply_length_matching_to_group               │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 1: 筛选成功路由的网络并计算当前长度                        │
│  - 提取每个网络的 new_segments 和 new_vias                       │
│  - 计算每个网络的当前走线长度                                    │
│  - 记录网络类型(单端/差分对)                                  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 2: 确定目标长度                                            │
│  - 找出组内最长网络的长度作为 target_length                      │
│  - 设置允许误差 tolerance (默认 0.1mm)                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 3: 构建空间索引 (ClearanceIndex)                          │
│  - 将所有已布线对象的线段、过孔、焊盘建立空间索引                │
│  - 网格大小: SPATIAL_CELL_SIZE (2.0mm)                          │
│  - 用于快速碰撞检测查询                                          │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 4: 按长度排序处理网络                                      │
│  - 从最长的网络开始处理(最长的可能不需要添加蛇形)             │
│  - 最短的网络最后处理(需要添加最多的蛇形)                      │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 5: 对每个网络应用蛇形化                                    │
│  调用 _apply_meanders_to_net_with_iteration()                   │
│  (详见 3.2 节)                                                  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 6: 更新已处理对象列表                                      │
│  - 将当前网络的新线段和过孔添加到已处理列表                      │
│  - 供后续网络的碰撞检测使用                                      │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    返回修改后的 net_results

3.2 单网络蛇形化流程 _apply_meanders_to_net_with_iteration()

复制代码
┌─────────────────────────────────────────────────────────────────┐
│         _apply_meanders_to_net_with_iteration                    │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  初始化: 计算需要添加的额外长度                                  │
│  delta = target_metric - current_metric                         │
│  extra_length = extra_length_func(delta)                        │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │  是差分对?     │
                    └─────────────────┘
                      │           │
                     是           否
                      │           │
                      ▼           ▼
        ┌──────────────────┐  ┌──────────────────────┐
        │ apply_meanders_  │  │ apply_meanders_to_   │
        │ _to_diff_pair    │  │ _route (单端网络)    │
        └──────────────────┘  └──────────────────────┘
                      │           │
                      └─────┬─────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 2: 迭代添加蛇形直到满足长度要求                            │
│  while (current < target - tolerance) and (iterations < 20):    │
│    - 增加 bump_count                                            │
│    - 重新计算需要的额外长度                                      │
│    - 调用蛇形生成函数(使用 min_bumps 参数)                    │
│    - 如果无法添加更多蛇形,则中断                                │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 3: 振幅缩放精确匹配目标长度                                │
│  for scale_iter in range(5):                                    │
│    - 如果已满足精度要求,则跳出                                  │
│    - 计算缩放比例: scale = delta / actual_added                 │
│    - 生成试验性蛇形(使用缩放后的振幅)                         │
│    - 如果试验结果更好,则更新;否则中断                          │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    返回 (修改后的结果, 线段列表, bump数量, 最终长度)

3.3 单端网络蛇形化 apply_meanders_to_route()

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                  apply_meanders_to_route                        │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 1: 寻找所有直线段运行                                      │
│  - 调用 find_all_straight_runs()                                │
│  - 最小长度要求: amplitude * 2                                  │
│  - 按长度降序排序                                               │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 2: 构建或使用空间索引                                      │
│  if clearance_index is None:                                    │
│    clearance_index = ClearanceIndex()                          │
│    clearance_index.build(pcb_data, config, ...)                │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 3: 遍历候选直线段,尝试添加蛇形                            │
│  for (start_idx, end_idx, run_length) in runs:                 │
│    - 检查是否与排除的中心线范围重叠                              │
│    - 合并直线段为一个整体线段                                    │
│    - 调用 generate_trombone_meander()                           │
│    - 如果成功生成bump,使用此直线段并跳出循环                   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 4: 替换原始线段                                            │
│  new_segments = segments[:start_idx] + meander_segs +           │
│                 segments[end_idx + 1:]                          │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
              返回 (new_segments, bump_count)

3.4 蛇形生成 generate_trombone_meander()

复制代码
┌─────────────────────────────────────────────────────────────────┐
│              generate_trombone_meander                          │
│              (Trombone蛇形结构生成)                              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  原始直线:  ────────────────────────────────────>              │
│                                                                      │
│  蛇形结构:   ──╮╭╮╭╮╭──>                                            │
│                   │││││││                                          │
│                   ╰╯╰╯╰╯                                          │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  初始化参数:                                                      │
│  - chamfer = 0.1mm (45度倒角大小)                                │
│  - bump_width = 4 * chamfer (每个bump的水平宽度)                │
│  - max_bumps = seg_len * 0.9 / bump_width                        │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  添加直线导入段 (lead-in)                                         │
│  - 在起点留出 margin 空间                                         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  while (需要添加更多bump):                                        │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 1. 检查是否有空间放置下一个bump                            │ │
│    │    dist_to_end < bump_width + margin ? break              │ │
│    └──────────────────────────────────────────────────────────┘ │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 2. 碰撞检测: 获取安全振幅                                  │ │
│    │    - 调用 get_safe_amplitude_at_point()                   │ │
│    │    - 如果当前方向受阻,尝试相反方向                        │ │
│    │    - 如果两个方向都受阻,跳过此位置                        │ │
│    └──────────────────────────────────────────────────────────┘ │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 3. 处理同方向bump的间隔(如果需要)                        │ │
│    │    - 如果当前bump与前一个同方向,添加退出/进入倒角        │ │
│    └──────────────────────────────────────────────────────────┘ │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 4. 生成bump的各个线段:                                    │ │
│    │    a) 进入倒角 (仅第一个bump)                             │ │
│    │    b) 上升立柱 (riser)                                    │ │
│    │    c) 顶部倒角 1 (继续远离 + 前进)                         │ │
│    │    d) 顶部倒角 2 (开始返回 + 前进)                         │ │
│    │    e) 下降立柱 (riser)                                    │ │
│    │    注意: 不添加退出倒角(下一个bump直接连接)             │ │
│    └──────────────────────────────────────────────────────────┘ │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 5. 更新进度:                                               │ │
│    │    - total_extra_added += extra_this_bump                 │ │
│    │    - bump_count += 1                                      │ │
│    │    - direction *= -1 (交替方向)                           │ │
│    └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  添加退出倒角 (exit chamfer)                                     │
│  - 将路径从 ±chamfer 偏移返回到中心线                            │
│  - 使用第一个bump的方向确定退出方向                             │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  添加直线导出段 (lead-out)                                       │
│  - 连接到原始线段的终点                                          │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
              返回 (new_segments, bump_count)

3.5 碰撞检测与安全振幅计算 get_safe_amplitude_at_point()

复制代码
┌─────────────────────────────────────────────────────────────────┐
│         get_safe_amplitude_at_point (二分搜索法)                │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  计算 clearance 需求:                                            │
│  - meander_clearance_margin = grid_step / 2                     │
│  - corner_margin = track_width / 2 * CORNER_BLOAT_FACTOR        │
│  - required_clearance = track_width + clearance + margins       │
│  - via_clearance = via_size/2 + track_width/2 + clearance       │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  准备测试振幅列表 (二分搜索):                                    │
│  test_amplitudes = [max_amplitude, 0.7*max, 0.49*max, ...,     │
│                     min_amplitude]                              │
│  (每次乘以0.7,直到达到min_amplitude)                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  for test_amp in test_amplitudes:                               │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 1. 生成当前振幅的bump线段                                  │ │
│    │    bump_segs = get_bump_segments(...)                    │ │
│    └──────────────────────────────────────────────────────────┘ │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 2. 计算bump的边界框                                        │ │
│    └──────────────────────────────────────────────────────────┘ │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 3. 使用空间索引查询附近对象:                              │ │
│    │    - nearby_segments = clearance_index.query_segments()  │ │
│    │    - nearby_vias = clearance_index.query_vias()          │ │
│    │    - nearby_pads = clearance_index.query_pads()          │ │
│    └──────────────────────────────────────────────────────────┘ │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 4. 逐个检查碰撞:                                          │ │
│    │    for bump_seg in bump_segs:                            │ │
│    │      for other in nearby_objects:                        │ │
│    │        if same_layer and not_same_net:                   │ │
│    │          dist = segment_to_segment_distance(...)         │ │
│    │          if dist < required_clearance:                   │ │
│    │            conflict_found = True; break                  │ │
│    └──────────────────────────────────────────────────────────┘ │
│    ┌──────────────────────────────────────────────────────────┐ │
│    │ 5. 如果无冲突,返回当前振幅                                │ │
│    │    if not conflict_found:                                │ │
│    │      return test_amp                                     │ │
│    └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
              返回 0 (所有振幅都有冲突)

4. 关键数据结构

4.1 ClearanceIndex 空间索引

python 复制代码
class ClearanceIndex:
    """
    空间索引,用于高效的碰撞检测。

    将PCB板划分为网格单元,每个单元格存储与之重叠的
    线段/过孔/焊盘引用。这允许高效查询特定区域内的项目,
    而无需检查所有项目。
    """
    cell_size: float = 2.0  # 网格单元大小 (mm)

    # 单元格 -> (线段, 层) 列表
    segment_cells: Dict[Tuple[int, int], List]

    # 单元格 -> 过孔列表
    via_cells: Dict[Tuple[int, int], List]

    # 单元格 -> (焊盘, 扩展层) 列表
    pad_cells: Dict[Tuple[int, int], List]

工作原理:

  1. 构建阶段: 将每个对象映射到其覆盖的所有单元格
  2. 查询阶段: 只检查查询区域所覆盖的单元格中的对象
  3. 优势: 将碰撞检测复杂度从 O(N) 降低到 O(K),其中 K 是区域内对象数量

4.2 蛇形Bump几何结构

单个bump由以下线段组成:

复制代码
          P点 (centerline + t*chamfer方向)
          │
          │  riser_height
          ▼
    ╔═════╝                        ← 顶部倒角2 (返回)
    ║
    ╚═════╗                        ← 顶部倒角1 (远离)
          │
          │  riser_height
          ▼
    C点 (centerline)
          │
    ╔═════╝                        ← 进入倒角 (仅第一个bump)
          │
    ─────S点───────────             ← 中心线

参数说明:

  • chamfer = 0.1mm: 45度倒角边长
  • riser_height = amplitude - 2*chamfer: 垂直立柱高度
  • bump_width = 4*chamfer: 单个bump的水平占位

5. 算法原理

5.1 Trombone蛇形原理

Trombone蛇形通过在走线路径中添加垂直于走线方向的往返运动来延长走线。

长度增加计算:

复制代码
原始直线长度: L = bump_width

单个bump长度:
- 有进入倒角: 3*chamfer*√2 + 2*riser_height
- 无进入倒角: 2*chamfer*√2 + 2*riser_height

额外长度 = bump长度 - bump_width
        ≈ 2*amplitude (当 amplitude >> chamfer)

5.2 碰撞检测原理

算法使用线段到线段距离进行精确的碰撞检测:

复制代码
distance(S1, S2) =
    if S1, S2 平行:
        端点到直线的最小距离
    else:
        |(P2 - P1) × (P2 - P3)| / |P2 - P1|

** Clearance 考虑因素:**

  1. 基础 clearance : track_width + clearance
  2. 拐角补偿 : track_width/2 * (√2 - 1) --- 45度拐角处铜皮会延伸
  3. 网格对齐容差 : grid_step / 2 --- 网格合并时可能的坐标偏移
  4. 差分对特殊处理: 对内匹配使用较小的 clearance

5.3 振幅缩放原理

当初始蛇形生成后,如果实际增加的长度与目标有偏差,算法通过缩放振幅进行精确调整:

复制代码
scale = target_delta / actual_added
new_amplitude = base_amplitude * scale

迭代上限:

  • 最多5次缩放迭代
  • 最小振幅限制: 0.2mm
  • 如果缩放后无改善则中断

6. 配置参数

参数 默认值 说明
meander_amplitude 2.0mm 蛇形最大振幅
track_width 0.2mm 走线宽度
clearance 0.15mm 最小间距
via_size 0.6mm 过孔外径
grid_step 0.05mm 网格步长
diff_chamfer_extra 2.0 差分对倒角额外倍数

7. 边界情况处理

7.1 无足够空间放置蛇形

  • 返回原始线段
  • 记录警告信息
  • bump_count = 0

7.2 部分空间受限

  • 逐点检测安全振幅
  • 动态调整每个bump的振幅
  • 必要时跳过某些位置

7.3 差分对内匹配

  • 使用中心线蛇形化
  • P/N走线由中心线偏移生成
  • 中心线与P/N使用最小 clearance

7.4 同方向bump处理

  • 添加退出/进入倒角返回中心线
  • 如果退出方向受阻,使用平面段替代

8. 性能考虑

8.1 时间复杂度

  • 空间索引构建: O(N),N为对象数量
  • 单个bump碰撞检测: O(K),K为附近对象数量
  • 完整蛇形生成: O(B*K),B为bump数量
  • 总复杂度 : O(N + GBK),G为网络组数量

8.2 优化策略

  1. 空间索引: 大幅减少碰撞检测的检查对象数量
  2. 层级检查: 先检查层,再检查网络,最后计算距离
  3. 提前退出: 发现冲突立即中断当前振幅测试
  4. 二分搜索: 快速找到最大安全振幅

9. 示例

9.1 单端网络等长匹配

python 复制代码
# 输入: 3个网络,目标长度 20mm
net_names = ['NET_A', 'NET_B', 'NET_C']
current_lengths = {'NET_A': 18.5mm, 'NET_B': 17.2mm, 'NET_C': 16.0mm}

# 处理后:
# NET_A: 添加 1.5mm (2-3个bump)
# NET_B: 添加 2.8mm (4-5个bump)
# NET_C: 添加 4.0mm (6-7个bump)

9.2 差分对等长匹配

python 复制代码
# 输入: DDR4 DQ 差分对组
diff_pairs = ['DQ0_P', 'DQ0_N', 'DQ1_P', 'DQ1_N', ...]

# 算法自动:
# 1. 计算每对的中心线长度
# 2. 在中心线上添加蛇形
# 3. 从蛇形中心线重新生成P/N走线
# 4. 确保所有差分对等长

10. 总结

Length Matching 算法通过以下关键技术实现高效的等长匹配:

  1. 智能位置选择: 自动寻找最长的直线段进行蛇形化
  2. 精确碰撞检测: 空间索引 + 二分搜索确保不违反设计规则
  3. 自适应振幅: 动态调整每个bump的振幅以适应局部约束
  4. 精确长度匹配: 迭代添加 + 振幅缩放确保满足精度要求
  5. 差分对支持: 中心线蛇形化自动处理差分对约束

该算法已成功应用于DDR4、USB、PCIe等多种高速接口的等长匹配。

相关推荐
y = xⁿ2 小时前
【LeetCodehot100】T114:二叉树展开为链表 T105:从前序与中序遍历构造二叉树
java·算法·链表
灰色小旋风2 小时前
力扣20有效的括号(C++)
c++·算法·leetcode·职场和发展
逆境不可逃2 小时前
LeetCode 热题 100 之 160. 相交链表 206. 反转链表 234. 回文链表 141. 环形链表 142. 环形链表 II
算法·leetcode·链表
weiabc2 小时前
今日C/C++学习笔记20260223
c语言·c++·学习
CoovallyAIHub2 小时前
AAAI 2026 | 华中科大联合清华等提出Anomagic:跨模态提示零样本异常生成+万级AnomVerse数据集(附代码)
深度学习·算法·计算机视觉
我的xiaodoujiao2 小时前
3、API 接口自动化测试详细图文教程学习系列3--相关Python基础知识2
python·学习·测试工具·pytest
~光~~2 小时前
【嵌入式linux学习】0_3位运算整理
linux·学习
npupengsir2 小时前
nano vllm代码详解
人工智能·算法·vllm
m0_569881472 小时前
C++中的组合模式高级应用
开发语言·c++·算法