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 行删除
+153
查看文件
@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
u"""D16 南带补线(服务器跑):以板上快照的全部铜为障碍,只补新入口相关的网 + 新件的 GND 地孔。
bash .claude/r2/remote_d16.sh # 本机:抓板 → 上传 → 服务器跑本脚本 → 拉回 _d16_route.out.json
DCDC_ROUTE_SERVER=1 /root/pypy/bin/pypy3 .claude/r2/d16_route.py [NET=宽度 ...]
读 _board_for_nets.json(本机抓的板上快照:旧入口的铜已删),写 _d16_route.out.json:
{'segs', 'vias'(本次新增,mm), 'nets'(逐网 前/后连通块数), 'logs'}。本机复核净距(四层)后才下发。
顺序:① SW12 固定线(U37.16 ↔ L4.1,夹在两只 PGND 角脚之间,1.0 宽)→ ② 早段地孔(U36 散热盘孔阵等)
→ ③ 逐网补线(route_nets 那套:按板上铜分连通块、每块挑代表、route_one 连树)→ ④ 晚段地孔(其余新件 GND 脚)。
L1 的 CC 走廊 / VIN_FUSED 小岛由 route_base._build 按 pcb_base.PD_L1 开(其余 L1 西侧仍是 12V 大铜、挡线)。
"""
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_nets as N # noqa: E402 带「内层线挡孔」补丁,内部 import route_base
import place_base as PL # noqa: E402
R = N.R
RT = R.RT
# (网, 线宽)。功率网的载流靠 pour_base 的铜块,这里只保证连通、宽度落得进小焊盘(U37 VIN 脚 0.4、0402 0.5)
# ⚠ 第一轮(2026-09-12)VCC12 排在 FB/RT/EN 前面:SPSP(南侧)→ VCC(东北角)只能绕东侧,一根线把 FB/RT/EN 的出路全封了
# ⇒ SPSP 改走 U37 肚子底下的固定线(VCC_LINK),VCC12 放到 FB/RT/EN 后面,只剩「6 脚 → C13」一小段。
# VIN_RAW 0.5 落不进 C 座 VBUS 那 0.3 宽的小焊盘条(第一轮「J1dc.A4B9 出不来」)⇒ 0.3,载流靠铺铜。
# ⚠ 第二轮:SW12(C18.1 → SW)先布时绕进 U37 北侧,把 BST 的出路封了;VCC 6 脚被 FB 那根贴着东侧封死;
# C 座东侧 VBUS 连着两轮「出不来」、西侧那根又压 J1 的 NPTH ⇒ 这三处也改成固定线(SW_C18 / VCC_C13 / VIN_RAW_FIX),BST 提到 SW 前
# ⚠ 第三轮:BST 排最前时 A* 贴着 U37 北沿往西拐,从 C19 和 VIN2 脚中间穿过 ⇒ 北侧 VIN 连 C19 都接不上;
# R10 采样 / NTC2 两轮之间一个通一个断(看先后运气)⇒ BST、北侧 VIN、R10 采样也改固定线(BST_FIX / VIN_N_FIX / R10_SENSE),
# NTC2 提到 CC 后面(它要从 U37 东边穿槽下走廊去东边的老线,地盘越早越空)
ORDER = [('PD_CC2', 0.2), ('PD_CC1', 0.2), ('NTC2', 0.2),
('BST12', 0.25), ('SW12', 0.3), ('FB12', 0.2), ('RT12', 0.2), ('EN12', 0.2), ('VCC12', 0.25),
('#CFG1', 0.2), # U36.9(CH224A CFG1)→ R7:自动网名,按脚现取(导入会换号)
('VIN_RAW', 0.3), ('VIN_FUSED', 0.4), ('VIN_SW', 0.4)]
SW_FIX = [(2, 17.2, 20.7, 19.3, 20.7, 1.0, 'SW12')]
# SPSP(11 脚,南侧东头)→ VCC(6 脚,东北角):从 SW 焊盘和东侧 RT/AGND/FB 那排脚之间穿过(离各焊盘 ≥ 0.15,d16_check 复核)
VCC_LINK = [(2, 22.393, 19.50, 22.393, 19.95, 0.15, 'VCC12'), (2, 22.393, 19.95, 22.25, 20.093, 0.15, 'VCC12'),
(2, 22.25, 20.093, 22.25, 21.585, 0.15, 'VCC12'), (2, 22.25, 21.585, 22.82, 21.585, 0.15, 'VCC12')]
# ⚠ 第一版最后一段斜着拐进 6 脚,离 FB(7 脚上沿 21.324)只有 0.137 ⇒ 竖线走到 21.585 再平拐(离 FB 0.186、离 BIAS 0.165)
SW_C18 = [(2, 18.78, 24.85, 17.60, 23.00, 0.3, 'SW12')] # C18 的 SW 端 → L4.1 焊盘东北角
VCC_C13 = [(2, 23.10, 22.20, 23.10, 23.86, 0.25, 'VCC12'), # 6 脚往北,过 R10 上方
(2, 23.10, 23.86, 24.43, 23.86, 0.25, 'VCC12')] # 拐东进 C13 的 VCC 端
VIN_RAW_FIX = [(2, 10.10, 8.30, 10.10, 10.35, 0.3, 'VIN_RAW'), (2, 10.10, 10.35, 11.90, 10.35, 0.3, 'VIN_RAW'), # 西侧 VBUS → F1.1
(2, 14.90, 8.30, 14.90, 10.35, 0.3, 'VIN_RAW'), (2, 14.90, 10.35, 13.10, 10.35, 0.3, 'VIN_RAW')] # 东侧 VBUS → F1.1
# BST:3 / 4 脚(RBOOT = CBOOT)往上并成一根,北上到 y 25.6 再往西、从上面落进 C18 的 BST 端 —— 绕开 C19
BST_FIX = [(2, 21.392, 22.60, 21.392, 22.85, 0.25, 'BST12'), (2, 21.892, 22.60, 21.892, 22.85, 0.25, 'BST12'),
(2, 21.392, 22.85, 21.892, 22.85, 0.25, 'BST12'), (2, 21.392, 22.85, 21.392, 25.60, 0.25, 'BST12'),
(2, 21.392, 25.60, 19.62, 25.60, 0.25, 'BST12'), (2, 19.62, 25.60, 19.62, 25.00, 0.25, 'BST12')]
# 北侧 VIN:VIN2 脚 → 穿 C19 的 VIN 端 → 往北一个孔上 L1 小岛(南侧那组由 A* 自己找孔上小岛)
VIN_N_FIX_S = [(2, 20.30, 22.55, 20.30, 24.35, 0.3, 'VIN_FUSED')]
VIN_N_FIX_V = [(20.30, 24.35, 'VIN_FUSED')]
# R10 的 VIN_SW 端(FB 采样上臂):东出 → 沿 x 26.25 北上(C23/C24 东边)→ y 29.75 往西 → C17 的 VIN_SW 端
R10_SENSE = [(2, 25.28, 22.64, 26.25, 22.64, 0.2, 'VIN_SW'), (2, 26.25, 22.64, 26.25, 29.75, 0.2, 'VIN_SW'),
(2, 26.25, 29.75, 20.44, 29.75, 0.2, 'VIN_SW'), (2, 20.44, 29.75, 20.44, 29.00, 0.2, 'VIN_SW')]
# 第一轮「找不到落过孔的地方」的 GND 脚:固定的「短线 + 孔」(孔位逐只推过,d16_check 复核)
GND_FIX_S = [(2, 19.13, 22.475, 18.75, 23.45, 0.3, 'GND'), # U37.1 PGND 西北角 → 孔在 C19 西、C18 南
(2, 19.13, 18.925, 18.75, 17.95, 0.3, 'GND'), # U37.15 PGND 西南角 → 孔在 C20 西
(2, 22.393, 22.60, 22.393, 23.35, 0.25, 'GND'), # U37.5 BIAS 往北
(2, 21.892, 18.80, 21.892, 17.95, 0.25, 'GND'), # U37.12 SYNC 往南
(2, 23.10, 20.70, 23.70, 20.70, 0.25, 'GND'), # U37.8 AGND 往东,从 R8 / R11 中间穿过去
(2, 23.70, 20.70, 23.81, 20.81, 0.25, 'GND'),
(2, 23.81, 20.81, 26.30, 20.81, 0.25, 'GND'),
(2, 8.60, 7.50, 9.10, 7.50, 0.25, 'GND'), # J1 B1A12 → 西侧外壳通孔脚
(2, 15.90, 7.50, 16.40, 7.50, 0.25, 'GND')] # J1 A1B12 → 东侧外壳通孔脚
GND_FIX_V = [(18.75, 23.45, 'GND'), (18.75, 17.95, 'GND'), (22.393, 23.35, 'GND'), (21.892, 17.95, 'GND'),
(26.30, 20.81, 'GND')]
EP_ONLY = ('U36',) # 盘中打孔阵只给真散热盘;0805 / 1206 / SMB 的 GND 盘(≥1.2)不许盘中孔(吸锡)
def L(s):
return math.hypot(s[3] - s[1], s[4] - s[2])
def main():
width = dict(ORDER)
for a in sys.argv[1:]:
if '=' in a and not a.startswith('--'):
k, v = a.split('=', 1)
width[k] = float(v)
b = json.load(io.open(os.path.join(HERE, '_board_for_nets.json'), encoding='utf-8'))
segs = [tuple(s[:7]) for s in b['segs']]
vias = [tuple(v[:3]) for v in b['vias']]
pads = R.load_pads()
_is_ep = R.is_ep
R.is_ep = lambda p: p[0] in EP_ONLY and _is_ep(p)
cpads = [p for p in pads if not str(p[1]).endswith(R.TH_SUFFIX)] # 连通判断不算通孔的 L1 障碍副本(#t)
refs = set(PL.PD_REFS) | {R.J1_ALIAS}
skip = set((p[0], str(p[1])) for p in pads if p[0] not in refs) # 地孔只打新件的
new_s, new_v, logs, stat = [], [], [], {}
def obst():
N.INNER[:] = [s for s in segs if s[0] in (15, 16)]
return [s for s in segs if s[0] in (1, 2)]
fix = SW_FIX + VCC_LINK + GND_FIX_S + SW_C18 + VCC_C13 + VIN_RAW_FIX + BST_FIX + VIN_N_FIX_S + R10_SENSE
fixv = GND_FIX_V + VIN_N_FIX_V
segs += fix
new_s += fix
vias += fixv
new_v += fixv
done = set()
for q in pads: # 已由固定构造接好的 GND 脚,平面扇出别再管
if q[7] == 'GND' and q[0] in ('U37', R.J1_ALIAS) and str(q[1]) in ('1', '15', '5', '12', '8', 'A1B12', 'B1A12'):
done.add((q[0], str(q[1])))
s, v = R.plane_vias(pads, obst(), vias, [], logs, skip, 'early', done)
segs += [tuple(x) for x in s]; vias += [tuple(x) for x in v]
new_s += [tuple(x) for x in s]; new_v += [tuple(x) for x in v]
cfg1 = [q[7] for q in pads if q[0] == 'U36' and str(q[1]) == '9'][0]
for net, _w in ORDER:
w = width[net]
if net == '#CFG1':
net = cfg1
ob = obst()
groups = N.components(net, cpads, segs, vias)
if len(groups) < 2:
stat[net] = [len(groups), len(groups), 0, 0.0]
print(u'%s:已经连通(%d 块)' % (net, len(groups)))
continue
lg = []
s, v = R.route_one(net, N.reps(groups), w, pads, ob, vias, [], lg)
s = [tuple(x) for x in s]
v = [tuple(x) for x in v]
segs += s; vias += v; new_s += s; new_v += v
after = N.components(net, cpads, segs, vias)
stat[net] = [len(groups), len(after), len(v), round(sum(L(x) for x in s), 1)]
print(u'%s:%d 块 → %d 块;新增 %d 段 %.1f mm / %d 孔(线宽 %.2f)%s'
% (net, len(groups), len(after), len(s), stat[net][3], len(v), w,
(u';' + ';'.join(lg)) if lg else ''))
logs += lg
s, v = R.plane_vias(pads, obst(), vias, [], logs, skip, 'late', done)
segs += [tuple(x) for x in s]; vias += [tuple(x) for x in v]
new_s += [tuple(x) for x in s]; new_v += [tuple(x) for x in v]
gnd_left = [m for m in logs if m.startswith('GND:')]
print(u'新增共 %d 段 / %d 孔;没连通的网 %s;GND 没打上孔 %d 只'
% (len(new_s), len(new_v), [n for n, st in stat.items() if st[1] > 1], len(gnd_left)))
io.open(os.path.join(HERE, '_d16_route.out.json'), 'w', encoding='utf-8').write(json.dumps(
{'segs': [list(x) for x in new_s], 'vias': [list(x) for x in new_v], 'nets': stat,
'pd_refs': sorted(PL.PD_REFS),
'logs': logs, 'grid': RT.G}, ensure_ascii=False))
if __name__ == '__main__':
main()