BGA Fanout 算法

1. 算法概述

1.1 目的

BGA Fanout 算法用于为BGA(球栅阵列)封装的芯片自动创建扇出走线,将内部焊盘的信号通过布线通道引导到BGA边界外,为后续的完整布线做准备。

1.2 核心思想

  • 45度扇出: 从每个焊盘以45度角引出短线(stub)连接到布线通道
  • 通道布线: 沿水平或垂直通道将信号引导到BGA边界
  • 层分配: 智能分配不同层以避免同一通道内的走线冲突
  • 差分对支持: 自动识别并成对路由差分信号

2. 算法输入与输出

2.1 输入参数

参数类型说明
footprintFootprintBGA封装的焊盘信息
pcb_dataPCBData完整PCB数据(包含现有走线)
net_filterList[str]可选:要包含的网络模式过滤列表
diff_pair_patternsList[str]可选:差分对网络匹配模式(如 *lvds*)
layersList[str]可用布线层列表(默认 ['F.Cu', 'B.Cu'])
track_widthfloat走线宽度(mm,默认0.1)
clearancefloat最小间距(mm,默认0.1)
diff_pair_gapfloat差分对P/N间距(mm,默认0.101)
exit_marginfloatBGA边界外的延伸距离(mm,默认0.5)
primary_escapestr主要逃逸方向(‘horizontal’或’vertical’)
via_sizefloat过孔外径(mm,默认0.5)
via_drillfloat过孔钻孔直径(mm,默认0.3)
check_for_previousbool是否检查并跳过已有扇出的焊盘
no_inner_top_layerbool是否禁止内部焊盘使用顶层(F.Cu)

2.2 输出结果

返回值类型说明
tracksList[Dict]扇出走线段列表,每个包含:
- start: 起点坐标 (x, y)
- end: 终点坐标 (x, y)
- width: 走线宽度
- layer: 层名
- net_id: 网络ID
vias_to_addList[Dict]需要添加的过孔列表
vias_to_removeList[Dict]需要移除的过孔列表

3. 算法流程

3.1 主流程 generate_bga_fanout()

┌─────────────────────────────────────────────────────────────────┐
│                    generate_bga_fanout                          │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 1: 分析BGA网格结构                                         │
│  - 分析焊盘位置提取网格参数                                     │
│  - 计算 X/Y 方向的间距 (pitch_x, pitch_y)                       │
│  - 识别行和列的位置                                             │
│  - 计算边界和中心点                                             │
│  - 计算布线通道(位于行/列之间)                                │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 2: 检查已有扇出(可选)                                   │
│  if check_for_previous:                                        │
│    - 扫描现有走线识别已扇出的网络                               │
│    - 记录被占用的出口位置                                       │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 3: 识别差分对                                             │
│  if diff_pair_patterns:                                        │
│    - 根据网络名模式匹配差分对                                   │
│    - 验证P/N焊盘位置关系(相邻)                                │
│    - 预分配逃逸方向以避免重叠                                   │
│    - 计算是否需要相邻通道布线                                   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 4: 为每个焊盘创建路由                                     │
│  for pad in footprint.pads:                                    │
│    - 跳过未连接的焊盘                                           │
│    - 跳过已扇出的焊盘(如果启用)                               │
│    - 判断是否为差分对的一部分                                   │
│      ┌─────────────┐                                           │
│      │是差分对?   │                                           │
│      └─────────────┘                                           │
│        │         │                                            │
│       是         否                                            │
│        │         │                                            │
│        ▼         ▼                                            │
│   处理差分对   create_single_ended_route()                     │
│   (P/N一起)   (单端信号路由)                                    │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 5: 连接相邻同网络焊盘                                     │
│  - 检测同网络且相邻的焊盘                                       │
│  - 直接连接它们而不单独扇出到边界                               │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 6: 通道上焊盘重分配                                       │
│  - 检测位于通道上的焊盘(零长度stub)                           │
│  - 如果直线路径被其他焊盘阻挡                                   │
│    → 重新分配到相邻通道并添加45度绕行                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 7: 智能层分配                                             │
│  assign_layers_smart():                                        │
│    - 边缘焊盘 → 顶层 (F.Cu)                                     │
│    - 差分对 → 同一层(避免层间不匹配)                          │
│    - 内部焊盘 → 按通道+方向分组                                 │
│    - 避免同通道内的走线重叠                                     │
│    - 检查与现有走线的冲突                                       │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 8: 计算出口折角(Jog)                                    │
│  calculate_jog_ends_for_routes():                              │
│    - 计算BGA边缘到首排焊盘的距离                                │
│    - 为每条路由计算出口处的45度折角                             │
│    - 差分对外侧走线延伸以保持恒定间距                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 9: 生成走线段                                             │
│  generate_tracks_from_routes():                                │
│    - 将路由对象转换为走线段字典                                 │
│    - 边缘焊盘:直接水平/垂直走线                                │
│    - 内部焊盘:45度stub + 通道走线 + 折角                        │
│    - 差分对:特殊处理以保持恒定间距                             │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 10: 碰撞检测与解决                                        │
│  detect_collisions():                                          │
│    - 检测新走线之间的冲突                                       │
│    - 检测与现有走线的冲突                                       │
│  if 发现冲突:                                                   │
│    resolve_collisions():                                       │
│      - 尝试重新分配层                                           │
│      - 尝试使用备用通道                                         │
│      - 失败的网络从输出中移除                                   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 11: 层重平衡(可选)                                      │
│  if collisions_remaining == 0 and len(layers) > 1:            │
│    rebalance_layers():                                         │
│      - 在各层之间均匀分配走线                                   │
│      - 改善层利用率                                             │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 12: 过孔管理                                              │
│  manage_vias():                                                │
│    - 为非顶层走线的SMD焊盘添加过孔                              │
│    - 移除顶层走线上不必要的过孔                                 │
│    - 通孔焊盘不需要过孔(已连接所有层)                         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    返回 (tracks, vias_to_add, vias_to_remove)

3.2 BGA网格分析 analyze_bga_grid()

┌─────────────────────────────────────────────────────────────────┐
│                  analyze_bga_grid                               │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  提取焊盘坐标                                                   │
│  x_positions = sorted(set(pad.global_x for pad in pads))       │
│  y_positions = sorted(set(pad.global_y for pad in pads))       │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  计算间距                                                       │
│  x_diffs = [x_positions[i+1] - x_positions[i] for ...]         │
│  y_diffs = [y_positions[i+1] - y_positions[i] for ...]         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  确定主导间距                                                   │
│  - 四舍五入到0.01mm精度                                        │
│  - 统计每个间距值的出现次数                                     │
│  - 选择出现次数 >50% 的间距值作为主导间距                       │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  返回 BGAGrid 对象:                                             │
│  - pitch_x, pitch_y: X/Y方向间距                               │
│  - rows: Y坐标列表(行)                                        │
│  - cols: X坐标列表(列)                                        │
│  - center_x, center_y: BGA中心                                 │
│  - min/max_x/y: 边界(扩展±pitch/2)                           │
└─────────────────────────────────────────────────────────────────┘

3.3 通道计算 calculate_channels()

┌─────────────────────────────────────────────────────────────────┐
│                  calculate_channels                             │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  创建水平通道(行之间)                                          │
│  for i in range(len(rows) - 1):                                │
│    y_pos = (rows[i] + rows[i+1]) / 2                           │
│    channels.append(Channel(orientation='horizontal',           │
│                             position=y_pos, index=idx))         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  创建垂直通道(列之间)                                          │
│  for i in range(len(cols) - 1):                                │
│    x_pos = (cols[i] + cols[i+1]) / 2                           │
│    channels.append(Channel(orientation='vertical',             │
│                             position=x_pos, index=idx))         │
└─────────────────────────────────────────────────────────────────┘

通道示意图:

焊盘布局:
  .   .   .   .   (行)
  ●   ●   │   ●   ●
        ────┼──────   ← 水平通道 (在行之间)
  ●   ●   │   ●   ●
        ────┼──────
  ●   ●   │   ●   ●

  垂直通道 ↓ ↓ ↓    (在列之间)

3.4 焊盘分类 is_edge_pad()

┌─────────────────────────────────────────────────────────────────┐
│                   is_edge_pad                                   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  检查焊盘是否在边界行/列                                         │
│  on_left   = |pad_x - grid.cols[0]|   < tolerance             │
│  on_right  = |pad_x - grid.cols[-1]|  < tolerance             │
│  on_top    = |pad_y - grid.rows[0]|   < tolerance             │
│  on_bottom = |pad_y - grid.rows[-1]|  < tolerance             │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  返回 (is_edge, escape_direction)                               │
│  - 边缘焊盘:返回True和逃逸方向                                 │
│  - 内部焊盘:返回False和空字符串                                │
└─────────────────────────────────────────────────────────────────┘

3.5 逃逸通道查找 find_escape_channel()

┌─────────────────────────────────────────────────────────────────┐
│              find_escape_channel (单端信号)                      │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │  是边缘焊盘?   │
                    └─────────────────┘
                      │           │
                     是           否
                      │           │
                      ▼           ▼
            返回 (None,      计算到各边缘的距离
             edge_dir)     dist_left/right/up/down
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  构建选项列表 (距离, 方向, 方向性)                               │
│  options = [                                                    │
│    (dist_left, 'left', 'horizontal'),                          │
│    (dist_right, 'right', 'horizontal'),                        │
│    (dist_up, 'up', 'vertical'),                                │
│    (dist_down, 'down', 'vertical')                             │
│  ]                                                              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  按距离排序(最近的边优先)                                      │
│  options.sort(key=lambda x: x[0])                              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  遍历选项,找到可用通道                                          │
│  for dist, escape_dir, orientation in options:                 │
│    if orientation == 'horizontal':                             │
│      best = closest_horizontal_channel_to(pad_y)               │
│    else:                                                        │
│      best = closest_vertical_channel_to(pad_x)                 │
│    if best found:                                               │
│      return (best, escape_dir)                                 │
└─────────────────────────────────────────────────────────────────┘

3.6 45度Stub生成 create_45_stub()

┌─────────────────────────────────────────────────────────────────┐
│                create_45_stub                                   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │ 水平通道?      │
                    └─────────────────┘
                      │           │
                     是           否
                      │           │
                      ▼           ▼
┌──────────────────────────┐  ┌──────────────────────────┐
│ 目标Y = channel.position │  │ 目标X = channel.position │
│      + offset            │  │      + offset            │
│                         │  │                         │
│ dy = target_y - pad_y   │  │ dx = target_x - pad_x   │
│                         │  │                         │
│ 保持45度角: dx = |dy|    │  │ 保持45度角: dy = |dx|    │
│                         │  │                         │
│ 根据逃逸方向确定dx符号:  │  │ 根据逃逸方向确定dy符号:  │
│ right: dx = +|dy|        │  │ down:  dy = +|dx|       │
│ left:  dx = -|dy|        │  │ up:    dy = -|dx|       │
└──────────────────────────┘  └──────────────────────────┘
                      │           │
                      └─────┬─────┘
                            │
                            ▼
              返回 (pad_x + dx, pad_y + dy)
              或 (target_x, pad_y + dy)

45度Stub示例:

水平通道情况:
                    ┌─────────────────────────────────
                    │  通道中心线 (channel.position)
                    └─────────────────────────────────
                         ↑
                         │ dy_to_target
                         │
    ●─────────────────────┘ 45度角
  pad
    └─→ dx = |dy| (向右逃逸)

3.7 差分对处理

3.7.1 差分对识别
┌─────────────────────────────────────────────────────────────────┐
│            find_differential_pairs()                            │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  解析差分对模式                                                 │
│  示例模式: "*_P", "*_N" 或 "*_LVDS_P", "*_LVDS_N"             │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  匹配P/N网络对                                                  │
│  for net_name in footprint网络:                                │
│    if 匹配P模式:                                                │
│      查找对应的N网络                                            │
│      if找到:                                                    │
│        验证焊盘位置(应该相邻)                                 │
│        创建 DiffPairPads 记录                                   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  返回差分对字典:                                                 │
│  {pair_id: DiffPairPads(p_pad, n_pad, pair_id)}                │
└─────────────────────────────────────────────────────────────────┘
3.7.2 差分对逃逸分配
┌─────────────────────────────────────────────────────────────────┐
│              assign_pair_escapes()                              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  按到最近边缘的距离排序(最近的优先处理)                        │
│  - 最近的边缘对选择最少                                          │
│  - 需要先分配以避免被占                                         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  对每对差分对:                                                   │
│  ┌──────────────────────────────────────────────────────────┐ │
│  │ 1. 尝试主要方向 (primary_orientation)                     │ │
│  │ 2. 检查通道和层是否可用                                   │ │
│  │ 3. 如果被占用,尝试次要方向                               │ │
│  │ 4. 标记出口位置为已占用                                   │ │
│  └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  特殊处理:                                                       │
│  - 边缘对:直接使用边缘方向                                     │
│  - 半边缘对:一边直出,一边绕行                                 │
│  - 内部对:通过通道逃逸                                         │
└─────────────────────────────────────────────────────────────────┘
3.7.3 相邻通道判断
┌─────────────────────────────────────────────────────────────────┐
│        判断是否需要相邻通道布线                                  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  计算差分对参数:                                                 │
│  half_pair_spacing = (track_width + diff_pair_gap) / 2         │
│  via_radius = via_size / 2                                      │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  计算最大允许偏移:                                               │
│  水平逃逸(左/右)→ 受pitch_y约束                               │
│  max_offset_h = pitch_y/2 - via_radius - width/2 - clearance   │
│                                                                  │
│  垂直逃逸(上/下)→ 受pitch_x约束                               │
│  max_offset_v = pitch_x/2 - via_radius - width/2 - clearance   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  判断:                                                           │
│  use_adjacent_channels_h = (half_pair_spacing > max_offset_h)  │
│  use_adjacent_channels_v = (half_pair_spacing > max_offset_v)  │
└─────────────────────────────────────────────────────────────────┘

相邻通道布线示意:

同一通道内布线(间距足够):
  ←─spacing──→
  ───P─────────channel─────────
  ───N─────────channel─────────
  (P和N在通道内偏移)

相邻通道布线(间距不足):
  ←pitch→
  ●  ●  via  via
  │  │
  ──┼──channel 1──P────────
  │  │
  ──┼──channel 2──N────────
  (P和N各占一个通道,都居中)

3.8 智能层分配 assign_layers_smart()

┌─────────────────────────────────────────────────────────────────┐
│              assign_layers_smart()                              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  初始化:                                                         │
│  - 边缘焊盘 → F.Cu(顶层)                                       │
│  - 半边缘差分对 → 全部F.Cu                                       │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  分组内部焊盘:                                                   │
│  按 (channel_index, escape_dir) 分组                            │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  对每组:                                                         │
│  ┌──────────────────────────────────────────────────────────┐ │
│  │ 1. 按通道上的位置排序(从内到外或从外到内)                │ │
│  │ 2. 贪心层分配:                                            │ │
│  │    for route in group_routes:                            │ │
│  │      if 差分对已分配 → 使用相同层                         │ │
│  │      for layer in candidate_layers:                      │ │
│  │        if 与本组已分配走线无冲突:                         │ │
│  │          if 与边缘走线无交叉冲突:                         │ │
│  │            if 与现有走线无冲突:                           │ │
│  │              route.layer = layer                          │ │
│  │              if 差分对: 分配partner到同层                 │ │
│  │              break                                        │ │
│  └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

3.9 走线生成 generate_tracks_from_routes()

┌─────────────────────────────────────────────────────────────────┐
│         generate_tracks_from_routes()                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │ 邻居连接?      │
                    └─────────────────┘
                      │           │
                     是           否
                      │           │
                      ▼           ▼
        ┌────────────────┐  ┌─────────────────────┐
        │直接连接段      │  │    边缘焊盘?        │
        │pad→exit        │  |                     │
        └────────────────┘  └─────────────────────┘
                                   │           │
                                  是           否
                                   │           │
                                   ▼           ▼
                        ┌──────────────┐  ┌──────────────────┐
                        │直接/收敛     │  │45°stub + 通道   │
                        │+ jog         │  │+ jog             │
                        └──────────────┘  └──────────────────┘
边缘焊盘走线生成:
单端边缘:
  pad ───────────────────→ exit
                              │
                              └─→ jog_end (45度折角)

差分边缘(收敛):
  pad_P ──→ stub_end ────────→ exit ──→ jog_end
        45°                   │
                              └─→ jog_end (外侧延伸)
  pad_N ──→ stub_end ────────→ exit ──→ jog_end
        45°
内部焊盘走线生成:
正常内部:
  pad ───→ stub_end ──────────→ exit ──→ jog_end
       45°    (通道走线)

半边缘内部(帐篷形):
  pad ───→ channel_pt ───→ channel_pt2 ──→ stub_end ──→ exit ──→ jog_end
       45°      (通道内)              (返回)

通道上焊盘(重分配后):
  pad
   │
   └─→ pre_channel_jog (45度绕行)
       │
       └─→ exit ──→ jog_end

3.10 碰撞检测 detect_collisions()

┌─────────────────────────────────────────────────────────────────┐
│              detect_collisions()                                │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  初始化:                                                         │
│  collision_count = 0                                            │
│  collision_pairs = []                                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  双重循环检查:                                                   │
│  for i, t1 in enumerate(tracks):                               │
│    for t2 in tracks[i+1:]:                                     │
│      ┌────────────────────────────────────────────────────────┐│
│      │ if 不同层: 跳过                                         ││
│      │ if 相同网络: 跳过                                       ││
│      │ if 同一差分对: 跳过                                     ││
│      │ if check_segment_collision():                          ││
│      │   collision_count += 1                                 ││
│      │   if len < max: collision_pairs.append((t1, t2))      ││
│      └────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  同样检查现有走线:                                               │
│  for t1 in tracks:                                             │
│    for t2 in existing_tracks:                                  │
│      ... (相同检查逻辑)                                         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
              返回 (collision_count, collision_pairs)

4. 关键数据结构

4.1 BGAGrid

@dataclass
class BGAGrid:
    pitch_x: float        # X方向间距
    pitch_y: float        # Y方向间距
    rows: List[float]     # 行的Y坐标列表
    cols: List[float]     # 列的X坐标列表
    center_x: float       # 中心X坐标
    center_y: float       # 中心Y坐标
    min_x: float          # 边界最小X
    max_x: float          # 边界最大X
    min_y: float          # 边界最小Y
    max_y: float          # 边界最大Y

4.2 Channel

@dataclass
class Channel:
    orientation: str      # 'horizontal' 或 'vertical'
    position: float       # 通道位置坐标(Y或X)
    index: int            # 通道索引

4.3 FanoutRoute

@dataclass
class FanoutRoute:
    pad: Pad              # 焊盘对象
    pad_pos: Tuple[float, float]  # 焊盘位置
    stub_end: Tuple[float, float] # 45度stub终点
    exit_pos: Tuple[float, float] # BGA边界出口位置
    channel: Channel      # 使用的通道(None表示边缘)
    escape_dir: str       # 逃逸方向
    is_edge: bool         # 是否为边缘焊盘
    layer: str            # 分配的层
    pair_id: str          # 差分对ID(None表示单端)
    is_p: bool            # 是否为P信号(差分对)
    jog_end: Tuple        # 折角终点
    jog_extension: Tuple  # 差分对外侧延伸点
    channel_point: Tuple  # 半边缘绕行的第一个通道点
    channel_point2: Tuple # 半边缘绕行的第二个通道点
    pre_channel_jog: Tuple # 通道上焊盘的重分配绕行点
    neighbor_connection: bool # 是否为邻居连接

4.4 DiffPairPads

@dataclass
class DiffPairPads:
    p_pad: Pad            # 正相信号焊盘
    n_pad: Pad            # 反相信号焊盘
    pair_id: str          # 差分对唯一标识
    is_complete: bool     # 是否完整(P和N都存在)

5. 算法原理

5.1 45度扇出原理

为什么使用45度?

  1. 电磁兼容性: 45度拐角比90度拐角反射更小
  2. 阻抗连续: 45度拐角能更好地保持特性阻抗
  3. 制造友好: 45度拐角比圆角更容易制造
  4. 空间效率: 在紧凑的BGA间距内最大化走线空间

45度Stub的几何关系:

真45度角的特性:|dx| = |dy|

焊盘 (pad_x, pad_y)
         │
         │ dy
         │
         └──────────→ 通道
              dx

其中: |dx| = |dy| (保证45度)

5.2 通道布线原理

通道的作用:

  • 在BGA焊盘阵列之间提供可布线空间
  • 避免与焊盘和过孔冲突
  • 通道位于行/列的正中间

通道选择策略:

对于内部焊盘:
  1. 计算到四个边缘的距离
  2. 选择最近的边缘方向
  3. 在该方向上找到最近的通道
  4. 45度stub连接到该通道

5.3 层分配策略

层分配原则:

  1. 边缘焊盘优先: 边缘焊盘信号最短,放在顶层
  2. 差分对同层: P/N必须在同一层以保持时序匹配
  3. 避免冲突: 同通道内使用不同层避免重叠
  4. 均匀分配: 各层走线数量尽量均衡

层分配算法:

贪心策略:
  1. 按通道+方向分组
  2. 每组内按位置排序
  3. 依次为每条走线分配可用层
  4. 检查与已分配走线的冲突
  5. 选择第一个无冲突的层

5.4 差分对特殊处理

差分对要求:

  1. 等长: P和N走线长度相等
  2. 等间距: 全程保持恒定间距
  3. 同层: 必须在同一层布线
  4. 最小反射: 拐角处外侧走线需延伸

外侧延伸计算:

45度拐角处外侧延伸 = pair_spacing × (√2 - 1)
                    ≈ pair_spacing × 0.414

这样可保持拐角前后的垂直间距不变

示例:

拐角前:  ────P     走线平行,间距 = d
             │
             N

拐角处:  ──┐P
             └──┐
               N
         外侧延伸 = 0.414×d

拐角后:      │P
             │     走线平行,间距 = d
             N───

5.5 碰撞检测原理

线段碰撞检测:

两线段 (p1, p2) 和 (p3, p4) 的碰撞判断:

1. 包围盒检查(快速排除):
   if max(min(x)) < min(max(x)) and max(min(y)) < min(max(y)):
     可能碰撞,继续详细检查
   else:
     无碰撞

2. 距离检查(精确判断):
   计算两线段之间的最小距离
   if distance < min_spacing:
     碰撞
   else:
     无碰撞

6. 配置参数

参数默认值说明
track_width0.1mm扇出走线宽度
clearance0.1mm最小间距
diff_pair_gap0.101mm差分对间距
via_size0.5mm过孔外径
via_drill0.3mm过孔钻孔
exit_margin0.5mmBGA边界外延伸
primary_escape‘horizontal’首选逃逸方向
force_escape_directionFalse强制方向
rebalance_escapeFalse重平衡逃逸分配

7. 边界情况处理

7.1 小间距BGA

  • 使用相邻通道布线
  • 每条差分线各占一个通道
  • 避免间距不足导致的冲突

7.2 已有扇出

  • 检测现有走线
  • 跳过已扇出的网络
  • 避免占用的出口位置

7.3 边缘焊盘冲突

  • 垂直逃逸可能水平边缘走线冲突
  • 优先分配不同层
  • 检测交叉点并调整

7.4 通孔焊盘

  • 通孔焊盘已连接所有层
  • 不需要添加过孔
  • 仍然阻挡所有层的走线

8. 性能考虑

8.1 时间复杂度

  • 网格分析: O(N),N为焊盘数量
  • 差分对匹配: O(N²),N为焊盘数量
  • 逃逸分配: O(M×K),M为差分对数,K为通道数
  • 层分配: O(R²×L),R为路由数,L为层数
  • 碰撞检测: O(T²),T为走线段数量
  • 总复杂度: O(N² + T²)

8.2 优化策略

  1. 早期过滤: 跳过未连接焊盘
  2. 分组处理: 按通道分组减少冲突检查
  3. 增量检测: 只检查相关层的碰撞
  4. 空间索引: 可扩展为使用四叉树加速

9. 示例

9.1 单端信号扇出

输入: BGA封装,10x10焊盘,间距1.0mm

输出:
- 边缘焊盘: 36个直接水平/垂直引出
- 内部焊盘: 64个通过45度stub + 通道引出
- 总走线段: 约200段(包括折角)

9.2 差分对扇出

输入: DDR4 DQ信号,8对差分

输出:
- 识别: 16个焊盘组成8对
- 分配: 优先水平逃逸
- 布线: P/N成对,同层等间距
- 特殊: 外侧走线延伸保持间距

10. 总结

BGA Fanout 算法通过以下关键技术实现高效的扇出走线:

  1. 智能网格分析: 自动识别BGA结构和布线通道
  2. 45度扇出: 符合信号完整性要求的标准扇出方式
  3. 通道布线: 利用焊盘间隙进行高效布线
  4. 智能层分配: 避免冲突的同时优化层利用率
  5. 差分对支持: 自动识别并正确处理差分信号
  6. 碰撞检测: 确保生成走线符合设计规则
  7. 增量处理: 支持多次运行和已有扇出检测

该算法已成功应用于多种BGA封装的扇出布线,包括:

  • DDR3/DDR4内存
  • 高速接口(USB, PCIe, Ethernet)
  • 处理器BGA封装
  • FPGA I/O扇出
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐