R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-10-04 19:24:45 +08:00
共同撰写人 Claude Opus 5.5
父节点 cd01640c40
当前提交 cf7cc9cf90
共修改 759 个文件,包含 76962 行新增和 5299 行删除
+128
查看文件
@@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
u"""服务器上一次做完「剩 3 只被围死的脚」(2026-09-11 混合布线收尾,用户选「拆邻线重排」):
DCDC_ROUTE_SERVER=1 /root/pypy/bin/pypy3 .claude/r2/fix3.py [--fence-y1 35.0]
读 _board_for_nets.json(板上快照)+ _base_preroute_0911.json(UP 两端预布段 / 孔),写 _fix3.out.json
1. UP 中心线重布(up_route.route_centerline,pitch 0.52),L1 上 J37 北排上方加禁区 (48, 33.0)~(62, FENCE_Y1):
UP 在这一段北移,给 J37.19 的 +5V 留出沿北排焊盘顶上走到 J37.32 的带子。障碍 = 板上除 UP 以外的全部铜。
2. 板面数据里的 UP 换成新 UP(+ 预布两端段 / 孔)。
3. 拆 U32 南 / 西南区的 strap 网(全是自动布线的短线)。
4. 按序补:+5V(J37.19)→ +3V3_HUB(C744.1 那块)→ CFGSEL0 → 拆掉的 strap 网(route_nets 的连通块 + 代表焊盘 + route_one)。
输出:新 UP、各网新增线 / 孔、被拆的网、每个网布前 / 布后的连通块数。本机拿去离线复核后再删旧加新。
"""
import io
import json
import math
import os
import sys
if os.environ.get('DCDC_ROUTE_SERVER') != '1':
sys.exit(u'!! 布线运算只在服务器上跑(DCDC_ROUTE_SERVER=1),见 CLAUDE.md')
if '--grid' not in sys.argv:
sys.argv += ['--grid', '0.125']
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.dirname(HERE))
sys.path.insert(0, HERE)
import route_base as R # noqa: E402
import up_route as UPR # noqa: E402
import route_nets as RN # noqa: E402
RT = R.RT
UP = ('UP_DP', 'UP_DM')
RIP = ['$4N596', '$4N597', '$4N598', '$4N602', '$4N603', 'CFGSEL1', 'NONREM0', 'NONREM1', 'VBUS_DET']
ORDER = [('+5V', 0.3), ('+3V3_HUB', 0.2), ('CFGSEL0', 0.15)] + [(n, 0.15) for n in RIP]
FENCE_Y1 = float(sys.argv[sys.argv.index('--fence-y1') + 1]) if '--fence-y1' in sys.argv else 35.0
L = lambda s: math.hypot(s[3] - s[1], s[4] - s[2]) # noqa: E731
def main():
board = json.load(io.open(os.path.join(HERE, '_board_for_nets.json'), encoding='utf-8'))
pre = json.load(io.open(os.path.join(HERE, '_base_preroute_0911.json'), encoding='utf-8'))
pads = R.load_pads()
logs = []
seg_all = [tuple(s[:7]) for s in board['segs']]
via_all = [tuple(v[:3]) for v in board['vias']]
if '--no-up' in sys.argv:
# UP 不动(整板障碍下中心线找不到通道,2026-09-11 两种禁区都失败):只拆 strap 网、按序补
# --rip A,B,... 只拆这些网(默认 RIP 全拆);--order A,B,... 补网顺序(默认 +3V3_HUB → CFGSEL0 → 拆掉的网)
rip = sys.argv[sys.argv.index('--rip') + 1].split(',') if '--rip' in sys.argv else list(RIP)
wid = dict(ORDER)
if '--order' in sys.argv:
order = [(n, wid.get(n, 0.15)) for n in sys.argv[sys.argv.index('--order') + 1].split(',')]
else:
order = [('+3V3_HUB', 0.2), ('CFGSEL0', 0.15)] + [(n, 0.15) for n in rip]
segs = [s for s in seg_all if s[6] not in rip]
vias = [v for v in via_all if v[2] not in rip]
print(u'拆:%s;顺序:%s' % (' '.join(rip), ' '.join(n for n, _w in order)))
out = {}
for net, w in order:
groups = RN.components(net, pads, segs, vias)
if len(groups) < 2:
print(u'%-9s 已连通,跳过' % net)
continue
lg = []
s, v = R.route_one(net, RN.reps(groups), w, pads, [x for x in segs if x[0] in (1, 2)], vias, [], lg)
segs += [tuple(x) for x in s]
vias += [tuple(x) for x in v]
after = len(RN.components(net, pads, segs, vias))
out[net] = {'segs': [list(x) for x in s], 'vias': [list(x) for x in v], 'before': len(groups), 'after': after}
print(u'%-9s %d 块 → %d 块;新增 %d 段 %.1f mm / %d 孔(线宽 %.2f)%s'
% (net, len(groups), after, len(s), sum(L(x) for x in s), len(v), w, (u';' + ';'.join(lg)) if lg else ''))
bad = [n for n, o in out.items() if o['after'] > 1]
print(u'仍断开:%s' % (' '.join(bad) or u'无'))
io.open(os.path.join(HERE, '_fix3.out.json'), 'w', encoding='utf-8').write(json.dumps(
{'ok': True, 'up_new': None, 'rip': rip, 'order': [n for n, _w in order], 'nets': out, 'bad': bad},
ensure_ascii=False))
return
# ── 1. UP 中心线重布(障碍 = 板上除 UP 以外的铜)
obs_s = [s for s in seg_all if s[0] in (1, 2) and s[6] not in UP]
obs_v = [v for v in via_all if v[2] not in UP]
UPR.PITCH = 0.52
UPR.EXTRA_FENCE = [(1, (48.0, 33.0, 62.0, FENCE_Y1))]
pad_pen, layer_dir = RT.PAD_PEN, RT.LAYER_DIR
RT.PAD_PEN, RT.LAYER_DIR = R.DP_PAD_PEN, {}
try:
up_new, _ = UPR.route_centerline(pads, obs_s, obs_v, pre, logs)
finally:
RT.PAD_PEN, RT.LAYER_DIR = pad_pen, layer_dir
if not up_new:
print(u'UP 中心线重布失败:%s' % logs)
io.open(os.path.join(HERE, '_fix3.out.json'), 'w', encoding='utf-8').write(json.dumps({'ok': False, 'logs': logs}))
return
worst, pair = UPR.check(up_new, obs_s, obs_v, pads)
lens = {n: sum(L(s) for s in up_new if s[6] == n) + sum(L(s) for s in pre['segs'] if s[6] == n) for n in UP}
print(u'UP 重布:DP %.1f / DM %.1f mm(差 %.2f),对异网最小 %.3f(%s),对内 %.3f;禁区 y 33.0~%.2f'
% (lens['UP_DP'], lens['UP_DM'], abs(lens['UP_DP'] - lens['UP_DM']), worst[0], worst[1], pair, FENCE_Y1))
# ── 2. 板面换成新 UP;3. 拆 strap 网
up_stub_s = [tuple(s[:7]) for s in pre['segs'] if s[6] in UP]
up_stub_v = [tuple(v[:3]) for v in pre['vias'] if v[2] in UP]
segs = [s for s in seg_all if s[6] not in UP and s[6] not in RIP] + up_stub_s + [tuple(s) for s in up_new]
vias = [v for v in via_all if v[2] not in UP and v[2] not in RIP] + up_stub_v
# ── 4. 按序补
out, before = {}, {}
for net, w in ORDER:
groups = RN.components(net, pads, segs, vias)
before[net] = len(groups)
if len(groups) < 2:
print(u'%-9s 已连通,跳过' % net)
continue
ps = RN.reps(groups)
lg = []
s, v = R.route_one(net, ps, w, pads, [x for x in segs if x[0] in (1, 2)], vias, [], lg)
segs += [tuple(x) for x in s]
vias += [tuple(x) for x in v]
after = len(RN.components(net, pads, segs, vias))
out[net] = {'segs': [list(x) for x in s], 'vias': [list(x) for x in v], 'before': len(groups), 'after': after}
print(u'%-9s %d 块 → %d 块;新增 %d 段 %.1f mm / %d 孔(线宽 %.2f)%s'
% (net, len(groups), after, len(s), sum(L(x) for x in s), len(v), w, (u';' + ';'.join(lg)) if lg else ''))
logs += lg
bad = [n for n, o in out.items() if o['after'] > 1]
print(u'仍断开:%s' % (' '.join(bad) or u'无'))
io.open(os.path.join(HERE, '_fix3.out.json'), 'w', encoding='utf-8').write(json.dumps(
{'ok': True, 'up_new': [list(s) for s in up_new], 'rip': RIP, 'nets': out, 'before': before, 'bad': bad,
'logs': logs, 'fence_y1': FENCE_Y1, 'up_worst': worst, 'up_pair': pair}, ensure_ascii=False))
if __name__ == '__main__':
main()