文件
phonerail/PROGRESS.md
T
zhou yang和Claude Opus 5 cdb63fd497 R2 刀片:台阶的连带影响 —— 金手指 J1 位号挪出被切区
开台阶时只查了铜/过孔/焊盘/器件,漏了丝印。补查 82 条(自由丝印 + 可见位号):
- J1 位号锚在 (26.45, 8.38)、框 x 25.74~27.16,左半截落在切掉的板材上(1.44 mm²)
  ⇒ 已挪到 (28.50, 8.378),左下对齐与居中两种口径都在舌头 27~63 内;板上已下发并读回核对。
- 丝印层的线 0 段越界。
- 封装自带的 NOT PCIe - PhoneRail blade 读不回来(PCB 文档不列封装内部图元),
  按锚点 28.50 + alignMode 默认 3(左下)推算 x 28.50~44.62,截图实测左端 ≈28.7 mm 佐证,安全。

⇒ 以后改板框(尤其切掉板材)时,检查清单必须包含丝印与位号,不能只查铜层。

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0168Bu6SEx3cXXCJLkdnE6zB
2026-09-12 12:52:49 +08:00

467 KiB
原始文件 Blame 文件历史

进度与交接

固定执行约束(2026-09-10 用户明确要求)

  • 以后所有 PCB 布线运算都用 10.5.5.13 服务器跑。 本机仅做快照、结果校验、 EasyEDA 下发与 DRC,不再做完整重算或局部 D± 试算。
  • 底板入口为 .claude/r2/remote_route.ps1;route_base.py 已加本机硬阻断,避免新会话误跑。
  • 刀片入口为 bash .claude/r2/remote_blade.sh ['变体' ...](2026-09-11 新加);router.py --replan/--pf 同样本机硬阻断。

⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ 2026-09-12:刀片金手指两侧开台阶(插不进插槽)+ 铺铜补齐(都已下发)

用户:「解决刀片板与插槽干涉的问题,完善刀片板铺铜」→「金手指两侧没有开槽,应该会插不进插槽」。

⛔ 根因:禁布区 ≠ 板材

ZONES['finger'] 早就写着「金手指区 11 mm(槽体高度 ~9 mm 要让开)」, 但那只是禁布区(别放件),板框从来没开台阶 —— outline_pts() 第一段就是 (r,0) → (BW-r,0),整块 90 mm 宽的板材一直到 y=0。而插槽塑料体只有 39 mm 且两端封闭 ⇒ x<29.5 / x>68.5 的板材压在塑料体两端壁上,插不到底。PCI-E 卡在这一段必须是「凸」字形。

定稿(用户拍板)与放行核算

项 值
舌头 36.0 mm(x 27.00~63.00);金手指本身跨 33.70,两侧各余 1.15
台阶高 12.0 mm(槽体 ~9,余 3)
内凹角 R1.0(外形铣刀下不去直角)
切掉 648 mm²,占 10.6%

被切区里 铜 0 / 过孔 0 / 焊盘 0 / 器件 0(板上读回 + _blade_pads.json 两种口径一致); 7 个定位孔最南 H5 y=14.46 全在板材上;两条铣穿槽 y 56.86 起,不受影响。

⚠ 唯一代价:手机主板投影底边(PHONE_ORG.y = 11.0)在舌头两侧悬空 1.0 mm (左 x 1.5027.00 / 右 x 63.0068.59,合计 31.09 mm²)。主板靠 7 个 M1.6 尼龙立柱压住、 不靠边缘托 ⇒ 无害。selfcheck 已改成判「悬空量 ≤ 1.5」+「定位孔不许落进台阶区」。

铺铜:原本外层整层缺失,现已补齐并跟着台阶重画

抓板发现刀片上只有 11 块铺铜(L2 九块 + L3 两岛)——L1 / L4 的 GND 底填全都不在板上, 而交接记录写的是"铺铜 25/25"。先补齐到 25 块,开台阶后重画成 31 块 (L2 11 + L1 9 + L4 9 + L3 两岛),填充 31/31;刀片 DRC 间距错误 0 条。

  • 新常量 pcb_pour.STEP_POUR_Y = 12.5(舌头两侧的铜下沿);gnd_tiles() 的竖条切分点加入舌头两端。
  • +12V 岛改成阶梯形单环:舌头段仍下探 y=9.0 接 12V 三指的 launch 孔(x 28.5~30.5 / y 11.75, 全在舌头内),舌头以东抬到 12.5;断面 11.0 → 7.5 mm(1 oz 内层 ≈ 5.1 A,需求 1.5 A 峰值)。

三条坑

  1. 禁布区不等于板材 —— 见上,这次就是栽在这上面。
  2. 阶梯形多边形不能按包围盒判:给 selfcheck 加「铜不许伸进台阶区」时按环的包围盒判, +12V 岛(min y 9.0、max x 84)直接误报 ⇒ 改成逐边采样(0.25 mm 步长)。
  3. pcb_pour.inner_poly() / outer_poly() 是死函数,plan() 只用 gnd_tiles()。 改铺铜边界时改那两个不生效,看着像"改了没用"。

⚠ 台阶的连带影响:丝印也要一起查(事后补查发现的)

开台阶时只查了铜 / 过孔 / 焊盘 / 器件,漏了丝印。补查 82 条(自由丝印 + 可见位号)发现 金手指 J1 的位号锚在 (26.45, 8.38)、文字框 x 25.74~27.16,左半截落在被切掉的板材上 (交叠 1.44 mm²)⇒ 已挪到 (28.50, 8.378),按「左下对齐」和「居中」两种口径算都在舌头内。 丝印层的线 0 段越界。

封装自带的 NOT PCIe - PhoneRail blade(画在金手指封装里)getAll() 读不回来 (PCB 文档不列封装内部图元)⇒ 按锚点 x=28.50 + alignMode 默认 3(左下)推算 x 28.50~44.62, 并用截图实测佐证(左端落在 ≈28.7 mm)⇒ 在舌头内,安全。

⇒ 以后改板框(尤其切掉板材)时,检查清单必须包含丝印与位号,不能只查铜层。

⬜ 待用户人眼确认台阶形状与铺铜(图已发)。

⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ 2026-09-12(定版):底板以用户自己布好的那版定稿 + 位号丝印重排(已下发)

用户:「按当前 eda 中底板状态定版,优化丝印放置」「现在 eda 中是我已经布好了的」 ⇒ 铜不再动(下面那一节我下发的窗口重布之后,用户又自己布过;板上 2060 段 / 671 孔是用户的版本)。

⚠ 因此下面那节里「22 个缝合孔压内层线、准备删掉」的收尾作废:按坐标去板上找,一个都不在了 (deleted 0 是没误删,不是失败)。要复查 DRC 请以板上当前状态重跑。

位号丝印重排(silk_base.py,已下发并读回核对)

板上 164 个位号(pcb_PrimitiveAttribute 里 key=='Designator' 那条),字高统一 1.143 mm、 居中、不镜像;顶层 10 / 底层 153 可见(C743 不可见,保持)。板上没有元件外框丝印,只有位号。

判据 改前 改后
压同面焊盘 128 4
压过孔 48 8
越板框 8 3
互相重叠 34 对 0

重排 150 个(理想位 143 / 降级压过孔 4 / 降级压焊盘 3),白名单保留 13 个; modify 150/150 成功,读回 150 个逐个对上、0 个没对上。离本体平均 2.19 / 最远 6.97 mm。

三条坑(细节见 R2 §39):

  1. rotation 是度且不归一化(900=180°、720=0°)。按 ×10 读会得到「9°/18°/27°」这种 看着像真的、其实全错的朝向分布。
  2. 别拿「本体 ≥5 mm」当保留白名单 —— 会把 R9A/D3/U13 这些该重排的中等件豁免掉 (残留 25 个里 18 个是这么来的)。改成显式名单 silk_base.KEEP(五个插槽 + U20 + 顶面件 + 板边 USB-C)后残留 25 → 12。
  3. 「离本体」要设硬上限(2.2 mm)+ 降级策略。不设的话贪心会把位号甩到十几毫米外的空地 (实测最远 22.2 mm),对装配毫无指向性。

可行性先算过:现字高下 163 个里 161 个有解 ⇒ 不用缩字号。

自由丝印(silk_str.py,同日补)

板上 pcb_PrimitiveString 原本 0 条 —— 方案 §5.2 要求的插槽警告、J1/J23 的接口标注, 一直只写在原理图的说明文字里,从没落到 PCB。已补 6 条并读回核对:

文案 层 坐标
PWR 20V 4(底面,J1) (12.500, 1.800)
USB 4(底面,J23) (84.250, 1.800)
NOT PCIe - PhoneRail blade only 3(顶面,插槽) (49.000, 79.480)
NOT PCIe ×3 3 (36.000, 18.520) / (62.000, 38.840) / (45.000, 59.160)

完整句只放得下一条,其余槽间放短标 ⇒ 五个槽每个都邻着一条。字高 1.2 / 线宽 0.15。

三条坑:

  1. ⛔ 丝印压铜不算冲突(铜上有阻焊,油墨正常印在上面)。第一版把压铜也当冲突, 六个候选位全军覆没,看着像「板上没地方放字」。真正要避的只有 焊盘 / 过孔 / 位号 / 安装孔 / 板框外。
  2. ⚠ alignMode 要显式传 5(CENTER),不传默认不是居中,字会整体偏出去。 位号 Attribute 用的 3 是另一套口径,别照抄。
  3. ⚠ 核对不能拿文案当字典 key:NOT PCIe 有三条,同名互相覆盖 ⇒ 「6 条都建上了」却报成「4/6 对上」。按文案 + 坐标 + 层配对,配上一条消费一条。

⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ 2026-09-12(收尾):按用户最新布局重布风扇窗口并已下发 —— 窝内只差 J27.4

用户:「将底板按照现在我的布局重新布线」→ 选「风扇窗口整体重布(拆掉那 43 段重来)」+「GND 回流孔一起补」。

动手前查清的板上现状(只读抓板,_snap_outer/_snap_inner):

  • 板上铜与 10:31 快照一致,变的只有摆位;用户在 10:56 之后又挪了 11 件(D2 DZ3 J27 Q9 R746 R750 R751 R752 R753 R754 R757)。
  • 板上比快照多 43 段,全在风扇窗口里 —— 是用户自己在客户端手布的(已按用户选择拆掉重来)。
  • 新摆位不压任何异网焊盘;唯一压铜处 R754.1 压 FAN_12V_SW −0.271(属待拆网,自解)。
  • ⚠ API 回的是 mil,不是 mm(抓板脚本第一版漏了 ×0.0254 ⇒ 连通性判据全错、每只脚都报"未接")。

⭐ 这一轮最有价值的发现:L1 走廊是按旧摆位开的

pcb_base.PD_L1 里那四条走廊的注释写着 R748.1(18.2,91.0) / R757.2(27.2,93.0) —— 都是旧坐标。 用户重摆后 R748.1 到了 (5.13,73.91)、R757.2 到了 (18.11,83.57),走廊和件对不上, 而 A6 那一带 L1 整片是 12V 主块、L4 被 22 件占满 ⇒ 四条长途网必然布不通。这就是根因,不是布线器参数问题。

  • 重开走廊后未完成 4 → 3 → 2(FAN_EN 一条就从 0 段变 11 段并布通)。
  • ⚠ 走廊必须盖住焊盘正上方:只盖"沿途"不行(那只脚得先在 L4 挪到走廊下方才打得了孔)。 v1 只盖沿途 = 3 条未完成,v2 盖住焊盘 = 2 条。
  • ⚠ L4 逐 x 扫过:没有一条彻底堵死(最窄也有 0.9 mm 纵向空隙)⇒ 布不通不是"墙",是单层绕不过来。
  • 新走廊六条(已固化进 pcb_base.PD_L1),逐 y 断面 11.80 mm;铺铜后实测 L1 12V 主块最窄 9.82 mm(§6.4 要 8.0)。
  • 再次印证「priority 是按下葫芦浮起瓢」:顶 FAN_EN 到最前 ⇒ 它通了、FAN_TACH_IN 掉了,总数不变。别再调。

板上最终状态(都已下发、读回核对过)

项 数
拆 43 段(窗口内 8 个网,用户手布的那批)
补 139 段 / 7 孔(_base_window_repair.cor2.json)
GND 回流孔 10 个(10 只脚全找得到落点;vs 内层 116 段手布线净距全达标)
缝合过孔 新增 25 个
板上合计 外层 1960 段 / 内层 116 段 / 过孔 669

校验:fan_check 对板上真实快照逐项净距达标(线 ≥0.15 / 钻孔 ≥0.25 / 含"新孔 vs 内层线"); 全部 669 个过孔 vs 本轮新线异网净距 0 处 < 0.15;铺铜自检全过、三条断面全过。

✅ 与拆线前逐网对比:本轮没引入任何新的连通性问题

用拆线前快照(_base_board_pre_fanwin_0912.json)按同一套并查集判据算,前后对照:

网 拆线前 现在
GND 186 块 / 未接 58 196 块 / 未接 48(补了 10 个回流孔)
FAN_PWM_L 0 块 / 未接 3 1 块 / 未接 1
FAN_TACH_IN 1 块 / 未接 2 2 块 / 未接 0
FAN_TACH_OUT 2 块 / 未接 1 2 块 / 未接 0
+3V3 4 块 / 未接 2 4 块 / 未接 0
+12V 11 块 / 未接 15 11 块 / 未接 15(一字不差)
XTAL1 / XTAL2 3 / 4 块 3 / 4 块(老误报,圆弧读不回)

⇒ board_check 现在 113 / 121,比历史基线 118/121 多的 5 条里,3 条是下面的风扇遗留, 另 2 条(+3V3 分块、+12V 的 DZ3.1)拆线前就存在。 (还核过一件事:这 8 条与「我把 _base_routes.json 换成板面版」无关 —— 还原缓存后重跑,输出一字不差。)

⛔ 还差的(如实交接)

  1. FAN_PWM_L: J27.4 一根铜都没有(窝内唯一没接上的脚)。已排除:走廊没盖到(盖了)、 落不下换层孔(0.25 mm 处就能落)、L1 不可达(泛洪 686 个格点可达)。剩下的怀疑对象是 A* 自己。
  2. 三条到 U20 的窗外段(FAN_EN / FAN_TACH_IN / FAN_TACH_OUT):窗内已布到窗缘, 缺口 15.6 / 17.7 / 16.6 mm,中间是槽体 x 28~70 的手焊 1 mm 针排,按设计不许穿 ⇒ 本来就留给窗外单独接。 ⚠ 这三条拆线前也没通,不是这一轮弄断的。
  3. FAN_TACH_IN 窝内只有 Q9.3↔R753.2 那一段(2 段)。

这一轮沉淀的工具

文件 用途
l1_section.py L1 12V 主块的逐 y 水平断面核算(§6.4 ≥ 8.0)。横带很贵、竖带几乎免费;--cfg 核 _base_window.json 里的候选走廊
fan_rip.py 按窗口 + 网名拆窗口内旧铜,拆前后比对全板线 / 孔总数(防连带删除)
repair_window.py 的 pd_l1_extra 走廊改成跟着 _base_window.json 走,试一组不用改 pcb_base.py(服务器单轮 1.5 s)

⚠ EasyEDA 会合并线段:拆线点名 43 段、实际外层只少 42(FAN_PWM 主干被延长); 下发 139 段、板上净增 142,有 8 段"按坐标找不到" —— 逐段核过全部被同网同层的铜覆盖,真缺失 0。 ⇒ 下发/拆线后不能只按坐标比对,要按"几何是否被覆盖"判。

⚠ _base_routes.json 现在是板面版(为 pour_base 的缝合避让与断面校核换的), 原布线器缓存备份在 job tmp _base_routes.orig.json。⛔ 别拿板面版去 --push。

⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ 2026-09-12(续):风扇区按用户摆的座位重排 —— 布局已下发,布线差 5 条

用户在客户端把两只座挪成 rot90 竖放并排(J26 (4.572, 85.852) / J27 (10.795, 85.852),顶面), 要求「按我现在摆放的风扇座位置,重新布局其外围器件」。

已完成:

  • 两只座各南移 2.61 mm(用户 2026-09-12 拍板):J26 (4.572, 83.240, rot90) / J27 (10.795, 83.240, rot90), 本体 y 78.14~88.34 ⇒ 完全退出 J34 那片刀片(原来各重叠 1.60 mm),两座净距 1.22 mm。
  • A6 那 22 件(place_base.FAN_REFS)按新座位重排并下发(--only-fan --fan-gap 2.5,链成组版)+ R751/R752 微调 (1.25 / 0.50 mm,网格对齐)。读回逐件对上;四项净距核查全 0,其中「焊盘压板上现有铜」也是 0 处(原先 8 处)。
  • R753.1 的 +3V3 支线已补(3 折 Z 形,净距 0.456)。

板上最终状态(2026-09-12 收尾,已与客户端读回核对一致): 164 件 / 顶面 10 件(C2 C3 J26 J27 J33 + 五个插槽)/ 铜 1936 段(其中内层 119)/ 过孔 629。

  • 两只座:J26 (4.572, 83.240, rot90, L1) / J27 (10.795, 83.240, rot90, L1),已退出刀片、净距 1.22。
  • 风扇 22 件按你摆的座位重排并下发(含「链不跨列」判据),焊盘压现有铜 0 处; $6N49 四只脚 x=13.55/19.25/22.75/26.00,全在 J27 通孔列以东。
  • 重布窗口 x 0.50~27.60 / y 73.53~97.50(东界压在槽体 28.0 之前):已拆空, 只剩必须保住的 $2N74 2 段。⇒ 这一窝的 14 个网区内是断的,等重布。
  • ⛔ 十轮试算没有一版下发,全部隔离在 $CLAUDE_JOB_DIR/tmp/*_REJECTED.json; 板面未被任何一版污染(客户端读回 1936/629 与输入快照逐项一致)。

⛔ 还没做完:这一窝 14 个网的布线。区内旧铜已拆 49 段,补线算到 149 段 / 17 孔, 但 5 条没布通($6N64:Q9.1、FAN_TACH_IN:R753.2、FAN_TACH_OUT:Q10.3/R757.2、FAN_EN:R748.1), 且有 1 条净距 0.076($6N49 距 DZ3.1)⇒ 一段都没下发,板上这一窝现在是「件已重排、线是断的」。

⚠ 座子本体与 J34 那片刀片重叠 1.60 mm(本体 y 80.7590.95,刀片板面 y 88.8490.44)。 两只各南移 2.61 mm(中心 y → 83.24)即完全退出,且焊盘不压任何背面件 —— 等用户定。

布不通的根因(已定位,不是随机现象)

  1. A6 那一带只有单层可用:L1 整片是 +12V 主块(x 0.527.8 / y 0.597.5)⇒ 过孔上得去但走不动, 而 L4 被 22 件占满。扫了 800 个座子落位 + 4 套布局间距(gap 1.5/2.0/2.5/3.0)都布不通。
  2. PD_L1 开走廊有效但不够:新增三条(FAN_TACH_OUT 贴东缘竖带 + FAN_EN / FAN_PWM 短横带) 把未完成从 6 压到 5。⚠ 指头只在主块以东 x > 27.50,主块内部没有指头 ⇒ 竖带不碰供电路径; 三条叠加后逐 y 最窄断面 12.80 mm(§6.4 要 8.0)。
  3. 排布器把本地链拉散了(真正的瓶颈):sat_target 让每个件各自去贴「同网锚脚」, 而 Q9 同时挂 $6N64(本地) 和 FAN_TACH_IN(跨区→U20),大件先摆时被拉到东缘 x≈25, 小电阻 R751/R752 后摆只好贴西边座子上的 FAN_IN_TACH_RAW ⇒ 同一条 $6N64 拉成 23.8 mm。 已加 local_nets() + 「本地链优先」排序键,但没有效果 —— 因为 FAN_IN_TACH_RAW 自己也是本地链, 键没有区分度。真正要改的是摆放顺序(让同一条本地链的件连续摆、后摆的优先贴先摆的那件), 不是排序键。⇒ 已实现(chain_id() 并查集 + _ord 让同链连续摆 + sat_target 的 same_chain 键), 实测链距合计 99.3 → 65.7 mm($6N64 23.8→6.8、$6N74 21.6→5.0)。 ⚠ 并链时要只并小链:脚数 ≤ CHAIN_MAX_PINS(4) 且不含手排锚件(TOP_EXTRA)—— 第一版没这两条过滤,FAN_12V_SW(7 脚,串起 C738/D2/两只座/Q7/R750/R754)把整窝 22 件并成一个组,等于没加。
  4. ⛔ 排布器根本不看板上已有的铜(2026-09-12 新发现,最隐蔽的一条):它只避「别的件 + 禁区」。 于是在已经布过线的板上重排时,R751 / R752 被直接摆到既有的 +3V3 走线上(实测净距 −0.14 / −0.03), A* 报「R751.1 出不来」,看着像那一带太挤 —— 其实摆位阶段就把件放到铜上了,而且这本身就是 DRC 违规。 ⇒ 已加 track_blocks()。必须只给正在重排的那个分区加:blockers() 是全板口径, 在那儿加会让别的分区的件(C607/C610/C611/C10…)也被现有铜挡住 —— 实测自检 17 → 139 条、3 件没摆。 加在 plan() 的 zfixed(zone == 'A6')才对。

验收这一窝的布局要过四项(缺一项就会在 DRC 或 A* 里以别的面目冒出来): ① vs 五个顶面通孔件(跨层,焊盘穿透两层)② vs 其余背面件 ③ 22 件彼此 ④ vs 板上现有铜。

  1. ⛔ 删线会连带清掉过孔和它在内层的延续(2026-09-12 实测):点名删 6 段,读回发现 线少了 12 段、孔少了 8 个 —— 多掉的 6 段全在 L15/L16($6N48 / $6N64 / $6N45), 8 个孔也都是这几个网的换层孔。删掉外层那一段后,过孔失去连接,客户端把孔连同它在内层的延续一并清理。 ⇒ 拆线前后都要比对全板线/孔总数,不能只数自己点名的那几段;这几个网原本靠内层换层, 路径被连带清掉之后补线立刻从 8 条退到 12 条没通(那一版还顺手把 +3V3 也带断了)。

⚠ 迭代到此为止的实测数据(别再盲目重排)

布局 补线未完成
gap2.5,不链成组、不避铜 5 条(最好)
gap2.5,链成组、不避铜 8 条
避铜 + gap 2.0 / 2.5 / 3.0 / 3.5 11 / 8 / 10 / 6
链成组 + 拆掉被压的 6 段 + 带 +3V3 一起补 12 条(内层换层路径被连带清掉)
  1. ⛔ +3V3 这种全板大网不能交给 repair_base 整网重布(2026-09-12 实测): 只因为拆了 4 段就把它列进待补网,布线器把新的 +3V3 线从 J34~J37 插槽针排的焊盘中间穿过去 (fan_check 实测十几处 −0.2,还压 U13.7 −0.449)。插槽是 1 mm 间距针排、手焊, 在那里乱穿是致命的。⇒ 大网只在出问题的局部补(几条短线手画 + fan_check 核净距), 别整网交给 A*。那一版结果已挪到 $CLAUDE_JOB_DIR/tmp/fan_repair_with3v3_REJECTED.json,不要下发。

  2. 窗口内整体重布(用户 2026-09-12 选):repair_window.py + remote_repair_window.sh。 做法是把 bypad 只装窗口内的脚(窗口外的同网铜留在占用图里当接口),窗口外加过路费 OUT_PEN。 ✅ 窗口约束有效:新线「跑出窗口 0 段」,+3V3 再没穿插槽针排。 ❌ 但未完成仍是 10 条。两条被排除的假说:

    • 「面积不够」——窗口 538 mm²,焊盘圈占 17% / 件本体 14%,走线余量只用了 15%(147 / 953 mm)。
    • 「0.25 网格下出口格点不够」——--grid 0.125 实测 111 → 116 段,失败脚一模一样,无改善。 ⇒ 剩下的怀疑对象是布线器自身(分档 / 宽度 / 过路费),不是板子挤。下次从这里查,别再重排。

    ✅ 两条线索都查到底了(repair_window.py 里加了「每一档收了哪些网 / 每个网产出几段」的诊断输出, 一跑就看出来):15 个网全部正常进了 0.150 mm 那一档、route_groups 也确实跑了(产出 119 段), 但其中 6 个网一段都没产出,而且分成两种完全不同的病:

    • 「窗外还有脚」的 5 个网(FAN_EN / FAN_PWM / FAN_TACH_IN / FAN_TACH_OUT / +3V3) —— 是 repair_window.py 第一版的设计缺陷。我原以为「窗外的同网铜留在占用图里, A* 碰到就算到达」,不成立:route_groups 布的是 bypad[net] 里的脚两两相连, 窗外主干根本不是它的终点判据。于是 FAN_PWM/FAN_EN 窗内只剩 1 只脚 ⇒ 跨度 0、无事可做; 两路 TACH 和 +3V3 的窗内两只脚彼此并不需要连。对照很干净:窗外脚数为 0 的 8 个网全部正常产出。 ⇒ 修法:把主干在窗口边界处的端点做成虚拟焊盘塞进 bypad(同 route_base 喂金手指 launch 点的办法)。 ⚠ 主干远在窗外的(FAN_EN / FAN_TACH_OUT 最近端点在 x≈4243,在槽区里,离窗 14.515.6 mm) 不许放宽上限去接 —— 那等于从窗口里拉一条 15 mm 的线横穿插槽区,正是上一版 +3V3 穿针排的老路; 只在窗缘给个落点,窗外那一小段另接。
    • ⛔ $6N49 的真因:J27 的通孔盘列把这条链劈成了东西两半(顶进 priority 最前也没用,仍 0 段)。 两只座 rot90 竖放后四只脚排成一列(J26 x=4.57 / J27 x=10.79,y 79.4387.05), 而 $6N49 的脚跨 x 8.0013.93 —— DZ3.2/R746.1 在西、Q7.1/R747.2 在东, 所有东西向连接都要绕过那列 1.6 mm 的通孔盘。本机绕行验证吻合:三对有路(净距 0.340.51), 只有 R746.1→R747.2 无路(0.107)。不是没救,是要绕,而 A 在默认代价下不肯绕那么远。* ⇒ 真正的解法在摆位层面:别让同一条本地链跨过座子的通孔列。 已加判据 place_base.chain_split_by()(selfcheck 报警 + plan() 挑位置时排除,只在 --only-fan 下启用)。 写这条判据踩了三次,每次都是「粒度」错: ① 按 chain_id() 的并查集组判 ⇒ $6N48 共用 Q8/R747,把 $6N45+$6N48+$6N49 传染成 一条 7 件大链(横跨 x 818,被劈是必然的,报出来也没法处理)⇒ 改成按网判; ② 按网判之后误报 10 件 ⇒ FAN_12V_SW / FAN_PWM_L 这些网一端本来就接在座子上(J26.2/J27.2), 天生跨列,不是摆位错误 ⇒ 加「本网自己接在这个件上就跳过」; ③ 还要按座子分别判:FAN_IN_TACH_RAW 接的是 J26,却真的跨 J27 那一列(西2/东1)—— 「天生跨列」的豁免只对它自己接的那只座成立。 ④ 墙只能由 ≥3 脚的通孔件构成(WALL_MIN_PINS):TOP_EXTRA 里的 C2/C3 是两脚 Φ8 电解, 焊盘框同样窄于 3 mm,被当成墙之后,Hub 区的 R712/R719/R725/U32、入口链的 Rs6/U13/R9A 这些隔着半块板的件全被报「被 C2 劈开」; ⑤ 墙只管贴着它的那一窝(WALL_NEAR = 8.0):否则 J26/J27 的列会去劈半块板外的件。 最终判据只报 DZ3 / R746 / R747 三件,对应一条真被劈的链 $6N49 (FAN_IN_TACH_RAW 接的是 J26、在 8 mm 范围外,不该算)。 ✅ 判据接进 plan() 的候选筛选后见效:重排一次,$6N49 从「西2/东2」变成 西0/东4(同侧), 四只脚落到 x 13.55~26.00 全在 J27 列以东;四项净距仍全 0、自检回到 17 条(全是板上既有问题)、 不带 --only-fan 时判据完全不生效(0 条)。布局已下发,读回逐件对上、最大差 0.002 mm。 ⚠ FAN_IN_TACH_RAW 仍跨 J27 列(西2/东1)—— 它一端就接在 J26 上,脚天生分居两侧,摆位解不掉。 ⚠ 挪位代价已量化:DZ3 挪到列东要走 14.5 mm,会把 $6N49 的最近同网脚从 3.55 拉到 8.35 mm, 不划算;R746 只需 4.5 mm、代价 1.7 mm,是划算的那一个。
    • $6N64 掉段与拥塞无关:加了虚拟落点那一轮它从 3 段掉到 0 段,但实测三只脚周围 挡住出口的是 0 处,先布的 $6N74(45 段, x 1.7517.75) 也没盖到它(它在 x 18.2523.13)。 更像 A* 被过路费 / 代价上限劝退,下次查 route_net 的返回而不是再调摆位。
    • ✅ 虚拟落点这一步确实有效:+3V3 0→25 段、FAN_TACH_OUT 0→14 段、FAN_TACH_IN 0→3 段, 总产出 119→157 段,且「跑出窗口 0 段」始终成立。
  3. ⛔ penalize 是定价,block_rect 才是墙(2026-09-12,这一轮最贵的一条): 窗口外过路费从 40 降到 12(为了让跨窗口的网够得着 U20 那边的主干)之后, $6N64 / FAN_EN 立刻跑到 x 28.7532.75 —— 正是槽体 x 2870 的插槽区, 183 段里 50 段跑出窗口(之前每轮都是 0)。把插槽方向单独提到 SLOT_PEN = 200 也没用: 本机实测过路费确实加到了 200(窗口内 0 / 插槽区 200),A* 照样钻 —— 它宁可付 200 × 若干格也要抄近路,因为窗口内绕不过去。 ⇒ 插槽方向必须 g.block_rect(...) 封死,其余三面才用 penalize 加价。 本机验证:封死后插槽区不可走、窗口内照常可走。 ⚠ 代价:FAN_EN / FAN_TACH_OUT 的主干最西端在 x 42~43(在槽区里面),封死之后 窗口内接不上它们,只能靠「窗缘落点」布到 x=27.30 为止,窗外那一小段留给人工。

  4. 窗口要跟着布局重算:窗口 y 上界原按旧布局的焊盘范围定在 94.33,而「链不跨列」判据把件往北推, 于是 D2 / R733 / R749 / R750 四件落到了窗外,它们的脚天然在窗外 ⇒ A* 出窗是被迫的。 重算后窗口 x 0.5027.60 / y 73.5397.50(东界压在槽体 28.0 之前),22 件全部落回窗内。

    另外两条已排除 / 已调的:

    • OUT_PEN = 40 量级偏高:同一套代价体系里 CHAIN_PEN = 4 / UNDER_12V_PEN = 6 / HUG_PEN = 12。 跨窗口的那四个网(FAN_PWM / FAN_EN / FAN_TACH_IN / FAN_TACH_OUT,另一端在 U20) 必须走出窗口才能碰到主干,很可能是被这个过路费劝退的。先降到 612 再跑一次。 (router.pen 是 bytearray,0255,40 不会溢出,不是溢出问题。)
    • ⛔ $6N49 整条链「一根铜都没有」至今没有解释:4 只脚全在窗口内、间距 7.4 mm、 窗口还空着 85%,焊盘尺寸正常(0.54~1.2)、width_of = rule_width = 0.15 正常、 分档也没问题。$6N74 / $6N45 同样。这不像拥塞,像布线器根本没处理这几个网 —— 下次直接在服务器脚本里打印 route_groups 每一档收了哪些网、每个网 A* 的返回值。

2026-09-12 收尾:用户自己重摆了整窝,布线随之大幅好转

用户把 24 件全重摆(两只座改回 rot0 横放)。这一版比我做的任何一版都好: 四项焊盘级冲突全是 0,链距合计从 118 mm 收到 55.8 mm($6N48 11.9→1.6、FAN_OUT_TACH_RAW 19.3→5.9)。 ⇒ 布局只改一处:J27 南移 1.15 mm 到 y=85.840(原本撞槽5刀片面 0.65 mm),南移后四项仍全 0、两座净距 3.45。 ⇒ 窗口跟着重算并收窄南界到 73.50:把 A2 那一窝($2N70 / +5V / BST3V3)和 6 段 +3V3 排除在外, 于是一段都不用拆(窗口内只剩必须保住的 $2N74 2 段),R753.1 / R757.1 也不会悬空。 ⇒ 窗口重布:142 段 / 3 孔、未完成 6 条(此前最好 10 条),跑出窗口 0 段, 10 个纯本地网里 8 个全通 —— 先前最顽固的 $6N49(10 段) / $6N64 / $6N74 这次都通了。

剩下 6 条的根因(都已查到底并修进 repair_window.py):

  1. 窗口内非待布网的焊盘没收过路费 ⇒ 新线贴着它们走:+3V3 距 Q8.3 只剩 0.003、 FAN_OUT_TACH_RAW 距 Q10.2 0.027,fan_check 拦下、整版不可下发。 route_base._pen 本来就靠 GND_PEN_PADS/LATER_PEN_PADS 收这笔费,我覆盖 RT.pen_pads 时漏了。 窗口内这类焊盘只有 15 只(GND 12 + +12V 3)⇒ 已加 _toll_pads() 并进去。
  2. 虚拟落点取到了 L1 ⇒ 那一层这一带整片是 +12V 大铜,格点是 255(多网叠加,谁都不能走), 实测 FAN_EN 落点「起步格点 0 个」⇒ 造落点时排除 L1,只在 L4 取。
  3. 虚拟落点压在自己的主干线上 ⇒ FAN_PWM 落点距主干 −0.127,起步格点同样是 0 ⇒ 沿主干往窗内退 RETREAT(0.75) 再取点。
  4. 过路费的判据是「不是当前正在布的那个网」,不是「不在待布网集合里」:第一版写成后者, 于是 Q8.3(网 $6N48,本身也在待布名单里)拿不到过路费 —— 实测它周围始终是 0, +3V3 的新线就贴着它走到 0.003。放宽后 toll 焊盘 15 → 57 只,Q8.3 / Q9.1 / R750.2 全部收到 10。
  5. router.pen_pads 第一行就是 g.reset_pen()(先清空再打)⇒ 自定义的窗口惩罚必须加在它之后。 实测:先加窗口惩罚再调 pen_pads,窗外过路费被抹成 0;反过来才保住 12。 5b. ⛔「收费挡不住」第二次印证:把窗口内别的网的焊盘并进过路费之后,实测 Q8.3 周围确实拿到 10, 但 +3V3 的新线照样贴到 0.003、FAN_OUT_TACH_RAW 贴 Q10.2 0.027 —— 和「插槽方向 SLOT_PEN=200 照样被钻」完全同源。penalize 只是定价,block_rect 才是墙。 ⇒ 改成外扩封死。封死立竿见影:未完成 9 → 5 条,$6N45/$6N49/$6N64 这几条本地链同时布通。 ⚠ 外扩量必须按「当前这一档的线宽」算,不能写死:第一版按 0.15 细线定成 0.10, 挡不住 0.40 的 +3V3(它半宽就有 0.20)—— 照样贴 Q8.3 到 0.003。 route_groups 本来就按线宽分档建图,每档传进来的 halfw(半宽+净距)直接用即可。 一刀切取最粗的 0.56 会误伤细线:窗口内可走格点 68% → 52%。 ✅ 改成 r = max(PAD_KEEP, halfw) 之后,fan_check 第一次报「全部净距达标」 —— 此前每一版都卡在净距上根本不能下发(0.003 / 0.027 / 0.058 之类), 这 107 段是第一个可下发的结果。未完成 5 → 6 只是换了人,而那个「5 条」的版本本身带违规、下发不了。 ⇒ 判断哪一版更好,先看 fan_check 过不过,再看未完成条数。
  6. ⛔ 顺序这个旋钮是「按下葫芦浮起瓢」,别再拨了:$6N64:R751.1 某一轮突然掉段,实测是被 本轮先布的 FAN_TACH_IN 五条新线围在 0.3250.500(未违规但占满出口)⇒ 把 priority 改成 「本地小链 → 窝内网 → 跨区网 → +3V3」之后,FAN_EN 确实从 0 段变 24 段(排除 L1 那条也一并生效), 但 $6N45 / $6N49 反过来掉成 0 段 —— 先布的小链被后布的大网挤掉了出口。 三轮下来未完成始终在 69 条之间换人,没有单调改善。⇒ 下次不要再调 priority, 要么按下面 ⓑ 从摆位减负,要么接受人工补完。

⚠ FAN_IN_TACH_RAW 整网 0 段不是被座子通孔列劈开(判据报「无」,三只脚同侧、出口也通畅): 实测 J26.3→R750.1 直连净距 0.036(差 0.114),但换 Z 形就有 0.508 —— 兜底连法已算好存在 $CLAUDE_JOB_DIR/tmp/fix_in_tach.json ((13.72,94.29)→(13.72,92.29)→(5.03,92.29)→(5.03,89.86) + R750.1→R751.2 直连,共 14.10 mm)。

⛔ 九轮参数迭代的数据(同一块板、同一批 14~15 个网)——别再调参了

方案 范围 未完成
A 不链成组 / 不避铜 gap2.5 全板补线 5(至今最好)
B 链成组 gap2.5 全板补线 8
C 避铜 gap 2.0/2.5/3.0/3.5 全板补线 11 / 8 / 10 / 6
D 链成组 + 拆被压铜 + 带 +3V3 全板补线 12
E 窗口重布 out_pen=40 窗口 10
F 同上 + --grid 0.125 窗口 10(失败脚一模一样)
G 窗口 + 虚拟落点 + 优先级 窗口 11
H 窗口 + 「链不跨列」布局 窗口 17
I 同上 + 封死插槽方向 窗口 17(但跑出窗口 0 ✓)

在 5~17 之间反复震荡,没有单调改善的趋势——每修好一处就顶出另一处。 已排除的假说:面积不够(窗口余量只用 15%)、网格太粗(0.125 毫无改善)、 线宽/分档异常(width_of 全正常)、拥塞(失败脚周围常只有 1~2 个障碍、出口格点合法)。 已定位并修进代码的真因有三条:窗外主干不是终点判据、链被座子通孔列劈开、penalize 不是墙。

第十轮(在「链不跨列」新布局上重跑全板补线):152 段 / 16 孔、未完成 10 条 —— 比窗口方案的 17 条好, 但 ⛔ 侵入插槽区:26 段落进槽体 x 28~70,FAN_TACH_IN 距 J35.19 焊盘 −1.074(直接压在 1 mm 手焊针排上), 另有 2 条净距 0.125。⇒ 再次印证「全板补线无保护不可用」,这正是当初设窗口的原因。

三种方案各有致命处,这就是目前的全部事实:

方案 未完成 致命处
全板补线(当前布局) 10 26 段侵入插槽,压 J35 焊盘 −1.074
窗口重布(封死插槽) 17 不侵入,但未完成最多
方案 A 那 111 段 5 与当前布局对不上(111 段里只剩 2 段端点还在焊盘上),已作废

⇒ 下一步不该再动参数,而是二选一: ⓐ 窗口方案 + 人工补完(保守,板不会被压坏):窗口重布不侵入插槽,把它那版下发后剩 17 条人工布; ⓑ 从摆位层面减负(治本):FAN_EN / FAN_TACH_OUT 的主干最西端在 x 42~43(槽区里面), 天然要跨半块板 —— 把 R753 / R757 这类只挂跨区网的件移出 A6、靠近 U20 放, 能同时解掉「跨窗口接不上」和「窗口内拥挤」两个问题,这是唯一还没试过的方向; ⓒ 若要用全板补线的 10 条那版,必须先给它加插槽禁区(route_base 已有 keep_rects() 机制, 把槽体 + 针排外扩后塞进去),否则它一定会从针排中间穿过去。

「避铜」和「布得通」是矛盾的:避开现有铜等于把件推离目标脚,线更长更难布。 真正的出路只有两条 —— ⓐ 把这一窝连同周边 +3V3 / GND 的局部整体重布(而不是在缝隙里塞件), ⓑ 剩下几条手工布。不要再单纯调 --fan-gap 或重排,四套间距 × 两种策略都试过了。

这一轮沉淀的工具(.claude/r2/)

文件 用途
fan_layout_scan.py + remote_fan_layout.sh 「布局 × 可布性」扫描:几套布局各跑一遍 14 网补线,按未完成条数挑(单套 ~2 s)
fan_gnd_vias.py 给这一窝重排后的 GND 脚补过孔(plane_vias 是全板口径、repair_base 不做平面网扇出 ⇒ 实测 10 只脚新位置底下没孔)。已算好 10 个落点,未下发
place_base.FAN_REFS / --only-fan / --fan-gap 只重排风扇这一窝,其余件钉在板上现位
place_base.th_pad_blocks() ⛔ 顶面通孔件的焊盘穿透两层,背面件不许摆上去。原来 selfcheck 遇到不同层就 continue ⇒ 排布器把 C738/R746/Q7/R754 摆到两只座的通孔盘上(最深 −0.496),几何全过、DRC 才报
repair_window.py + remote_repair_window.sh 窗口内整体重布:bypad 只装窗口内的脚 + 给跨窗口的网补「主干接口」虚拟落点 + 窗口外加价、插槽方向 block_rect 封死。配置在 _base_window.json(窗口 / 网名 / out_pen / priority)。诊断输出会打印「每一档收了哪些网、每个网产出几段」——$6N49 整链 0 段就是这么发现的
place_base.chain_split_by() ⛔ 同一个网的脚不许被顶面通孔件的焊盘列劈到两边(座子 rot90 竖放就是一道墙)。粒度踩过五次坑,见上文

回退点($CLAUDE_JOB_DIR/tmp)

ROLLBACK_board_before_fanplace.json(重排前全板铜 1997 段 / 640 孔)、 ROLLBACK_live_before_fanplace.json(重排前 164 件位姿)、 ripped_backup_fan2.json(拆掉的 49 段几何,可原样画回)。

⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ 2026-09-12:五件挪到底板顶面(已下发,待人眼确认)

用户:「将风扇座,C2,C11,J33 移到顶面」→ 追问时澄清 C11 其实是指 C3(两颗 Φ8 电解一起上顶面), 并选「两只风扇座都南移」。最终板上:

件 结果 代价
C2 C3(Φ8 470 µF)/ J33(2.54 应急口) 原位翻面,坐标一格没动 零 —— 焊盘全在封装 y=0 那一排,翻面只把原生偏移的 y 取反 ⇒ 孔位 / 针序 / 铜一根不变
J26(进风座) 原位翻面(13.5, 94.5) 零 —— 拆掉的线已原样画回
J27(排风座) 挪到 (9.0, 85.5, rot180) 拆 22 段 / 补 60 段 3 孔
  • 顶面的限高不归 H_BOTTOM 管:插槽把刀片底边抬到板上约 11 mm,刀片自己 y 0~11 又是金手指 禁布区(两面无件)⇒ 顶面件只有横向约束:不许碰竖在 y = 槽心 ±0.8 的刀片板面、不许压槽体。 place_base.selfcheck 已加这一支(CARD_HALF / CARD_CLR),pcb_base.TOP_EXTRA 是顶面件名单。
  • J27 为什么非挪不可:原位 (13.5, 88.0) 本体 y 85.5~90.5 正好穿过 J34 那片刀片(89.64)。 而 x=13.5 那一列往南,焊盘先压 C738 再压 R754(通孔盘 1.6,背面那一窝驱动件让不开)⇒ 只能往西。

⛔ 这一轮最贵的三个坑(都已修进代码)

  1. 西北角 L1 整片是 +12V 大铜(l1_12v_rects 主块 x 0.527.8 / y 0.597.5)⇒ 过孔上得去但走不动, A6 那一带等于单层布线,而 L4 已被驱动件占满。「两只座都南移」扫了 800 个候选落位始终有 34 条布不通。 ⇒ 解法是给 FAN_PWM_L 在 pcb_base.PD_L1 里开一条 L1 走廊 (4, 86, 21, 89)(只挡导线图; 铺铜不用开窗,12V 铺铜本来就绕异网导线,CC 那两根走的同一路子)。断面账:主块 27.30 宽, 一根 0.15 的线占约 0.5 ⇒ 仍 ≈26.8 mm ≫ §6.4 要的 8 mm;y 8689 不碰任何一槽的 12V 指头。
  2. route_base.TH_GROW 把所有通孔盘放大 0.6(本是为 J1/J23 的长槽孔加的)⇒ 2.54 间距的风扇座 在占用图里变成 2.2×2.2、相邻两脚只剩 0.34,而出口要 1.33 才落得下第一格 ⇒ 中间那几只脚一根铜都出不来。 症状是「J26.3 没布通 / 一根铜都没有」,看着像没地方走。已改成按件区分(th_grow(),只有长槽件放大)。
  3. 搜索判据缺一条就会被精准钻空子:fan_scan 起初没查「两只座本体不许重叠」, 扫出来的「最优」是 J26=(13.5,94.5) / J27=(12.0,94.5) —— 两只座摞在一起,布线代价当然最低。已补判据。

沉淀下来的工具(都在 .claude/r2/)

文件 用途
repair_base.py + remote_repair.sh 底板的定点补线:只布指定的几个网,别的一根不动。现有铜取板上快照(不是 _base_routes.json,它和板上差几十段)。EXTRA=--repair / FAN_W= 可透传
fan_scan.py + remote_fan_scan.sh 「落位 × 可布性」联合扫描:候选落位逐个真跑一遍补线(PyPy 上单轮 ~1.2 s),按未完成条数挑
fan_check.py 下发前的本机净距自算,含布线器不查的那一项:新过孔 vs 内层线(板上有 125 段手布的内层线,不查就是往人家线上扎孔)

⚠ _base_routes.json 不含这一轮的改动(定点补线只改板、不回写缓存)。要整板重算前先想清楚: 板上还有 125 段手工内层线(L15/L16,HUB_RST / +3V3_HUB / IO0 / INT# …共 742 mm), remote_route.ps1 --replan 会把它们连同本轮的改动一起覆盖掉。

⭐⭐⭐⭐⭐⭐⭐⭐⭐ 2026-09-11 深夜:D16 底板电源入口改 USB-C PD(原理图已下发;PCB 未动;设计记录 R2 §36)

用户:「能否将电源输入改为 Type-C 从 120W 氮化镓取电」→「能不能兼容各个手机厂商的氮化镓电源」→「走方案 B」(做进底板)。

  • ✅ 结论:各家 100120W 都是私有协议(要手机鉴权),公约数只有 PD;单口 20V×5A ⇒ 12V 侧 ≈95W(常态 1025W 够,最坏 108W 靠固件限功率)。 20V 不能直接进 12V 母线(U29 泄放 / SMBJ13A / 刀片 TPS54622)⇒ 加一级 20V→12V 降压,12V 以下全不改。
  • ✅ 新页 A1B-PD(602ac9904a220348,r2/a1b_pd.py):J1 USB-C(C165948,与 J23 同料)→ F1 → U36 CH224A(R7 120k 只要 20V) → D3 SMBJ22A + 输入电容 → U37 LM61495QRPHRQ1(720 kHz,L4 SRP1265A-4R7M,FB 110k/10k = 12.0V,EN 门限 13.9V)→ VIN_SW。 CH224A 的 I²C 走 TCA9548A 通道 5(SDA6/SCL6),固件读 0x50 定功率上限。
  • ✅ A1-INPUT 删旧入口 6 件(J1 DC-005 / F1 / Q1 / DZ1 / R1 / C1),母线从 D1 起,VIN_SW 旗接 A1B;顺带清掉了页上 88 条历次残留文字。
  • ✅ A3-CTRL TCA9548A 脚 15/16 引出 SDA6/SCL6;A2 地旗挪 (530,290)。a3-ctrl-nc.json 以前是旧的(推送时从不写),现在推送会写回 + 存盘。
  • ✅ 验证:三页 verify_sch(带各自 NC 清单)PASS、xpage 真正漏接 0、port_audit --r2 BASE 可疑 0;本机截图自查过(A1B 第一版有一行说明压在 R11 地旗字上,已下移重推)。 改前 A1/A3 快照 r2/_a1_a3_pre_d16.json。eda-parts.json 新增 8 颗料(CH224A / LM61495 / SRP1265A / SMBJ22A / 1µ50V / 100n50V / 110k / 120k)。
  • ⬜ 等用户截图确认三页(CLAUDE.md 规矩)。
  • 🔶 PCB(2026-09-11 深夜 ~ 09-12,细节 R2 §36.5):
    • ✅ 导入:上次推三页后 74 件原理图 uniqueId 全空(存盘也不分配)⇒ 原理图侧 setState_UniqueId 写回 PCB 那套; 对话框从表格 React props 读 rows;导入前后全板焊盘网名逐脚比对,差异 = 对话框清单。
    • ✅ 摆位:place_base.PD_HAND 27 件手排 + --only-pd(图 v4 发过用户)。U37 rot 270 SW 朝西接横放 L4、信号边朝东。
    • ✅ 用户定:CC 走 L1 细线(两组 VBUS 之间打孔)/ U37 两侧 VIN 经 L1 VIN_FUSED 小岛连通 / RT602 改测降压。
    • ✅ 脚本:pcb_base.PD_L1 窗口、route_base 12V 挡线图扣窗口、pour_base.PD_POUR 手画铜块、rules_base SW12(已推、审计过)、 _base_npth 补 J1 定位孔;新 d16_route / d16_check / d16_push / d16_tap / d16_reach / d16_rip / remote_d16.sh。
    • ✅ 南带补线(服务器 4 轮,固定构造 32 段 / 6 孔先本机校核 0 条):除 I²C 外全通,197 段 / 49 孔已下发存盘 (快照 r2/_base_board_post_push_d16.json;EasyEDA 把两段同网线拼了一下,按实际坐标核过覆盖 / 间距)。
    • ⚠ 删旧铜后线数对不上:EasyEDA 删除过程中把残段合成了新线 ⇒ 删完一律「旧快照 − 删除清单」与读回逐条比。
    • ⚠ reload_base 重载前会先 save():拆束那次 d16_push 读回差 1 段(0.25 mm 的 SCL 碎段,被旁边 0.25 宽的同网线整段盖住、 EasyEDA 合并掉了)没存盘,但命令写成 d16_push … | tail && reload_base,管道的退出码是 tail 的,失败被吃掉,reload 顺手把这版存了。 这回无害,以后这类链条一律 set -o pipefail 或不接管道。
    • ✅ SDA6/SCL6 布不通 → 用户选 CH224A 改挂主 I²C:泛洪诊断(d16_reach)U35.15/16 被 J36 一带原有布线圈在北半边。 两页原理图已改(verify_sch PASS / xpage 0,截图已发)、已导入(焊盘网名比对 0 意外)。
    • ✅ SCL 能接;SDA 被 U13 东侧三线束夹死(INA_ALERT / SDA / SCL 0.5 mm 并排,本体底下还有 SCL 的 2—5 脚连线) ⇒ 用户选拆 U13 这一束重布:d16_rip.py 12 组先后 × 线宽全都断 1 块以上;候选落点 30 → 100 + 逐步排序 INA:1 → SDA:1 → SDA(U36):2 → SCL:1 → SCL(U36):2(线宽 0.15)0 块断开,拆 20 段 / 1 孔、新 132 段 / 21 孔(~303 mm, U13 的 SCL 绕了 44 段 / 13 孔、INA_ALERT 36 段 / 5 孔),已下发。
    • ✅ 收尾(修正 3):R10 采样线 0.2 → 0.3(VIN_SW 属 PWR9A,DRC 线宽下限 0.3);删 3 个服务器扇出的孔(紧挨固定孔, 钻孔间距 0 / 8.5 mil —— d16_check 原来只查异网,已补同网钻孔间距);J36.4 / Q6.2 各补一个 GND 孔(新线切了铺铜 / 铺铜分块变了)。
    • ✅ 最终(快照 r2/_base_board_final_d16.json,图 r2/M18-d16-routed.png):2104 段 / 648 孔;重铺 147 填 135(功率铜面全填, 空的 12 块是 GND 小瓦片);DRC 间距 0 / 连接性 0,只剩插槽封装自带 5 条(老问题);board_check 118 / 121 (GND 6 只通孔 / 铺铜脚 + XTAL1/2 圆弧,都是老误报)。断面 VIN_SW 7.95 / L1 12V 13.20。
    • ✅ 2026-09-12 复核板面并恢复(用户问「现在板上是不是最后那版」):与 _base_board_final_d16.json 逐项比, 少了 16 块铺铜,全是内层地(GND-L2-07 / GND-L3-07;L2/L3 各 13 → 5),L1 55 / L4 66 一块没少,线 / 孔 / 弧 / 填充 / 164 件位姿全同; 另有 USB2 差分对 5 段直角改成 45° 斜切((46.4,63.77)、(46.9,63.27)、(48.5,63.27) 三个角)—— 四层精确净距重算 0 条, 是改好不是改坏,保留不动(恢复成直角属于倒退)。⇒ 只 pour_base --push 重建 147 块 / 填 135, 重铺前后线 0 变、孔 0 变、元件位姿 0 变;board_check 118/121、DRC 间距只剩插槽封装 5 条 / 连接性 0。 恢复前存档 r2/_base_board_before_repour_0912.json,恢复后基准 r2/_base_board_d16_restored_0912.json。
    • ⬜ 待用户:① 截图确认 A1B-PD / A3-CTRL 两页;② 客户端看一眼南带(尤其 U37 肚子底下 SPSP→VCC 那根 0.15 线、两处 L1 CC 走廊和小岛); ③ NTC2 绕了 108 mm / 12 孔、U13 的 SCL 绕了 44 段 / 13 孔 —— 通,不好看,要不要再收;④ git 未提交。
    • 已知:board_check 把 L4.1(3.5×5)读成 5×3.5(C18→L4.1 那段终点已伸进焊盘绕过);它不读铺铜、不读圆弧。
  • ⬜ 固件:CH224A 驱动(主 I²C 总线 0x22/0x23,读 0x09 / 0x50;禁止写 0x0A=5)+ 整板功率上限按 0x50 取。
  • ⬜ 丝印:J1「PWR 20V」、J23「USB」。

⭐⭐⭐⭐⭐⭐⭐⭐ 2026-09-11 晚:刀片重新布局 —— 主板转 180°、两条槽到顶部、U4 到右侧(进行中;设计记录 R2 §35)

用户:「重新优化刀片板布局」→「排线插槽和排线移到顶部,主板不旋转」→(电池排线 38 mm 够不到顶边,核账后) 「都挪,主板跟着旋转」 →「U4 移到右侧」。板框仍 90 × 68。下面这一节取代 §34 那版的布局,§34 的待办大半作废。

  • ✅ 几何:pcb_blade.PHONE_ROT = 180 + phone_xy();槽 y 56.86~59.86;七个孔 / 侧键跟着转(自检过)。
  • ✅ 摆位:pcb_place J5/J4 底层 rot 180、C60/J3 顶部服务带、U7 在 J4 正南 rot 180;功率链 flip_pwr() 刚体转 180°(F1 底 / U3 顶, 锚 (80, 27.6) —— 27.6 是让 U2 四边脚落 0.25 格点);U4 (60, 38) rot 0;九个区重划;自检过。
  • ✅ 修掉的老毛病:ctrl_hand 的上拉电阻 R40/R41 位号写反(SDA↔SCL 交叉)+ 盘朝向(改按网名反查、pad1 朝 U4); Rs1.1 的 4 A 只有一个 Ø0.3 孔进 L3(KELVIN_SENSE + VIAS_PER_PAD)。
  • ✅ L3:VO_P 岛到上半(U 形绕电池槽)、+12V 岛到下半(金手指 → F1);+12V 不再在 L1/L4 走线(PLANE_VIA_NETS)。
  • ✅ D±:dp_blade() 新走法,53.07 / 53.07 mm、每根 1 孔、零交叉。
  • ✅ 离线试布线工具:synth_pads.py(按新摆位刚体变换旧焊盘表,旧表 / 旧摆位存 r2/archive_blade_prerot_0911/)、 remote_blade.sh 加 NOSNAP=1(板上还是旧铜,不抓板)、pads_diff.py(下发后读回对账)、router 新开关 --gndvia / --vkeep。
  • ✅ 服务器试布线 11 轮(细节 R2 §35.5):按铜 0 个断网、router_check 精确净距 0 条(§34 那版断 5 网 + 27 只 GND 脚够不着)。 候选 = _blade_routes.r11new-v0.json(= 现在的 _blade_routes.json,--gndvia --first EFUSE_FLT,EDL_EN):678 段 / 275 孔(GND 51)/ 2012 mm, D± 53.07 / 53.07 mm。图 r2/M17-rot180-routed-offline.png。还有 16 只 GND 脚没打上孔(C13.2 J4.16 K4.1 Q3.2 Q4.2 U1.2/3/15 U2.1/16 U3.2 U4.9/21 U6.2 U7.2), 等铺铜后按 DRC 连接性看哪些真没接上。 这几轮修掉 / 新加的:U2 对格(U1_Y 27.6)、上拉 R40/R41 位号和朝向、排布器「连线不交叉」+「接芯片的盘朝芯片」(flip_2pin)、 U2 八只脚 + U4 两只脚的预扇出(router.QFN_FAN,U4 那两只就地换 L1)、--gndvia 放到信号之后且每个孔精确核净距、虚拟焊盘改名 qfan:。 _blade_stitch_pts.json(旧布局的缝合孔落点)已删,归档里有;_blade_board_vias.json 现在是 [](离线试布线用)。
  • ✅ 已下发(用户确认,= 板上现在这版)。下发前板上其实已经没有铜(§34 的 1207 段 / 359 孔不在了)、15 件被用户手动挪过 (U4 (67.3,13.8) rot 270、U5、U3、Rs1、Q1、R9~R15、R18、C13、C68、J3)—— 问过用户后覆盖;下发前整板快照 r2/_blade_board_prerot_0911.json(含那 15 件坐标)。 顺序:pcb_blade --slots/--holes → pcb_place --push(读回 82 件全对)→ pcb_route --dump → pads_diff(359 块焊盘和合成表逐块一致, 底层 rot 180 / 90 的推导都对)→ router --push 678 段 / 275 孔 → 缝合孔 72(服务器算,NOSNAP + 离线导线快照,布线孔当零长度导线一起避让)→ miter --push(4 轮到 0 直角)→ reload_base --blade(逐项相等)→ pcb_pour --push 25/25 → board_check 信号 56/56 → DRC。 DRC 第一遍 10 条 = PCIe 缺口 4 + GND 连接性 6(U1.2/3、U3.1/2、J4.12/16)⇒ 补 6 段 + 1 孔(U1 两只地脚接散热盘、U3.2 接 U3.1、J4.12 直连 J4.8 的孔、 J4.16 往南出排再下 L2,router_check 0 条后下发,已并进 _blade_routes.json)⇒ DRC 4 条,全是 PCIe 缺口 0.2 mm(§27.8 老问题),连接性 0。 最终快照 r2/_blade_board_rot180_final_0911.json,图 r2/M17-rot180-routed.png(离线渲染,= 缓存;板上另有斜切)。
  • ✅ PCIe 缺口 1.9 → 1.6(用户拍板,CEM 值):缺口边到 11/12 号指 0.2 → 0.35。fp_edge.NOTCH_W 改 1.6,fp_edge.py --notch 只重画封装板框层(个人库原件 + 工程库副本 a1ad3dea…,J1 实例引用的是它;改副本 + 重载 J1 就跟着变,焊盘网名 64/64 不变)→ pcb_blade --outline → 铺铜 25/25 → board_check 信号 56/56 → DRC 0 条。代价:插槽防呆筋配合余量少 0.15(CEM 卡本来就是 1.6)。 ⚠ lib_Footprint.search 在这台客户端上抛 o[r].match is not a function,fp_edge.py --push 走不通,要重建整个封装得先改成按 uuid 开。
  • ⬜ 待用户:② 机械:SIM 卡托转到左边会伸出板左沿 ~1.8、C60 顶离板边 1.0(顶边导轨 ≤ 1 mm)、 排线实物走一遍(尾插 55 mm / 电池 38 mm 都是从接口边往上翻,长度比原来宽裕);③ 客户端里看一眼摆位 / 丝印方向; ④ 小瑕疵:+12V_F 从 Cin 绕一大圈才到 R4(EN 分压上臂,电流很小),想收短可手改。
  • ⚠ 下面 §34 那一节的待办(HANDROUTE-90x68.md 5 个网 / 27 只 GND 脚)全部作废(布局整个换了)。

⭐⭐⭐⭐⭐⭐⭐ 2026-09-11 傍晚:刀片压到 90 × 68(进行中;设计记录见 R2 方案文档 §34)

用户:「完善刀片板」→ 中途改为「将刀片板压缩到 90*68mm」。可行性核账后拍板两处:手机上移、C60 留两槽之间;删 J2。

  • ✅ 原理图:B4-EDGE 删 J2、B3B-USB 的 U7 两侧对调(手性,零交叉)、J1 不进 BOM;verify_sch / xpage / port_audit 全过;截图自查过,⬜ 等用户确认。
  • ✅ 导入:uid_sync_base.py --blade 对齐 uniqueId(J2 挪到 zzdel_J2)→ 导入 → 82 件逐件对上 → 重抓焊盘。
  • ✅ 板框 90×68 + 两条槽 + 七个孔下发、pcb_blade --verify 过;旧铜(1183 线 / 424 孔 / 2 铺铜)清空,改前整板快照 r2/_blade_board_0911_before.json。
  • ✅ 摆位下发、读回 82 件全对(pcb_place.py 用 DX/DY/PX 平移;U7 手排 J4 正北;J3 挪服务带)。
  • ✅ D± 构造 router.dp_blade():42 / 40.5 mm、每根 1 孔(旧 6063 mm、67 孔);本机静态校核 0 处 < 0.15。设计规则已重推(差分线宽下限 0.15)。
  • ✅ 服务器布线第一轮:v0 19 条没搞定 / v1(--pf)21 条。根因:+12V 五根指出口被电池槽 + R19/Q2 + 先布的 +3V3 长线封死; 外加我引入的三处摆位退化(EFUSE_AUX 变窄、U6 离 J3 20 mm、C68 离 U5 7.9 mm)。已改:+12V 排 FIRST_NETS 最前; EFUSE_AUX 回 11.2 宽、SVC_E 先于 EFUSE_AUX、SVC_R 收紧;新摆位已下发读回。(第一版 remote_blade.sh 的 run.sh 0 字节空转,已修。)
  • ✅ 第二~四轮(细节见 R2 §34.5):+12V 通了;比变体一律看 blade_conn.py(按铜判连通),布线器自报的「铜分成 N 块」多半是误报。 DIS(R19+Q2)挪到电池车道正北(原位堵死 U4 往东的唯一走廊);第四轮最好 v3 只断 2 个网(+12V_F: R4.1、+5V: U5.1)。 router_check 查出六组都有 U2 角孔压线 −0.095。先按「过孔图口径」修(同 VIA_MAP_FIX),第五轮六组逐段不变 ⇒ 不是它; 真根因是 plan_all() FIRST_NETS 循环没把本轮已放的过孔打进占用图(route_groups 早修过同一对 EFUSE_EN/$1N147、$1N164/EFUSE_PG),已补。 顺带修:router_check 不再本机算缝合孔(假问题 + 违规)、board_check 刀片 +3V3 按布线网查、repair_route --cache + MODE=repair、reload_base --blade。
  • ✅ 第六轮(补上「已放的过孔进占用图」):非 --pf 三组精确净距 0;选 v2(缓存 r6-v2 = 现 _blade_routes.json),按铜断 5 个网;服务器定点补线接不上。
  • ✅ 已下发并收尾(= 板上现在这版):router.py --push 711 段 → 缝合孔 78(服务器算;布线器的 23 个地指母线 GND 孔保留)→ 斜切 241 角 → reload_base --blade 逐项相等 → 铺铜 25/25 → board_check 51/57 → DRC 间距 4(全是 PCIe 缺口老问题)/ 连接性 39。 中途修了一个斜切在凹侧压孔的角(DRC 7 → 4)。详细数和两个工具毛病见 R2 §34.6。
  • ⬜ 待用户定(报告里给了选项):
    1. 5 个信号网 / 7 只脚没接上($1N147 U2.15、EFUSE_PG U2.2、INA_ALERT、SDA、$5N71)—— 作业单 r2/HANDROUTE-90x68.md:客户端手补(环绕 / 忽略障碍模式,别推挤、别挪件), 或者继续自动化(要改结构:+12V 那条 L4 竖线和 D± 墙并排把东西向封死,或把 U4 往中间挪)。
    2. 27 个 GND 细间距焊盘铺铜够不着(J4 ×14、U2 1/16/EP、U3 1/2/7、U1 2/3、U4 20/21、Q4.2、K4.1、C13.2)—— 要加「GND 引出 + 过孔」一步(服务器算落点),或手补。
    3. PCIe 缺口 0.2 mm(§27.8);C60 焊盘离铣槽 0.45;手机顶离板边 1.0(顶边导轨 ≤1 mm);H2 螺钉头离板边 0.6。
    4. B4-EDGE / B3B-USB 两页原理图截图确认。 补完之后:reload_base.py --blade → pcb_pour.py --push → board_check.py → drc_read.py --blade --conn(--conn 新加,列连接性明细)。
  • ⚠ 投板前要确认:C60 焊盘离两侧铣槽 0.45;手机顶离板边 1.0(顶边导轨 ≤ 1 mm);H2 螺钉头离板边 0.6;PCIe 缺口 0.2 mm(§27.8,老问题)。

⭐⭐⭐⭐⭐⭐ 2026-09-11 中午:EasyEDA 自动布线评审 → 混合布线(进行中,板上现在只有锁定的预布铜)

评审(用户在客户端点了 EasyEDA 自带自动布线,快照 r2/_base_board_autoroute_0911.json,图 r2/AR-easyeda-autoroute-*.png): 信号网 112/112 全通、143 孔、铜长 ≈ 0.94 × MST —— 布通率远好于我们的 A*(r34 断 26)。但不能投板: ① L2/L3(设计 = 整层 GND)上走了 983 mm 信号,含 UP / USB5 / USB6 三对 D± 整段在内层;USB1/2 在 L4 上跨 L3 缝 5~6 次; ② 全板 0 个 GND 孔、无缝合,内层地只靠几只通孔地脚接地;③ Kelvin 当普通 +12V 网布(U13.10 从 J37.2 拉 19 mm 线 + 经 L3 接 Rs6.2); ④ XTAL2 走 L3、FB3V3 上 L1、$2N72 走 L2(各 2 孔);⑤ USB4 / USB6 对内孔数不等(1/3、2/3);FAN_12V_SW 走 L2; 另:它不知道 L1 西半 12V 大铜 / L4 入口链铜面不许走信号,上一版有信号线穿 L1 12V 区。

用户选定:混合布线(脚本 r2/ar_hybrid.py):

  1. ✅ --clear:删四层导线 2634 / 过孔 143 / 铜面 119(读回全 0)。
  2. ✅ --push --verify:r2/_base_preroute_0911.json(r34-v0 中 GND / +12V / 入口链 / Kelvin / XTAL1/2 / RBIAS / VDD18(PLL) / FB3V3 / $2N72 / +3V3_HUB / USB1~6 整网 + UP 两端到第一个孔,599 段 / 312 孔,离线预判 DRC 0/0)全部锁定下发。 读回 572 段(EasyEDA 合并共线段)/ 312 孔全锁;按「被同层同网同宽锁定线盖住」核对只差 J23 口 UP 两段 0.15 颈缩(疑被并进 0.28 段,布完查连通)。 r34 里这些网自己就没接上的:XTAL1–R723.1、VIN_SW–C4.2、+3V3_HUB 6 只脚、+12V R4.2 无孔,交给自动布线补。
  3. ⬜ EasyEDA 自动布线由用户手动点(pcb_Document.autoRouting 在 3.2.149 上不存在,--route 用不了): 布线层只勾顶层 / 底层、保留已有导线和过孔、忽略 GND / +12V / VIN_RAW / VIN_FUSED。 ⚠ 用户手动点了 5 次,全部「进度很快走完、无提示、零产出」—— 连清除布线后的空板也一样(上午同一窗口还能布)。已排除: 锁定(--unlock 全解;过孔的 modify({primitiveLock:false}) 不生效,只能删了按 5 参数原位重建)/ 已有铜违规(删了 UP 6 段 0.15 颈缩 + 微段后 DRC 只剩插槽封装自带 5 条)/ 飞线卡住(reload_base 后照旧;active 只表示「启用」)/ 层属性(四层都是 SIGNAL、未锁)/ 只读窗口(用户是清空后撤销恢复的,同一窗口)。客户端日志(sys_Log.sort())没有任何布线记录。 导出的布线输入(pcb_ManufactureData.getAutoRouteJsonFile)看着正常:route [1,2]、109 网全开、忽略的 4 网已剔除。 对话框只有「忽略网络」一项(没有层 / 保留移除 / 选中网络)。上午能布的那次 +12V 被布了 ⇒ 当时没忽略 +12V; 下一个嫌疑:忽略列表(带 + 的网名?)或服务端。预布铜仍在板上(569 段 / 312 孔,均已解锁)。 ⭐ 用户找到原因:自动布线要勾上内层才会跑(只勾顶 / 底层就是那种秒结束零产出)。
    • 试 1:L2/L3 各加整板 NO_WIRES 禁布区(ar_hybrid --keepout;API 没有「禁止过孔」类型)+ 四层全勾 ⇒ 内层 0 线、预布铜未动、 XTAL1–R723.1 接上了,但全板只新加 6 个孔(UP 4 / USB3 2),其余网都单层走 ⇒ 离线断 48 个网(r34 是 26)。 导出的布线输入里 prohibitedRegions 只有 path + layers、不带规则类型 ⇒ 布线器把整板禁布区当「连孔也禁」。快照 r2/_base_board_ar_keepout_0911.json。
    • 试 2(用户选定,进行中):ar_hybrid --strip --unkeepout --plane —— 删自动布线加的 1020 段 / 6 孔(预布铜补回 2 段合并连带删掉的), 删两块禁布区,L2/L3 层类型改成内电层(PLANE),读回确认。⚠ 内电层怎么接 GND 还没定(API 里没有内电层区域图元), 铺铜计划里 L2/L3 的正片 GND 铺铜要换掉;试不成就 ar_hybrid --signal 改回信号层。
    • 试 2 结果:内电层 + 对话框全勾 ⇒ 不跑;内电层 + 只勾顶 / 底 ⇒ 也不跑(我经 DOM 点「运行」,1 分钟零产出)。 ⇒ 结论:这版 EasyEDA 自动布线只有勾上「信号层类型的内层」才运行;「内层零走线 + 正常打孔」用它自带的布线器做不到。 已 ar_hybrid --signal 改回,读回四层 SIGNAL、板上 572 段 / 312 孔(= 预布)、无区域、无铺铜。
    • 试 3(用户选「只放开 L3」):内层改回 SIGNAL,对话框只勾 顶 / 底 / 内层2(L2 不勾),我经 DOM 设好并核对后点「运行」⇒ 也不跑(1 分钟零产出)。 ⇒ 汇总:只有四层全勾才运行(SIGNAL + 全勾 = 用户上午那两次);只勾顶底 / 顶底+内层2 / 内电层 都不跑 ⇒ 布线层勾选实际上只能全选, 避层只能靠禁布区,而整板禁布区 = 连孔也禁。L3 关键区禁布区已离线算好(会话临时目录 l3_keepout.json:D± 走廊 ±0.9、晶振、两颗 Buck、入口链,5 块 / 1429 mm²),未下发。
    • 试 4(用户选「关键区禁布 + 四层全勾」):ar_hybrid --zones 在 L2/L3 各建 4 块 NO_WIRES(_base_inner_keepout_0911.json:USB1~6 的 L1/L4 D± 段 ±0.9 + 晶振 / 两颗 Buck / 入口链,1578 mm²),我经 DOM 四层全勾点「运行」⇒ 能跑(进度到 ~65% 结束,约 6 分钟)。 快照 r2/_base_board_ar_zones_0911.json:2543 段(L1 491 / L4 978 / L2 51 / L3 1023)/ 445 孔;离线只断 6 个网 (+3V3_HUB〔r34 本来就没接全〕、CFGSEL0、SDA〔U13.4 / U35.23〕、VBUS_DET、VDAC1、VDAC4);预布铜完好(L1 303 段都还在 L1)。 问题:① 差分对不理禁布区 —— USB6 每根在 L3 上又并了一条(+58 mm、孔 2→5,碎段中位 0.27 mm),USB3 各多绕 5.5 mm / 1 孔; UP 全程它布、~90 mm 几乎都在 L3(L3 的近邻是 L4 器件层,没有像样参考);② L1 12V 大铜里:VIN_SW 0.3 线 18 mm(去 C4.2)、 风扇区 $6N45/48/49/64 / FAN_EN / +5V 10 个孔 + $6N49 7.6 mm、SCL3 2.5 mm;③ 内层普通信号 L2 17 网 / 321 mm、L3(去掉 UP/USB6)约 240 mm。 USB1/2/4/5 未被动。界面上的「报告错误」是器件库面板的按钮,不是布线出错。
    • 收拾(用户四项全选,按序做): ① ✅ USB3/USB6 重复路径删掉(ar_hybrid.strip({'USB3_*','USB6_*'}),398 段 / 8 孔;读回两对都回到预布、连通)。 ② ⏳ UP 对:自动布线布的 UP 铜已删(615 段 / 5 孔,预布两端到孔的段保留),用户在客户端用差分对布线(Alt+D)在 L1/L4 手布; 端点孔 U32 侧 (45.18, 39.83) / (45.78, 39.08),J23 侧 (82.5, 6.5) / (84.5, 8.88)。布完读板核长度差 / 孔数 / 间距。 ③ ⏳ L1 12V 大铜:断面校核(pour_base.check_cuts 喂板上快照)L1 12V 主块最窄 13.20 → 8.95(要 8.0),刀口就是 VIN_SW 那根 L1 线; 风扇区 10 孔 + $6N49 / SCL3 不是瓶颈 ⇒ 留着。C4.2 被 +12V Kelvin 线挡住、L4 接不过去 ⇒ 服务器扫描(c4_scan,孔不进焊盘) 用户选「接功率铜」:ar_hybrid.C4_FIX_*(孔 A (15.5, 45.125) → L1 4.5 mm → 孔 B (13.688, 41.0) → Rs6.1 旁 2.0 宽 VIN_SW 铜), 离线校验异网最小净距 0.218、L1 12V 主块回到 11.90。等 UP 布完再 fix_c4()。 ④ ⬜ 剩下断网:+3V3_HUB / CFGSEL0 / SDA / VBUS_DET / VDAC1 / VDAC4 —— 拟用对话框「所选网络」只对它们再跑一次自动布线 (会话临时目录 run_ar5.py:doCrossProbeSelect 选网 → 所选网络 + 保留 + 四层全勾 → 运行 → 轮询)。 ⑤ ⬜ 然后:缝合孔(stitch_points 喂板上快照)→ pour_base --push → board_check --base → DRC;内层关键区禁布区投板前要不要删再定。
    • ⚠ 所有布线搜索(含扫孔位这种小脚本)都上 10.5.5.13(用户 2026-09-11 纠正):本机只做快照 / 校验 / 下发。
    • 用户:「你来布完剩下的」(2026-09-11 下午),进度: · ✅ C4.2 改接(ar_hybrid.fix_c4(),服务器 c4_scan 选孔):L1 12V 主块最窄 8.95 → 11.90。 · ✅ UP 对:服务器 up_route.py --centerline --pre-only --free-3v3hub --pitch 0.52 —— 「DP 先 DM 跟」DM 总是 1012 孔(DP 抢 J37 东端 1.2 mm 窄缝); 改成整对当 0.78 宽线只在 L1 上 A*、再向两侧各偏 0.26 劈开:UP_DP 70.7 / UP_DM 68.8 mm(差 1.9)、每根只有预布 2 孔、全在 L1、 对内净距 0.213 / 对异网 0.195。为它腾位置删了 +5V / EFUSE_EN4 / HUB_RST 的自动布线铜 + +3V3_HUB 离 U32 > 8 mm 的远程线(ar_hybrid.apply_up)。 ⚠ 期间踩坑:strip() 补预布段不看网,把刚删的 +3V3_HUB 远程线补回来压在 UP 上 —— 已删、已修(指定 nets 只补这些网);读板 UP 对异网净距 0 处不足。 · ✅ 基准改为 _base_preroute_0911b.json(去 +3V3_HUB 远程线,加 C4 改接、UP、服务器补网 A),ar_hybrid.SUBSET 已指向它。 · ✅ 「所选网络」自动布线补 9 网(run_ar5.py:单选框要调 input 的 onChange,点 label 不生效):接通 VBUS_DET / EFUSE_EN4 / HUB_RST。 · ✅ 服务器 route_nets.py(按板上铜分连通块、每块挑代表、route_one 连树)补网 A:SDA / VDAC1(97 mm / 11 孔)/ VDAC4(142 mm / 13 孔)接通, +3V3_HUB 7 块 → 2 块;离线复核 0 处净距不足,已下发。 · ⬜ 还剩 3 只脚真被围死(线宽 0.150.2 也布不出):+5V J37.19(L1 上北 UP、南 USB4、下面 L4 是 U32 南排脚)、 CFGSEL0 U32.41→R711.1、+3V3_HUB C744.1+U32.52 那一块(U32 西南角)。要么挪 UP / 拆邻线重排,要么客户端手连。 · 快照:r2/_base_board_ar_final_0911.json(补 9 网后);服务器结果 _up_route.cl_b52.json、_route_nets.A.json。 · 用户选「继续拆邻线重排」:
      • ✅ +5V J37.19:UP 中心线整板障碍下加禁区重布失败(fix3.py),改走焊盘南沿:L1 y 31.0 东去接 J37.32(同为 +5V), 0.2 宽,离线校验异网最小 0.181(USB4_DP 孔 0.195)—— 构造 3 段已下发,读板 +5V 一整块。
      • ❌ U32 南 / 西南区(fix3.py --no-up --rip ... --order ...,服务器 8 种拆法 / 顺序,结果 _fix3.V1~8.json / _fix3.noup.json): 没有一种优于现状(现状断 2:+3V3_HUB 的 C744.1+U32.52 块、CFGSEL0;各变体最少断 3)。$4N596(U32.51→R724.1)一拆就布不回, 它和 C744.1 抢同一条出路;CFGSEL0/1、NONREM0/1、VBUS_DET 挤在 U32 南排 0.5 脚距出口,贪心 A* 解不开 ⇒ 保留现状不拆。
      • ⬜ 投板前必须手连(或微调 C744 / R711 摆位):C744.1→+3V3_HUB 主网(U32.52 = VDDA33)、U32.41→R711.1(CFGSEL0 strap)。 · 收尾第一轮:缝合孔 103(服务器 stitch_srv.py 喂板上快照算,本机复核 0 问题后下发)→ reload_base → pour_base --push (119 块填 110,功率铜面全填;L1 12V 主块 13.20、VIN_SW 7.41)→ board_check:没通 +3V3_HUB / CFGSEL0(上面两处)+ GND 6 只脚 (J1.2/J1.3/J26.1/J27.1 通孔、U13.7/U20.40 早就挂着「要补地孔」)。 · ⚠ DRC 查出内层短路 —— 我的锅:route_nets.py 第一版只拿 L1/L4 的线当障碍,补网 A 的孔戳在 EasyEDA 留在 L2/L3 上的线上 (VDAC1 孔压 L3 SDA、+3V3_HUB 孔压 L2 NTC2 = 0 mil;SDA / VDAC1 孔离内层线 3.9 / 4.9 mil;+3V3_HUB 叠孔 1 对);本机复核也只查了 L1/L4。 另 USB5 对内 5.6 mil(「所选网络」那轮 EasyEDA 在 USB5 上加的碎段)→ strip(USB5) 已清。 修法:route_nets.py 加内层线挡过孔(包 grid_now,沿内层线在 L1 过孔图上封一圈)——但整网重布 VDAC1 要 273 mm / 25 孔,不要; 改用服务器 via_nudge.py 只挪那 4 颗孔(0.141.47 mm,改接线长度基本不变),删 1 颗叠孔;本机四层全查 0 问题。 ⇒ 以后本机复核过孔一律查四层。基准 0911b 已同步(挪孔 + J37.19 改接)。 · ✅ 收尾第二轮(= 板上现在这版,快照 r2/_base_board_final_0911.json,图 r2/AR-hybrid-final-4layer.png): 挪孔下发读回 0 缺 → reload_base 逐项相等 → pour_base --push 119 填 110(功率铜面全填)→ board_check:110 / 113 全通, 没通 = +3V3_HUB(C744.1+U32.52 块)、CFGSEL0(U32.41–R711.1)、GND 6 只脚(同前)→ DRC 间距错误 0 条真问题: 剩插槽封装自带 5 条 + UP 在 J23 口 0.15 颈缩 6 条(USB-C 盘 0.3 宽,已知);连接性 39 条(这个计数不可靠,以 board_check 为准)。 板上:1779 段(L1 629 / L4 1024 / L2 53 / L3 73)/ 567 孔(GND 346 含缝合 103)。L2/L3 上 8 块关键区禁布区还在(投板前删不删待定)。 · ⬜ 待用户:① 手连 C744.1→+3V3_HUB、U32.41→R711.1(投板前必须);② U13.7 / U20.40 补地孔;③ 内层禁布区删不删; ④ 手连之后跑 pour_base --push + board_check --base + drc_read --base。 · ⚠ 用户手连之后(2026-09-11 傍晚)板子变坏了:客户端推挤把 U32 周围一片线挤走,对「手连前」快照删 156 段 / 加 176 段, board_check 没连通 3 → 13(新断 XTAL1/XTAL2〔各 4 块〕、NONREM0/1、VBUS_DET、CFGSEL1、HUB_RST、VBUS_UP、BLD_A、BLD_REF、 +3V3_HUB 改断 R716.2;CFGSEL0 仍断);好的一面:C744.1 接上了、U13.7 / U20.40 挨上铜了(新增 GND 孔 23)。 ⚠ 我跑的 pour_base --push 在 JS_DEL 那步删完铜面就 save() 了(随后被我停掉:119 块只建回 96、未填)⇒ 用户的手改已被存盘, 「回到上次保存」回不到手连前。手连前完整快照:r2/_base_board_final_0911.json(1779 段 / 567 孔)。等用户定怎么恢复。 · 用户选「按快照恢复、留地孔」:手改后的板备份 r2/_base_board_usermanual_0911.json;用户新加的 GND 里对手连前铜(四层)不冲突的 14 孔 / 30 段留下(r2/_usermanual_gnd_keep.json)。restore.py(会话临时目录):删四层全部线 / 孔 → 下发快照 + 留下的 GND。 ⚠ 第一次批量删漏了 4 段(EasyEDA 删到一半在拆 / 并段,ID 变了),脚本的「没删干净就停」挡住了;逐段再删后重跑,下发 1809 段 / 581 孔 0 失败, 读板回到 110 / 113(U13.7、U20.40 挨上地了)。 ⚠ 但 DRC 间距错误 5 → 50(0 mil 碰铜一片):用户手连时还挪了 17 个元件(Y1 / C740 / C741 / R723 晶振一团、C744 / C745、 R709R712 / R716 / R719 / R720 strap 电阻、R4 / R5 / R6 / U29),旧走线对不上新焊盘;board_check 用的是旧焊盘文件看不出来。 ⇒ 按 _base_place_live.json(其余 128 件与板上逐件一致,= 手连前摆位)用 place_base.push(only=17 件) 挪回,读回 145 件全对。 ⇒ 以后判恢复要同时比元件位置;读板判连通前若怀疑动过件,先 place_base --dump 重抓焊盘。 挪回后 DRC 仍多 15 条(晶振一带 XTAL1/2 碰 Y1 焊盘、SDA5 碰 XTAL2,西侧 +12V 填充压 BLD_A / R6.1): getPrimitivesInRegion 查出手连还留下 10 段圆弧(XTAL1/2 ×9 在 Y1 周围、+12V ×1,客户端布线的圆角)和 2 块 +12V 填充 (L4,(4.46.7, 42.944.9) 与 (2.25.1, 50.053.2);底板脚本从不画填充,V12_PATCH 是铺铜)—— 恢复只重建了线 / 孔,它们还在手连时的位置。 ⇒ 全部删除后重铺 + DRC。恢复 / 对比板面要查全图元类型:线、孔、圆弧、填充、元件位置。 · ✅ 恢复完成(= 板上现在这版,快照 r2/_base_board_restored_0911.json):圆弧 10 / 填充 2 删净 → pour_base --push 119 填 110 (功率铜面全填)→ board_check 110 / 113(没通 = +3V3_HUB 的 C744.1 块、CFGSEL0 U32.41–R711.1、GND 通孔 J1.2/J1.3/J26.1/J27.1; U13.7 / U20.40 这回挨上地了)→ DRC 间距错误只剩插槽封装自带 5 条 + UP 在 J23 口 0.15 颈缩 6 条(连接性 26 条不可靠)。 板上 = 手连前(_base_board_final_0911.json)+ 用户新加且不冲突的 GND 14 孔 / 30 段。 · ⬜ 仍待用户:C744.1→+3V3_HUB、U32.41→R711.1 两处手连(布线模式先切「环绕」或「忽略障碍」,别用推挤;也别挪件); 内层 8 块关键区禁布区投板前删不删。手连完:pour_base --push → board_check --base → drc_read --base。 · 🔁 用户随后要求「恢复我刚才手连的那版」:restore_manual.py(会话临时目录)清空线 / 孔 / 圆弧 / 填充 → 17 件挪回手连位置 (place_base.push)→ 按 _base_board_usermanual_0911.json 下发 1799 段 / 582 孔 + 8 段圆弧(快照里有完整参数;后来区域查询多出的 2 段小弧来历不明,没恢复)+ 2 块 +12V 填充 → place_base --dump 重抓焊盘 → 重铺 → 连通 / DRC。 三版存档:手连前 _base_board_final_0911.json / 恢复版 _base_board_restored_0911.json(摆位 _base_place_live.premanual.json、 焊盘 _base_pads.premanual.json)/ 手连版 _base_board_usermanual_0911.json。 ✅ 已恢复(快照 r2/_base_board_manual_restored_0911.json):读回线 1810(拆段)/ 孔 582 / 弧 8 / 填充 2、17 件位置 0 不符;重抓焊盘; 重铺 119 填 109(功率铜面全填);DRC 间距错误只剩插槽封装自带 5 条。 ⭐⭐ 更正:「手连挤断 11 个网」是我误判 —— 当时 board_check 用的是件挪动之前的旧焊盘文件。重抓焊盘后用户那版: CFGSEL0、strap 网、BLD_A/BLD_REF、HUB_RST、VBUS_UP 全通。board_check 报 XTAL1/XTAL2「3 块」也是误报 —— 它不读圆弧, 手连的圆角圆弧就是连接段;按弦近似算进去两网都是一整块。 真没通的只有:① +3V3_HUB:C746.1 / C750.1 / U32.5 / U32.64 那块与主网断开,最近缺口 ~0.9 mm(L4,(48.63, 41.38)↔(49.25, 42.03), 手连前这里有一段 0.3 线,被改线拆掉了);② U29.3(GND,U29 被挪 + 转向)一根铜都不挨,看 L4 地铜盖不盖得到。 ⬜ board_check.py 要补读圆弧(pcb_PrimitiveArc),否则手连出来的板永远误报。 用户问「看着连上的线 DRC 却报、重连一次就不报」= API 批量建线后客户端的连接性索引没刷新(drc_read.py 头注早记过)。 reload_base(逐项相等)→ 重铺 119 填 109 → DRC 连接性 357 → 2,间距错误仍只剩插槽封装 5 条;XTAL1/2 DRC 不报(它认圆弧)。 剩下 2 条连接性 = +3V3_HUB 那块(~0.9 mm 缺口)+ U29.3 地。⇒ API 下发后跑 DRC 之前一律先 reload。
    • 自动布线对话框可经 DOM 操作(布线 菜单 → 自动布线...;单选:布线网络 / 拐角 / 已有导线过孔 保留·移除 / 效果优先级; 勾选框 [class*=checkbox_] 依次 = 所有层 / 顶层 / 底层 / 内层1 / 内层2(active 类 = 勾上);忽略网络列表会记住;按钮「运行」)。 点法照 pour_base.JS_REBUILD 的 React onClick;脚本在会话临时目录 run_ar.py / dom3.js / dom4.js,要长期用再收进 ar_hybrid.py。
  4. ⬜ 布完:只读快照 → 查内层 0 线 / 锁定铜未动 / 敏感网 / D± / 信号线侵入 L1 12V 与 L4 功率铜面(按真实走线做断面校核)→ 缝合孔(pour_base.stitch_points 要改喂板上快照,它默认读 _base_routes.json)→ pour_base --push 重建铜面 → board_check --base → DRC。 ⚠ 铜面现在全删了,不管后面做不做,最后都要 pour_base --push。

⭐⭐⭐⭐⭐ 2026-09-10 深夜:参考公开资料后用户拍板的一整套改动(进行中,按顺序做)

调研(Microchip USB2517 检查清单 / AN26.2 / AN15.17、乐鑫 ESP32-S3 布局指南、TI TPS54622 / INA226 / SPRAAR7、 Ott 叠层、多槽集群底板)之后,用户选定全部:

  1. 补 Hub 晶振 1 MΩ 跨接电阻(XTAL1–XTAL2)—— 底板和 R1 载板都补。检查清单 p9 原文: "A 1 MΩ resistor connected across XTAL1 and XTAL2 is required. Failure to place this resistor will result in unstable crystal operation.";数据手册 p52 Figure 9-1 也画着 1Meg。板上 XTAL1/XTAL2 只有 U32 / Y1 / C740 / C741 —— 漏了。
  2. Hub 改回 strap 模式(CFG_SEL=000)用 PRT_SWP 翻转北去三对 D± 极性,删掉 U 形掉头 —— 推翻决策 #19(仅底板;R1 不变)。
  3. U32 周边按检查清单重摆:VDDA33 每脚 0.1 µF、VDD18PLL 1 µF 紧贴,RBIAS 紧贴 + 独立地孔,晶振 + 1 MΩ + 负载电容成团。
  4. 敏感网单层 + 换层回流:晶振 / RBIAS / VDD18PLL / Buck 反馈 / Kelvin 只走 L4;D± 每根 ≤ 4 孔;换层孔旁补 GND 孔;天线附近缝合孔 2~3 mm。
  5. L2 由整层 +3V3 改整层 GND(双地,+3V3 ≈ 0.5 A 改走线)—— 改 D15 的 L2 决定。 顺序:原理图(1、2)→ 叠层 / 规则(5)→ 摆位(3)→ 布线规则(4 + 去掉 U 形掉头)→ 服务器重布 → 下发 → 铺铜 → 验收。

第 1 阶段(底板原理图,A4-HUB 页)进度:

  • a4_hub.py 已改(离线自检全过:38 件 / 137 段 / 短路 0 漏连 0 悬空 0):to_strap_mode() 把 13 脚端口改名 GND、 R712 上拉旗换地旗;add_straps() 加 R724R726(51/49/47 上拉 +3V3_HUB)、R727R729(33/31/17 下拉 GND)、 R723 1 MΩ(Y1 正上方 y=800,两端 XTAL1/XTAL2 端口);NC 表移出六个 strap 脚;B 块说明重写。
  • 1 MΩ 器件:lib_Device.search() 本机报 TypeError: o[r].match is not a function(传不传库 UUID 都一样), 改用 lib_Device.getByLcscIds(['C26083']) 取到,lib_Device.get() 核过是 0402WGF1004TCE 1MΩ;已写进 eda-parts.json。
  • 文档已同步:五路设计文档决策清单 19a、§21.4 晶振表补 1 MΩ 行;R2 方案文档 BOM 表 Hub 行。
  • U32 strap 脚在底板上的位置(2026-09-10 实测):51/49 在西(外侧 1.6~2.2 mm 是 C751/C749)、47/33 在南、31/17 在东, 外侧只有去耦和可改道的布线 —— 比 R1 载板(三颗 0 解)好,摆位阶段与去耦一起求解。
  • ⭐ 补一处关键遗漏:PRT_SWP 只在片内对调,原理图上 Hub 侧 1/2、3/4、6/7 脚的端口网名也要对调 (1 脚 → USB1_DP …,SWAP_ROWS),否则片内翻了、网表没翻,极性反而错。
  • 截图发现的三处排版问题(几何验证 / text_clash / flag_clash 都抓不到)已修: ① R728(31 脚)的 GND 端口标签被 R709/R710 的 VBUS_DET 输出端口 (90,360) 标签顶住 → 改挂一面朝上的地旗; ② 平移过来的 VBUS 分压块 VBUS_UP 标签压 R725 → 整块再右移 40(SHIFT_D=(40,380)),R725 右端改竖线接到 47 行(端点相交); ③ strap 电阻相邻两行只差 20:上一颗阻值字(Name={Value},本体下 15)落在下一颗位号(本体上 5)上 → 下发后用 sch_PrimitiveAttribute.modify(id,{valueVisible:false}) 隐藏 R724~R729 阻值字(R725 实测读回生效; 清页重画会冲掉,已写进 a4_hub.hide_strap_values() 随每次下发走)。 新增 port_label_clash():端口标签盒 vs 端口标签 / 两脚件本体,只报告不拦(R1 原有 VDD18 竖端口那条是盒子模型误报)。
  • verify_sch 的 FAIL = NC 表那 26 只故意悬空的脚(U32 ×24 + J23 A8/B8),逐只对上,预期内;xpage 漏接 0。
  • ✅ A4-HUB 已下发(第四版):38 件逐脚核对、strap 阻值字隐藏并存盘;verify_sch 的 ! = NC 26 只;xpage 漏接 0。 截图 a4_hub_000d.png 已发给用户,等用户确认方向与排版。
  • ✅ 用户确认排版(2026-09-11)。PCB 导入变更已完成:
    • 整页重画让 A4-HUB 31 件的 uniqueId 整体 +66(skill §9o-2),对话框原本是 31 移除 + 38 增加(整窝 Hub 删了重建)。 新脚本 r2/uid_sync_base.py 按位号两阶段写回(旧值快照 _base_uid_pre_sync.json),回查 138 件一致无重号。
    • 之后对话框(p1_import_rows2.py --base,已加 --base)只剩真实差异:增加 R723~R729 ×7、改网络 18 条 (U32 1/2、3/4、6/7 对调;13 脚与 R712.2 → GND;六只 strap 脚接新网;J23 CC 两个自动网名换号)、通道ID 属性 33 条。 pcb_import.py --base --apply --verify:145 件位号 + 封装逐件对上;板上焊盘网名逐项读回核对无误。
    • place_base.dump_live_positions 的件数断言由写死 138 改为读原理图 BOM 件数。

第 3 阶段(U32 周边按检查清单重摆)✅ 已下发并读回(place_base.py --only-hub --push,29 件, 其余 116 件钉在板上现位不动;145 件位姿逐件对上、离线镜像模型与板上焊盘最大差 0.003 mm、朝向断言全过):

  • ⭐ 用户拍板(2026-09-11):放开 U32 正南(J37 北排投影)/ 正北(J36 南排投影)两条窄带给 Hub 卫星件 (place_base.HUB_STRIPS,推翻「插槽过孔带不放件」在这两小段上的适用)。不放开的话 46 脚去耦只能到 7~8 mm、 5/10 脚去耦 >15 mm(47 脚夹在 46 西边只能南出;5/10 脚被 D± 围死)。
  • 摆法见 place_base.HUB_SAT 注释(逐脚手排,坐标相对 U32 中心,U32 挪了会跟着走);卫星件免 U32 扇出圈。 焊盘中心距:VDD18PLL C743 1.37 / RBIAS R708 1.67 / VDD18 C742 1.34 / VDDA33 5·10·52·57 = 1.29·1.29·1.38·2.97 / VDD33 46 C749 1.29 / 24 脚 C748 1.45 / VDD33PLL 64 C746 2.18;晶振 Y1 rot 0(X1 西北、X2 东南)+ C740/C741/R723 成团; strap 2.0~5.6(直流,不敏感)。C751 10µF 放 DP_H 车道东边(+3V3_HUB 入口)。
  • 南带:C749 正对 46 脚;45→40 脚的件往东排成阶梯(每脚一层,40 脚 33.15、往西每脚低 0.30),R709 挂在 44 脚那层延长线上; 最西的 R720 下沿到 y 30.27(进 J37 两排之间 ~1 mm)。这段 J37 北排槽针的过孔要改打到两排之间。
  • ⚠ 第 4 阶段要一起定的:① C750/C747(5/10 脚)地脚朝北,地孔拟打在正上方 J36 南排 GND 针 49.5 / 52.5 的盘中孔 (要塞孔);北去 D± 出脚各让 ~0.06 mm,USB3 在 C750 上方向西;② 57 脚的线要从 USB6 / 上行两组过孔之间斜下到 C745; ③ R716(HUB_RST 上拉)的 +3V3_HUB 脚在 J37 两排之间,要沿 L4 接回西南 R726 那一撮; ④ HUB_CX 东移 2.5 的理由(USB3 U 形掉头)已不存在,D± 构造重画时再决定回不回 49.0(卫星件坐标跟着 U32 走); ⑤ 板上 Hub 区的旧走线已全部失效,服务器重布前 --clear。

第 5 阶段(L2 由整层 +3V3 改整层 GND)✅(2026-09-11):

  • 代码侧上一轮已改好(pour_base 计划 GND-L2 ×13、route_base.PLANE_NETS = ('GND', '+12V')、board_check --base 把 +3V3 当布线网查);这轮补了 pcb_base / route_base 里还写着「L2 +3V3」的注释。
  • 板上:pour_base.py --swap-l2(新)只删 L2(Inner1)的 13 块 +3V3 铜面轮廓、按计划建 13 块 GND-L2,没有重填; 读回 L2 = GND ×13。填充留到服务器重布之后 pour_base --push 统一做。两层内层在 EasyEDA 里本来就是 SIGNAL(正片)层,不用改层属性。 离线 pour_base.py 自检全过,断面校核不变(VIN_SW 7.41 / L1 12V 13.20)。
  • ⚠ 后果(第 4 阶段处理):+3V3 现在是 47 只焊盘的布线网,其中五个槽各 3 只(南排 x 39.5、北排 x 40.5 / 41.5,在 L1), 以前一针一孔扎进 L2 就完事,现在要一条 ~0.5 A 的干线(线宽 0.4)把 U19 → U20 / U35 / U6 / U7 / 五槽串起来; 板上还有 34 个 +3V3 过孔原来是往 L2 扎的,现在悬空,重布 --clear 时一起删。

第 4 阶段(布线规则 + 去掉 U 形掉头)✅ 代码完成、本地静态校验全过,还没上服务器重布(2026-09-11):

  • D± 构造重画(route_base.dp_construct):口 13 片内对调后北去三对与槽上同序,usb3_uturn 删除。 USB1/2 阶梯 45° 上车道北上,到目标槽南排外侧东拐、在焊盘南端外打孔从南边进;USB3 贴着 C750 东侧直直北上, 在 J36 两排之间西拐(DP 48.2 / DM 48.9)打孔从北边进;USB4 越过 5/10 脚供电孔再东去 DP_H(47.75 / 48.25); USB5 出脚就东拐(43.75 / 44.25,C747 压在上面)。**USB15 每根 1 个孔**,构造长度差 0.5~1.9 mm。
  • U32 北沿(u32_north 重写):5/10 脚竖线北上 → 半路横接 C750 / C747 → J36 两排之间打孔上 L1 → L1 东去 x 66 落孔回 L4(虚拟落点 fan:e3v3 交给 A* 接 +3V3_HUB);电容地孔也在两排之间(不用盘中孔了);13/19 脚直连散热盘。 为此 C750 挪到 5 脚西侧 (49.20, 45.91)、C747 挪到 10 脚东侧 (52.95, 45.25)(焊盘中心距 2.52 / 1.93 mm): 5/10 脚除了去耦还要供电,窄带正上方是 J36 南排焊盘,供电孔只能落在两排之间,电容只能偏到供电线一侧; C744 南移 0.15(离 USB6_DM 西侧孔原来只有 0.04)。三件都已下发并读回。
  • 敏感网只走 L4(NOVIA_NETS,A* 过孔代价 1e9、路径一换层就作废记未完成):XTAL1/2、RBIAS、VDD18PLL、VDD18、 两颗 Buck 的 FB(U19.4 = FB3V3、U30.4 = 自动网名,按脚取);Kelvin 两条走同一个无孔 A*。
  • D± 换层旁补 GND 回流孔(dp_return_vias,离最近换层孔 ≤ 1.3、最远 ≤ 2.6,可共用):静态校验 7 处放了 6 颗 (上行 / USB6 共用一颗);每根 D± ≤ 4 孔(dp_via_limit,超了记未完成)。
  • 天线净空周边加密缝合(pour_base.stitch_points):离净空框 0.9~3.0 mm、间距 ≥ 2 mm,干跑 10 颗(西 6 / 南 3 / 北 1); 缝合孔统一落在 1 mm 子网格上,pour_base.stitch 删旧孔、route_base.load_board_vias 认缝合孔都改按子网格判。
  • 本地只跑了纯几何的构造 + 回流孔校验(j23_bridges / u32_north / dp_construct / dp_return_vias,不含 A*)。
  • ✅(2026-09-11 做完,见下面「第 4 阶段服务器重布」)服务器重布(remote_route.ps1 -AcceptAny,摆位 / 构造都改了,旧缓存没有可比性)→ --clear → --push --force → via_check → reload_base → pour_base --stitch / --push → board_check --base → DRC。 +3V3 这回先按普通网布一轮(47 只脚,其中五槽各 3 只在 L1),不行再上构造干线。
  • ⚠ 预判的难点(第 3/4 阶段一起设计):极性翻转后 USB3 从 U32 6/7 脚直接西北斜出接 J36 46/47; 夹在 USB2 与 USB3 之间的 5 脚(+3V3_HUB)原来往北出线,会被这对斜线挡住,脚距 0.5 旁边塞不下过孔; 10 脚同理。13 脚改 GND 后可从焊盘向内接散热焊盘(EP=GND)。u32_north / usb3_uturn / dp_construct 的 脚号 ↔ 网名假设都要按新网表改(现在 1 脚是 USB1_DP)。
  • ⬜ R1 载板补 1 MΩ(C2-HUB 页 + PCB2 找位置)。

第 4 阶段服务器重布(2026-09-11 上午):

  • 第 16 轮(-Variants @('--grid 0.125','--grid 0.125 --repair') -AcceptAny):v0 单轮胜出 147 条(拆线重布 50 轮 56 → 56 原地踏步,20 分钟白跑)。 总数比板上 160 版少,但信号网 25 → 40(降的全是 GND 打孔 80 → 51、+3V3 10 → 0);存 _base_routes.r16-147.json,没下发。 XTAL1 / RBIAS / VDD18PLL 仍然没通 —— 第 3/4 阶段就是冲着它们去的。
  • ⭐ 根因:D± 回流孔落进晶振通道。dp_return_vias 在所有信号网之前落孔、挑离换层孔最近的空格, 上行那组挑了 (45.12, 40.62) —— 正好堵死 U32.60/61 往西的直出通道(孔外径 0.6,y 40.22~41.02 全堵); XTAL2 只好北绕,绕出来的线又把 61/62/63 圈在它和芯片之间(NOVIA 不能打孔逃出来)。 ⚠ 起初以为是卫星件的 GND 孔 —— 不是,普通 GND 孔是 late(信号之后)打的。
  • ⇒ 新构造 route_base.u32_west()(在 dp_construct 之后、route_pairs / 回流孔之前,坐标全按焊盘推算): XTAL2 60 脚直西进 Y1.3(比脚心线高 0.07 让上行 DP 孔,3.85 mm);XTAL1 61 脚西去、从 Y1 上下两排焊盘正中穿过进 Y1.1 (盘距 0.5,0.15 线两边各 ~0.175,6.64 mm;Y1 对角焊盘、XTAL1 的盘在远端,这已是最短);VDD18PLL → C743.1 1.46 mm; RBIAS → R708.1 2.02 mm;VDDA33 57 → C745.1、52 → C744.1(各只有一条缝,回流孔改落 (45.25, 38.25) 时又正压 57 脚那条)。 本机静态校验(构造 + geom_check + 回流孔,不含 A*)全过;构造图 static_west.png(会话临时目录,没存)。
  • 第 17 轮(单轮 --grid 0.125,PyPy 153 s):144 条、信号网 38,XTAL1 / RBIAS / VDD18PLL 通了;新坏 $4N596(51 脚被 52 脚构造 + C744 挤到 只剩一条 ~0.19 宽的竖缝)。D± 七对与第 16 轮完全相同(USBUP DM 仍 8 孔超上限);NPTH 零压孔。已接受为 _base_routes.json。
  • 剩余信号网:Hub 本地 CFGSEL1 / VBUS_DET / $4N596 / +3V3_HUB(e3v3 落点、C744.1 另一头);回流孔 USB6 / 上行 ×2 落不下; 其余是老面孔(U20 扇出一带 EFUSE_EN3~5 / PRSNT2#/5# / NTC1 / INT# / RXD0 / UP_VBUS_DET / FAN_TACH_IN、U35 的 SDA1/4/5 / SCL4、 风扇区 $6Nxx / FAN_12V_SW / FAN_PWM_L、J23 VBUS_UP、+5V J37、SDA / INA_ALERT、VIN_SW C4.2、+12V R4.2)。
  • ⚠ +3V3 改布线网后占 367 mm 铜 + 47 孔(平面时 41 mm),约全板走线的 9%,U20 / U35 一带变差多半是它挤的 ⇒ 下一步考虑构造干线。
  • ✅ 第 17 轮已下发并验收:--clear(旧线 1857 / 孔 730)→ --push --force 2042 段 → via_check 642 孔逐个对上 → reload_base 通过(板上 1999 段:EasyEDA 把共线相接的段合并了,离线数出可合并 62 处,不是丢线)→ pour_base --stitch --push:缝合 116 颗、铜面 119 块填 113(功率铜面全填,空的 6 块是 GND-L4 碎瓦片),VIN_SW 最窄 6.80(要 6.5)→ board_check 没连通 35 个网,与布线器清单逐个对上(布线器自检另报 XTAL1/XTAL2/$3N173/NTC2「铜分成 2 块」,读板判全通,是它的误报)。 DRC 间距错误 21 条:+3V3/+5V 线到异网孔 4.9~5.4 mil ×15(新)、GND 回流孔离 USB3_DM 构造孔 5 mil ×1(新)、 UP 对内 7.3 mil ×4、板边 $1N114 1 mil ×1;另插槽封装自带 5 条、UP 对 7 段 0.15 颈缩。
  • ⭐ DRC 新增那 16 条的根因:占用图「过孔图」的口径错了(route_base.grid_now 与 router.route_groups / pf_solve 同一段写法): ① 已布线打进过孔图时 extra = VIA_D/2 + CLR − (halfw + ww/2),丢了这条线自己的半宽 —— 0.4 的线旁 0.625 处照样能落孔(净距 0.125); 正确是 VIA_D/2 + CLR − halfw(和 build_grid 里焊盘 / 旧孔同一套)。历来的「+5V 线离信号孔 4.9~5.4 mil」也是它。 ② 已布的孔只 block_rect 进了走线图、没进过孔图,free_via 看不见它们 —— 回流孔 / GND 孔可以贴着别人的孔落。 ③ 修 ① 后过孔图沿用走线图的保守取整(向外多封一整格),细线旁 0.625 这种合法落孔点也没了 ⇒ Grid.block_both / block_track 加 exact_v:过孔图用精确模式(格点中心在框内才封),线再补 VIA_EXACT_M = 0.04 的采样余量。 ⚠ router.py 前半段 plan_all / fanout(刀片那套)的 block_track 没改;刀片若再用 route_groups 会同样受益(更严)。 ⚠ 本机默认网格 G = 0.25,服务器是 --grid 0.125 —— 本机做静态校验 / 单测一定带上 --grid 0.125,否则取整会骗人。 单测(0.125 网格)6 点全对:0.4 线旁 0.625 封 / 0.75 放;0.15 线旁 0.625 放 / 0.5 封;旧孔旁 0.75 封 / 0.875 放。回流孔 7 处全落下。
  • 第 18 轮(西沿构造 + 过孔图修复,单轮 158 s):158 条,但信号网 38 → 37(新好 $2N70 $3N176 RXD0,新坏 $4N579 PRSNT1#), 多的全是 GND 打孔失败 49 → 63(第 17 轮 49 条里读板只有 5 只 GND 脚真不挨铜,其余靠铺铜连上 —— 基本无害); UP_DM 过孔 8 → 6(仍超 4)。离线预判 DRC:线到异网孔 0、孔到异网孔 0(第 17 轮 15 + 1,预判器拿第 17 轮验过与 DRC 一条不差)。 remote_route.ps1 按总条数判优劣把它拒了 ⇒ 手动接受(_base_routes.r18-158.json → _base_routes.json;第 17 轮存 r17-144.json)。 ⚠ 以后比较版本看信号网数 + 预判 DRC,别看总条数(GND 打孔失败会把总数搅乱)。工具:.claude/r2/route_compare.py [新] [旧](默认 新 = _base_routes.json、旧 = r17-144)。
  • ✅ 第 18 轮已下发并验收(= 板上现在这版,onboard.json 已更新):NPTH 零压孔;--clear → --push --force 2007 段 → via_check 627 孔逐个对上 → reload_base 通过 → 缝合 125 颗、铜面 119 块填 113(功率铜面全填)、断面校核同上 → board_check 没连通 34 个网(不挨铜的 GND 脚仍是 J1.2/J1.3/J26.1/J27.1/U13.7 五只 ⇒ 多出的 GND 打孔失败全被铺铜兜住)→ DRC 间距错误 21 → 1(只剩板边到 $1N114 1 mil;UP 对内 7.3 mil ×4 也跟着没了,同一个 bug);差分对 0.15 颈缩 6 段、插槽封装自带 5 条。

+3V3 定点构造(2026-09-11,用户选「定点改」,不做全套干线):

  • 服务器对照实验(route_base 新增诊断开关 --3v3-plane / --3v3-skip,结果只做对照、别下发):以第 18 轮为底, 信号网 37 → 33(每脚一孔的平面式)/ 29(完全不布) ⇒ +3V3 最多挡 8 个网,现实能救 3~5 个。 完全不布时救回:U20 组 EFUSE_EN4/5、INT#、NTC1、PRSNT1#/5#、UP_VBUS_DET;U35 组 SCL4、SDA1、SDA5;电源旁 $2N72。
  • 冲突定位(拿 skip 版里这些网的走线对第 18 轮的 +3V3 铜):① 西南段(电源经 x≈29 斜下喂 U13/C11 + y 56.1 那排孔)→ INT#/SCL4/SDA1; ② x≈40 附近 L1 跳线(J36 过孔带里绕 U35 西侧)→ SDA5;③ 北东支 y≈81 / 南东支 y≈55 / U20 的 +3V3 脚附近 → INT#/PRSNT5#/EFUSE_EN5。 离 +3V3 铜 1 mm 内卡住的脚只有 6 只,全是上拉 / 分压电阻的信号脚(另一脚就是 +3V3,横排里 +3V3 脚与信号脚交替)。
  • ⭐ 两条几何事实:插槽键位缺口(偏移 −5.5 / −4.5 没针,x 42.85~45.15)五个槽两排都没针没孔;但 NPTH 定位柱也在 x 44.15、槽心线上。 U13 四面被围(西 Kelvin、北 12V/VIN_SW 脚、东列信号、南 VIN_SW 铜),+3V3(6 脚)只能从东南角进;L1 在 x < 27.8 是 12V 主块, U13 的信号往东只能先走 L4 —— 旧走法从东北喂 U13 必然横在它们前面。
  • ⇒ route_base.v3_trunk()(u32_west 之后,诊断开关下跳过): A. U35 东侧旁路:J36 北排 9/10 L1 并线 → 槽内孔 → L4 y 51.0 东到键位列 x 44.0 → 北上穿 A4M 走廊 → y 67.6 西拐 → 孔上 L1 接 J35.40 / 9 / 10; B. U13 东南进:U13.6 ↔ C11.1 往下到 y 40.45 → L4 东去 → 孔 (28.5, 39.5) 上 L1 → y 39.5 东去横穿 I²C 车道 → 孔 (39.3, 39.5) 回 L4 北上 → 槽外孔上 L1 接 J36.40。 17 段 / 5 孔 / 62 mm,WALL 落点 3 个(旁路走廊中段 ×2、L1 横段中点);另查 L1 不压 12V 主块 / 指头。 ⚠ 起初 B 段走 y 41.9,服务器 geom_check 报「+3V3 线离 +12V 线 −0.325」—— 本机静态校验漏跑了 kelvin() / v12_handoff()。 本机静态校验要按 plan_all 完整顺序(Kelvin → 12V 过渡 → J23 桥 → U32 北沿 → D± → U32 西沿 → v3_trunk)。 ⚠ 代价:C11 本来就坐在 VIN_SW 铜的东北角里,y 40.45 这条线在 VIN_SW-2 顶端(x 17.119.75)划一道 ~0.7 的缝(电流去 Rs6.1 可从缝下过)。
  • ⭐⭐ 顺带挖出 U13 的 SDA / INA_ALERT 一直不通的真正原因:Kelvin IN− 把 U13 东列圈死了。 IN−(+12V,只走 L4、最先布)37 mm:U13.9 → 从芯片底下穿到南边 → y 41.25 东去 → x 23.5 北上 → 斜到 (27.75, 47.4) → 北上 50.5 → 沿 y 50.6 西拐到 x 15 → 下到 Rs6.2。它 + IN+(6.7 mm)+ Rs6 + U13 围成闭合圈,东列 SDA/SCL/INA_ALERT 在圈里:L4 出不去,L1 在 x < 27.8 是 12V 主块。 根源是引脚顺序与 Rs6 方向交叉:U13.10(IN+,在上)接 Rs6 南端 VIN_SW,U13.9(IN−,在下)接 Rs6 北端 +12V —— 两条都只许 L4,必有一条绕远。 最干净的解:原理图对调 IN+ / IN−(INA226 读数变负,固件取一次反)。⬜ 待用户拍板(改原理图 + 导入 + 固件)。 固件侧(firmware/components/drv/ina226.c):电流有符号,SW-8 反灌判据靠符号 ⇒ 输入那片(0x43)读数取反即可;ALERT 寄存器没配置过,无告警方向要翻。
  • 第 19 轮(定点构造第一版):信号网 37 → 40,但 +3V3 断成 6 块,结果不作数。两个原因: ① router.route_net 的 WALL 种子 bug:种子挑「出口最多」的焊盘(C9),src = 它的出口 ∪ WALL 格点,两者之间没铜 ⇒ 长成两棵树还报布通。 已修:有 WALL 就只从 WALL 里挑种子。 ② 我的 A、B 两段构造本身不相连(A 接 J36 北排 9/10、B 接南排 40,同槽两排不相通)⇒ 两组 WALL 在两块铜上。已修:B 的 L4 竖线北上穿 J36 槽内东接孔 J。 另:B 竖线 39.3 时它的孔在走线图上保守封到 x 40.0,XTAL1 绕 Y1 西侧的 2 格通道被堵 ⇒ 改 38.9。
  • ⭐ 布线器自己的 [连通性] 不可靠:第 18 轮它报「+3V3 铜分成 11 块」,读板 board_check 判通;穿盘而过的线也会报「一根铜都没有」。 ⇒ route_compare.py 加离线铜连通检查(按铜重叠并查集,平面网 GND/+12V 不判);拿第 18 轮验过:33 个网 = board_check 的 34 条去掉 GND 那条。 以后比较版本必须看这一项(信号网数 + 预判 DRC + 离线连通)。
  • 第 20 轮(构造连成一块 + WALL 种子修复):信号网 40,+3V3 仍断成 3 块。离线找两两最近点:两颗 +3V3 孔 (43.38, 57.50) / (44.62, 56.12) 贴着旁路竖段、差 0.12 没碰上 —— route_net 给 A* 的是 WALL 落点外一圈的出口格点(离中心 0.625),引出脚却只给种子那一只补, 其余 WALL 的出口只进了 src。已修:出口格点 → 所属落点记成字典,路径从哪个出口出发就给哪只落点补引出脚。 ⚠ 这版总数 158 与缓存打平,remote_route.ps1 自动接受了(它只比总条数)⇒ 缓存已换回第 18 轮(= 板上版本)。 remote_route 的接受判据以后该加「离线连通 + 信号网数」,现在先人工把关。
  • 第 21 轮(WALL 引出脚修复):+3V3 终于连成一块,DRC 预判 0 / 0,但信号网 37 → 44 (新好 CFGSEL1 VBUS_DET EFUSE_EN3/4/5 SCL4 SDA1 VDAC1 UP_DM FAN_12V_SW FAN_PWM_L $6N45; 新坏 19 个,集中在 U32 南侧 strap CFGSEL0 / NONREM0/1 / HUB_RST、U35 北侧 SCL1/2 SDA2 MUX_RST#、VDAC3/4、EFUSE_EN1、NTC2…)。 ⇒ 定点构造结论:不采用。+3V3 真连通之后比第 18 轮布线器自己的走法差 7 个网;「最多挡 8 个」是上限,定点改兑现不了。 v3_trunk 改为默认关(--v3-trunk 才启用,代码留着);两处 WALL 修复(种子只挑 WALL、全部 WALL 补引出脚)与离线连通检查是真 bug 修正,保留。 板上仍是第 18 轮,缓存 = onboard.json = r18-158。各轮缓存:r19 / r20 / r21(均未下发)。

⬜ 下一步(按收益排):

  1. +3V3 构造干线 → 定点构造做了 4 轮(19~21),不采用(+3V3 连通后信号网 44 > 37),保留第 18 轮。 ✅ U13 Kelvin IN+/IN− 对调(2026-09-11 用户同意):
    • 原理图 a1_input.py:删 R1 的三段(10 脚直通 Rs6.1、9 脚短竖线、9 脚并 VBUS 横线),9 脚折线进 Rs6.1、10 脚挂 +12V 旗 (原理图上也会交叉,所以用旗);说明并进右下【D】组(第一次放 (60,326) 压了 R6 旗的标签 —— text_clash 只比引脚点,截图才看出来)。 verify_sch PASS(U13.9 与 Rs6.1 同在 VIN_SW、U13.10 = +12V)、xpage 漏接 0、port_audit --r2 BASE 355 个可疑 0。
    • ⚠ a1_input.py --push 原来不存盘 ⇒ 新元件没有 uniqueId;存盘了也还是空,要打开一次「从原理图导入变更」对话框才分配 (gge176~200)。之后 uid_sync_base.py --push 对齐 25 件,对话框只剩:通道ID 属性 + 无名网络换号 + U13:9 +12V→VIN_SW、U13:10 VIN_SW→+12V。 --push 末尾已补 sch_Document.save()。pcb_import --base --apply --verify:145 件逐件对上。
    • 固件(firmware/,⚠ 这套是 R1 的固件):ina226_set_reversed(addr, bool) / ina226_is_reversed(),只对分流电压 / 电流取反, 默认全部正接(R1 不变);test_ina226 17 → 20 例全绿,host/test.sh 14 组全绿。R2 固件启动时要对 0x43 打开(R2 方案文档 §8.1 A3 那行已写)。
    • route_base.KELVIN:U13.10 ↔ Rs6.2、U13.9 ↔ Rs6.1。
    • ⚠ 导入之后发现板上布线不是第 18 轮(四层都有线、136 孔、无 GND 孔,疑似客户端里跑过 EasyEDA 自动布线,07:38 之后;摆位 145 件未动)。 已备份到 _base_board_backup_0911_0807.json(2654 段 / 136 孔),用户选「覆盖,按新 Kelvin 重布」。
    • place_base.py --dump 的 DUMP_JS 原来不自己开文档(skill §9l):导入后前台是原理图 ⇒ getAll() 回 null、报 reading 'map'。已补 openDocument。 重抓后 _base_pads.json:U13.10 = +12V、U13.9 = VIN_SW(836 焊盘 / 113 网)。
    • 第 22 轮(新 Kelvin,-AcceptAny):信号网 38、离线断开 35、预判 DRC 0/0。两条 Kelvin 缩到 7.3 / 6.7 mm(原 IN− 37 mm 的圈没了), U13 的 SDA / SCL 通了;INA_ALERT 仍不通 —— 2/5 两只 SCL 脚从芯片底下连(挡西)、SDA 出脚就斜向东北(挡东),3 脚夹死。
    • 第 23 轮(EARLY_NETS = ('INA_ALERT',),普通信号之前先布):信号网 35、离线断开 32(历来最好),INA_ALERT 通了, 但反过来 SDA / SCL 在 U13 断了。三个网抢同一侧出口,调顺序只能二选一。
    • ⇒ route_base.u13_fan():3 / 4 脚各平直东出 1.2 mm(0.5 间距上 0.15 线净空 0.35),末端给 A* 落点;2/5 两只 SCL 照旧。 第 24 轮:信号网 38、离线断开 35、预判 DRC 冒出 2 处(GND 线离 EFUSE_EN5 孔 0.03),SDA 从直出末端照样出不去 —— 卡的不只是出脚。 u13_fan 改为默认关(--u13-fan 才开)。
    • ✅ 下发第 23 轮(总体最好:信号网 35 / 离线断开 32 / 预判 DRC 0)。代价:U13 的 SDA / SCL 没接上(INA_ALERT 通)。 第 22 轮正相反(SDA/SCL 通、INA_ALERT 不通,但总数多 3)—— 缓存 r22 留着,要换随时换。 各轮缓存 r22 / r23 / r24。
    • ✅ U13 三个网整体构造出线(2026-09-11 下午,route_base.u13_esc(),默认开,--no-u13-esc 关;接替 --u13-fan):
      • 走法(全在 L4,39 mm,2 孔):SCL 2/5 在芯片底下 x 20.5 相连,5 脚东出 → 45° 下到 y 41.9 东去 → 孔 (28.3, 41.9) 上 L1 (J37 / J36 两个 12V 指头之间那条 L1 横带);SDA 4 脚 45° 东北 → 外壳盘孔阵两行之间 y 48.0 东去 → 孔 (33.0, 48.0) 上 L1; INA_ALERT 3 脚 45° 东北(与 SDA 平行,中心距 0.354)→ R9E 与 J36 外壳盘孔阵之间那条 1.4 mm 走廊 x 27.05 北上到 y 57。 三个末端各给 A* 一个落点(esc:SCL/SDA/INA)。本机静态校验:geom_check 过,早段 GND 孔阵(R9E.2 / J36.65)余量 0.235、一颗不挤。
      • ⭐ 第 22~24 轮的过孔快照里有 131 颗 EasyEDA 自动布线残孔(_base_board_vias.json 08:21,推第 23 轮之前抓的), 那三轮都把它们当固定障碍绕着布(U13 旁就有一颗 VIN_SW、U35 西头有 INA_ALERT / SDA 的)。 重抓后「其余」= 0(715 = 布线 593 + 缝合 122)⇒ 第 25 轮起才是干净的对照,第 23 轮的 32 不能直接拿来比。
      • 服务器结果(离线铜连通口径): · r25-v0 不构造(干净基线):31 个网断开,U13 的 SDA 不通 · r25-v1 构造、SDA 孔 31.5:33,U13 三网全通(多断的在 U35 西头 SCL4 / SDA1 —— SDA/SCL 真接上后占了穿 J36 北排那条 L4 带) · r25-v2 构造 + --no-early:37,INA_ALERT 反而不通 · r26-v0/1/2/4(INA 北端 53 / SDA 不打孔 / SCL 不打孔 / 组合):33,与 r25-v1 断开清单完全相同 · r26-v3 SDA 孔 33.0:29,U13 三网全通(逐块核过:SDA 7 脚 / SCL 8 脚 / INA_ALERT 3 脚各一整块),预判 DRC 0/0,NPTH 零压孔。 布线器自检报「SDA 分成 2 块 / SCL 3 块」是误报。对第 23 轮按日志口径:新好 SCL SDA SCL1/2 SDA1/2/5 MUX_RST# CFGSEL0 NONREM0/1 HUB_RST FAN_EN FAN_PWM IO0 EFUSE_EN1 $2N72;新坏 INT# NTC1 EFUSE_EN3/4/5 SCL3 XTAL1 CFGSEL1 VBUS_DET UP_DM(6 孔)$2N70 风扇区 $6N45/48/55/64。
      • 缓存:r25-v0/1/2、r26-v0~4(同名 .log 是服务器日志)。
      • ✅ r26-v3 已下发并验收(用户同意;= 板上现在这版,_base_routes.json = onboard.json = r26-v3): npth_base --check-routes 零压孔 → --clear(1953 段 / 715 孔;清完读板四层导线 0、过孔 0,无内层残线)→ --push --force 1935 段 → via_check 618 孔逐个对上 → reload_base 通过(板上 1870 → 1869 段,共线段合并)→ 缝合 113、铜面 119 填 111(功率铜面全填,空的 8 块是 GND 碎瓦片)、VIN_SW 最窄 7.41 / L1 12V 13.20 → board_check 没连通 30 个网(第 23 轮 33;其中 GND 那条是不挨铜的 5 只脚 J1.2/J1.3/J26.1/J27.1/U13.7, U20.40 / U30.1 这回挨上了 ⇒ 真断 29 = 离线预判)→ DRC 间距错误 1 条(板边到 $1N182 1 mil,老问题); 差分对 UP 0.15 颈缩 6 段、插槽封装自带 5 条,同前。
      • remote_route.ps1 改:每个变体都拉回本机 _base_routes.var<k>.json / .log(原来只留最优,对照组随远程目录一起删了)。 ⚠ 它仍按总条数自动接受:第一批自动接受了最差的 v2,第二批接受的 v3 碰巧是对的 —— 跑完一律按离线连通人工复核、手动换缓存。 route_base --u13e NAME=VAL(可多次)在变体里改 U13E_* 参数,不用改文件。
      • ⚠ A* 对小改动极敏感(一处改动就有 10~15 个网洗牌):单次结果看不出趋势 —— 先定好对照组,参数扰动开一批并行变体挑最好的(服务器 24 核)。
    • ✅ 第 23 轮已下发并验收(= 板上现在这版,onboard.json 已更新):NPTH 零压孔 → --clear(⚠ 只删 L1/L4 的 1926 段)→ 另删内层 15/16 遗留的 724 段(自动布线留下的;我们的设计从不在内层走线;循环删到 0,L1/L4 数目不变)→ --push --force 1986 段 → via_check 593 孔逐个对上 → reload_base 通过(145 / 1953 / 593 / 836)→ 缝合 122、铜面 119 填 113(功率铜面全填)、VIN_SW 最窄 7.41 → board_check 没连通 33 个网 → DRC 间距错误 1 条 (板边到 $1N182 1 mil,老问题,R9 串联段换了自动编号)。 ⚠ route_base --clear 只清 L1/L4 —— 板上若有别处来的内层线,要另删。
    • ⬜ 不挨铜的 GND 脚 5 → 7 只,新增 U20.40(ESP32 接地焊盘)/ U30.1(5V Buck 地),要补地孔(其余 J1.2/J1.3/J26.1/J27.1/U13.7 同前)。
    • ⬜ A1 页截图(a1_swap2.png 已发用户)等用户确认方向与排版。
    • ⚠ 改 .claude/r2/ 下的文件时别和 remote_route.ps1 同时跑:它先在本机 import route_base(间接 import place_base)再打包 —— 改到一半的 place_base 让第 22 轮第一次在 --snapshot-vias 就挂了。
  2. U32 南沿 strap / VBUS_DET 构造:$4N596(51 脚被 52 脚构造 + C744 挤到只剩 ~0.19 的竖缝)、CFGSEL1(42 → R712)、 VBUS_DET(44 → R710);+3V3_HUB 的 R716.2 / C749.1 / e3v3 落点。
  3. 回流孔:USB6、上行两处落不下(西侧孔带两边是晶振 / 57 脚构造);UP_DM 6 孔超上限 4。
  4. 风扇区($6N45/48/55/64/74、FAN_12V_SW、FAN_PWM_L)、J23 VBUS_UP、+5V 槽针(11 只全落空)、VIN_SW C4.2、+12V R4.2 —— 老面孔。 (SDA / INA_ALERT 已由 u13_esc 解决,第 26 轮。)U20 北沿一带 INT# / NTC1/2 / EFUSE_EN3~5 / PRSNT 这回集中断在一起,值得单独看。 U20 一簇诊断(2026-09-11 下午,进行中):
    • 切线量化(U20 以南横切 y 58/50/40/30、竖切 x 70/60/50):L4 每道切线空余 100+ 条道、只用了 4~7 条 ⇒ 不是通道容量; 失败的 13 个网一根都没穿过任何一道切线 ⇒ 是端点被围。
    • 新工具 reach_audit.py(只在服务器跑,占用图上泛洪;本机 /tmp 下 remote_audit.sh 打包上传):默认 = 每个失败网的连通域 + 围墙网; --dump 导出两层掩码(本机画图);--starts 按 route_net 的起步方式复核;--replay 在最终占用图上原样重放 route_net; --dbg-cmp 和 route_base --dbg-nets(失败那一刻存占用图)配对,逐格比「当时封、最终空」。
    • 结论分两类: · B 类(最终图上也不通,真被围):一道横贯全板、两层同时封死的南北墙 —— L1 是 U20 扇出后西去的横线带(y 5563、y 7683)
      • U20 西侧 x 70~75 那列扇出孔,L4 同一处是 U35 / U6 / U7 与它们的竖线、孔(围墙网:MUX_RST# RXD0 TXD0 IO0 +3V3 $6N49 GND)。 被它切断:INT#、NTC1、SCL3、$6N55、$6N64、FAN_PWM_L、+5V(J34/J35 段);局部口袋:NTC2(U20.38)、EFUSE_EN3(J36.11)、SDA4(U35.10)、PRSNT2# / PRSNT5#、风扇区几个。 · A 类(最终图上重放布得通,布线当时却报没布通):EFUSE_EN4/5、UP_VBUS_DET(第 26 轮还有 PRSNT1#)。最终图的铜只比当时多,本不该这样。
    • 顺带修掉 route_net 途中封过孔图的口径 bug(刚打的孔按 VIA_D + 2·CLR = 0.92 保守封 ⇒ 应为 VIA_D + CLR = 0.76 精确;刚布的线少算了自己的半宽), 和 grid_now / route_groups 开轮建图同一套:router.VIA_MAP_FIX = True,route_base --old-viamap 退回旧口径做对照。 第 27 轮 A/B:旧口径与 r26-v3 一段不差(开关干净、结果可复现);新口径 离线断开 29 → 29、孔 618 → 602、预判 DRC 0/0 (新好 PRSNT1# PRSNT2#,新坏 FAN_TACH_IN PRSNT3#)。A 类没救回来 —— 这个 bug 是真的,但不是主因。缓存 r27-v0/1/2(均未下发)。
    • ⭐ A 类根因(route_base --dbg-nets + reach_audit --dbg-cmp,服务器 /tmp 下 remote_dbg.sh):失败那一刻比同一次运行的最终占用图 多封了 L1 458 格 / L4 276 格,按归属网的外框看全在各网自己的焊盘附近(SDA 的外框正好框住 U35/U6/U7/U20/R602)。 存缓存前没有改几何的后处理(只有 dedupe_vias),所以不是「后处理收紧」—— 是 route_net 途中给引出脚占位时挑形状用的是主图 g (种子那条干脆不传图),而 route_groups 真正发出去的引出脚用 gt,两边挑出的直线 / L 形不一样:占的是一个形状、发的是另一个, 每个网在自己脚边多封一小片「影子引出脚」,后布的网被它们挡死。route_groups 里本来有「两边判据必须同一张图」的注释,route_net 漏了。 ⇒ router.STUB_SAME_GRID = True(占位也用 gt;引出脚的过孔图口径和干线一致),route_base --old-stub 退回。 ⚠ 更正:第 28 轮 A/B 三组(新 / --old-stub / 扰动)与 r27-v0 逐段相同 —— 引出脚占位的形状在实际上没有差别,「影子引出脚」不是根因。 STUB_SAME_GRID 让占位与发线同判据,本身是对的,保留(对结果零影响)。缓存 r28-v0/1/2(= r27-v0,均未下发)。 存缓存前只有 dedupe_vias(只补线不删线)、单轮 --passes 1 ⇒ 失败时刻的铜应是最终铜的子集,那 458 格从哪来仍待查; 另一个嫌疑是 route_net 的起点 / 终点筛选(终点只取最近 24 个出口、种子按出口数挑)—— 最终图上的重放是按最终图重算的起终点,不一定和当时一样。 ⭐⭐ 真正根因(--dbg-cmp 扩展版):① 在失败那一刻的图上泛洪,J37.11 → U20.fan:20 确实不通(可达 3011 mm² 但碰不到)⇒ 不是 A* 漏路; ② 差异格点离本网最终走线中位 0.46~0.55 mm,四个样本点全都挨着一颗落点孔(SDA 构造孔 (33.0,48.0)、SCL 构造孔 (28.3,41.9)、 INA_ALERT 扇出孔 (78.5,76.63)、VDAC5 孔 (49.63,13.38)),最终缓存在那儿都有「两层各一段 0.2 宽」的 dedupe 补接段。 ⇒ 影子孔:扇出 / 构造的落点(虚拟焊盘)只挂在 L1,A* 要走 L4 时不在现成的孔上换层,而是在旁边 0.28 mm 再打一颗孔; 它在布线途中一直占着地方(走线图半径 0.7),布完才被 dedupe_vias 合掉(「同网过孔合并」每轮 5760 颗),最终图上看不见。 U20 西侧那列扇出孔本来就密,EFUSE_EN4/5、UP_VBUS_DET 就是被这些影子孔挡死的。 ⇒ router.VIA_LAUNCH_BOTH = True:落点形状标 'VIA'(扇出落点、U32 北沿 fan:e3v3、U13 构造的 SCL / SDA 孔;D± 构造的落点暂不动)的, route_net._nodes 把孔心另一层也算出口(种子 / WALL 多源 / 终点三处都用它)。 第 29 轮 A/B:影子孔 59 → 25 颗(剩下的是没改的 D± / GND 落点),但离线断开 29 → 32(新好 INT# NTC2,新坏 RXD0 TXD0 SDA2 PRSNT1/2/3#), EFUSE_EN4/5、UP_VBUS_DET 仍不通;旧行为对照与 r28-v0 一段不差。⇒ 影子孔是真 bug,但去掉它救不回这几个网 —— U20 西侧就是挤,贪心顺序此消彼长。VIA_LAUNCH_BOTH 默认关(--via-launch 才开,代码留着)。缓存 r29-v0/1/2(均未下发)。
    • 现在的默认 = r28-v0(VIA_MAP_FIX + STUB_SAME_GRID 开):离线断开 29、孔 602、预判 DRC 0/0,与板上 r26-v3 同分 ⇒ 不重新下发。
    • ⭐⭐ B 类那道南北墙是摆位 / 引脚分配层面的结构问题(查了墙上各网的两端): FAN_TACH_OUT / FAN_TACH_IN / FAN_EN 从 U20 南排(y 60.2,x 8286)一路西去西北角风扇电路(x 1727、y 75~95),MUX_RST# 从 U20 北排西去 U35.3(36.7, 62.2), 这几根东西向长线加 L4 的 U35 / U6 / U7 一片,在 y 6080 合成两层都封死的墙;往南去槽的 INT# / NTC / EFUSE_EN / PRSNT 全要穿它。 (RXD0 / TXD0 / IO0 / EN 只是北去 J33,短,不是主因。)ESP32 在东、风扇在西北、槽在西南 —— 布线器怎么排都是此消彼长,第 2629 轮四组 A/B 已证。 可选方向:① ESP32 引脚重分配(GPIO 矩阵可任意映射:风扇那组换到 U20 北排 / 西列靠北的脚,槽信号换到南排)—— 改原理图 + 固件引脚表,要先做可行性扫描; ② 风扇电路整体东移靠近 U20(改摆位);③ 定点构造给 INT# / NTC1 / NTC2 等穿墙网画死通道(像 u13_esc,副作用不可控);④ 剩下的在客户端手工补。 ✅ 用户选 ①(2026-09-11)。现分配方向全反:NTC1/NTC2 在北排东头(IO1/IO2)却要去南边(RT601 (85,26) / RT602 (19,8)),风扇组在南排(IO4~7)却要去西北角。 试算不先改原理图:route_base --u20-remap A|A2 只改布线器读到的 U20 焊盘网名(自检:每个网在 U20 上仍恰好一只脚), 缓存记 u20_remap、--push 看到就拒绝;route_compare 按同一张表换网名做离线连通。候选(模组脚 → IO,全走 GPIO matrix,不碰 USB / UART0 / strapping / PSRAM): · A :4(IO4, ADC1_CH3)=NTC1 5(IO5, ADC1_CH4)=NTC2 39(IO1)=FAN_TACH_OUT 38(IO2)=FAN_TACH_IN 24(IO47)=FAN_PWM 25(IO48)=FAN_EN 6(IO6)=UP_VBUS_DET 7 / 23 空出 · A2:同 A,FAN_PWM / FAN_EN 放 23(IO21) / 24(IO47),25 空出 引脚审计(s3_pin_audit.audit(),R8 八线严格口径):现分配 / A / A2 都是 ERROR 0,WARN 同为 IO0 strapping + UART0 两脚(有意为之)。 第 30 轮(离线断开,基线 r28-v0 = 29;缓存 r30-v0/1/2,带 u20_remap 标记、不能下发): · A:29(新好 INT# NTC2 UP_VBUS_DET $6N64;新坏 EFUSE_EN2 PRSNT2# RXD0 TXD0) · A2:29(同 A) · A + --via-launch:28(新好 NTC1 NTC2 INT# EFUSE_EN5 PRSNT5# $6N64;新坏 EFUSE_EN2 PRSNT2# RXD0 TXD0 SDA2)—— 目前最好;预判 DRC 均 0/0 RXD0/TXD0 断是 A 自己造成的:它们固定在北排 36/37(UART0)要北上 J33,A 把风扇测速放在北排东头 38/39 往西走,正好横过。 ⇒ 方案 B:38/39 空出,风扇整组集中到西列北段 22(IO14)=FAN_TACH_OUT 23(IO21)=FAN_TACH_IN 24(IO47)=FAN_PWM 25(IO48)=FAN_EN, PRSNT5# 挪到南排 7(IO7),NTC1/NTC2 = 4(IO4)/5(IO5),UP_VBUS_DET = 6(IO6)。引脚审计 ERROR 0(同上 3 条 WARN)。 第 31 轮(缓存 r31-v0/1/2,带 u20_remap、不能下发):B:27(新好 FAN_TACH_IN NTC2 PRSNT5# UP_VBUS_DET;新坏 PRSNT2# RXD0)· B + via-launch:28 · A2 + via-launch:27;预判 DRC 均 0/0。 六组重分配合看:NTC2 六组全通、INT# / PRSNT5# 四组通、UP_VBUS_DET 三组通(方向对了、收益稳定); 但 RXD0、PRSNT2# 六组全断(系统性代价,不是洗牌);EFUSE_EN3/4/5、PRSNT3#、SCL3、SDA4 仍不通 —— U20 一簇 12 → 10,没根治。 RXD0 为什么断:扇出孔落在北排焊盘南边(84.875, 76.625,模组本体一侧),J33 在北 18 mm —— fanout() 只挑「离焊盘最近」的空格点 (奇数脚再往外错 1 mm),不管这个网要去哪;重分配后北排周围格局变了,最近的空位正好翻到背离目的地那一侧。PRSNT2# 同样是扇出孔那头没接上。 ⇒ fanout() 加 --fan-dir W(默认关):候选按 距离 + W × 背离程度 排,目的地 = 本网其余脚(含落点)的质心。 第 32 轮(r32-v0/1/2):B + fan-dir 1.0:29 · B + fan-dir 2.5:28(EFUSE_EN3/4/5 全通,但新坏 EFUSE_EN2 PRSNT2# RXD0 SCL2 SDA3 SDA5 VDAC5 $3N192)· 不重分配 + fan-dir 1.0:31 ⇒ --fan-dir 不采用(RXD0 照断、洗牌更大;默认关,代码留着)。
    • 小结(2026-09-11 晚,停在这里等用户拍板):U20 一簇做了 7 轮服务器试算(第 2632 轮)。 · 布线器两个真 bug 已修、默认开:途中封过孔图口径(VIA_MAP_FIX)、引出脚占位与发线同图(STUB_SAME_GRID)⇒ 默认结果 = r28-v0(29 个,孔 602)。 · 影子孔(--via-launch)、朝目的地扇出(--fan-dir)都是真现象,但去掉后不净增 ⇒ 默认关。 · 唯一稳定的正收益是引脚重分配:方案 B 29 → 27(NTC2 / PRSNT5# / UP_VBUS_DET / FAN_TACH_IN 通;代价 RXD0、PRSNT2# 在所有重分配变体里都断; EFUSE_EN3/4/5、NTC1、PRSNT3#、SCL3 仍不通)。 · 落地 B 要改:A3-CTRL 原理图(pinmap_base 那套删端口重建;24/25 要新接线、38/39 改 NC,不是纯互换)→ pcb_pinswap.py 的 WANT 表 → a3_ctrl.py GPIO 表 / _esp32_pinmap.json r2_base(本来就过时)/ R2 文档 GPIO 表 → 服务器正式重布 → 下发验收; RXD0 那一截(扇出孔落在北排南侧)另行手补或单独构造。 · 板上仍是 r26-v3,缓存 = onboard;各轮缓存 r25r32 都在 .claude/r2/(r30~r32 带 u20_remap 标记,--push 会拒绝)。 ✅ 用户选「落地方案 B」(2026-09-11),已落地(设计记录见 R2 方案文档 §29.6): · 原理图 u20_remap_b.py --push:A3-CTRL 删端口 / 旗 13、删线 7、J33 挪到 (450,420)、画线 7、建端口 12,读回改后状态逐项对上。 J33 挪位是因为 U20 的 24/25 两行(y 480/470)原被 J33 的地线和端口标签占着;J33 地线改接 U20 地干线节点 (320,440)。 · 验证:verify_sch 单脚网络 0 / 无名端口 0 / 一网多名 0(悬空 20 只全是有意空脚,数目与改前相同);xpage 真正漏接 0; port_audit --r2 BASE 354 个可疑 0;截图已发用户(⬜ 等用户确认方向与排版)。 · PCB pcb_pinswap.py --push(WANT = §29.5 那 9 只 + 方案 B 这 10 只,38/39 置空):19 只读回逐个对上; place_base --dump 重抓 _base_pads.json(836 焊盘,U20 与方案 B 表全对);pcb_import --base --probe:导入对话框 0 条变更。 · 源头表:a3_ctrl.py 的 PINMAP / U20_NC / J33 画法改成方案 B(顺带更正 §29.5 那 9 只一直没同步进来的网名),本地自检通过、 _esp32_pinmap.json 的 r2_base 由它重写;R2 文档 §20.6 更正、新增 §29.6。 · ✅ 第 33 轮(按真实焊盘重布)已下发并验收(= 板上现在这版,缓存 = onboard = r33-v0): r33-v0 与试算 r31-v0 逐段 / 逐孔 / 逐条日志完全相同(原理图 → PCB 焊盘 → _base_pads.json 一路与试算一致); 离线断开 27(新好 NTC2 PRSNT1# PRSNT5# UP_VBUS_DET,新坏 PRSNT3# RXD0)、预判 DRC 0/0、NPTH 零压孔;--via-launch 那组 28,不用。 --clear 1869 段 / 731 孔(清完四层 0 线 0 孔)→ --push --force 1953 段 → via_check 617 孔逐个对上 → reload_base 通过(并 4 段)→ 缝合 113、铜面 119 填 111(功率铜面全填)、VIN_SW 最窄 7.41 / L1 12V 13.20 → board_check 没连通 28(GND 那条同前 ⇒ 真断 27;上一版 30)→ DRC 间距错误 1 条(板边到 $1N182,老问题)。 · ✅ RXD0 补上 —— 第 34 轮已下发并验收(= 板上现在这版,缓存 = onboard = r34-v0): route_base --early-add A,B 试算开关;EARLY_NETS 默认改为 ('INA_ALERT', 'RXD0')(普通信号之前先布)。 第 34 轮三组(离线断开,r33-v0 = 27):+RXD0:26(新好 RXD0 INT# EFUSE_EN5 $6N64;新坏 SCL4 SDA2 VDAC1)· +RXD0,PRSNT3#:29(TXD0 断)· +RXD0,TXD0:28 ⇒ 取 +RXD0。预判 DRC 0/0、NPTH 零压孔。 --clear 1885 段 / 730 孔(清完四层 0 线 0 孔)→ --push --force 2056 段 → via_check 636 孔逐个对上 → reload_base 通过(并 4 段)→ 缝合 100、铜面 119 填 113(功率铜面全填)、VIN_SW 最窄 7.41 / L1 12V 13.20 → board_check 没连通 27(GND 那条 = 不挨铜的 6 只脚 J1.2/J1.3/J26.1/J27.1/U13.7/U20.40 ⇒ 真断 26,RXD0 通)→ DRC 间距错误 1 条(板边到 $1N182,老问题)。 · 方案 B 这一簇前后对比(离线断开):第 26 轮 29 → 第 33 轮 27 → 第 34 轮 26。U20 一簇仍断的:EFUSE_EN3/4、NTC1、PRSNT2#/3#、SCL3/SCL4、SDA2/SDA4、VDAC1。 · ⬜ 待办:A3 页截图等用户确认方向与排版;U20.40(ESP32 接地焊盘)补地孔;R2 固件写的时候按 R2 文档 §29.5 + §29.6 的引脚表。 · ⚠ 板上现在没有铺铜:想借 reload_base.CANCEL_JS 关导入对话框,import reload_base 却直接跑了它的主流程 (模块顶层就执行,没有 __main__ 保护)⇒ 删了 119 块铺铜边框、存盘、重载。重载前后元件 145 / 导线 1869 / 过孔 731 / 焊盘 836 逐项相等, 布线和元件无损。下发流程里本来就有「reload → 重铺」,走完下发 pour_base --push 会重建;若最后不下发,也要单独跑一次 pour_base --push 补回铺铜。 ✅ reload_base.py 已改:顶层 41 行收进 main() + __main__ 保护,import 只拿常量 / 函数(实测 import 后 CANCEL_JS 可用、不触发重载)。 落地路子(选定方案后):pinmap_base.py 改原理图 → pcb_pinswap.py 直接改 PCB 焊盘网名(§29.5 那次就是这么换的,不走导入对话框)→ a3_ctrl.py 的 GPIO 表、_esp32_pinmap.json 的 r2_base(⚠ 现在还是 §29.5 之前的旧分配,已过时)、固件引脚表一起改。
  5. R1 载板补 1 MΩ(C2-HUB 页 + PCB2)。

⭐⭐⭐⭐ 2026-09-10 深夜:填铜查通了、发现插槽定位柱压线,正在服务器重布(进行中)

已查清 / 已改(代码都在 .claude/r2/):

  1. 「重建所有」填 0 块 = 飞线计算卡住(skill §9ae-4)。新脚本 reload_base.py:数基线 → 删铜面边框 → save → reload → 再数、必须与基线逐项相等才放行。实测重载后导线 2222 → 2220(L4 丢 2 段,board_check 37 个网逐条不变,判为重合段被合并)。
  2. 重载后填出来了,但 83 块只填 61 块,缺的正是大电流铜:L1 12V 主块 + J37/J38 指头、L4 VIN_SW 主块、VIN_RAW 两块。 原因:读回的优先级不按创建顺序(整板 GND 拿到 18,功率铜 23~34),GND 先铺占完地盘。 ⇒ pour_base.plan() 加 pwr_cut:GND 底填在几何上挖掉同层功率铜面,不再依赖优先级(skill §9j 第 6 条)。 GND-L1 / GND-L4 各 13 → 31 块瓦片;断面校核不变(VIN_SW 7.41 / L1 12V 13.20)。 ⚠ board_check --base 判不了填充(按计划铜面判),填没填上要数 pcb_PrimitivePoured 对应的铜面名。
  3. DRC「挖槽区域」= 封装自带的 NPTH:五个 PCIE-SMD_PCIE-64P11L 插槽各两颗 Ø1.21 定位柱,在槽心线上 x 44.15 / 67.30 (e149 / e150)+ J23 两颗 Ø0.61(81.1 / 86.9, 6.456)(e55 / e56)。布线器原来没有这些孔: USB1 D±(北上车道正压 J36/J35/J34 西柱)、+5V、SCL3、FAN_EN、PRSNT4#/5#、VDAC5、UP_DP、UP_VBUS_DET、VBUS_UP 共 17 处压孔, 下单钻孔会切断。新脚本 npth_base.py(从 L2/L3 反焊盘拟合圆弧反解,写 _base_npth.json;--check-routes 校验缓存)。 route_base:NPTH 进 keep_rects()(全层、线 + 孔),构造铜的 geom_check / usb3_uturn 加 _npth_bad; USB1/2 北上车道 DPC_SHIFT 3.5/2.5 → 2.47/2.35(竖线 45.28/45.78/46.40/46.90,西界柱、东界 USB3 掉头孔)。 pour_base 缝合孔也避开 NPTH。
  4. 重布前的过孔快照是错的:router.load_vias() 读板上全部过孔,布线下发之后再重算会把旧的 604 个布线孔 + 122 个缝合孔 当固定障碍。route_base.load_board_vias() 已改为剔除「布线缓存里的(同网 0.2 内)」和「缝合网格上的 GND」。 顺带发现 USB3_DM 掉头孔在板上比缓存偏了一格 5 mil(-0.127, +0.127),原因不明,下发后要逐孔对位置。
  5. drc_read.py 以前把「差分对错误」「同封装内间距错误」两类吞掉(只剩标题),已补。

DRC 里另有待定(不是这次引入的):六对 D± 对内长度差 56~136 mil > 规则 1 mm → ✅ 用户定:放宽到 150 mil (rules_base 3.81 mm,R2 文档 §6.3 已记,规则已下发);UP 对 7 段 0.15 线宽(颈缩);GND 线碰 $6N45 线 0 mil(真短路,重布后再看); 插槽封装自己的外壳盘 65 压定位柱(封装内,库的事)。

服务器重布结果(带 NPTH 障碍 + 干净的过孔快照,-Variants @('--grid 0.125','--grid 0.125 --repair')): v1(拆线重布,17 分钟)胜出,169 条(旧 191):信号网 40 个(旧 40,换了 7 个:新坏 $3N173 $3N189 $6N64 FAN_TACH_OUT SCL3 UP_VBUS_DET VDAC1,新好 EFUSE_EN1 FAN_PWM_L INA_ALERT NTC1 PRSNT2# PRSNT5# RXD0),GND 打孔失败 85 → 67、+3V3 10 → 15。 npth_base --check-routes ✅ 零压孔;七对 D± 长度差 0.2~3.5 mm(USBUP 3.5 最大,规则 3.81 以内);⚠ USBUP 的 DM 打了 8 个孔(DP 2 个)。 已接受为 _base_routes.json,正在下发(--clear 已删旧线 2220 段 / 孔 726)。下发后先 via_check.py(逐孔对位置)再重载。

已下发并验收(169 条这一版):via_check 620 个布线孔逐个对上 + 缝合 109;reload_base 通过(导线少 3 段 ≤1%); 铺铜 119 块填 110,功率铜面全部填出(空的 9 块是没有本网孔的 GND/3V3 小瓦片);board_check 没连通 35 个网(旧 37); DRC 间距错误 40 → 18:压定位孔 27 条、GND 碰 $6N45 短路都没了。剩 +5V 线到信号孔 4.9~5.4 mil(要 5.9)14 条、 UP 对内线到孔 7.3 mil(要 7.9)4 条、板边到 $1N114 1 mil 1 条;另 UP 对 7 段 0.15 颈缩、插槽封装自带 5 条。

走线方向(2026-09-10 用户定):插槽面 L1 顺槽长走(x)、背面 L4 横穿槽阵走(y),逆向每格代价 ×2(软偏好)。 route_base 原来 RT.LAYER_DIR = {}(八方向同价),已改为 {2:'v', 1:'h'} + CROSS_COST 2.0,--no-dir 可退回对照。 下一轮服务器重布不加 -AcceptAny:比 169 差就进 _base_routes.rejected.json,不覆盖板上这版。

  • 第一轮(方向偏好对全部网生效):v0 细网格 168 条,信号网 40 → 30,GND 打孔失败 67 → 81;方向符合度(dir_stats.py) L1 顺 x 38.6% → 78.4%、L4 顺 y 31.9% → 67.7%;铜总长 4122 → 5051 mm(+22%)、过孔 620 → 652。 新坏 XTAL1 / RBIAS(U32 本地短连接)等 10 个、新好 20 个。存为 _base_routes.r14-dir168.json,没下发: ⚠ USBUP 长度差 3.5 → 5.7 mm(超 3.81 规则),USB6 / USBUP 过孔每根 2 → 4 个 —— 方向偏好把差分对也逼去换层了。
  • ⇒ route_pairs() 里临时 RT.LAYER_DIR = {}(D± 八方向同价,布完恢复),其余网照开 ×2。正在服务器重布。
  • 过孔快照改为按 _base_routes.onboard.json(route_base --push 成功后自动写,= 板上现在那一版)认布线孔; 以前按 _base_routes.json 认,缓存被没下发的新结果换掉后,板上 620 个旧孔会被当成固定障碍。 当前 onboard.json = r13-onboard169(板上现在就是这版)。
  • 第二轮(D± 八方向同价、其余网 ×2):v1 拆线重布胜出,160 条、信号网 27(历来最好),GND 打孔失败 80、+3V3 11; L1 顺 x 71.9%、L4 顺 y 64.3%;铜总长 4248 mm(比 169 版 +3%);七对 D± 与旧版完全相同(USBUP 3.5 mm ✓);NPTH ✅。 剩余信号网:$2N70 $6N45 $6N49 $6N64 $6N74 +3V3_HUB +5V EFUSE_EN2/3/5 FAN_12V_SW FAN_TACH_IN INA_ALERT INT# NTC2 PRSNT5# RBIAS RXD0 SCL4 SDA SDA1 SDA4 VBUS_DET VBUS_UP VDAC4 VDD18PLL VIN_SW。 ✅ 已下发(= 板上现在这版,onboard.json 已自动更新):via_check 643 + 缝合 87 逐个对上;重载通过(导线并 3 段); 铺铜 119 块填 109,功率铜面全部填出;board_check 没连通 26 个网(旧 35);DRC 间距错误 5 条(旧 18): UP 对内线到孔 7.3 mil ×4、板边到 $1N114 1 mil ×1;另 UP 对 7 段 0.15 颈缩、插槽封装自带 5 条。
  • U32 卫星件离脚太远(摆位问题,不是布线参数):VDD18PLL 去耦 5.6 mm、RBIAS 7.6 mm、晶振 3.9/5.1 mm、 +3V3_HUB 各脚到最近去耦 2.5~7.5 mm —— 这几个网正是一直布不通的那批。用户要求参考公开资料优化布局 / 走线,三路调研进行中。

下发顺序(重布结果回来后):npth_base --check-routes → route_base --clear → route_base --push --force → pour_base --stitch → pour_base --push → 数功率铜面是否全填 → board_check --base → drc_read --base。 路由缓存备份:_base_routes.r12-onboard191.json(= 板上现在这版)。

⭐⭐⭐ 下次从这里开始(2026-09-10 夜,清上下文前写)—— 已被上一节接手

板子现在的状态(EasyEDA BASE-PCB 7b9524b6aaf6f235,4 层,98×98):

  • 摆位已定稿并下发(138 件,5 插槽在 L1、133 件在 L4),读回校验全过。叠层 L1 槽面 / L2 整层 +3V3 / L3 整层 GND / L4 器件面。
  • 布线已下发(--force,带着未完成):导线 ~2213 段、布线过孔 604、GND 缝合孔 122。D± 七对全通。
  • 铜面轮廓 83 块已建,但没有填铜(poured = 0)。
  • 已处理:J1 通孔地脚里误打的 12 个过孔已从板上和缓存里删掉(route_base.TH_KEYS 的改名 bug 已修); 天线缺口西沿 90.5 → 90.8(U20 地脚 1/40 到缺口边 0.155 → 0.454 mm),板框已重画、校验通过。

下一步(按顺序):

  1. 填铜:在客户端手动「工具 → 铺铜管理器 → 重建所有 → 确认」。自动点击(pour_base.rebuild())在底板上返回 fired: true 但不出铜, 手动也不行再查(对照刀片 pcb_pour.py,它在刀片上是好的)。填完跑 /c/Python314/python .claude/r2/board_check.py --base。
  2. DRC「挖槽区域」26 条(对象 e149 / e150 / e55 / e56,板上图元里查不到):在客户端点 DRC 条目定位是什么。 e55/e56 疑为 J23 外壳腿长槽孔;e149/e150 散在几个插槽附近 —— 若是插槽封装里真铣穿的槽,从下面穿过的 USB1/USB2 会被切断,要先确认。 /c/Python314/python .claude/drc_read.py --base(--no-check 只读不重跑)。
  3. 补 37 个没连通的网(清单 = board_check.py --base 的输出;手工补或逐条构造):插槽针上的 EFUSE_EN14 / PRSNT13# / SCL4/5 / SDA1/4/5 / INT#(J35) / +5V(J37.32),U32 周围 CFGSEL0/1、NONREM1、VBUS_DET、VDD18PLL、+3V3_HUB 5 脚, U20 周围 EN / NTC1 / MUX_RST# / FAN_TACH_IN,风扇区 FAN_12V_SW(整网)/ FAN_PWM_L / FAN_OUT_TACH_RAW / $6N48, Buck 的 BST3V3 / $2N72 / $2N70,+3V3 七只脚没孔,VBUS_UP、SDA(U13)、VIN_SW(C4.2)、GND(U19.1)。
  4. 剩余 DRC:+5V 线离信号孔 0.124 mm 6 条、UP 对内线到孔 0.185 mm 5 条(0.125 网格量化)、板边到 $1N114 线 1 条、GND 线碰 $6N45 1 条。
  5. 布线若要重算:remote_route.ps1 -Interp /root/pypy/bin/pypy3 -Variants @('--grid 0.125') -AcceptAny(会覆盖缓存; 板上已有布线,重下发前先 route_base.py --clear)。

别再试的:换 A* 顺序 / PathFinder / 拆线重布 / 小件扇出 / 12V 区过路费 —— 信号网都停在 37~42,细节见下面各段。 工作区:已提交到 994c5c2;R2 以外的未跟踪文件(kicad/、shots/、根目录 A*/B* 截图、.claude 下的日志)没提交,是有意留着的。

⭐⭐ 新会话从这里开始:底板 4 层重做(D15,2026-09-10 用户已确认分区)

分区草图(用户已确认):.claude/r2/M14-4L-floorplan.png(左 = 正面 L1,右 = 背面 L4,同一套坐标、从上往下看)。 决定:4 层、98 × 98、正面只放 J34~J38 五个插槽、其余 133 件全在背面、叠层 L1 槽面 / L2 GND / L3 GND / L4 器件面、 插槽手焊(工厂只贴背面)。方案文档 D15(刀片式方案规划-R2.md §0.1),§6 已标 2 层版作废。

进度(2026-09-10 晚)

  • ✅ 第 1 步 规则:rules_base.py --push 已跑,铜层 2 → 4,补了 0.5 oz 线宽 / 内层间距, 过孔 0.3/0.2,差分 W 0.28 / S 0.18(intra-pair skew 仍 1 mm),安全间距 0.2 → 0.15(同刀片), USBPAIR 0.15;--audit 七项全过。
  • ✅ 第 2 步 几何:pcb_base.py 已重写(2 层版的 BUS_W/rib_rects/wall3v3_*/DP_CHAN/USB6_CHAN/ DP_N_LANE/THROAT/HUB_ESC/PROJ/MID/EAST 全删,下游引用会 AttributeError —— place_base / route_base / pour_base 都要重写)。板框 98×98 折线 + 4 个 M3 已下发,--verify 全过。 新增:TOP/BOTTOM、side_of(ref)、PIN_ROWS(两排 32 针实测表)、pad_rect()、shell_rects()、 via_bands()、ZONES(A1/A2/A3/A5/A6/A4H/A4M)、zone_of(x, y)、height_ok(h)(背面限高 14)、 l1_12v_rects()(L1 12V 主块 + 15 块指头,自检:每只 12V 针被盖到、离异网针/外壳盘 ≥ 0.2)。
  • ⚠ 更正上表:12V 指头东界是 x 34.95,不是 35.1 —— 两排 12V 东边紧挨的 x 35.5 是 GND; 北排 12V 西边 x 32.5 也是 GND,所以北排指头是「排外横帽 + 东侧一颗牙」的 L 形。
  • ⭐ 背面镜像规则(实测,U13 翻面读焊盘):封装原生偏移 (dx, dy) 在底层变成 R(rot)·(dx, −dy) —— 先关于 x 轴翻(y 取反),再按和顶层一样的 CCW 转 rot。modify(id, {layer: 2, rotation: …}) 一步到位, rotation 0 要传 360。推论:顶层 rot 0 的「朝西、1 脚在北」在底层做不出来(底层 rot 0 = 朝西 1 脚在南, rot 180 = 朝东 1 脚在北)。U13 现在已在底层 rot 0(原位,第 3 步会重摆)。
  • 🟡 第 3 步 摆位:place_base.py 已重写(手排 36 件 + 按「翻面后真实脚位」瞄准的卫星排布器 102 件), 离线自检全过,尚未下发。对比图 .claude/r2/M15-4L-place-hub3-vs-hub2.png(plot_base.py 画)。
    • U32 底层 rot 270(口朝北、USB1 在西)是唯一不交叉的朝向:USB1/2 直上,南去的几对从北沿出来绕 U32 东侧 (那一边几乎全是 NC);上行 / USB6 在西侧打孔走 L1 过去。U20 底层 rot 90 天线朝东;J1 rot 90、J23 rot 180 口朝南;Q1 rot 180。
    • ✅ 用户拍板:Hub 在走廊 2、mux/DAC 在走廊 3(与分区草图对调;pcb_base.ZONES 已改、R2 文档 §6 已记)。 D± 估算 45/23/3/40/58 mm(草图原样是最远 80,--hub3 可复现对比)。M5 验收线 45 只有 U32 坐进中间槽正下方的过孔带才做得到,没选。
    • ✅ 已下发(place_base.py --push):138 件位姿 / 层逐件读回对上,离线镜像模型与板上 822 个焊盘最大差 0.003 mm, 朝向断言全过(U32 口朝北 USB1 在西、插槽 12V 在西 D± 在南排、U20 天线朝东入缺口、J1/J23/Q1)。 --native 已按新板重建 _base_native_pads.json。截图:板上 M15-4L-place-live.png,离线 M15-4L-place-final.png。 ⚠ verify 的脚号会重复(插槽外壳盘两个都叫 65、J23 的 A1B12/A4B9 多块),逐脚对账按「同号取最近」。
    • ⬜ 等用户看截图确认方向与排版(CLAUDE.md 规矩)。截图里 J23 / U20 角上有几个黄色 × 标记,还没查是什么。
  • 🟡 第 4 步 布线(2026-09-10 晚,进行中):route_base.py 已按 4 层重写(2 层版在 archive_base2L/), 服务器入口不变(remote_route.ps1,schema 升到 8,旧 v7 缓存改名为 _base_routes.2L-v7.json)。 ⚠ 在 Windows PowerShell 5.1 里调 remote_route.ps1 别加 2>&1:远端 time 打到 stderr,会被当错误抛出,结果下载不回来。
    • 主流程:Kelvin(U13↔Rs6 专线)→ Rs6.2 西侧 L4 +12V 铜块 + 15 孔上 L1 → J23 同名 D± 脚两座桥(DP 南绕 / DM 北绕)→ USB3 U 形掉头(构造)→ 其余六对 D±(DP 先 A*,DM 按「贴 DP」过路费)→ +12V / 散热盘孔 → 输入链 → 信号 → 电源三段 A* → GND 每脚一孔(最后做)→ 打不上的直连到邻近已接地焊盘。
    • ⭐ 北去三对 D± 极性是反的(U32 北沿 DM 在西、槽上 D+ 在西):换位靠一次 U 形掉头(左右自然对调,不交叉)。 为此 U32 东移 2.5 mm(place_base.HUB_CX 49.0 → 51.5),USB3 从 J36 南排底下北上、两排之间向西掉头、打孔回焊盘。
    • 服务器实测:首轮 212 → 第二轮 195 条未完成。失败原因与对策:GND 孔先打会封死 1 mm 针排里的信号脚(→ 挪到最后); 宽的电源先布会把芯片围死(→ 三段);UP/USB6 从 U32 西南绕出去堵 strap 脚(→ L4 收过路费逼上 L1)。
    • 看图:plot_routes.py <png> [--zoom x0 y0 x1 y1](L4/L1 并排,失败脚红叉)。
    • 第三轮(U32 东移 + 三段 + GND 救援之后)198 条(remote_route.ps1 -AcceptAny:摆位改过后旧缓存没有可比性; 东移前那份存为 _base_routes.pre-shift.json)。诊断:板上大部分是空的,失败几乎全是「芯片脚出不去」—— U32 ~36 / U20 27 / U35 17 / U6·U7 16 / J23 9 + 风扇角 + 部分槽 GND 脚。器件全在一面,脚、去耦、strap、GND 孔 挤在 L4 同一圈里;芯片正上方的 L1 是空的,但只有脚边落得下过孔才上得去。U32 另有三难:口排里夹 3V3(5/10/13)、 北去三对要换位、南沿到 J37 只剩 0.4 mm。
    • 用户选「扇出上 L1 + U32 按构造画」:已做扇出圈(place_base.FAN_RING)、U32 北沿 3V3_HUB 构造(u32_north)、 U35/U6/U7/U20 扇出(fanout,62 只脚打孔)→ 第四轮 205 条;接进 PathFinder(--pf --pf-iters 20)→ 188 条(当前缓存)。 PF 冲突格点 5342 → ~2700 就平了。
    • 量化:需布线 105 个网;横穿槽体的需求 J36 26 / J35 21 / J37 15 / J34 5 / J38 0 条,而槽体下 x 35~70 按 0.5 mm 一道约 60 道 ⇒ 不是全局容量不够。188 条按区域:Hub 区 48、J36 槽下 33、U20 区 29、mux 区 17,其余零散 ⇒ 是 U32→J36 那一小块的 局部几何(D± 拓扑 + 3V3 + strap + I²C 全挤在 J36 西段)和 U20 扇出孔排成墙。
    • 用户选「构造 D± + 划横穿车道」:USB1~5 全部按构造画(dp_construct + usb3_uturn;北去在目标槽两排之间向东掉头, 南去东绕,USB4 用它本来就要的 L1 跳线越过 USB5),上行 / USB6 在 U32 西侧打孔从 L1 起步 → ✅ 七对 D± 全通。
    • 当前最好:_base_routes.json = _base_routes.r6-pf192.json(PF,192 条 = 信号网 37 个 + GND 打孔失败 97 + 连通性重复)。 37 个信号网成簇:U20 北沿那排上拉 / NTC 阻容 21、U32 西 / 南(晶振、RBIAS、VDD18PLL、strap)10、mux 区 7、零散若干。 GND 打不上孔的交给铺铜 + 缝合,铺完用 board_check 读回铜皮连通性再判。
    • 试过无效:给 A3/A4M/A5 所有小件扇出(第七轮 43 个信号网,存 _base_routes.r7-fanzone220.json,已关)。 根子在摆位:排布器按 0.6 mm 装配间距把卫星件排成一排贴芯片,没给布线留余量。
    • 用户选「加大小件间距重排」:place_base.SAT_GAP = 1.5(去耦例外仍 0.6、且先摆,去耦离脚平均 3.8 mm), 9 A 过孔阵加进摆位禁区(LANES['V12_VIAS']);U32 本地网先布(u32_local)。重排已下发、读回全过。 → PF 199 条 / 信号网 38(mux 区变差、A3/A4H 变好,失败换地方,总量不降)。
    • --grid 0.125(线距可以按 0.3 排,不再被 0.25 网格卡成 0.5)+ --zone-pen → 183 条(当前缓存,历来最好), GND 打孔失败 100 → 69,但信号网仍 38;大头变成电源:+5V 到槽 7 处、+3V3 到槽 / U20 上拉 / mux 去耦二十来处。 细网格顺序布线服务器 ~4 分钟,可接受。--zone-pen(12V 大铜区对过路网收费)实测无效果(与不加时逐项相同)。 历次缓存:_base_routes.r6-pf192.json(信号网 37)、r7-fanzone220、r9-pf199。
    • 结论:换算法 / 顺序 / 间距 / 网格,信号网都卡在 37~38;剩下的四成是 +3V3 / +5V 这种几十只脚的电源树。
    • ⭐ 用户拍板(2026-09-10):叠层改为 L1 槽面 / L2 整层 +3V3 / L3 整层地 / L4 器件面(D15 已改,R2 文档 §0.1)。 +3V3 并入平面网(route_base.PLANE_NETS),每脚一孔;+5V / +3V3_HUB 仍布线。pour_base 要在 L2(id 15)铺 +3V3、L3(16)铺 GND。
    • +3V3 改平面网后细网格 183 条 = 信号网 40 + 平面网打孔失败(GND 75 / +3V3 11 / +12V 1);+5V 整棵落空(10 只槽针) → +5V 干线 0.6 → 0.4、挪进信号段(POWER_LATE 只剩 +3V3_HUB / FAN_12V_SW)→ 191 条(当前缓存): +5V 基本接上(10 只槽针只剩 J37.32),平面网打孔失败 GND 85 / +3V3 9 / +12V 1,信号网 42,D± 全通。 失败的脚几乎都是一两毫米的本地连接:U20 西排 / 扇出点 17、U32 南 strap + 3V3_HUB 11、风扇区 9、mux I²C 6、 Buck 自举 / 反馈 4、J23 VBUS 等 8。十几轮换算法 / 顺序 / 间距 / 网格 / 电源平面,信号网始终 37~42 ⇒ 调参数到头了。
  • 🟡 2026-09-10 夜:已下发到板上(用户选「先下发、剩下手工补」)
    • route_base --push --force:当前最好的布线(缓存 191 条那版)。板上读回 L1 856 + L4 1357 段(缓存 2288, 差数是 EasyEDA 建线时合并同向首尾相接的段),过孔 616(GND 229 / +3V3 37 / +12V 31)一个不差。
    • pour_base --stitch:GND 缝合孔 122 个(修过:删旧孔时跳过布线缓存里的 GND 孔 —— 有 2 个恰好落在缝合网格上)。
    • pour_base --push:83 块铜面按层建好(L1 33 / L2 13 / L3 13 / L4 24),但「重建所有」没填出铜(poured = 0)。 GUI 点击返回 fired: true,等 4 分钟仍 0;这套流程从 2 层版继承,底板上从没验证过 ⇒ 待查,或在客户端手动「铺铜管理器 → 重建所有」。
    • board_check --base(已修:通孔焊盘两层都算):37 个网没连通,清单即手工补线清单(见会话 / 重跑该命令)。
    • drc_read --base:间距错误 46 条 ——「挖槽区域」26 条(对象 e149/e150/e55/e56,板上图元里查不到,疑为封装内部 长槽孔 / 开槽,要在客户端点 DRC 条目定位;若是插槽键位真铣穿的槽,从下面穿过的 USB1/USB2 会被切断)、 J1 通孔地脚里误打的孔 4 条(代码已修 TH_KEYS,板上 4 个孔待删)、U20 地脚 1/40 离天线缺口 0.155 mm (缺口东移 0.3 即可)、+5V 线离信号孔 0.124 mm 6 条 + UP 对内线到孔 0.185 mm 5 条(细网格量化)、板边 / 线线 2 条。 「连接性错误」557 那一支没意义(要重载客户端才准,且铜没填)。
    • 板上截图:.claude/r2/R12-board-routed-poured.png。
  • 🟡 pour_base.py 已按 4 层重写:L1 12V(主块按 M3 孔切瓦片 + 15 指)+ GND 底填 / L2 整层 +3V3 / L3 整层 GND / L4 入口链三段(自南向北,按焊盘空隙在 F1、Q1 处切开,挖栅极缺口)+ Rs6 西侧 12V 铜块(读布线缓存 v12_patch)+ GND 底填。 离线自检全过;断面:VIN_SW 7.41(要 6.5)✅、L1 12V 主块 13.20(要 8)✅;VIN_RAW 6.77 / VIN_FUSED 4.35 不强制(瓶颈在 DC 座与保险,已接受)。 判据修过三处:tiles() 对 y 方向不相交的禁区会切出越界瓦片(已修);同网相邻瓦片先合并再量; 焊盘 / 过孔 / 平面网的短引出脚按「洞」扣(铜从四周绕过去),只有信号线算切断。 缝合孔 --stitch 读布线缓存算点(避开硬功率铜);下发顺序:route_base --push → --stitch → --push → DRC + board_check。
    • pour_base.py 还没改(仍引用 2 层名字):内层 id L2 = 15、L3 = 16;入口链电流现在自南向北 ⇒ 断面沿 y 轴、查 L4 的线。 车道意图已固化在 place_base.LANES:DP_H(U32 东侧 x 55.559.5,USB4/5 南下)、DP_M(走廊 3 x 4449,USB1/2 北上)、 I2C_H/I2C_M(x 28.5~31.5)、U32_WVIA(上行 / USB6 上 L1 的过孔带)。

起点状态(本段以前)

  • EasyEDA BASE-PCB(7b9524b6aaf6f235)仍是 2 层、110×125 板框、138 件在 2 层版位置、0 导线 / 0 过孔 / 0 铺铜。
  • _base_routes.json 是 2 层版最好结果(63 条未完成),已作废;2 层版脚本 + 缓存原样存档在 .claude/r2/archive_base2L/。
  • place_base.ROUTE_FIX(DZ1/C1/R1 翻转等)和 USER_INPUT 都是 2 层版坐标,新摆位落地后要清空。
  • 服务器没有在跑的任务;/root/pypy 已装好(见下)。git 工作区大量未提交改动(本会话没提交)。

新几何(全部 mm;x = 槽长方向,y = 槽堆叠方向;左下角原点)

项 值
板框 98 × 98,四角 R5,折线(不是导线,§27.3 老坑)
安装孔 M3 NPTH Φ3.2:(4,4) (94,4) (4,94) (94,94)
槽阵 SLOT_CX=49、ARRAY_CY=49、槽距 20.32;槽心 y = 8.36 / 28.68 / 49.00 / 69.32 / 89.64(J38 最南 … J34 最北,USB_SLOT_REF 1→J34 不变)
槽体 42 × 9.2(x 2870),rot 0,两排焊盘 y = 槽心 ±3.6,PIN_U 针位表沿用 pcb_base.py(12V 在 x 32.534.5,I²C 36.5/37.5,3V3 39.5~41.5,EN 42.5,D± 47.5/48.5,VDAC 49.5,5V 52.5/65.5,PRSNT 64.5)
外壳 GND 盘 每槽两端 3×5,x = 29.5 / 68.5
槽间走廊 y 12.9624.08 / 33.2844.40 / 53.6064.72 / 73.9285.04
天线缺口 (90.5, 58) ~ (98, 80),东板边(暂定,U20 摆位后按天线实际位置调)
正面 L1 +12V 大铺铜 x 0.5~31.5 全高 + 每排 12V 脚伸一指到 x 35.1;槽的 GND 脚打孔到 L2/L3;信号脚打孔下 L4
背面 L4 分区 A1 输入链 (0.5,0.5)-(27.5,56);A2 降压 (0.5,57.5)-(27.5,74);A6 风扇 (0.5,75.5)-(27.5,97.5);A3 控制(U20/J33…)(70.5,40)-(97.5,97.5);J23+U31 (70.5,0.5)-(97.5,38.5);Hub U32 在走廊 53.60~64.72(x 28.569.5);**U35/U6/U7 在走廊 33.2844.40**;每槽焊盘正下方 x 31~67、y 槽心 ±4.6 是过孔带(不放件)
D± / I²C D± 在 L4 沿 x≈48 竖向成束(参考 L3),最远 ~41 mm;I²C/VDAC 沿 x≈37

面积核账:133 件本体 1640 mm²(A1 540 / A3 516 / A4 262 / A6 186 / A2 136),背面可用约 9300 mm²。 背面最高 12 mm(C2/C3),底板下要 ≥ 15 mm 铜柱,外壳跟着改。

下一步(按顺序,每步的坑写在后面)

  1. rules_base.py 改 4 层:LAYERS = 4;下发前先 eda.pcb_Layer.setTheNumberOfCopperLayers(4)(换层数会换能力集,§24.1); 把刀片 pcb_rules.py 第 192~210 行那段「补 copperThickness0.5oz 线宽 + copperThicknessInner0.5oz 间距」搬过来 (内层表从 multi 能力集拷,看该文件里 multi 怎么取的);过孔下限 0.3/0.2;差分改刀片那组 W 0.28 / S 0.18 (两层版 1.2/0.2 在 4 层薄介质上阻抗严重偏低);--push 后跑 --audit。⚠ 别在 GUI 换「配置」下拉框(整套重置)。
  2. pcb_base.py 换新几何:上表的数;删掉 2 层专有的 BUS_W/rib_rects/wall3v3_*/DP_CHAN/USB6_CHAN/DP_N_LANE/THROAT/HUB_ESC 和刀片投影限高(背面没有刀片);--outline / --holes / --verify。
  3. place_base.py 重排:五槽留正面到新位置,其余全部翻到背面,按上面分区。⚠ 翻面 API 还没查 —— 先看 easyeda-api skill 里 pcb_PrimitiveComponent 的 layer/翻转写法;翻完必须 --dump 重抓焊盘、--bbox 重抓外框, 并像本会话核 rot90/270 那样,用实测焊盘确认背面镜像后 1 脚落在哪一侧(正面实测:rot90 → 1 脚朝南、rot270 → 朝北、焊盘长宽互换)。 摆完截图给用户确认(CLAUDE.md 规矩)。然后清空 ROUTE_FIX / USER_INPUT。
  4. 布线 / 铺铜脚本改 4 层:router.py 仍是两层 A*(LAY=(1,2))⇒ 1 = L1 正面、2 = L4 背面,内层只做 GND 平面不布线。 route_base.py 删掉 BOT_PEN/DIFF_KEEP/TOP_ONLY/12V 干道/铜墙/车道那一整套;D± 从 U32 到槽 D± 脚按构造画(L1 焊盘 → 过孔 → L4)。 每只 GND 焊盘都要一个到内层的过孔(新增扇出步骤,GND 仍不布线);12V:Rs6 输出打过孔阵上 L1 铺铜,槽 12V 脚直接在铺铜里。 pour_base.py 本会话的改动可复用:焊盘空隙定刀口(clip_chain)、GATE 缺口、GND_PAD_VIAS 地岛专用孔、漏极铜、缝合孔不进硬铜面。 CACHE_SCHEMA 升到 8(旧 2 层缓存必须被拒)。
  5. 服务器布线:remote_route.ps1 -Interp /root/pypy/bin/pypy3 -Variants @(...)(PyPy 单轮 47 s vs CPython 222 s; PyPy 输出是确定的,扫 --hash-seed 对它无用);结果只在「未完成不增加」时接受。

⭐ 2026-09-10:底板改 4 层(D15)—— 2 层版停止

  • 用户拍板:底板 4 层、≤ 98 × 98;插槽面(顶层)只放 J34~J38 五个插槽,其余 133 件全部放背面; 叠层 L1 插槽面(槽焊盘 + 12V 大铺铜 + 少量线)/ L2 GND / L3 GND / L4 器件面(全部器件 + 信号 + D± + 3V3/5V 局部铜); 插槽手焊,工厂只贴背面。方案文档 D15(取代 D5),§6 开头已标作废。
  • 为什么改:2 层版卡在 63 条,其中 30 条是 D± 墙(19 个网要从 U20 一侧跨到槽侧,0 条通过: 顶层是连续差分线、底层 D± 下方禁开口)、6 条是西侧 12V 干道(顶层硬铜,只能底层跳);局部逃逸只能救约 20 条。
  • 可行性核账:除插槽外 133 件本体面积 1640 mm²,98×98 背面扣天线 / 安装孔约 9300 mm²(5.7 倍); 五槽外包 42 × 90.5(槽距 20.32、槽体 9.2),98 方向两边各剩约 3.75 mm。插槽是贴片立式(PCIE-64P11L, 64 贴片脚 + 2 个 3×5 固定盘,全在顶层),背面不被插针占。背面最高 12 mm(C2/C3),底板下要 ≥ 15 mm 铜柱。
  • EasyEDA 改层数:setTheNumberOfCopperLayers(4),然后规则重下一遍(R2 文档 §24.1,刀片踩过)。
  • 2 层版脚本 / 缓存存档在 .claude/r2/archive_base2L/;服务器上 2 层版的拆线重布已停。
  • 下一步:① 新几何(pcb_base:98×98 板框、槽位、天线缺口、安装孔、板边接口)→ ② EasyEDA 改 4 层 + 规则 → ③ 133 件翻到背面重新摆位(U32 在中间槽正下方)→ ④ 路由 / 铺铜脚本改 4 层(去掉 2 层专有的底层过路费、 D± 走廊封底层、12V 西干道)→ ⑤ 服务器布线。

2026-09-10:Q1 栅极网络挖缺口 + 服务器并行变体(进行中,未下发)

  • 根因:GATE: Q1.4 出不来 不是拥塞 —— Q1.4 北/西/南三面是 VIN_FUSED 硬铜面、东面是 VIN_SW,中间只剩 0.25 mm 缝;DZ1/C1/R1 也都落在这两块铜面里。挪栅极件救不了,必须改铜面。
  • 潜伏 bug(已修):pour_base 读实测焊盘、route_base 读 ROUTE_FIX 后的焊盘,两边不一致; 而 clip_chain() 按「重叠区中点」竖切,在 Q1(南排源极 / 北排漏极)处会切进 Q1 里面。 以前没出事只因 DZ1 旧位置(x=38.6)把 VIN_FUSED 撑到 x=40;DZ1 一挪,分界退到 30.5, VIN_SW 盖住 Q1.3。现改为:换算函数挪到 place_base.route_fixed_pads() 两边共用; 刀口按「两网相邻焊盘的空隙中点」定(VIN_FUSED/VIN_SW 仍在 x≈33.0);栅极件不撑大外框。
  • GATE 缺口:pour_base.gate_notch() = 主块 x 31.90–37.52 / y 0–3.57 + Q1.4 竖条, 从 VIN_FUSED、VIN_SW 两块里用 tiles() 挖掉(多块同名前缀,check_cuts 已支持)。 断面:VIN_SW 最窄 13.40 → 11.31 mm(要 6.5,OK);12V 9.15 不变;selfcheck 全过。
  • 摆位(ROUTE_FIX,未下发):DZ1 (31.0,1.65) 翻 180° 使 GATE 脚正对 Q1.4; C1 (34.5,1.2)、R1 (36.5,1.2) R1 翻 180°(GATE 朝西与 C1.2 相邻)。 C1/R1 原计划 34.0/36.0 时 C1 离 DZ1 仅 0.22 mm,已东移 0.5;摆位自检无新增冲突。 离线模型支持平移与 180° 翻转;90°/270° 实测约定为 rot90 → 1 脚朝南、rot270 → 1 脚朝北、焊盘长宽互换,尚未接入。
  • C1.1 固定细线:route_base.gate_tap() 画 0.30 mm(PWR9A 下限)C1.1 → 板边 y=0.70 → DZ1.1, 不走 A*(2.0 mm 网在 0402 上起不了步;板边那条缝在 0.25 网格上也表示不出来)。
  • --repair(route_base.rip_repair):底板以前从没用过 router.repair();现只对普通信号启用, D± 与铜面网不参与。旧模型实测 v1 第一轮 71 → 71,无改善 —— 剩余失败主要是通道容量,不是顺序。
  • remote_route.ps1 -Variants:服务器并行跑多组参数(脱离 ssh,setsid nohup),取未完成最少者 走原有校验 / 接受逻辑。pour_base --push/--stitch 在 ROUTE_FIX 未下发时会硬拒绝。
  • 已确认 EasyEDA 底板当前 138 件 / 0 过孔 / 0 导线(旧文里的 162 个过孔已不存在)。
  • 中间模型(只有 GATE 缺口)服务器实测:单轮 70 → 64;拆线重布变体首轮 64~70。
  • Q1 漏极排接入 VIN_SW:pour_base.drain_piece() 补 PWR-VIN_SW-Q1(x 28.29–34.12 / y 7.05–11.49), 东头与 VIN_SW 搭接、南沿让开 VIN_FUSED。以前 9 A 只靠 A* 的 2 mm 线从四只漏极脚爬进铜面。
  • GND 贴片脚专用过孔(GND_PAD_VIAS):D1.2(TVS)挖朝南地岛 + 3 孔、C5.1 挖朝北地岛 + 2 孔、 R1.1(在 GATE 缺口里)1 孔。route_base 把它们当固定 GND 过孔进占用图,stitch_points 永远保留。 C2.2/C3.2/J1 是通孔地脚,本来就穿到底层地平面,不用处理。
  • 缝合孔不再打进硬功率铜面(PWR-*):以前 4 mm 网格照打,在 VIN_SW / 12V 上掏洞,断面校核看不到。 离线核对:492 个缝合点、硬铜面内 0 个。
  • 断面校核仍全过:VIN_SW 最窄 11.31 mm(要 6.5)、12V 9.15 mm(要 8.0)。
  • 完整新模型服务器实测(单轮四变体):默认 64 / --priority-cache 63 / --bot-pen-w 1 70 / 两者叠加 64。已接受 v1:1490 段 / 316 过孔 / 63 条未完成(上一版 70,且是旧模型)。 输入链(GATE / VIN_FUSED / VIN_SW)全部布通;6 个专用 GND 过孔已在缓存里。 底层跳线降价(--bot-pen-w 1)两次都最差,不再试。
  • 剩余 63 条集中在:U20 12、U35 6、U6/U7 8、U32 3、J23 3;U35/U6/U7 那一窝 SDA/SCL/VDAC 随顺序此消彼长 —— 是 I²C/DAC 西侧簇的通道容量问题,下一步仍是这几颗的局部逃逸。
  • 服务器装了 PyPy(免安装包,/root/pypy → /root/pypy3.11-v7.3.23-linux64,不动系统 Python)。 remote_route.ps1 -Interp /root/pypy/bin/pypy3 切换解释器。 实测同模型单轮:默认 222 → 47 s、--priority-cache 314 → 69 s(4.6~4.7×),内存 95 → 290 MB。 未完成 68 vs CPython 63/64 —— 不是算错:两份输出异网同层线距违规都是 0;失败 47 条相同, 其余差异都在 U32/U7/U35/U6 与风扇那几窝。原因是集合遍历顺序不同(PyPy 一律按插入序, CPython 按哈希序),A* 起点 / 直连的平局先后因此不同。 ⚠ PyPy 是完全确定的:--hash-seed 1~8 八个输出逐字节相同(68 条 / 1480 段 / 307 孔), 扫种子对它没用。--hash-seed 只对 CPython 有用(可复现;CPython 各种子是否真有差异尚未实测)。 ⚠ 8 个 PyPy 并行时单个从 47 s 涨到 ~97 s(24 线程多半是 12 物理核 + 超线程),并行别开太满。 ⇒ 用法:改模型后的快速验证用 PyPy;最终取最好结果仍按条数选,哪个解释器都行。 服务器下载站只有 ~76 KB/s,官方包 35 MB,直接 curl 会超时,要 curl -C - 续传。
  • remote_route.ps1:临时文件带 PID,可同时起两批;收尾 pkill 改用 [p]ython3 <目录>/, 以前模式串也命中执行它的远程 shell,ssh 返回 255、目录没删掉。

2026-09-10:M13 底板差分与功率拓扑打通(普通信号仍未完成)

  • 所有试算均在 10.5.5.13 完成;远程入口新增 -DpOnly,并修复上传包文件名不一致。 结果缓存现在只接受“未完成数不增加”的方案,更差结果保存为 _base_routes.rejected.json。
  • 五槽保持 20.32 mm 间距,整体北移 3 mm。同步重排 C7/C731/C10/R610/R722/C737 和 R9C~R9E;新增摆位冲突 0,原有几何告警从 9 条降为 8 条。
  • 七对 D± 全部改为确定性构造:USB1~5 与 USB6 全顶层;上行 USB 只在 U32 北侧用 两段短底层跨越完成局部换序(4 个信号过孔),随后立即回顶层。 服务器精确检查:异网净距 0 错、未连通 0;对内长度差 0.9/0.3/0.3/0.2/0.3/0.3/1.0 mm,均满足 <=1 mm。
  • Rs6 从 90° 改为 0°,焊盘沿输入电流方向排列;U13 从板西侧移到 Rs6 上方, U29 让位到旁边。VIN_SW 不再用 2 mm 粗线横切 12V 主干。
  • 功率计划校核通过:VIN_SW 最窄 13.40 mm(要求 6.5),12V 西侧主干 9.10 mm(要求 8.0);12V/3V3 槽脚覆盖完整,异网侵入 0。
  • 当前最佳全板缓存:1539 段 / 312 过孔 / 75 个网络 / 70 条未完成连接。 与本轮开始的 81 条相比减少 11 条,但仍禁止 --push,EasyEDA 板端仍是纯布局。
  • 已证伪:继续随机优先重排在 70~73 间振荡;对全部密脚或只对 U20 做同层预扇出, 分别退化到 73 / 77 条。下一步应针对 U20/U32/U35/U6/U7 做局部通道与端点逃逸, 不再重复上述两类服务器试算。

2026-09-10:底板布线迁移到 10.5.5.13 服务器计算

  • route_base.py 新增 --snapshot-vias / --offline:本机只负责从 EasyEDA 刷新 162 个既有过孔到 _base_board_vias.json,服务器离线计算时缺快照会硬失败。
  • 新增 .claude/r2/remote_route.ps1,自动创建服务器 /tmp/dcdc-route.* 隔离目录、 打包上传、远程计时计算、校验 schema/插槽方向/USB 映射、备份并替换本地缓存。 使用:powershell -ExecutionPolicy Bypass -File .claude/r2/remote_route.ps1 -PriorityCache。
  • 10.5.5.13(Ubuntu 20.04 / Python 3.8.10)首轮实跑 292.93 s,CPU 99%, 峰值内存 93,876 KiB;本机同类单轮约 7 分钟。结果已拉回 _base_routes.json: schema 7,1640 段 / 351 过孔 / 73 个网络,仍有 81 条未完成连接,未下发 EasyEDA。
  • 同轮修正:五对槽 USB 不再因固定 y 阈值让 USB3 自交叉,长度差为 0.918/0.250/0.250/0.250/0.250 mm;+3V3_HUB 两块软铜覆盖从 1/21 修正为 21/21,并用一条固定桥连接;12V 主干覆盖 25/25 个槽电源脚、异网侵入 0, 含实际路由切口后的最窄断面 8.50 mm(要求 8.00 mm)。
  • ⛔ 仍不可下发:USB6/上行 D± 尚未成对布通,且 USB5_DP 在当前布局下切过 VIN_SW 铜面,使其有效断面仅 3.31 mm(要求 6.50 mm)。--push 继续由 “存在未布通记录”硬阻断;下一步需调整南侧输入链或 USB5 走廊拓扑。

2026-09-10:M12 底板采用 EasyEDA 当前布局

  • 以 EasyEDA 当前画布为唯一摆位基准,138 件已重新读回到 .claude/r2/_base_place_live.json;place_base.py 不再叠加 ROUTE_FIX 强制坐标。
  • 用户在画布上完成了新一轮功能重排:U35/U6/U7 及配套件已在西侧, U32=(89.111,52.903),U20=(93,97)。旧的一次性 I2C 搬件脚本已删除, 不得再用旧坐标覆盖当前布局。
  • 已重抓 822 只焊盘、138 件封装外框和旋转数据。五路 USB 曼哈顿估算长度 变为约 87/67/46/53/73 mm。当前整板截图 .claude/r2/M12-current-layout.png。
  • 当前画布有 12 条几何告警:C11/C607/R614、C609/R614、 C610/C612/R616、C7/C731、C8/R749/U19、RT601/U31 过近; C737/R9D/R9E 进入旧 D± 通道。这些位置按用户当前布局原样保留。
  • 板端仍是 0 导线 / 0 过孔 / 0 铜面。I2C 已占用旧顶层 12V 母线区, U32 也已离开 M12 旧逃逸框;下一步必须按这版布局重做 12V 干道和 D± 路由, 不得下发 M11/M12 旧缓存。

⚠️ 2026-09-10:M12 底板布线逻辑迁移(未下发)

  • route_base.py 已从旧的「D± 西通道 / 12V 东母线」迁移到 M11: USB1~5 明确映射到 J34~J38,槽端改从南排逃逸,东侧三条车道构造路由。
  • 五对下行 D± 为 84 段 0.15 mm 逃逸 + 81 段主干/等长段; 离线净距 0 错,每对长差均为 0.25 mm(规则 ≤1 mm)。
  • 12V 改为西侧 14 mm 干道 + 逐排分段铜墙,覆盖 25/25 只槽电源脚; 3V3 铜墙修正 NC 边界后覆盖 15/15,两者均为 0 异网侵入。
  • 功率铜面离线断面:VIN_SW=12.17 mm、+12V=14.00 mm,强制项全过。 U13.10 是 Kelvin 监测脚,已从 9 A 铜面包围盒排除。
  • 旧 rot=180 路由缓存现在会被 schema 拒绝;有任何未布通记录时 --push 也会拒绝下发,不再把部分路由写入板端。
  • 完整 A* 试算两轮为 105 / 104 个未完成连接,排序重试几乎无改善; 其中 R610 / R722 横断 U32→U20 的 USB6 专用通道。
  • 两件的新位置规划为 R610=(78,109.5) / R722=(78,111),布局自检通过; 但 EasyEDA 窗口已断开,定点下发返回 HTTP 500,板端尚未移动。 当前底板仍是 0/0/0 的纯布局状态,不可投板。

⚠️ 2026-09-10:底板 PCIe 插槽再旋转 180°

  • 用户要求将 BASE-PCB 的 J34~J38 在 M9 方向上再旋转 180°: 五个插槽已定点下发并读回为 rot=0,其他 133 件未动。
  • 这不是纯机械改动:+12V 脚从东侧换到西侧(约 x=47), +5V / PRSNT# 换到东侧(约 x=79~80)。M9 的“12V 东侧铜墙 / 控制西侧”拓扑因此失效。
  • 旧铜已清空:导线 0 / 过孔 0 / 铜面 0,避免旋转后焙盘压到异网旧铜。 route_base.py / pour_base.py 已加新方向硬阻断,旧拓扑不能误下发。
  • 当前底板是布局状态,不是可投板状态。下一步必须按新针位重做东西分区、电源铜墙和全板布线。
  • 截图:.claude/r2/M10-slots-rot180.png。

✅ M11:按原理图功能模块重排底板器件

  • 槽阵整体西移:中心 x=46.5,槽体 x=25.5~67.5;南→北为 J38/J37/J36/J35/J34, 与 U32 旋转后 USB5→USB1 的引脚顺序同向,五对 D± 不交叉。
  • 西侧 = 12V 功率区:x=0.5~10.5 主干 + x=10.5~32.5 槽支路; 南侧输入链 J1/F1/Q1/D1/C2/C3/Rs6/U13 按电流方向聚类,C1/R1/DZ1 贴 Q1。
  • 东侧 = 数字/高速区:x=67.5~81 为 D± 专用通道,x=81~110 放 U32 Hub、U20 ESP32、U35 I²C mux、U6/U7 DAC 及就近 RC。
  • U20 改为天线朝东,板框缺口同步移到东边 x=102.5~110 / y=86~108; EN/NTC 滤波、Hub VDD18/VDD18PLL 去耦、DAC 参考 RC 均按读回脚位重新钉位。
  • 北带 = 两路辅助 Buck + 风扇/应急口;南带 = 输入功率 + 上行 USB-C。
  • 验收:138 件逐件读回一致;板框 1 条 / 49 点闭合;安装孔 4 个; 重叠 / 越框 / 限高 / 槽带侵入 / 天线净空均为 0。截图 .claude/r2/M11-functional-layout.png。
  • 仍是纯布局稿:导线 / 过孔 / 铜面 = 0/0/0;布线器要按新的东侧 D± 通道重写后才能下发。

✅ M11b:相关电阻电容按引脚收紧

  • A2-AUX:C7/C731/C732 从东侧控制列收回 U19/U30 的 12V 输入脚, C9/C10 收回 L2 的 3V3 输出;原先 67~77 mm 的跨板距离消失。
  • A3-CTRL:C601/C602/R601/C603 围绕 U20 的 3V3/EN,R606~R610 按 PRSNT1#~5# 的实际 GPIO 脚排列;C604/R602/R603 收回 U35; U6/U7 的 C607~C612/R614~R617 按脚侧重排,C608/C611 另外旋转 180°让信号端朝芯片。
  • A4-HUB:C744~C751 在 U32 右侧组成紧凑去耦阵列, R710/R711/R712/R720/R716 组成 strap/reset 电阻列,与去耦阵列分开; C737/C752/C753 贴 U31。
  • A1-INPUT:C1/R1/DZ1 收回 Q1 GATE/VIN_FUSED 引脚周围。
  • 读回审计的“去耦离引脚 >3 mm”只剩 5 条:四条为 3.0~3.2 mm; C738 为两只风扇座共用储能,放在两座之间,距单侧 6.3 mm 是对称折中。
  • 用户指出 J33 位置不合理并在客户端调整:定稿为 J33=(107.06,76.83,270°),位于 U20 下方的右板边,从侧边接插; 其本体 x≈105.8~108.3 在刀片横向投影 x=-2.5~95.5 之外,11.5 mm 高度不干涉。 北边只保留相邻的 J27(70) / J26(83) 风扇座。D2/R748/R753/R755/R757 同步让位。

✅ M11c:两颗 NTC 改到最高热密度点

  • RT601=(92.0,24.5,270°):贴 U31 右侧,监测 Hub LDO。U31 最坏耗散 0.42 A × (5-3.3) V ≈ 0.71 W,是底板最明确的持续热点;NTC1 信号端朝北。
  • RT602=(72.5,10.0,180°):贴 J1/DC-005 右侧,监测 9.99 A 输入接触热。 DC-005 家族额定仅 2~5 A,此处是已知的安全热点;邻近 Q1 最坏约 I²R ≈ 0.5 W。
  • C736/C739 仍贴在 U20 ADC1 脚旁做 MCU 端滤波,不跟 NTC 跨板移动。
  • 读回脚位:RT601.1/NTC1=(92.00,25.25),RT602.1/NTC2=(73.25,10.00); 布局碰撞、越框和刀片投影检查通过。

✅ M11d:Q6 / Q10 / U13 按信号流重排

  • U13=(19.0,5.5,0°):回到 Rs6=(19,11) 正下方,并原地转到 0°; 读回 8/9/10 脚(+12V/+12V/VIN_SW)在 y=7.61,朝上正对 Rs6,保留 Kelvin 最短路径。
  • Q6=(72.5,113.5,180°):放在 J27 下方,R733/R734 围绕其输入网; Q6.3 FAN_PWM_L=(73.5,113.5) 朝风扇接口。
  • Q10=(98.0,114.5,90°):放到 U20 上方的空区,R754~R757 收成一组; Q10.3 FAN_TACH_OUT=(98.0,113.5) 朝南对着 U20。
  • D2/R747/R748/R753 同步让位,R660/R661 回到 U20 ADC 滤波组。 本轮使用 push_subset.py 只下发 15 个相关器件,没有覆盖用户正在客户端调整的其他位置。

✅ M11e:用户定稿新的电源输入链布局

  • 以客户截图和板上读回坐标为准,16 件已写入 USER_INPUT 机械锚点: J1/F1/Q1/D1/C1/R1/DZ1/C4/C5/C6/C2/C3/Rs6/U13/RT602/C11,不再做 0.25 mm 吸附。
  • 功率流从左到右:J1(20.83,10.79) → F1(31.08,3.63) → Q1(37.08,6.63) → D1/C2/C3 → Rs6(75.99,8.74)。
  • U13=(76.71,16.89,180°) 位于 Rs6 上方;8/9/10 采样脚读回为 (76.71,14.78)/(77.21,14.78)/(77.71,14.78),可从 Rs6 两端单独引 Kelvin 线。
  • RT602=(32.07,9.16,0°) 移到 F1/Q1 输入热点中间,代替 M11c 的 J1 右侧位置。
  • 检查器同步修正两个模型:器件间距改用带偏移的真实本体框; 用户贴边锚点按 0.25 mm 板内余量判定。U13 顶部进入 D± 通道南端约 0.8 mm, 已登记为后续路由的局部固定障碍。
  • 16 件已定点下发并全部读回一致。

✅ M11f:采用 EasyEDA 当前整板布局

  • 用户要求“按我现在的布局修改”;已通过 place_base.py --adopt-live --verify 读取 BASE-PCB 当前全部 138 件。
  • 快照 .claude/r2/_base_place_live.json 保存每件的 x/y/角度/层。 place_base.plan() 现在优先使用该快照,并跳过自动吸附和自动翻转; 以后普通 --push 不会把当前手工布局拉回旧位置。
  • 验收:138 件坐标/角度逐件一致;重叠、越框、限高、螺钉头、槽带侵入、天线净空均为 0。

⚠️ 2026-09-08:架构分叉,先看这里

用户拍板重做方案以节约打样成本:改成「底板 + 5 片相同刀片(PCIe 竖插)」, 新方案规划写在 刀片式方案规划-R2.md(架构、成本模型、引脚表、风险、里程碑都在里面)。

  • R1(下面这一整份进度)全部保留,不删不改,工程 / 原理图 / PCB 一个字节没动。
  • R1 停在「下板欠 50 个网络 / 97 条连接 + 5 条 DRC 间距错误」,pcb1_route_sig 那一步未下发。
  • ✅ M1 已完成:工程 PhoneRail-R2 = 05a65c22f14846b8b95360ef8d3c1e2e, 两块板 BLADE(后来变 5 页)/ BASE(6 页)骨架建好,UUID 全在 .claude/r2/r2-uuids.json。
  • ✅ M2 已完成(刀片原理图 5 页全画完,2026-09-08): B1-POWER(42 件) / B2-CTRL(13) / B3-PHONE(17) / B3B-USB(9) / B4-EDGE(2)。 五页 verify_sch --nets 六项全过、port_audit --r2 BLADE 146 个旗端口可疑 0、 本地交叉点 0、xpage 真正漏接 0。
  • ✅ T4 也在同一轮做掉了(B4-EDGE 的前置条件):PCI-E CEM 引脚表逐脚核对, 揪出方案文档 §5.2 两处凭记忆写错的针位,其中一处会烧板 —— B3 是 +12V 不是 RSVD,原方案要把 +5V 放上去,刀片误插进真 PC 槽时 主板会把 12 V 灌进刀片的 +5V 轨(U5 TPS22918 abs max 6 V)。 +5V 已改挂 A19 / A32(每一版 CEM 里都是 RSVD,两个方向都安全)。 另一处:B12 从 CEM 2.0 起是 CLKREQ#,不能当自由针 ⇒ 改接地。定稿针位表见方案文档 §5.2 / §17。
  • ⚠️ 本轮又踩到两处 skill §6c「rot 语义与源页相反」:C60 2200µF 电解极性接反 (四道校核全过,只有网表明细看得出来)、U5 TPS22918 的两排引脚镜像 (不是 180°,自动翻转循环修不了)。做法与判据写在方案文档 §16.3。
  • ⚠️ 2026-09-08 改判 D11b:DAC63204 挪到底板(2 颗喂 5 路 = R1 的 U6/U7 原样搬), 刀片上不再有 DAC,位号顺移(PCA9555 U5→U4,B3 的 TPS22918/TS5A3157 → U5/U6)。 依据是设计文档 §4.4 的失效表:VDAC 断线 = 3.890 V、对地短路 = 4.010 V,注入增益 0.2007。 详见方案文档 §15。
  • ✅ 金手指封装已建(2026-09-08,M4a):EDGE-64P-P1.00_PCIE-X4-GF = 569096fb8fed4743b85c69f4b6581ead,脚本 .claude/r2/fp_edge.py(几何唯一事实来源, --push 幂等重画 + 读回校验:64 焊盘逐脚对上、板框缺口 16 段折线闭合、丝印零侵入指区)。 已挂到器件 PCIE-X4-EDGE-64P 上,Add into BOM 也一并改成了 no。落地记录见方案 §22。 ⚠️ 它顺手推翻了文档里两条:斜边角度 20° 在嘉立创下不了单(只有 30/45,已改 30), 「斜边吃掉 1 mm 镀层」不成立(CEM 本来就留 1.3 mm 裸边,斜边啃的就是它)。 ⚠️ 第一版把手指画在了板外(y 反了),四道几何校验全过,靠截图才看出来。
  • ✅ M3 已完成(底板原理图 7 页全画完,2026-09-08): A1-INPUT(25 件) / A2-AUX(17) / A3-CTRL(30) / A4-HUB(31) / A5-SLOTS(3,槽 13) / **A5B-SLOTS(7,槽 45 + 5 个 D± 退路座)** / A6-FAN(30)。 七页 verify_sch --nets 六项全过、xpage 真正漏接 0、 port_audit --r2 BASE 372 个旗/端口可疑 0。落地记录见方案文档 §18/§19/§20。 后四页是四个子代理并行设计 + 主代理串行下发:全机只有一个 EDA 客户端、 openDocument 是全局状态,同时推会把图元放进别人的页,而且六项验证照样全过。
  • ⛔ 底板从 6 页变成 7 页:5 个 64 针插槽一页放不下(每槽净宽 355 × 5 = 1815 ≫ 可用 1125), 新开 A5B-SLOTS = e493f8bce3a51aae,已写进 .claude/r2/r2-uuids.json。
  • ⛔ 差点烧进去的一条(子代理揪出来的):PRSNT# 在底板上不能镜像刀片。 底板是主板侧 ⇒ A1 = GND、B31 = PRSNT{n}#;短接 A1↔B31 的是刀片。 若照字面镜像并成一个网,回路里一个对地通路都没有 ⇒ 插不插刀片都读「未插入」, 而 DRC / ERC / 几何验证 / 跨页对账四道全过。见方案 §20.1。
  • ✅ T6 结了,结论与方案 §8.2 的问法相反:DS00001598C Table 7-1 的 0Ah/0Bh 默认 00h ⇒ CFG_SEL=110 下七个下行口全开,第 6 口不用改 strap,只要连线。见 §20.2。
  • ✅ T8 已结(2026-09-08):J34~J38 换成 SOFNG PCIE-64P11L(C19188869), PC 标准 x4 64PIN。根因也修掉了:lib_Device.search 不是只能回 38 条 —— 带上 itemsOfPage / page 就能翻页(search(key, libraryUuid, classification, symbolType, itemsOfPage, page))。
  • ✅ T7 也一起结了:刀片 F1 = F1206SB2000V063TM(C310998) 2A 慢断 1206/63V, 取代同封装的 10A 占位件 C310989。AEM F1206 的系列码:SB=慢断 / HI=耐涌 / FA=快断 / HC=大电流。B1-POWER 已重画并验证(六项全过、xpage 漏接 0、 port_audit --r2 BLADE 146 个可疑 0)。 ⚠ 裕量只有一档:刀片满载 1.45 A = 额定的 73%,压在「连续 ≤75%」的降额线上; 真无故熔断就换 F1206SB2500V032TM(C310999),同封装,改一个常量。见 §20.7。 ⚠ 立创描述写「PCI-E 4X」的有两颗其实是 x1(实测 36/38 脚),判据只能是放到空页读回引脚数。 ⚠ 脚号体系不同:插槽是 164(脚名才是 A1B32)+ 一根 65 屏蔽脚, 引脚相对坐标与金手指完全一致 ⇒ 版图一个字没改,只加了一层 slot_pin() 换算。 ⛔ 新风险:贴片立式,保持力只靠焊盘剪切 —— M4 布板必须给刀片另加固定件。见 §20.3。
  • ⚠️ 本轮又交了五次「几何验证抓不到」的学费,全靠放大截图发现(§20.5): R1 搬来的图元整块落进标题栏、钽电容 C737 的 rot 语义与跨距都与假设相反、 三处文字被旗标签 / 元件值标签压穿。建议给检查器补两个盒子模型 (旗的「符号+标签」矩形、元件位号/值文字的矩形)。
  • ✅ T1 / T2 / T3 已全部定稿(2026-09-08),§0.3 那张表现在一条待办都不剩。见方案文档 §21:
    • T1 = 槽距 20.32 mm,不必实测。 根因是 §3.2 的公式里写着「立柱 3.0」—— 那是 2026-09-06 就被中框式焊盘(间隙 1.0 mm,R1 §22.3e)取代的顶针方案。 改正后 h_phone + g = 14.72,即使把 h_phone 取成小米8 整机厚度 7.6 mm(物理上限)仍余 7.1 mm ⇒ 对这个数不敏感。
    • T2 = 10.5 mm(C3828985 6.3TPV2200M10X10.5 SMD)。这个数 2026-09-06 换矮封装时就有了,只是没回填。 ⛔ 只能放 A 面没有手机压着的服务带/功率列(可用 11.713.7,余 1.23.2);B 面只有 3.0 mm 放不下。
    • T3 = 做斜边(20°/1.0 mm)。加价压在已经要付的金手指工艺费之上, 而它挡掉的是「刮伤已焊死的插槽簧片 → 偶发掉线,功能测试测不出来」。 ⚠ 顺带纠正 §5.3 一个错:金手指区要电镀硬金,不是 ENIG(ENIG 只有 0.05~0.1 µm,是为焊接设计的,插拔会磨穿见镍)。
  • ⇒ M4(刀片 PCB)已开工:M4a = 金手指封装(见上); ✅ M4b 也做完了:板框 98×76 / R2 已画并校验闭合,J1 摆在 (49, 0), 83 件全部导入 PCB(位号 + 封装逐件对上)。脚本 .claude/r2/pcb_blade.py / .claude/r2/pcb_import.py。落地记录见方案 §23。 ✅ M4c 前三步也做完了(见方案 §24): 设计规则已下发并审计全过(⛔ 发现 BLADE-PCB 建出来是 2 层,已改 4 层; 安全间距只能给 0.15 —— 0.2 会刷出 23 条全在 J4 BM24 封装内部的假错); 83 件摆位已下发并读回校验(机械锚定件从 mi8_mech.json 换算, 功率列自下而上 = 输出端→输入端,U3 已挪到 Rs1 边上保 Kelvin,B 面留空); DRC 间距错误 0,剩下的 320 条全是「还没布线」。 ⛔ 铺铜踩到一条大的:全贴片的板子不打缝合过孔,四层里有三层的铜面会被当孤岛整块删掉, 而 rebuildCopperRegion()、铺铜管理器、DRC 三个地方都不报错。已按 5 mm 网格 打了 196 个 GND 过孔。⇒ 正确顺序是 摆位 → 缝合过孔 → 布线 → 铺铜。 ✅ 铺铜也做完了:6 块铜面里 4 块已填,L2 是一整块没割断的地平面 (截图 .claude/r2/M4c-blade-L2-gnd.png);VO-L3/V12-L3 两块等布线打了过孔才会填。 DRC 间距错误 0,连接性错误 252(= 还没布线)。 ⛔ 卡在布线:自动布线两条路都跑不出一根铜 —— pcb_Document.autoRouting 这个 API 在本机客户端上根本不存在;GUI 的「布线 → 自动布线...」对话框能开、能配、运行 点得下去,但立刻关闭且铜线数 0,不报错不弹进度。见方案 §24.6。 ⇒ 要么我自己写一个够用的布线器(~55 网络 / 361 焊盘),要么用户在客户端里手跑一次。 ⚠ 刀片外框的底边必须在缺口口部断开(相对封装原点 x = -6.45 / -3.55), 坐标从 fp_edge.notch_mouth() 取,别手抄;拼不上会得到一个开口板框。 唯一还悬着的是 ESP32-S3-WROOM-1 是 R8 还是 N8:现在按 R8 保守留空 IO35/36/37, 全板只剩 IO47/IO48 两个备用脚(§20.6)。定了 N8 就能多放三个脚出来。
  • ✅ M6(2026-09-09,用户拍板):屏蔽窗去掉、器件全到 B 面、加两条排线穿板槽。 A 面只剩 K1~K4 + J1,78 件在 B 面;C60/J2/J3 能下去是因为B 面服务带净空 18.7(对面 A 面清空了),「B 面只有 3.0」是把 L1 高度当预算写的,§3.2 已更正。 排线槽照搬 R1(layer 12 填充 = 挖槽区域):BAT x 20.832.8 / TAIL x 46.666.6,y 24.8~27.8; 排线穿槽 U 形折回 B 面,J5(26.8,32) / J4(56.6,33) 在槽北边、底层 rot 0(推导 + 读回, 见方案 §26.3,首件仍要通断复核)。⚠ J1401 的锚从 48.76 改成本体中心 53.58(§22.3b), 差 4.8 mm,按旧值尾插车道会压在 H4 螺钉头上。旧布线全部作废,缝合过孔重打 182 个;重布 714 段 / 289 过孔、铺铜 24 块全填、 DRC 间距 0 / 物理 0,没接完 16 处 / 10 个网(功率列 U2/U4 那一窝老拥塞,见方案 §26.5)。 ⚠ VO_P 岛改成 L 形跟着 J5 北伸(pcb_pour.VO_LOBE);USB_DP 收尾压 DM 扇出孔那一处是手改的, 不在路由缓存里。 ⛔ 「继续布完」试了四种办法都到不了(换顺序 / PathFinder / 只补线 / 过孔扇出,方案 §26.6 有表), 板上实际只欠 5 只脚(U2.15 U2.17+R11.1 U2.2 R16.1 R8.2),全是出口被自家邻脚封死。 作业单 .claude/r2/HANDROUTE-M6.md,在客户端交互式布。 好的路由缓存备份 _blade_routes_m6_good.json。截图 .claude/r2/M6-place-bot.png / M6-place-top.png。
  • ✅ M7(2026-09-09):板框改折线 → B 面重排 → 自己布线,714 段全通 ① ⛔ 板框根因:pcb_PrimitiveLine 在嘉立创里是导线(铜),不是图形线。 M4b 起板框一直是用它画在层 11 上的 —— 渲染成品红、DRC 干净、四道几何校验全过, 但客户端不认它是板框(属性面板 图层 那一格 data-value=11 却显示空白)。 是用户看属性面板发现的。改成一条 54 点的闭合 pcb_PrimitivePolyline (缺口用 outline_polygon() 插进底边)之后,自动布线才第一次跑起来。见方案 §27.2/§27.3。 ② ⛔ 官方自动布线放弃:板框修好后它能跑到 100%,但收尾时把自己布的铜全丢掉 (大跑小跑都一样,最后只剩运行前就有的那几段),toast 却报「自动布线已完成!」。 探针全装(console/onerror/fetch/XHR/Worker)一条都抓不到。⇒ 用 router.py。 ③ B 面重排(用户拍板):排布器从「按面积自下而上塞」改成**「挑离目标最近的空位」, 目标点精确到对端焊盘**、有芯片对端时只认芯片。U4 PCA9555 按用户要求挪到左下角 (12.5,15.6)(⚠ 用户拖的 y=10.67 会压进金手指区,已抬到禁布区上沿); U2 东边四只脚的外围件拆出 EFUSE_E 区,一列按 p17→p20 排; R3(U1 的 EN 分压)从东边挪到 U1 西边的口袋。 ④ 新判据:place_audit.py(摆错边 / 被挡 / 去耦太远,判「侧」不判「穿本体」)、 board_check.py(从板上读回铜做并查集,缓存/getNetLength/DRC 三个都不可信)、 grid_snap.py(把件微调到 0.25 布线网格,对齐焊盘 8 → 83)。 ⑤ 结果:铜 714 段 / 2563 mm,过孔 316 + GND 缝合 131,铺铜 24 块全填, L2 一整块地平面;board_check 57 个网 56 个全连通(GND 靠铜面); DRC 间距错误 4 条,全是 PCIe 缺口到 11/12 号指的 0.2 mm(几何定死的,见下)。 截图 .claude/r2/M7-routed-top.png / M7-routed-bot.png / M7-L2-gnd.png / M7-u2-area.png。 ⛔ 要用户拍板的一条:缺口到 11/12 号指铜边距 0.2 mm,嘉立创规则要 0.3。 这是 CEM 几何定死的(1.0 间距 / 0.7 指宽 / 1.9 缺口宽),任何 PCIe 卡都一样; 以前不报是因为板框那时是「导线」。⇒ 下单前问嘉立创能不能做,或把缺口收窄到 1.6。 ⑥ 直角走线全部改 45° 斜切(miter.py,自动跑到收敛,3 轮共切 227 个): 只去铜不加铜 ⇒ 净距只会变好,DRC 前后都是 4 条。剩下的 90° 全是结构性的 (焊盘/过孔上 11、T 型分支、腿太短 45),见方案 §27.10。 ⛔ 第一版把端点归并写成「坐标除以容差取整」的固定分桶,相差 0.011 的两点掉进 两个桶 ⇒ 同一个角切两次、原直角腿留在板上,板上留下 31 处悬空小碎块 (用户放大 KPAD_VUP 那一带一眼看到,而 DRC / board_check / router_check 三道判据全过)。改成空间哈希就近归并 + 新增「断头铜」判据,现在 7 处, 全是布线器本来就有的(地母线故意外伸 1 mm / 引出脚余量)。见方案 §27.11。 ⚠ 顺序:布线 → 下发 → 缝合过孔 → 斜切 → 铺铜;重跑布线器要重跑这一步。 ⚠ 还欠一件:D12 的割断位(金手指到 J2 之间要留一段带丝印的割断位,方案 §17.4)。
  • 🔧 M8(2026-09-09,进行中):底板 PCB BASE-PCB 从空文档做到摆位定稿 ① 板框:建工程时留的 4 条 pcb_PrimitiveLine(110×125 默认框)就是 M7 §27.3 那个坑 —— 导线不是板框。已删掉,改成一条 45 点闭合 pcb_PrimitivePolyline(四角 R5)。 ② 安装孔 4×M3(Φ3.2 NPTH,离板边 4 mm,四角)。用户拍板去掉原来东西两侧中点那两个 ⇒ 5 片刀片一起插拔时板中部无支撑,装配要从槽阵下方托住再插。 ③ 143 件全部导入(pcb_import.py --base),位号 + 封装逐件对上。 ④ 设计规则(rules_base.py,2 层 JLCPCB Capability(Two Layers Board)): 5 条线宽(PWR9A/PWR12V/FAN12V/PWR5V/PWR3V3)+ 七对差分 (5 槽 + ESP32 原生 USB + 上行 USB-C)+ 安全间距 0.20,审计六项全过。 ⑤ 摆位 143 件下发并读回校验(place_base.py)。五条带: input 015.5 / hub 15.543.5 / slots 43.5~85.5(只有 5 个插槽,走廊留给布线) / ctrl 85.5113.5 / fan 113.5125。投影带内限高 8 mm 是硬碰撞判据。 ⛔ 三条「几何验证抓不到、错了就报废」的朝向,已做成读回断言: · U32 rot=270(下行口原生朝西,转 270 才朝北正对槽阵); · 插槽物理顺序是反的:西→东 = J38 J37 J36 J35 J34 —— U32 北排的 x 顺序是 USB1 在东、USB5 在西,按位号自然顺序摆会让五对 D± 互相交叉, 两层板上交叉 = 打过孔换到 L2 = 在 D± 走廊正下方割地平面(方案 §6.2 第 1 条明令禁止); · 插槽 rot=90 ⇒ +12V(A2/A3/B1B3) 落在南边 y4850 正对入口链,D±(B14/B15) 在东列。 ⛔ U32 只能放 y=30,不能放 37:hub 带一共 28 mm,要塞下「12V 横脊 5 + 南边逃逸 4.3 + 本体 9.75 + 北边 D± 扇出 8.6」。放 37 时北沿离插槽焊盘只剩 1.6 mm, 布线器实测 USB2 直接布 0 段。 ⛔ 9 A 的 12V 分配走了两次弯路,最后是「输入带母线 + 一条横脊」: · 第一版让排布器摆入口链 ⇒ F1/Q1/D1 被摊到整条 110 mm 上,VIN_FUSED 一个网的 焊盘外框 42 mm 宽、四段铜面互相重叠。⇒ 入口链整条手排(J1→F1→Q1→D1→C2/C3→Rs6→U13)。 · 第二版在槽阵南沿画了一条横杠 ⇒ 五对 D± 要竖着穿过它,两层板上一根竖线就把横杠整个切断, 而铺铜管理器 / DRC / 断面校核三个都只说「有铜」。⇒ 改成脊 + 布线器画的 1.5 A 支路。 · 第三版想给每槽加一根铺铜「齿」走两列焊盘之间那条 5.2 mm 无盘带 ⇒ 槽的第 65 脚(屏蔽脚 3.0×5.0,接 GND)正坐在那条带的正中,断面量出来 0.00 mm。 定稿:BUS_IN(6391 × 0.816.0,15.2 mm) + SPINE(291 × 15.820.8,5 mm), 断面校核(pour_base.py,逐刀竖切取最窄、跳过串联元件本身)全过。 ⚠ VIN_RAW 1.70 / VIN_FUSED 6.35 不强制:瓶颈是 DC-005 三只脚和 1206 保险本身, 板上加铜救不了 —— 与「插座超额定」是同一条已知风险(设计文档 §22.6)。 ⛔ 布线器要知道铺铜的存在:顶层电源铜面按各自的网标进占用图, 否则别的网会大大方方从 12V 母线中间穿过去,铺铜时把母线整段切断。 📌 用户拍板(2026-09-09):最远一路 D± 接受 ~70 mm(方案 §6.3 预估 41 mm 的前提是 「D± 排在靠 Hub 那一端的针位」,而 M2d 定稿把它放在 B14/B15 = 42 mm 长插槽的正中)。 仍远短于 R1 载板的 92 mm,且 J39~J43 五个 D± 退路座本就是为这一条留的。 ⇒ M5 的验收判据「六对 D± ≤45 mm」改写成 ≤75 mm。 新脚本:pcb_base.py(板框/分区/槽阵/母线,唯一事实来源)、rules_base.py、 place_base.py、route_base.py、pour_base.py;miter.py / board_check.py 加了 --base。 截图 .claude/r2/M8-place-all.png。
  • 要继续 R1 的布线,从下面「⭐ 新会话从这里开始」往下读; 要做 R2,读 刀片式方案规划-R2.md:§0.3 的 T1~T8 现在全部已结,§13 是 M1 落地记录、§21 是三项收口。
  • ⛔ 手工活只剩一件:工程树里删掉误建的空工程 PhoneRail-R2b。 (原来的「Add into BOM 要在客户端手改」已作废:addIntoBom 是 boolean, 当初传的是字符串 'no' 才改不动。MI8-KEY-PAD 那条同样的结论也应该重试。) (原来第二件「量主板厚度」已由 §21.1 的上下界论证取代;装配首件仍要用塞尺确认一次 R1 §22.3e 那处自相矛盾 ——「贴合面屏蔽罩 1.4 mm」vs「贴合面元件全低于 1 mm」, 螺柱间隙就是按 1.0 定的。真顶到就把垫片换 1.5,槽距那边有 7 mm 富余。)

更新:2026-09-08 第三十四轮。✅ 12V 输入换成圆形 DC 电源座(DC-005-5.5*2.5, 用户拍板不降载、接受插座超额定)——原理图与 PCB 两侧都已落地并逐项回查通过; 摆位 301 件已下发,悬空铜清零,扇出补口完成。⛔ 两条要紧的: ① 「从原理图导入变更」对下板已不能用 → 已修好:根因是 20 个元件的 uniqueId 与原理图错位一格,pcb1_uid_sync.py 对齐之后变更清单归零(见交接二); ② 8 A 输入链的三块顶层铺铜要重画 → 已重画并下发,断面全过(见交接三)。 布线只欠 50 个网络 / 97 条连接,不是整板重布;DRC 间距错误 5 条,全在 U6/U7 那一窝。


更新:2026-09-08 第三十三轮。✅ 载板已完全恢复:原 PCB2-CARRIER 在一次桥接失同步 事故中损坏,靠工程历史还原将其移除,新建 PCB2(942ed1560d81c1e7) 按快照重建, DRC 已回到 115 条基线、连接性 0。下板 PCB1 全程未受影响。细节见「交接(一)」。

本轮已定稿的设计决策:U32 改 CFG_SEL[2:0]=110(strap 禁用),取代上一轮的 「PRT_SWP1~5 各 100k 下拉」——那五颗里有三颗(CH1/CH2/CH3)在载板上布不出线。 原理图侧已经改完并验证通过、已存盘,不受载板事故影响。 BOM 净 −7 颗。


⭐ 新会话从这里开始

第三十四轮交接(一):✅ 12V 输入换成圆形 DC 电源座,两侧都已落地

用户 2026-09-08 拍板:J1 XT30PW-M → DC-005-5.5*2.5(立创 C9900017043),且不降载。

⚠️ 这是全板唯一一处「明知超额定还要装」

整机 12V 最坏工况 9.99 A(§22.6),而 DC-005 家族额定 2~5 A (同族 XKB DC-005-2.5A-2.5 = C319098 官方标 2.5 A)。 发热点在插头内芯与插座弹片的接触面,板上加铜救不了那一处。 ⇒ 这不是「算过没问题」,是已知风险、用户明确接受。设计文档 §1 / §22.6 要写明。

板端唯一能做的缓解已经做了:pin2 / pin3 两个负极触点都接地并联,接触电阻减半。

引脚定义(查过三处,两处互相印证;装配前仍要用万用表核一次)

脚 含义 接法
1 中心极,正 VIN_RAW($1N223)→ F1.1
2 负极静触点(无插头时与 3 短接) GND
3 负极动触点(插头外壳压这一片) GND

万用表判据:无插头时 2-3 导通、1 对 2/3 断开。

做了什么

步骤 脚本 结果
原理图换件 p1_dcjack.py verify_sch --nets 悬空 0 / 单引脚 0 / 一网多名 0;port_audit 458 个可疑 0;xpage 漏接 0
PCB 换件 pcb1_dcjack.py 封装 / 三个引脚网络 / 元件数 全部回查通过
摆位 pcb1_place.py --push 301 件,本地六项全过;edge_access J1 离西板边 0.5 mm、桶口朝外(已截图确认)
悬空铜 pcb1_orphan_cu.py 25 段线 + 2 孔清掉,回查碎片 0
扇出补口 pcb_fanout.py --only <31 件> +13 孔,GND 缺口 26 → 23

板上现在:301 件 / 铜层 2878 线(顶 2174 / 底 704)/ 955 孔 / 1046 焊盘 / 15 铺铜。


第三十四轮交接(二):✅ 「从原理图导入变更」已修好(根因是 uniqueId 错位)

换 J1 时撞上的,这条比 J1 本身重要。

对话框提出的是一大串风扇块重映射:J27→J26、Q10→Q9、R753→R752、 Q7 换成 4 脚座封装、R747 换成 DC 座封装……而下板和原理图按四项逐个对过、全部一致:

对账项 结果
位号(301 个) 全对
封装名 只有 J1 一处不同(就是要改的那处)
器件值(风扇块 23 件) 全对
焊盘网络(风扇块) 全对,且自洽(J26→FAN_IN_TACH_RAW→R750/751/752→Q9→FAN_TACH_IN)

根因是 skill §9o:P6-FAN-EDL 页整页重画过,通道 ID 全变了, EasyEDA 按内部 ID 重新配对、配错一格。应用下去会把一个本来正确的风扇块打乱。

✅ 已修(同日)

getState_UniqueId() 一查就清楚了:原理图的 ID 比 PCB 的整整大 1, 全板 301 件里错 20 个、全在 P6-FAN-EDL 那一页。

位号   PCB       原理图
J26    gge418    gge419
J27    gge419    gge420     ← 板上 J27 拿着原理图 J26 的 ID
Q9     gge423    gge424

pcb1_uid_sync.py:按位号对账 → 两阶段写回(先临时 ID 再目标 ID, 因为这是个排列,直接写会中途重号)→ 回查逐个一致且无重号。

结果:变更清单从一大串塌成一行(J1:通道ID (空) → $1I219 —— API 新建的元件本来就没有通道ID)。应用它,再开对话框 0 条变更。 应用前后风扇块 20 件的焊盘网络逐条未变,元件 301 / 铜 2878 / 孔 955 / 铺铜 15 也没动。

⇒ 导入对话框现在可以正常使用了。

⚠️ 改的是元件身份,DRC / 几何检查 / 连通性一条都不会报 —— 唯一验收是「重开对话框看还剩几条」。写之前存了快照 _pcb1_snap_pre_uidsync_20260908.json。

💡 教训已并入 skill §9o-2 / §9o-3: 判据是「位号 / 封装 / 器件值 / 焊盘网络 四项对账」,不是对话框; 其中只有焊盘网络那一项有判别力(同封装、同型号之间的错位前三项都看不见)。

💡 读那个对话框时不要用 pcb1_import.py --probe:它读滚动容器的 innerText, 而那张表是多列分开渲染的,offsetTop 在列之间碰撞 —— 表头(位号)和明细行会被拼错,读出来的清单似是而非。 新写的 p1_import_rows2.py 逐 <tr> 读、每行自带单元格。 但结论仍要以「PCB vs 原理图四项对账」为准,不要以对话框为准。


第三十四轮交接(三):布线只欠 116 条,不是整板重布

PROGRESS 之前写「下发摆位 = 作废 2951 段铜、整板重布」。实测不是。 31 件挪位之后,270 个没动的元件的铜完好,欠的是 52 个网络 / 116 条连接。 ⇒ 不要跑 pcb_fanout.py --clean(那条会把 955 孔 + 2935 线全删),走增量补线。

✅ 8 A 输入链的铺铜已重画并下发(2026-09-08 收尾)

新链路是单向的,比旧版顺:J1(−18.8) → F1(−12.5) → Q1(−6) → D1(1) → 东北去 C2/C3/Rs6。 旧版是 J1 一路向东到 Q1、再折回西边找 Rs6。C2/C3/C4/C5/C6/Rs6 一颗没动, 所以旧的干道 / 两个电容口袋 / 北柱原样保留,只重画了西端进料三块 + 干道西延。

断面 实测 需要
RAW J1.1→F1.1 (x=−14.2) 5.75 mm = 11.5 A 8 A
FUSED F1.2→Q1 源极 (x=−10.7) 4.50 mm = 9.6 A 8 A
SW Q1 漏极东出 (x=−1.5) 9.25 mm = 16.2 A 8 A
SW 过 D1 北上 (x=2.0) 12.75 mm = 20.5 A 8 A
SW 干道 / 北柱到 Rs6.1 4.75 / 3.75 mm = 10.0 / 8.4 A 8 A

回读实测:$1N223 25 mm² / $1N227 23 mm² / $1N236 256 mm²,各自一个连通域; 15 块铺铜全部填充;异网最近距 0.25 mm(下限 0.20)。

⚠️⚠️ 顺带修掉一个藏了很久的东西:pcb1_pour.py 也在用假板框

它的栅格一直是 160×125 @(0,0)(pcb1_place.py 第三十轮就改成真板框了,这个文件没跟)。 平时看不出来 —— 所有铜都在第一象限;一把元件挪到 x<0,三块顶层铜当场报 「碎成 0 块 / 断面 0.00 mm」,而板上的铜其实没问题(多边形是原样下发的)。 已把毫米↔栅格收进 gi/gj/gx/gy 四个函数,板框改成 220×135 @(−30,−5)。 副作用是地平面现在真的覆盖整块板(Inner1 从 28351 → 29099 mm²)。

⛔ 但改共享常量震到了 pcb1_netlist.py:它 G = PO.G 却自己写 int(x / G), 于是坐标偏了 30 mm,报出「+12V 从 4 个碎片炸成 23 个」的假象, pcb1_orphan_cu.py 据此误删 12 个过孔(所幸那些碎片本来就不含焊盘,电气无损)。 已修,教训写进 skill §9ah / §9ah-2: 「连通性突然大面积变差」+「实测回读健康」= 先怀疑度量,别改板子。

还剩的 5 条 DRC 间距错误(都在 U6/U7 那一窝)

SAT_FLIP 把 C609/C612 转了 180°,两个脚南北互换,旧铜接到了对方脚上 —— 那是真短路不是 DRC 挑剔(端点落在对方焊盘轮廓里)。 pcb1_satflip_fix.py 删掉了那 4 段(9 → 5 条)。剩下 5 条是 $6N532/$6N547/SDA/VDAC4 与挪位后的卫星件抢地方,交给 --ripup 布线。 C609.1/C612.1 的地扇出补不进去(那一窝太挤),同样交给布线。

下一步的第一件事:整块重布

pcb1_pour.py 里 VINRAW-TOP / VINFUSED-TOP / VINSW-TOP 三块的多边形坐标 是按旧摆位手写的([email protected]、[email protected]、Q1@24、D1@31),而输入首级整体西移了 30 mm (J1→−18.8、F1→−12.5、Q1→−6、D1→1)。现在那些多边形和 §「8 A 输入链」那一排 断面校核线量到的都是空气。文件里已有一条同样的教训注释(第二十三轮那次)。

这是全板电流最大的一段(8 A,顶层 1 oz),要按新几何重画,不能顺手拖一下。

剩下的链(按顺序)

# 0. ⛔ 先重画输入链三块顶层铺铜 + 断面校核线(pcb1_pour.py,见上)
/c/Python314/python .claude/pcb1_i2c_l3.py            # 挪件之后换层孔要重算
/c/Python314/python .claude/pcb1_route_sig.py --refresh --ripup --push
/c/Python314/python .claude/pcb1_route_usb.py         # J22 东移了 30 mm
/c/Python314/python .claude/pcb1_pour.py --push
/c/Python314/python .claude/drc_read.py

⚠️ FAN_12V_SW 的 0.8 mm 线宽已经落实:pcb1_rules.py 新增 FAN12V 类 (0.8 / min 0.3 / max 3.0)并指派给该网络,34 个网络指派全部下发成功。 依据(Q7 居中、东西各 0.67 A、51/39 mV,而 0.20 mm 下是 204/155 mV)写在文件里。

⚠️ J1 换件之后 AUTO_TRACK['VIN_RAW'] 的探针从 J1.2 改成了 J1.1 (XT30 是 1=−/2=+,DC-005 是 1=中心正极)。探针写错时 pcb1_rules.py 是硬失败、 不会静默放过 —— 这次就是它拦下来的。

本轮新增的常驻工具

文件 用途
p1_dcjack.py 原理图侧 J1 换 DC 座(幂等,自带线段集合比对的回查)
pcb1_dcjack.py PCB 侧定点换件,绕开导入对话框;记着 create() 的签名是 (器件, layer, x, y, rot, ?)
pcb1_orphan_cu.py 找/删悬空铜:判据是「碎片里既无焊盘也无铺铜」。删除按坐标带容差匹配,且线/孔分开走各自 API
p1_import_rows2.py 逐 <tr> 读导入对话框(替代 --probe 的 innerText 拼接)
pcb1_uid_sync.py 把 PCB 元件的 uniqueId 与原理图对齐(两阶段写,避免中途重号)—— 导入对话框失灵时的正解
pcb1_satflip_fix.py 删 C609/C612 转 180° 之后接错脚的旧铜(真短路)

第三十三轮交接(一):⛔ 载板 PCB 文档损坏 → 换文档重建

事故链(责任在流程漏了一道判据,防呆已补)

pcb2_cfgsel.py --push   建 6 线 2 孔 + save()            ← 正常
pcb2_reload.py          删铺铜边框 → save() → location.reload()
                        ↑ 重载回来载板已经进入「Load Empty」状态、铜层读回全 0,
                          而当时判断「重载成功」只看 windowId 变没变,**没有数图元**
pcb2_pour.py --push     建 7 块边框 → 填充一直 0/7 → 中途 save()   ← 这一下把「空」写回文档

根因是 skill §9ae 的失同步,而真正毁数据的是失同步之后那次 save()。 location.reload() 本身不删数据。

防呆(已落地)

  • pcb2_reload.py 新增 assert_alive():重载后强制数元件/导线/焊盘, 任何一项为 0 就 sys.exit,并写明「不要 save()、不要跑 pcb2_pour.py」。 ⇒「重载成功」的判据从此是图元数不为零,不是 windowId 变了。 这条防呆当天就救了一次:重建完成后再跑 pcb2_reload.py,同样的失同步又发生了, 脚本在 save() 之前停住,一个字节都没写。
  • skill 新增 §9ae-2,记了整条失败链、迷惑性现象和三条规矩。

⛔ 结论:PCB2-CARRIER 这个文档修不好,必须换

三条互相印证的判据:

  1. 重开之后电气对象(元件/焊盘/导线/过孔/铺铜)一律读回 0, 而同一次会话里 PCB1 读回 301 件 / 2951 线 / 944 孔完全正常 ——「换一块板对照」是区分「客户端坏了」和「这个文档坏了」最快的办法。
  2. getCurrentRenderedAreaImage() / zoomToAllPrimitives() 只在这个文档上挂到超时 (不是 §9t 那种 88 字节空 PNG),PCB1 上正常。
  3. 决定性的一条:上一轮把孤儿图形删干净(1546 → 40)并存盘、存盘后回查过, 完整重启 EasyEDA 之后那些图形又回来了而且翻倍(1546 → 3091) ⇒ 对这个文档的写入根本没有真正落盘。

⚠️ 文件 → 工程历史... 在这台客户端上点不开,所以历史恢复这条路走不通。 这句是错的(已于 2026-09-08 推翻) —— 点不开是因为当时的活动文档正是卡死的 PCB2, 把活动文档换掉之后它就能开。最终正是靠它恢复的,见下面「实际怎么解决的」。

✅ 重建配方(已经完整跑通并逐条对账过一次)

事故前一刻正好跑过 pcb2_cfgsel.py --refresh,把整板几何 dump 进了 _pcb2_route_geom.json:导线 2033 段(层 1/2,带网名和线宽)、过孔 894 个、 焊盘 1000 个(216 元件 + 38 自由)。那份快照是在 --clean 之后、--push 之前抓的, 所以再加上 _pcb2_cfgsel.json 账本里那 6 线 2 孔,正好等于事故前的最终状态。

上一轮在旧文档里按这个配方重建了一遍,对账结果:

项 结果
216 个元件摆位 ✅ 与快照逐位一致
1000 个焊盘(962 元件 + 38 自由) ✅ 位号 / 坐标 / 网络 / 层 全对上,缺 0 多 0
2039 段铜层导线 ✅ 逐条一致(网络、层、两端点、线宽),最大偏差 0.000 mm
896 个过孔 ✅ 逐个一致
板框 1 / 穿线槽 10 / 手机位丝印 25 线 + 5 标签 ✅

也就是说材料是齐的、方法是验过的,只差一个健康的文档。

✅ 实际怎么解决的(2026-09-08):工程历史还原 + 新建 PCB 重建

先纠正上面一句话:文中曾写「文件 → 工程历史... 在这台客户端上点不开」—— 那是因为当时的活动文档正是卡死的 PCB2。把活动文档换掉之后它就能开了。 ⇒ 判据修正:某个 GUI 功能点不开时,先怀疑当前活动文档卡住,而不是那个功能坏了。

顺带另一个佐证:右键 PCB2-CARRIER → 删除 时服务端直接回 delete failed ! —— 连删都删不掉,这个文档在服务端侧就是坏的。

实际执行的顺序

步 操作 结果
1 文件 → 工程历史...(从健康文档打开) 版本列表可用:21:25:27 … 22:24:51
2 选 9月7日 21:37:52(= pcb2_cfgsel.py --push 存盘后、pcb2_reload.py 之前) ——
3 「还原」→ 弹出「当前历史记录之后同时存在其他PCB的更改,是否同时还原?」→ 选同时还原 损坏的 PCB2-CARRIER 被整个移除
4 核原理图 ✅ 完好:27 元件、单引脚网络 0、U32.13/R712.2=+3V3_HUB、R711.2=GND ⇒ CFG_SEL=110 仍在
5 工程树右键 Board2 → 新建PCB(P) ✅ 新文档 PCB2 = 942ed1560d81c1e7
6 pcb2_retarget.py 942ed1560d81c1e7 --write 27 个文件 / 31 处,旧 uuid 一处不剩

⚠️ 为什么敢选「同时还原」:21:37:52 之后被改过的只有 PCB2-CARRIER(那是我的重建尝试); 原理图最后一次存盘在 21:28:01,在还原点之前,所以 CFG_SEL=110 不受影响; PCB1 本轮全程没动过板。选之前先把时间线和每个版本对应的操作核一遍,别凭感觉点。

💡 pcb2_reload.py / pcb2_pour.py 那两次存盘在历史列表里根本没有条目 —— 这反过来证实了「对损坏文档的写入没有真正落盘」,也正是 21:37:52 是最后一个好版本的依据。

✅✅ 载板已完全重建并验证通过(2026-09-08 收尾)

新文档 PCB2 = 942ed1560d81c1e7,逐项对账结果:

项 结果
元件 216,摆位与事故前快照逐位一致
焊盘 1000(962 元件 + 38 自由);缺 0 / 多 0 / 坐标·网络·层全一致
铜层导线 2038(顶 505 / 底 1533),与快照逐条一致,最大偏差 0.0001 mm
过孔 896,逐个一致,最大偏差 0.0000 mm
板框 / 穿线槽 / M3 孔 / 手机定位孔 / 丝印 1 / 10 / 8 / 30 / 25 线 + 5 标签
设计规则 差分对 11/11、线宽指派 12/12、铺铜全连接 5/5、对内间距 11 条
铺铜 7/7 填充,优先级 1<3<5<7<9<11 (L15) + 1 (L16),地平面覆盖 0.6,0.6~219.4,134.4
inner1_verify 五个 VO 岛主域占本网铜 100%,3 焊盘 + 11 过孔全在主域,最窄断面 7.05~28.75(下限 7.0)✅
pcb2_net_conn 0 / 22 个网络不连通
DRC 115 条 = 间距错误 115(基线)+ 连接性 0 ✅

比事故前还干净一点:顺手清掉了一条 PRTDIS7 的游离导线(见下)。

收尾时踩到并已修的两件事

  1. 铺铜填充一直 0/7,两次都卡住。判据是 §9k 的「窗口底部有条 0% 进度条」—— 实测到了。先查出 pcb_Document.getCalculatingRatlineStatus() 是 active(飞线在算), stopCalculatingRatline() 停掉了但没解决;最后靠 pcb2_reload.py 重载才通, 重载后 assert_alive() 回查 216/2644/896/1000 全对,接着铺铜一次 7/7。 ⇒ 修正上文那条「全程不要跑 pcb2_reload.py」:重载本身没问题, 问题一直是那个损坏的文档。在健康文档上重载是安全的,而且是清掉卡死后台任务的唯一办法。 前提:重载前先存盘并回读验证,重载后必须过 assert_alive()。
  2. 一条漏网的重叠导线:PRTDIS7 L2 (101.250,62.133)→(101.820,62.133)。 pcb2_cfgsel.py --clean 当时按两小段删(101.250→101.322、101.322→101.821), 报告「12/12 全删中、回查清零」,但板上还有一条跨整段的重叠图元没被覆盖。 它跟着进了 _pcb2_route_geom.json 快照,重建时又被重放回来,DRC 报「游离的导线」。 已删并补进 pcb2_cfgsel.DEAD_LINES。 ⇒ 教训:删除的回查判据要问「该网络还剩几条」,不能只数「删中了几条」。

驱动 EasyEDA GUI 的几条实测(本轮踩出来的)

  1. 右键菜单要点在 tree-node_* 那一层,点文本叶子无效。而且必须发完整的 pointerdown/mousedown/pointerup/mouseup + contextmenu,坐标取该行的包围盒中心。
  2. 菜单项只认 React 的 onClick:合成鼠标事件对子菜单项静默无效 (文件 → 新建 → PCB(P) 点了三次都没反应)。要往上找 __reactProps$ 里的 onClick 直接调。
  3. 列表行的选中反过来只认原生鼠标事件:工程历史的版本行用 onClick 点了 fire 报成功但 tree-rowline-selected_* 不出现,改发原生 pointer/mouse 序列才选中。 ⇒ 两种都要备着,按元件类型试。
  4. 「删除文档」的确认框里复选框是真 <input type=checkbox name=delReply>, 要 checked=true + 触发 React 的 onChange,光 click() 不行。

换文档重建的完整步骤

第 0 步(用户在 GUI 里做):工程树右键 Board2(phonerail-carrier) → 新建 PCB。

# 1. 把 27 个脚本里硬编码的载板 uuid 一次性换掉(31 处)
/c/Python314/python .claude/pcb2_retarget.py                  # 先看清单
/c/Python314/python .claude/pcb2_retarget.py <新uuid> --write # 换,并回查旧 uuid 一处不剩

# 2. 新建的 PCB 默认 2 层,先切 4 层
/c/Python314/python .claude/pcb2_place.py --layers

# 3. 元件(216 个全部是「增加元件」,「包含设计规则」必须不勾)
/c/Python314/python .claude/pcb1_import.py --board2 --probe
/c/Python314/python .claude/pcb1_import.py --board2 --apply

# 3b. 设计规则 ⚠️ **必须在导入元件之后**跑
#     先跑的话网络还不存在,实测:差分对 0/11、线宽指派 0/12、铺铜全连接 0/5 全落空,
#     而规则本体会成功下发 —— 看起来像成功了。导入后重跑一次就是 11/11、12/12、5/5。
/c/Python314/python .claude/pcb2_rules.py --push

# 4. 摆位 + 机械
/c/Python314/python .claude/pcb2_place.py --push              # 216 件
/c/Python314/python .claude/pcb2_place.py --outline           # 板框 + 10 槽 + 8 个 M3 孔
/c/Python314/python .claude/pcb2_place.py --mech              # 30 个定位孔 + 手机位丝印

# 5. 铜(2039 线 + 896 孔,分批 250,自带下发前守卫和落盘后回查)
/c/Python314/python .claude/pcb2_restore.py --copper --push

# 6. 铺铜 + 校核
/c/Python314/python .claude/pcb2_pour.py --push
/c/Python314/python .claude/inner1_verify.py
/c/Python314/python .claude/drc_read.py --pcb2                # 对 115 条基线

⚠️ 关于 pcb2_reload.py(此处曾写「全程不要跑」,2026-09-08 已推翻): location.reload() 本身没问题 —— 它在健康文档上完全正常(重建收尾时用它清掉 卡死的后台任务,重载后回查 216/2644/896/1000 全对)。当初把载板打进「Load Empty」的 是那个已损坏的文档,不是重载动作。 ⇒ 用它之前的两条硬前提:① 先存盘并回读验证;② 重载后必须过 assert_alive() (脚本已内置,读回 0 就 sys.exit 且禁止任何 save)。

新工具

文件 用途
pcb2_restore.py --survey 清点 / --clean-orphans 删孤儿图形 / --copper [--push] 从快照重放铜层。守卫只数铜层(1/2),别再被封装带进来的 605 条丝印顶掉
pcb2_retarget.py 一条命令把 27 个脚本的载板 uuid 换掉,并回查旧 uuid 一处不剩

第三十三轮交接(二):✅ CFG_SEL=110 —— 原理图侧已完成,不受事故影响

用户在本轮拍板走 (c) 改 CFG_SEL[2:0]=110 关掉 strap(三条路的比较见下面第三十二轮交接)。

依据(datasheet DS00001598C 原文逐条核过,见 §21.6b)

  • Table 5-2:110 那一行只有「Internal Default Configuration • Strap Options are Disabled」—— 没有 111 的 Ganged Power/OC Switching,没有 100/101 的 Dynamic Power Switching, 没有 010 的 Bus Power Operation。它是唯一干净的「关 strap」档位。

  • Table 5-1 + 5-3 的缓冲类型决定三个脚各自怎么接:

    脚 缓冲类型 复用 怎么接
    CFG_SEL2(13) I 纯输入 无 直接接 +3V3_HUB,不加电阻
    CFG_SEL1(42) I/O12 HS_IND(输出) 必须经 R712 10k 上拉
    CFG_SEL0(41) I/OSD12 开漏 SCL/SMBCLK R711 10k 下拉,不动

    ⇒ 上一轮计划里的 R713 不需要:pin 13 本来就是硬接 GND 的,改成硬接 +3V3_HUB 完全对称,还省掉一次「在 U32 西侧找位置 + 布线」。BOM 净 −7 而不是 −6。

  • 必须连带删掉 R714/R715:strap 一停口 6/7 就开着,那两颗 10k 上拉对 Hub 内部 15k 下拉分压得 3.3 × 15/25 = 1.98 V,正好压在全速设备「已插入」的 2.0 V 判据上 → 幽灵口。 删掉之后 D+/D− 悬空、被 Hub 自己的 15k 下拉拉低 = 空闲,无害。

原理图 C2-HUB 已完成(已存盘)

.claude/c2_cfgsel.py:删 7 元件 / 4 旗 / 5 端口 / 13 条导线图元,新建 5 线 / 3 旗 / 1 端口, 页面文字全部重写。验证结果:

检查 结果
verify_sch.py --nets 单引脚网络 0 / 无网络名 0 / 一网多名 0;悬空 27→34(+7 全是有意的:5 个 PRT_SWP + USBDN6_DP/USBDN7_DP)
网表读回 U32.13 [+3V3_HUB]、R712.2 [+3V3_HUB]、R711.2 [GND] ⇒ CFG_SEL[2:0] 正好是 110 ✅
port_audit.py --board Board2 455 个旗/端口,可疑 0
xpage.py 真正漏接 0(PRTDIS6/PRTDIS7 两个网络已随端口一起消失)
截图 shots/C2-HUB-cfgsel110.png,已看过,排版干净

⚠️ 画面上抓到、几何验证抓不到的一处:+3V3_HUB 电源旗原本放在 (970,330), 标签跟右边 25 单位外 (995,330) 的地旗标签正好相接,而且一个电源旗夹在两个地旗中间看着就像地。 已下移到 (970,305)。(skill §10「几何验证证明不了看着对」的又一例。)

设计文档已同步

  • 决策清单 #18 划掉(改为「已被 #19 取代」),新增 #19 = CFG_SEL=110。
  • §21.6b 待补:CFG_SEL=110 的完整依据与接法(目前依据都在 c2_cfgsel.py 文件头里,内容完整)。

第三十三轮交接(三):下板 PCB1 —— 修掉「假板框」留下的 4 个不可达接插件

用户本轮的要求是「整体重新布局,降低布线复杂度」。量化之后结论和直觉相反,先记结论:

1. 「整板重排 301 件」这个说法不成立 —— 202 件被载板钉死

类别 件数 为什么动不了
五路通道模块 200 x 由载板 VOn_P 三针钉死(VO_X = 10.4 / 46.0 / 53.6 / 126.7 / 142.0),CH_PLACE.bx = VO_X ∓ 6
J24 / J25 2 由载板 J31/J32 焊盘反推,脚本里写着「不许改」
可自由重排 99 其中还有 32 件功率件、26 件贴身从属件 ⇒ 真正能按 HPWL 挪的只有 37 件

2. 「南北分配」这个自由度也没有油水

真板框下合法的南北分配有 8 种(假板框下只有 1 种,原注释「VO1 太靠左板边、VO3 太靠右板边」在真板框下不成立)。 逐一算加权 HPWL:当前的「南 CH1/CH4/CH5 / 北 CH2/CH3」已经是 8 种里最优的,其余 +1.7% ~ +7.1%。

3. 布线复杂度堆在哪(10 mm 网格实测)

y 带 信号铜 过孔 y 带 信号铜 过孔
15~25 536 101 65~105 1788 321
25~35 669 118 105~125 180 44
35~45 921 145 −5~15 0 0
45~55 864 146

y 15~55 这 40 mm 吃掉 55% 信号铜 / 54% 过孔,而 y −515、左条 x −300、右条 x 160190 完全空着。 但热区里 57/76 件是钉死的通道件(CH1/CH4/CH5 各 19 件,块高 41 mm 占满 y 1859), 只有 19 件是控制簇 —— 所以「把热区疏散开」能做的事有限。

4. 理论下界:−10.7%,且有相当部分不该兑现

37 件信号件全部放到 HPWL 最优(忽略重叠/板框/高度):6129 → 5471(−10.7%)。 但下界解里位移最大的 5 件是把两路 TACH 接收电路从板边搬回板中间 —— 那会把 12 V 摆幅的裸 TACH 信号重新拉成 100 mm 长线。⇒ 加回约束后现实可得约 −4%~−6%。


✅ 本轮真正做成的:4 个接插件从「插不上线」变成「能装」

这才是「假板框 160×125」留下的实质后果。edgechk 扫全部连接器,判据是「离真板边多远」:

位号 用途 离旧假框 离真板边(改前) 改后
J1 XT30 12V 进线,90° 板装,rot 270 开口朝 −X 0.5 30.5 ⛔ 0.5 ✅
J22 调试 USB-C,rot 90 开口朝 +X 0.4 30.4 ⛔ 0.4 ✅
J26 / J27 风扇 4P 直针座(壳高 11.5 mm) −29.0 1.2 / 9.0 ⛔ 1.0 ✅
J24 / J25 板对板排针 7.0 37.0 不需要板边(与载板垂直对插)✅

它们原来都缩在板内 30 mm,而上方 13 mm 处压着载板 —— 插头根本插不进去。 这不是优化问题,是板子能不能用的问题。

改动(共移动 28 件,pcb1_place.py 里已改并通过六项检查)

组 动作
J1 + 输入首级 F1/Q1/D1/DZ1/C1/R1 整体西移 30 mm,J1 到 x=−21.5(本体 −29.5~−15.3)
J22 + CC 下拉 R602/R603 东移 30 mm 到 x=184.4(R602 距 J22.A5 1.30 mm,SAT 表自动跟随)
J26 / J27 到真西/东板边 (−23.9, 115) / (183.9, 115)
两路 TACH 接收(R750~R753 / R754~R757 + Q9/Q10) 跟着各自风扇座到板边

代价与依据

  • 全板加权 HPWL 5886 → 6129(+4.1%)。这是板子可装配的必要代价,不是退步。
  • ⚠️ FAN_12V_SW 压降复核:Q7 居中、两支各只走 0.67 A,0.8 mm 线宽下 西支 125 mm→51 mV、东支 95 mm→39 mV,远低于 FAN_CIRCUIT 的 0.2 V 预算。 当年注释担心的 0.26 V 是「单条 196 mm 走全电流」,分两支后不成立。 ⇒ FAN_12V_SW 要按 0.8 mm 下发,别当普通信号走 0.20。
  • TACH 拆分净效果只有 −18 mm(RAW −148、TACH_IN/OUT +183 * 抵消), 保留它的理由不是长度而是信号性质:换掉的是 12 V 摆幅裸信号(现在 12.9 mm), 留下的是经 N-MOS 整形后的 3.3 V 逻辑。在源头整形是对的做法。

⚠️ 这些改动只在 pcb1_place.py 里,还没 --push。 下发会作废现有 2951 段铜, 所以要和「下板整板重布」放在同一轮做:--push → 扇出 → pcb1_route_sig → 铺铜 → DRC。

新分析脚本(都在 scratchpad,需要长期保留的话再收进 .claude/): rows.py(南北分配枚举)、fp.py(floorplan HPWL 评估)、dens.py(10 mm 网格拥挤度)、 slack.py(离网络重心距离)、bound2.py(理论下界)、edgechk.py(板边可达性)。 其中 edgechk.py 值得收进 .claude/ —— 「接插件离真板边多远」这条判据本轮抓到 4 个真缺陷。


下一步(按优先级)

1. ⛔ 载板:用户在 GUI 里新建 PCB → 我按脚本重建(唯一的阻塞项)

Board2(phonerail-carrier) 右键 → 新建 PCB。建好之后按「第三十三轮交接(一)」 那张命令表跑,第一步是 pcb2_retarget.py <新uuid> --write(27 个脚本 / 31 处一次换掉)。

2. 下板 PCB1:把本轮算好的摆位下发 + 整板重布

pcb1_place.py 里已改好并通过六项检查 + edge_access.py,但还没 --push。 下发会作废现有 2951 段铜,所以要连着做完整条链:

/c/Python314/python .claude/pcb1_place.py --push        # 28 件新位置
/c/Python314/python .claude/pcb1_place.py --outline     # 板框/定位孔(若板框常量变过)
/c/Python314/python .claude/pcb_fanout.py               # ⚠️ 扇出必须在铺铜之前
/c/Python314/python .claude/pcb1_route_sig.py --refresh --ripup --push
/c/Python314/python .claude/pcb1_pour.py --push
/c/Python314/python .claude/drc_read.py

⚠️ 重布前要先落实的两条(第三十轮就欠着):

  • pcb1_pour.py 里「三块 +12V 各自含一个本网通孔焊盘」的保命条件,在 J26~J30 的 +12V 脚消失之后已经不成立,现在靠扇出的过孔活着 ⇒ 必须先扇出再铺铜。
  • FAN_12V_SW 按 0.8 mm 下发(2×0.67 A 分两支,见「第三十三轮交接(三)」的压降复核), 别当普通信号走 0.20。FAN_PWM_L / 两路 TACH 也还没布线。

3. 仍然欠着的(不阻塞)

  1. 12V 预算被 FAN-R2 顶到约 9.99 A,10 A 适配器没余量 —— 要么限功率/降载,要么换适配器。待用户拍板。
  2. 同级固件还是旧的单路 TACH,四个引脚定义没补 (FAN_EN=IO5 / FAN_PWM=IO18 反相 / FAN_TACH_IN=IO16 / FAN_TACH_OUT=IO4)。 kicad 侧有现成的 kicad/firmware-sync/fan-r2.patch 可参考。
  3. 两边都还没解决的:独立回流(FR-1/SR-12,五路电池 P−/侧键地/USB 地在网表里全是全局 GND, 而载板已经铺完铜);MCU 的 IO0/RXD0/TXD0 没引出恢复测试点; Hub 端口供电由 MCU 控但 OCS 未接。

4. 本轮新增的常驻工具

文件 用途
edge_access.py 板边可达性:接插件离真板边多远。本轮靠它抓到 4 个「插头插不进去」的真缺陷,而六项检查和 DRC 全过。--board2 查载板
pcb2_restore.py 载板事故恢复:--survey / --clean-orphans / --copper [--push]
pcb2_retarget.py 一条命令换掉 27 个脚本里的载板 uuid
pcb2_reload.py:assert_alive() 重载后强制数图元,为 0 就停 —— 当天就救了一次

第三十二轮交接(决策已在第三十三轮拍板为 (c),本段只作依据存档)

这一轮没有半成品留在板上。 两块板都在自洽状态,随时可以往任一方向走:

元件 导线 过孔 铺铜 说明
下板 PCB1 301 2951 944 15 第三十一轮的 FAN-R2 已落地,间距错误 0
载板 PCB2 223 2649 896 7 = 加 strap 之前的基线,DRC 115 条基线

⛔ 待拍板:U32 的 strap 脚怎么处理

事情的来龙去脉

设计文档 §21.6 原来写「LED_A*/LED_B* 都是 Strap,悬空取默认 '0'」—— 2026-09-07 核 datasheet 时发现手册里没有这句话(依据见 §21.6a,都核过原文):

  • DS00001598C Table 5-3:IPD = 带内部弱下拉、IPU = 带内部弱上拉, 而这十个脚全是 I/O12(纯输入输出缓冲,一个字没提内部下拉)。 (同表里 OCS[1..7]_N 确实是 IPU、TEST 确实是 IPD —— §21.6 那两条是对的。)
  • Table 5-2:CFG_SEL[2:0]=000 → 「Internal Default Configuration • Strap Options are Enabled」, 这些脚确实会在 RESET_N negation 时被采样。
  • §7.5 + Figure 7-3(第 42 页):厂家推荐的无源方案是 100 kΩ。

用户已拍板缩到五颗:只做 PRT_SWP1~5(U32 脚 51/49/47/33/31 = R900~R904)。 理由是 Table 7-1 内部默认值本来就是 Port Swap=00h/Boost=00h,电阻不改变设定、 只是把采样值钉成本来就该有的那个 ⇒ 按「万一采到 1 会怎样」筛:

strap 采到 '1' 装不装
PRT_SWP1~5 对应口 D+/D− 内部对调 → 那一路手机枚举不出来(且随漏电/温度变,每次上电可能不一样) ✅ 装
PRT_SWP6/7 口 6/7 已被 R714/R715 上拉禁用 ❌
BOOST0/1 只影响 SI/EMI ❌
GANG_EN 本设计 PRTPWR/OCS 一根没接 ❌

⛔ 然后撞上了物理限制

原理图五颗已经画完并验证通过(见下面「已完成」)。但载板上摆不下:

pcb2_strap_solve.py 对每颗枚举「位置 × 4 个朝向」,当场把地过孔和 strap 走线一并试, 四件全过才算解。结果:

电阻 U32 脚 边 摆得下 有地过孔 走线
R903 33 PRT_SWP4 东 1 1 ✅ 可解
R904 31 PRT_SWP5 北 2 1 ✅ 可解
R902 47 PRT_SWP3 东 132 116 ⛔ 0
R901 49 PRT_SWP2 南 195 174 ⛔ 0
R900 51 PRT_SWP1 南 169 143 ⛔ 0

摆得下、地也接得出去,就是线出不了 U32 的东/南边。 根因: U32 是 QFN-64 9×9、0.5 mm 间距,焊盘之间只有约 0.2 mm 缝,穿不过 0.20 mm 的线, 唯一出路是垂直出边;而东边紧贴着 R710/C750/R709 排成的一列(x≈106.3)—— 从 U32.47 往东只能走 0.7 mm(1.0 mm 处离 R710.2 净距 0.134,1.4 mm 处 0.000)。

解不出来的正好是口 1/2/3 = CH1/CH2/CH3 三台手机,所以「只做能做的两颗」不成立。

三条路(等你选)

(a) 挪开挡路的元件:R710/C750/R709 那一列往东让 0.5~1 mm,给 U32 东边开通道。 代价:这几个网络要局部重布;而且南边 PRT_SWP1/2 是另一处,还得再想办法。

(b) 只做能解的两颗:⛔ 不建议 —— 等于给 CH1/CH2/CH3 留着最难查的失效。

(c) ⭐ 改 CFG_SEL=110(Table 5-2:Internal Default Configuration • Strap Options are Disabled) strap 根本不采样,一颗电阻都不用,U32 拥挤的东/南两侧一点不用动。 Table 7-1 已经逐条核过:

寄存器 默认 对我们
FAh Port Swap 00h 极性正常 ✅ 正是要的
F6h/F7h/F8h Boost 00h 不加强 ✅
09h Non-Removable 00h 和现在的 strap 设定一致 ✅
07h CFG2 20h PORT_PWR=0/CURRENT_SNS=00 = 联动通断/过流;我们 PRTPWR/OCS 没接,无所谓 ✅
06h bit7 1 自供电 ✅(只有 CFG_SEL=010 那行强制总线供电,110 不改)
0Ah Port Disable 00h ⚠️ 七个口全开 —— 见下面这条

⚠️⚠️ 走 (c) 必须连带去掉 R714/R715。口 6/7 现在是靠 strap 关的 (§21.5:两颗 10k 上拉 USBDN6_DP/USBDN7_DP)。strap 一停这两个口就开着, 而那两颗上拉对 Hub 内部 15k 下拉分压得 3.3 × 15/25 = 1.98 V, 正好压在全速设备「已插入」的 2.0 V 判据上 → 两个幽灵口反复枚举失败。 去掉之后 D+/D− 悬空、被 Hub 自己的 15k 下拉拉低 = 空闲,无害。

(c) 的完整改动清单(BOM 净 −6 颗):

改什么 怎么改 在哪
CFG_SEL2(U32 pin 13) 现在直接焊到 GND(所以从来没有 R713),改成 10k 上拉到 +3V3_HUB U32 西边(东/南两条死路都不碰)
CFG_SEL1(R712) 下拉改上拉 已有件,改一根短线
CFG_SEL0(R711) 不动(110 = SEL2/SEL1 高、SEL0 低) —
R714 / R715 不装 见上
R900~R904 删掉 原理图 + 板上(板上现在零铜)

上一会话的倾向是 (c),但这是配置决策(动 §21.3 那条已定稿的 CFG_SEL=000), 留给你定。没有任何东西是必须先回滚的:板上零 strap 铜。


这一轮已经完成、可以直接用的部分

项 状态
依据核实:Table 5-2 / 5-3 / 7-1 / Fig 7-3 原文逐条对过 ✅ 写进设计文档 §21.6a
设计文档:新增 §21.6a、决策清单 #18、§21.6 那条错误结论已改 ✅
原理图 C2-HUB:R900~R904 接 PRT_SWP1~5,两条地母线两个地旗 ✅ 六项检查全过
顺带清掉 C2-HUB 的三重叠影文字(22 条 → 9 条,原来有一份压在 J23 上) ✅
载板 PCB 导入变更、223 件、五颗地端都是 GND ✅
pcb2_place.GLOBAL 里有这五颗(若走 (c) 要删掉) ✅

⚠️ 这一轮抓到的两个真错误(都已修,也都写进了 skill / 脚本注释)

  1. 地旗落在合并后的导线中段(skill §8a-3):共地母线分两段建、旗放在中间那点, EasyEDA 会把共线两段合并成一条 ⇒ 旗变成压在中段,判定不相连。 PCB 网表里那几颗的地端成了 $16N236 而不是 GND。 verify_sch 四道检查全过 —— 是从 PCB 读回焊盘网名才抓到的。 ⇒ 旗必须落在「真的有支线接进来」的点上(本项目用某颗电阻的分支 x)。
  2. 清理脚本删过头:写成「y==250 的地旗全删」,把页面原有的 +3V3_HUB(1130,250)(RESET_N 上拉 R716 的电源端)一起删了 —— 是导入对话框那条 R716:2 +3V3_HUB → $16N210 暴露的。已恢复。 ⇒ 只加不删的脚本要自己记账;删除条件必须只认自己放过的坐标。

这一轮新增/改动的脚本

文件 用途
c2_straps.py C2-HUB 放 strap 电阻 + 连线 + 重写页面文字(含全页碰撞检查)
pcb2_strap_solve.py 位置/朝向/地过孔/走线四件联合求解;搜索走 2 mm 网格索引,D.check() 只在最后验一次
pcb2_straps.py 只摆这几颗(不动整板 223 件)
pcb2_strap_gnd.py 局部地扇出(不动整板 415 个扇出孔)
pcb2_strap_place.py / pcb2_strap_route.py 分步版,已被 pcb2_strap_solve.py 取代,留作记录

为什么最后要「联合求解」:分三步走(摆位 → 地扇出 → 走线)连着踩了三次, 每次都是前一步只顾自己那一项:① 只按空地摆 → 迷宫布线器 10 条只通 3 条; ② 改成贴着引脚 → 地过孔被围死;③ 加了地过孔判据 → 电阻方向反了 (0402 的 GND 端朝着 U32,strap 线要跨过自己的地)。分步搜索每步都局部最优、合起来无解。 另外:搜索循环里不能调 D.check()(O(全部障碍),单颗跑 40 分钟出不来)。


⚠️ 另一件顺手修掉的事:下板板框常量过期了(影响很大)

pcb1_place.py / pcb1_env.py 里一直写着 BW, BH = 160, 125(原点 0,0), 而 pcb1_outline.py 从 2026-09-06「两板尺寸取大对齐」那一轮起画的是 220 × 135、左下角 (−30, −5)(回读板上多段线:x −1181.17480.3 mil / y −196.95118.1 mil,逐条对上)。

旧值是真板框的子集,所以越框检查一直「偏严但不错」—— 代价是 左右各 30 mm、上下各 6 mm 的板面从来没进过摆位视野 (301 个件全挤在 x 0.5159.6 / y 1.4124.0)。 pcb1_env.py 还把高度禁区裁到 min(r, 160/125),右边和上边那两条 zone 整个丢了。

已修:两个脚本改用 BX0,BY0,BX1,BY1 = -30,-5,190,130, pcb1-env.json 重新生成(zone 现在最远到 x=188.2),并加了 board_rect 字段。

连带后果:第三十一轮把风扇座摆到「两个上角」(6.3,121)/(151,121) 是按假板框算的, 其实都在板子中间。按真板框重扫,左板边 y −4128.5 整条、右板边 y −449 和 65~128.5 都放得下 11.5 mm 的 4P 风扇座。


下一步(用户已拍板,但还没开工)

下板 PCB1 按真板框 220 × 135 整板重排 301 件。 会作废现有 2951 段铜,但下板本来就排着「整板重布」那一步,铜反正要重来。 重排时顺带把风扇座挪到真板边。

其余仍然欠着的:

  1. 下板 FAN_12V_SW / FAN_PWM_L / 两路 TACH 还没布线。 FAN_12V_SW 是新电源网(2×0.67 A、最长约 89 mm),按电流定宽度,别当普通信号走 0.20。
  2. pcb1_pour.py 里「三块 +12V 各自含一个本网通孔焊盘」的保命条件, 在 J26~J30 的 +12V 脚消失之后已经不成立,现在靠扇出的过孔活着。重铺前必须先扇出。
  3. 12V 预算被 FAN-R2 顶到约 9.99 A,10 A 适配器没余量了 —— 要么限功率/降载,要么换适配器。
  4. 同级固件还是旧的单路 TACH,四个引脚定义没补 (FAN_EN=IO5 / FAN_PWM=IO18 反相 / FAN_TACH_IN=IO16 / FAN_TACH_OUT=IO4)。 kicad 侧有现成的 kicad/firmware-sync/fan-r2.patch 可参考。
  5. 两边都还没解决的:独立回流(FR-1/SR-12,五路电池 P−/侧键地/USB 地在网表里全是全局 GND, 而载板已经铺完铜);MCU 的 IO0/RXD0/TXD0 没引出恢复测试点; Hub 端口供电由 MCU 控但 OCS 未接。

第三十一轮交接(先读这一段)

这一轮做的是「把 kicad 的设计变更同步到立创」,不是布线轮。范围是用户拍板的两项: FAN-R2 + C737(其余 kicad 侧改动经评估不需要同步,见下面「评估结论」)。

项 怎么做的 状态
P6-FAN-EDL 整页重画 p6_fan.py --place → --wire:删 J28J30 / R730R732、J26/J27 换 4 针、新增 19 件、60 段线 24 件全断言过 ✅ 六项检查全过
P3-CONTROL 三个端口 FAN_TACH → FAN_TACH_IN;IO5 加 FAN_EN、IO4 加 FAN_TACH_OUT(端口只能删了重建,§3b) ✅
跨页对账 xpage.py:真正漏接 0;两个 *_TACH_RAW 加进 NAMING_ONLY(天然单实例) ✅
端点审计 port_audit.py 下板 457 个旗/端口,可疑 0 ✅
C737 换固态钽 原理图删旧建新(1 脚 = 正极,看渲染图确认的,引脚名只有 1/2);PCB2 导入 2 条变更 ✅
C737 落板 底层 (93.17, 54.0) rot 180(转 180 是因为 +3V3_HUB 铜在东;东移 0.17 让净距回到 0.265) ✅
载板收尾 reload → pour --push → inner1_verify → drc_read --pcb2:DRC 115 = 基线,五岛全达标,net_conn 22/22 ✅
下板导入变更 38 条(增 19 / 删 8 / 改网络 11),逐条滚出来核过,复选框全未勾 ✅
下板摆位 pcb1_fan.py --push:只动风扇那 24 件,拆废铜 164 线 / 40 孔 ✅
下板收尾 pcb1_reload → pcb1_pour --push → drc_read:间距错误 0、连接性 153(未布线) ✅
截图 shots/P6-FANR2.png、PCB2-c737-zoom.png、PCB1-fanr2-north.png 待用户看

这一轮踩到 / 抓到的

  1. ⚠️ 位号撞车:kicad 把风扇电阻编成 R740~R751,而载板 EDL 下拉已经占了 R741~R745。 kicad 两块板是两个独立工程所以撞不出来,立创是单一位号空间。 立创侧整块改用 R746~R757,映射表在设计文档 §22.6a。
  2. ⚠️ 摆位本地检查过 ≠ DRC 过:第一版把风扇块摆进北中块 y 66~88, 焊盘对焊盘全过,DRC 65 条间距错误 —— 底下压着已布好的 +12V/+3V3/GND 铜, 而 route_sig --ripup 只拆自己画过的信号线,拆不掉电源铜。挪到 y 104~121 的空带后归零。
  3. ⚠️ 风扇座的 y 是被载板压出来的:4P 塑壳 11.5 mm,下板 hmax 来自载板底面元件的投影。 y 102~117 被载板 C801/C821 那两颗 10.5 mm 罐压到 1.5;左边 y 68~92 被输入链占满。 最后取两个上角,J27 的 x 还得内收到 151(x ≥ 156.29 是排线槽,hmax 3.0)。
  4. pcb1_pour.py 里「三块 +12V 各自含一个本网通孔焊盘」的保命条件, 在 J26~J30 的 +12V 脚消失之后已经不成立,现在靠扇出的过孔活着(已写进脚本注释)。
  5. 三条新的 EasyEDA 行为(已写进 skill §6e / §2b / §12a): create(rot=180, mirror=true) 的 SOT-23 = G 左 / S 上 / D 下(高边 P-MOS 的标准画法, 单靠 rotation 做不出来);createNetFlag('Power', 任意网名, …) 是有效的; 地旗的 GND 字样掉在符号下方约 15 个单位,会压住下面一行文字。

下一步:

  1. 用户看三张截图确认方向与排版(几何验证证明不了「看着对」)。
  2. 下板 FAN_12V_SW / FAN_PWM_L / 两路 TACH 还没布线(连接性 153 里就有它们)。 FAN_12V_SW 是新电源网(2×0.67 A、最长约 89 mm),按电流定宽度,别当普通信号走 0.20。
  3. 下板整板重布(第三十轮清单第 3 条)仍然欠着:pcb1_route_sig.py --refresh --ripup --push, 重布前先扇出、把上面第 4 条那个 +12V 保命条件重新落实。
  4. 12V 预算被这一版顶到约 9.99 A,10 A 适配器没余量了 —— 要么落实限功率/降载, 要么换更大适配器。这是个待用户拍板的项(设计文档 §22.6 已记)。
  5. 同级固件还是旧的单路 TACH:要补 FAN_EN=IO5 / FAN_PWM=IO18(反相) / FAN_TACH_IN=IO16 / FAN_TACH_OUT=IO4 四个定义。kicad 侧有现成的 kicad/firmware-sync/fan-r2.patch 可参考。

评估结论:kicad 侧今天其余改动为什么不用同步

  • 引脚电气类型(906 处 passive 等)、NC 标记、PWR_FLAG、ERC 台账 —— 立创没有等价强度的 ERC。
  • 五条迁移修复(Y 轴翻转、USB-C 长圆孔旋转、裸铜带 F.Paste、tenting 丢失、网名转义) —— 都是 EasyEDA→KiCad 转换器的 bug,立创侧本来就是对的。
  • kicad 的 PCB 布局布线(R2 布局、三批局部布线、J1 转向)—— 那是另起炉灶的一套, 立创 PCB2 已到第三十轮,绝不能反向覆盖。
  • ⚠️ 两边都还没解决的:独立回流(FR-1/SR-12,五路电池 P−/侧键地/USB 地在网表里全是全局 GND, 而载板已经铺完铜);MCU 的 IO0/RXD0/TXD0 没引出恢复测试点;Hub 端口供电由 MCU 控但 OCS 未接。

第三十轮交接(先读这一段)

做完的(全部已下发到载板 PCB2;回滚快照 _pcb2_snap_r30_before.json):

项 怎么做的 状态
站 1~3 重摆到新 PW/PH pcb2_place.py --clean-copper --outline --push --verify(清铜 2411 线 / 605 孔 / 7 铺铜;槽 10/10 重建;218 件回读全 ✔)。j7_assert 51 条常量不符随之消失 ✅
U21 / U22 转向 rot 90 → U21 (116.5,66.5) rot 0、U22 (127.5,66.5) rot 180(脚排朝南北,16 根脚沿 9 mm 边出线;设计文档 §26.23「第三十轮落地」) ✅
铜全部重建 顺序:差分 route_dp --push → vo_vias --push → dump_pads + fanout --push(415 孔)→ route_sig --push(GND 梳子 + 123 网)→ gnd_patch(悬空 0)→ reload → pour → inner1_verify → DRC ✅
布通 123 / 123(K 线提前到 KEY_* 之前、HUB_RST 提前到 VBUS_UP 之前、_connect 加 'near' 窗口 + MAX_CELLS 3.2M) ✅
Inner1 岛 最窄 7.00 / 7.30 / 7.20 / 7.70 / 19.2(下限 7.0);VIA_KEEPOUT 加 VO2 北带 (86.5,75,101,83.5) ✅
DRC 115 = 全部封装内焊盘对焊盘基线;pcb2_net_conn 22/22;via_relabel 0 处 ✅
截图 shots/PCB2-r30-routed.png、PCB2-r30-595corridor.png 待用户看

这一轮修掉的 bug / 坑(都写进了脚本注释,skill 加了 §9aa)

  1. pcb2_vo_vias.py / pcb2_gnd_patch.py / pcb2_dp_long.py 的路径 key 还在用 # 分隔(上一轮 KSEP 已改 chr(0))→ 校核把自家过孔当异网,13 条假「过孔-焊盘」。已改用 R.KSEP。
  2. 扇出必须在差分之后:GND 扇出先跑会把差分手写车道的位置占掉(10 条 −0.1 冲突)。顺序 dp → vo_vias → fanout。
  3. pcb2_route_sig.push_sig 删旧铜时无条件删 BM24 框里的 GND 梳子,--net 补布只删不画 → 65 个 GND 脚悬空。现在只有这一轮重画 GND 才删。
  4. pcb2_gnd_patch.clusters() 纯两两比对,件数上去之后 20 分钟不出来。加了包围盒预筛。
  5. pcb2_reload 会丢掉铺铜填充;而 API 新建的线要 reload 之后 DRC 才认(否则新推的网络成片报「断开」)。收尾固定顺序:reload → pour → inner1_verify → DRC,pour 之后不许再 reload。
  6. 布线器两个留孤岛的 bug(DRC 连接性 10 条,本地校核不报):从「已连上那坨」出发的起点落在圆焊盘外接矩形的角(J32.7/9/40 铜外 0.2);45° 切角挪走了后布支路钉着的直角铜(VBUS2/+3V3_HUB)。新增 reanchor_routes() 在切角后兜底。
  7. --net 补布之前必须刷障碍表(上一次 --push 之后板子就变了):+3V3_HUB 按旧 K1_PWR/+5V 位置算,压线 19 条。加了 --refresh 开关,以后一律 pcb2_route_sig.py --refresh --net ... --push。
  8. J23 封装里两个 Ø0.6 NPTH(_pcb2_npth.json)原来只有 vo_vias 读,$16N224 的过孔直接打在孔上。现在 pcb2_route_dp.geom() 把它们当无网络通孔盘,两个布线器都躲。

换层 45° 化(用户中途追加,已整板落地):via_chamfer_routes() 在切角之后把「顶层横线进孔、底层竖线出孔」的直角切成 45° (过孔沿出线方向挪 d,进线用斜段接进新孔位)。整板重布一遍:换层直角 244 → 29、同层直角 132 → 28、123/123 布通、 DRC 115 = 基线(连接性 0),五块岛最窄 7.05 / 7.30 / 7.20 / 7.70 / 28.75,差分 22/22。剩下的 29 + 28 处是净距或孔间距不允许再切的。 顺带又抓到一个 EasyEDA 行为:一条线穿过本网焊盘时,create() 会在焊盘中心把它截断、后半段连同同位置的出线段一起丢掉 (KEY_RCLK 穿 U22.12)。push_sig 现在把离本网焊盘中心 < 0.15 的线段在中心处拆成两段再建(skill §9ac)。

下一步:

  1. 用户看截图确认 U21/U22 转向后的走廊排法(PCB2-r30-595corridor.png)和换层 45° 的样子(PCB2-r30-viachamfer.png)。
  2. 用户中途追加的「跨层垂直布线」已问清:换层点两侧的线不许成 90°,要加一段 45°。已做成 via_chamfer_routes()(设计文档 §10.1b),整板按它重布过一遍——结果见本节末尾「换层 45° 化」。
  3. 下板 PCB1 还没按新规则整板重布(pcb1_route_sig.py --refresh --ripup --push 那条固定序列);下板 35 条布不通的处理方案见 _pcb1_unrouted_report.md。
  4. 第二十九轮清单里的「一次装夹复测触点位置 ±0.3」「清 J41~J60 厂商料号」仍欠。

第二十九轮交接(先读这一段)

做完的(全部已下发到载板 PCB2 并读回核对):

项 怎么做的 状态
原理图 J41~J60 换封装 ⚠️ 改库里的器件不会传到原理图实例(skill §9z)。用「工具 → 封装管理器」DOM 批量换成 PAD-D1.5_MI8-KEY,读回 20/20 ✅
Add into BOM 实例上 addIntoBom=false(API 生效);厂商/料号字段 API 清不掉,但已不进 BOM ✅
PCB 导入变更 对话框恰好 20 行「修改封装」,应用后 --verify 218/218、封装 0 不符;pcb2_parts_dump.py 已重抓 ✅
20 个盘重摆 新脚本 pcb2_keypads.py --push:只动这 20 个,按 pcb2_place.plan() 坐标,读回偏差 ≤ 0.002 mm ✅
GND 盘扇出 pcb2_keypads.py --gnd:5 个 GND 盘各一条 0.30 短线 + Ø0.61 过孔(u− 侧 1.85 mm),本地净距校核过 ✅
15 根 POGO 重布 pcb2_route_sig.py --net POGO* 15/15 布通、净距全过、已下发(先删 55 线/18 孔) ✅
H5 不打 pcb2_place.SKIP_HOLES={'H5'},--mech 重下发 30 孔;J6n 离孔的 5 条报错随之消失,本地检查 0 错 ✅
本地检查假阳性 pcb2_place.check() 对触点盘改按圆判(盘心距 ≥ Ø1.5+0.20),原 20 条「重叠」是包围盒误报 ✅
走线方向规则 用户拍板「顶层水平、底层纵向」→ pcb2_route_sig.route_grid 加 DIR_PREF/DIR_PENALTY=2.0(软代价),下板 pcb1_route_sig.py 复用同一引擎自动生效。设计文档 §10.1b / §0.1 #15 ✅ 代码
7 个定位孔被旧线穿过 新脚本 pcb2_hole_dodge.py:孔周 2.2 mm 圆盘内本网铜整块删掉,断口用布线器 _connect() 重接(8 个网络 / 15 条路径,删 26 线 2 孔)。VBUS2/3、+3V3_HUB 因此不用整网重布 ✅
Inner1 VO2_P 岛颈 6.30 → 7.05 是 VBUS_EN2 的过孔 (87.1,70.2) 压在岛颈东沿。布线器新增 VIA_KEEPOUT(过孔禁区,外层布线器看不见内层岛),单独重布 VBUS_EN2 ✅ 五块岛全达标
铺铜 / DRC 重载 → pcb2_pour.py --push 7 块全填充 → inner1_verify 五块岛 ✅ → drc_read --pcb2:间距错误 45 → 15(剩的 15 条全是第二十七轮的老账:e2289 GND 过孔压 VO3_P 线 / J83_1、J63_3 下 4 个无名过孔、C801_2 下 2 个 VO4_P 过孔),连接性 117(第二十七轮 506,当时没铺铜) ✅

⚠️ 这一轮发现的一个系统性偏差(没修,故意的):pcb2_place.PW/PH 上一轮从 67.09×44.66 改成 67×44, 但载板上站 13(rot 180)的全部元件还是按旧值摆的,和 plan() 差 (0.09, 0.66) mm(站 4/5 rot 0 不受影响)。 后果:pcb2_route_sig.j7_assert 对 J71/J72/J73 报 51 条常量不符。本轮只重布 POGO(不碰 J7n), 把断言改成 --net 不涉及 J7n 时降级为警告。**整板 route_sig --push 或重画 BM24 梳子 / 差分对之前, 必须先 pcb2_place.py --push 把站 13 摆到新坐标**(然后 route_dp --dump → route_dp --push → route_sig --push → pour)。 新摆的 20 个盘和 30 个定位孔都是按新 PW/PH 的,它们之间是自洽的。

本轮沉淀的脚本 / 改动

文件 用途
pcb2_keypads.py 只摆 J41~J60(--push)+ 5 个 GND 盘扇出(--gnd)
pcb2_hole_dodge.py 穿定位孔的旧线局部改道(孔周圆盘整块删、断口重接)
pcb2_route_sig.py DIR_PREF/DIR_PENALTY/DIAG_K(H/V 规则)、VIA_KEEPOUT、j7_assert(strict=)
pcb2_place.py SKIP_HOLES={'H5'}、触点盘按圆判重叠
pcb2_pour.py 填充等待 4 min → 90 s(卡住就早退去重载)
skill §9z 封装管理器 DOM 流程 / 器件改动不传实例 / BOM 字段哪些 API 能改

下一步:

  1. 走线方向规则目前只对本轮重布的 15 根 POGO 生效过;两块板的其它信号要在下一次整板重布时按新规则重来 (载板要先做上面那条 pcb2_place.py --push;下板按「固定收尾序列」pcb1_route_sig.py --refresh --ripup --push)。
  2. 载板 8 个没布通的网络(第二十八轮清单第 1 条)仍在。
  3. 触点位置只到 ±0.6 mm(Ø1.5 盘只容许 0.50)—— 打样前用户要做「一次装夹」复测(第二十八轮那一节)。
  4. 用户在客户端里可顺手把 J41~J60 的厂商/料号清掉(纯观感,API 做不到)。
  5. pcb2_via_relabel.py 试跑说有 12 条导线标签可疑(+3V3_HUB → EDL_EN2/3/$24N140、VBUS3 → GND ×2、$20N3x4 → GND), 过孔 0 处。这些多半是 8 个没布通网络留下的出线段碰到了邻脚,没 push;处理那 8 个网络时一并看。
  6. 剩下 15 条 DRC 间距老账:e2289(GND 过孔压在 VO3_P 线上、离 J83_1 6.7 mil)、J63_3 下 4 个无名过孔、C801_2 下 2 个 VO4_P 过孔 —— 都是「过孔该删还是该重标」的判断题,relabel 判不了(接触网络不唯一),要人看一眼。

第二十八轮交接(上一轮,仍有效的部分看「还欠的实测」)

做完的(细节看下面「主板机械数据重测」整节):

项 结论 状态
主板本体 67 × 44(旧 67.09 × 44.66;71 是含 SIM 卡托) ✅
7 个定位孔 全部重测,最大错 4.8 mm ✅ 两条独立链印证
孔径 主板 Φ2.0 ⇒ 载板过孔 Φ1.8 + M1.6 尼龙 ✅
侧键触点几何 菱形,相邻 1.87 / 对角 2.65,盘 Φ1.35 ✅ 中框金焊盘量的
侧键触点簇心 (3.42, 5.07),离 H1 5.24 mm ✅ 用户卡尺 5 mm 判死
触点身份 GND=u− · PWR=u+ · VDN=v− · VUP=v+ ✅ 两条独立描述印证
接触方式 顶针取消 → 中框式焊盘,螺丝压紧,立柱 3.0→1.0 mm ✅ 库已改完
载板下发 --mech --force:35 孔 Φ1.8 + 25 条丝印,读回偏差 0.0001 mm ✅

下一步按这个顺序做:

  1. 手工(客户端 UI,API 改不动):器件 MI8-KEY-PAD D1.5 的 Add into BOM 改 no、清掉 C5221288 / YIYUAN 料号, 否则 BOM 会让人买 20 颗用不上的顶针。
  2. 载板导入变更 → J41~J60 换成 Ø1.5 盘 → 重抓封装框(_pcb2_pads.json 里还是旧 Ø1.9, 不重抓的话 pcb2_place.py 会一直报那 20 条假重叠)。
  3. 把 J41~J60 摆到新坐标。⚠️ --push 会重摆全部 218 个元件,风险大,只摆这 20 个要单写一条路径。
  4. 重布这 20 个网络(原顶针位置的走线全废)。
  5. J6n vs H5 二选一(挪五个 EDL 座重布 10 条线 / 干脆不打 H5,后者代价为零)。
  6. 11 处孔与已有铜擦边(最深 0.95 mm)局部收一收 → 重铺铜 → pcb2_reload + DRC。

还欠的实测(都要用户动手,方法见对应小节):

  • 触点位置只到 ±0.6 mm,而 Ø1.5 盘只容许 0.50 —— 打样前要用「一次装夹」收到 ±0.3 (同一张测量底板上先戳 7 孔、再把弹片涂红盖印)。
  • 贴合面(侧键面)还没有可信的板坐标映射,那张 MI8-mating-face.png 的标记是错的、已作废。 要重做得先在贴合面那一侧的 7 个孔口点红漆再扫。
  • 四个触点的电气身份没用万用表验过(几何身份是可信的)。

⚠️ 最重要的一条方法论教训在「环形滤波器配准出来的 7/7 不可信」那一节,动手前必读 —— 这一轮为此错了两次,其中一次还被当成「独立印证」写进过文档。

一句话现状

原理图:下板 11 页 + 载板 12 页(新增 C6-KEYS),verify_sch 16 页全过、xpage 两板 0 漏接、port_audit 两板 0 可疑。 排针 J24/J25 第三版:37 信号 + 43 地(跨板只剩 KEY_SER/SRCLK/RCLK/OE# 四根移位链 + 老的 VO/+5V/VBUS_EN/I²C)。

PCB:两块板都做了「导入变更 → 重抓封装框 → 清死网铜 → 重摆 / 重布」,收尾链的结果见下面「板子当前状态」 (这一段是 _chainB.log 跑完之后补的,如果那里写的是「待补」,说明链没跑完,先看 .claude/_chainB.log)。

本轮改动一览(细节在设计文档 §26.23 落地 / §26.24)

板 原理图 PCB
下板 P2×5 删按键块(sch_cut.py 闭包整块删);P3B 删 U21/U22/C604/C605/R609;P6 删 U33/C739;P5-BTB 表第三版重画;P3B/P6 文字重写 导入变更 −52 件(290 件);J24/J25 被重建到板外,按老坐标放回;pcb_prune_nets.py 删 737 线 / 208 孔 + 253 段孤儿网;板框放大到 220×135(原点不动,画到 (−30,−5)),定位孔 6 个与载板对齐
载板 新页 C6-KEYS(c6_keys.py);C4-SITE1~5 加三颗 MOS + 六个 0402(c4_site.py 【B】重排);C1-CONN 重生成 导入变更 +52 件(218 件);pcb2_place.py --push 整板重摆(U21/U22 在走廊中段 UP 差分以北那条带、U33 在 J32 东侧、MOS 在顶针背面);顶角补两个 M3 孔、尾插槽东端 65→64;删排母来的 POGO/EDL_EN/HUB_RST 旧铜;36 个新 GND 脚扇出;pcb2_route_sig.py --push 全部重布

本轮沉淀的脚本

文件 用途
sch_cut.py 按连线闭包整块删子电路(元件 + 导线 + 旗/端口),闭包碰到别的元件引脚就拒绝
c6_keys.py 载板 C6-KEYS 页(三片 595 + 去耦 + OE# 上拉)
pcb_prune_nets.py 删死网铜:正则命中 or 网名不在任何焊盘上(孤儿网)
pcb2_via_relabel.py 按实际接触的铜给过孔重标网络(导入变更后必跑)
pcb1_outline.py 下板 220×135 板框 + 6 个对齐定位孔(不依赖旧 pcb_place)
pcb1_import.py --board2 同一套导入/对账流程用在载板上(产出 _p2_sch_bom.json / _pcb2_comps.json)

⚠️ 本轮踩到 / 复核的坑

  1. 端口不能放在直线的中点(skill §8a-2 又一次):漏极→顶针两段共线导线被合并,POGO 端口落到中段,port_audit 报 15 处。改挂到横支线末端。
  2. 整页重画的页导入 PCB 时,EasyEDA 拿待删元件复用成新元件(skill §9o):载板对话框里一堆「修改封装 C0402→SOIC-16」「J43→J54」。 对策就是导入后 pcb2_place.py --push 整板按位号重摆再 --verify;下板 J24/J25 同理被扔到 (25,−8)。
  3. 595 在走廊里摆了三次才对:先是 rot 90 面对面缝 1.56(出线口互堵、12 根 K 线全布不通),再是 rot 0 压在横穿整个走廊的 UP 差分上(查空地只查了端点在框里的线)。
  4. pcb2_route_sig.py 吃的是 _pcb2_route_geom.json(pcb2_route_dp.py --dump 产出),不是 pcb2-copper.json—— 改完布局漏跑 --dump,布线器报「网络表里没有 KEY_/K」。第一遍收尾链就是这么空跑的。
  5. drc_read.py 的结果要先 pcb2_reload.py,否则报的是重摆之前的幽灵位置(一堆「钻孔到贴片焊盘 0 mil」)。
  6. KEY_OE# 里的 # 又把载板布线引擎的路径 key 拆坏了(下板 §26.19 同款),pcb2_route_dp.KSEP 改成 chr(0);pcb2_dp_long 不能反向 import 它(循环)。
  7. 导入变更会把老过孔的网络标签重映射(载板 49 个,铜没动):DRC 成排「导线到过孔 0 mil」、布线器当异网障碍。pcb2_via_relabel.py 按接触的铜重标。以后整页重画导入之后必跑。
  8. pcb2_route_sig.push_sig 一个请求里逐条删 + 建 1589 段线,桥接 30 s 超时但 EasyEDA 继续画(skill §9c 同款):下一步抓几何抓到画了一半的板子,第二遍把 +3V3_HUB/+5V 画到别人身上(DRC 319)。已改成批量删 + 每 250 段一个请求。
  9. pcb2_route_sig.py --net 补布时障碍表照样摘掉全部 123 个自家网络(mine 没按 --net 收窄),补的 8 根线看不见第一遍的 115 个网络,DRC 319 条 0 mil。已改成只摘这一轮要重画的。
  10. 交接表里「25 颗开关 / 73 件」是错的:每路只有 3 颗按键 MOS(Q%03~05),整窝 52 件。

⬅️ 下一步

  1. 载板 8 个没布通的网络(§26.23 落地末尾的表):根因是 U21/U22 西排 8 个脚共用一条缝。二选一:U21 北挪 + U22 转 rot 270,或把 EDL_EN×3 和 +3V3_HUB 去 site1 那支写成 PRE 前置段。改完 route_dp --dump → route_sig --push(整板)→ pcb2_pour.py → inner1_verify → reload → DRC。
  2. 载板 pcb2_pour.py:Inner1 五块岛 + GND 底填要不要因新件重铺(新 GND 孔已打在 layer 16;595/MOS 都在岛外的走廊 / 顶针背面, inner1_verify.py 跑一遍确认岛没被切)。
  3. 两板对齐后的机械件:下板四角孔在 (−22,3)/(182,3)/(−26,126)/(186,126),载板顶角 (4,131)/(216,131) 离板边只有 1 mm 焊盘边—— 打样前让用户看一眼要不要把顶角孔改成非金属化 / 缩焊盘。
  4. pinmap.py 里 P3B/P6 那两段 595 端口改名表已经没有对象(端口都删了),--check 会报 miss;它是历史记录,别再跑 --push。
  5. 下板 35 条布不通的处理方案已经由子代理算好(纯本地,没碰桥接):.claude/_pcb1_unrouted_report.md, 配套脚本 pcb1_unrouted.py / pcb1_ch_reroute.py / pcb1_usb_corridor.py(都是离线的),pcb1_place.py 里已改 ESCAPE_NUDGE 0.50/0.45 + 新增 SAT_FLIP(C609/C612 转 180°)。 要点:只有 U6.16/U7.16 两条是几何真出不去(NUDGE 救不了,要 C609/C612 转 180°);C 类是 R%10 转 180° 就通; USB 束走廊在顶层 y≈17 沿板边东行再 x≈142 北上;D 类是 INA226 南侧口袋被自己人堵,一起拆了按 0.05 栅格重布。 下发顺序见报告 §8,第一条就是「定点动 8 颗,绝不 pcb1_place.py --push」。
  6. ⚠️ 同一仓库还有另一个 Claude 会话(dcdc-cd)在动载板文件(22:02~22:08 改过 pcb2_place.py 的 PW/PH、mi8_mech.json、c1_conn.py,之后又改回去了)。桥接只有一条,两个会话不能同时打;开工前先 ListAgents / 问一句。

⚠️ 主板机械数据重测(2026-09-06 第二十八轮)

起因:给用户出了一张 1:1 打印尺寸图(shots/PCB2-carrier-1to1.pdf,pcb2_pdf.py), 他拿真主板一比 —— 孔位最大错 4.8 mm,板长错 1.09 mm,孔径错了 1 mm。 原来那套(67.09 × 44.66 + 7 孔)是从照片提取的,标称 ±0.5 mm,实际远不止。

新值(已写进 pcb2_place.py 与 .claude/mi8_mech.json,旧值保留在 _prev_2026-09-06)

本体 67 × 44;孔 7 个,孔径实测 ≈Φ1.5(M1.4 类)⇒ 载板侧取螺钉过孔 Φ1.6 (原来 Φ2.5 / M2.5,整套错的)。

新 (u, v) 旧最近邻 移了
H1 (1.4, 9.9) H6 2.8
H2 (0.7, 27.3) H5 1.9
H3 (1.7, 38.7) H2 1.0
H4 (41.9, 1.9) H7 1.9
H5 (57.4, 41.2) H1 3.4
H6 (64.6, 28.1) H4 2.7
H7 (64.8, 32.4) H3 3.2

⚠️ 编号整体重排过,别拿旧 H 号对新 H 号。u 沿长边、原点在没有卡托伸出的那一端; v 从接口边起算。

数据来源与复核(两条独立链,这是本轮最值钱的东西)

  1. 测量底板(pcb2_measure_sheet.py):描轮廓 + 每孔戳一点 → 平板扫描 → pcb2_measure_read.py 按四个基准圆做单应反算。
  2. 真主板平板扫描 7/7 残差 0.38 ❌ 这条作废,是假匹配。2026-09-06 用户看图发现对不上, 高倍逐孔审计(audit_c.png)证实:7 个十字没有一个落在真孔上。 成因:候选点来自灰度环形滤波器,这块板上圆形特征几百个,7/7 + 小残差可以纯靠撞。 同样作废的还有贴合面那张 MI8-mating-face.png 的孔/触点标记(底图和摆正是对的,标记错位)。
  3. ✅ 真正站得住的第二条:把主板用 7 颗螺丝装回中框、螺丝头涂红再扫,HSV 分出品红点 —— 候选就是那 7 颗螺丝本身(不是滤波器噪声),镜像=False 无解、只有 True 有解,残差 0.48 mm。 高倍审计 shots/MI8-holes-proof.png:7 个十字全部落在螺丝正中心。螺丝长在孔里 ⇒ 直接证据。 ⇒ 孔位模型可以按 ±0.6 mm 用,支撑是两条独立链(测量底板戳孔 + 红螺丝),不是三条。

⚠️⚠️ 教训:环形滤波器配准出来的 7/7 不可信,必须高倍逐孔看

这块板上圆形特征几百个(过孔、焊盘、测试点、罐体),7 点模型又近似镜像对称 —— 「7/7 命中 + 残差 0.4~0.6 mm」在这种候选集里可以纯靠撞出来,两种镜像还都能撞出来。 本轮为此错了两次(中框螺柱那次、裸板那次),第二次还被我当成"独立印证"写进了文档。

判据只有两条:

  1. 候选集必须本身就是要找的东西(涂红的螺丝、涂红的触点),不能是通用滤波器的输出;
  2. 出图之后必须逐孔高倍截图看(audit_*.png 那套,每格 ~11 mm),低倍扫一眼看不出错位。

「拿板子外形轮廓去套」也不行(2026-09-06 试过):测量底板上那条描线被笔宽撑大 22% (3371 vs 2770 mm²),板子无论正反都能整个塞进去 ⇒ 两种镜像的 IoU 一模一样(0.8157 = 面积比), 判不了手性。要用外形配准,得先有一条不带笔宽的真实轮廓。

⇒ 贴合面(侧键面)到现在还没有可信的板坐标映射。 要做那张图,得在那一面也放上 不含糊的标记:往 7 个孔里各点一点红漆(或把螺丝从贴合面这一侧拧进去)再扫, 然后照红螺丝那套 HSV 提点 + 配准 + 逐孔审计。

⚠️ 板长 67 是裸板。用户描轮廓时卡托还插着,卡托在 u 大的那一端伸出约 3.3 mm, 描出来是 70.3。他早先给的 u 值是拿 66 / 70.3 混着换算的 —— 那 3~4 mm 的"怪差"就是这么来的, 不是测量误差。判据:改按「距下端边」算,两边完全一致。

⚠️ 侧键顶针坐标同样是错的 —— 已实测替换

旧的 POGO_XY(§22.3b 四个点,跨度 3.3 x 4.5)也是从照片认的,位置差 2~4 mm、内部间距也不对。 顶针打偏 = 按键不通,而且几何验证和 DRC 都测不出来,是本轮最危险的一处。

实测办法:用户把四个触点涂红再平板扫描,用 7 个定位孔配准(7/7,残差 0.6 mm)反算。

u v
C1 −0.41 5.74
C2 1.65 5.77
C3 −0.29 8.61
C4 2.30 8.21

2×2 排布,跨度 2.71 × 2.87 mm,最小间距 2.06 mm(顶针选型要卡这个数)。

手性是怎么定的(这条要记住):7 孔图案近似镜像对称,两种镜像都能 7/7 拟合(残差 0.37 / 0.60), 配准本身定不了手性,两解相差 60 mm。判据只能来自扫描件内部的相对关系: 触点簇心离 H1 只有 2.7 mm,而 H1/H2/H3 是挤在一端的那三个孔;用户确认「三个孔在上端」 ⇒ 触点在 u≈0 那一端。⚠️ 以后凡是拿这块板做配准,手性都要另找证据,别信拟合残差。

✅ 已下发到 EasyEDA(2026-09-06):35 个定位孔 + 5 个主板投影丝印

pcb2_place.py --mech --force。读回复核:35 个孔全部 Φ1.8 NPTH,与模型最大偏差 0.0001 mm; layer3 丝印 25 条。板上其它东西(元件、布线、铺铜)一个字没动。

--force 是本轮新加的:只下发 --mech / --outline(孔、槽、丝印)时,元件级报错按警告放行 —— 那些是「元件 vs 孔」「元件 vs 元件」,和要写的机械图元无关。--push(重摆元件)永远不允许 force。

⚠️ 下发时挂着 25 条本地报错,都还没解决(见「还欠什么」)。铺铜和 DRC 也没跑 (pcb2_reload.py 会删掉全部铺铜边框,不能随手跑)。

侧键触点定稿值(2026-09-06)

几何来自中框上那四个金焊盘(配合件,边界干净):菱形,相邻 1.87 / 对角 2.65 mm,焊盘 Φ1.35。 ⚠️ 相邻只有 1.87 mm ⇒ 顶针本体直径必须 < 1.87,常规 Φ2 以上排不下,得选 Φ1.3~1.5 的细顶针。

位置相对孔给(载板靠 7 孔定位手机,顶针就得相对孔摆):

顶针簇心 = H1 + (-0.59, -2.82)     ⇒ 簇心 (0.81, 7.08),离 H1 孔心 2.88 mm

这一项是在同一张扫描件里量的,不经过配准、也不吃手性 —— 这是它比别的路子可信的原因。 (曾用「中框螺柱 + 7 孔配准」得到簇心 (3.42, 5.07),它推出「簇心到最近孔 5.2 mm」, 和上面 2.88 的直接实测矛盾;那次 262 个候选里撞出的 7/7 是假解,已弃。)

身份 GND 在 u− · PWR 在 u+ · VDN 在 v− · VUP 在 v+(半径 1.32)—— 两条互相独立的描述都对上了:① 中框基准(蓝圈最近=VUP、正下方=VDN、最靠中框边缘=GND、与 GND 正对=PWR) 经中框配准的旋转折算;② 主板自身边基准(离短板边最近=GND、与其正对平行=PWR、 与其垂直且离长板边最近=VDN、与其垂直且比 VDN 更靠另一长边=VUP)。两条都指向同一组。 顺带印证了坐标系:板的 u = 机身宽度方向(u=0 端 = 侧键所在的机身侧边),v = 机身长度方向。

⚠️ 菱形左顶点算出 u = −0.51(名义在板边外)。原因是 u 原点取自描的笔迹内沿,比真板边偏内约 1 mm, 它同时偏了孔和触点,相对关系不受影响。真要修板边(画手机本体投影时), 把 PW 和所有 u 一起 +1,别只改一个。

⬅️ 顶针这条还欠一件事

⭐ 方案改动(2026-09-06 用户拍板):顶针取消,改成中框式焊盘

装配改成:螺丝把主板压在载板上,主板自己的侧键弹片直接压在载板的裸铜盘上 —— 复刻中框的接触方式。

  • 立柱高度 3.0 → 1.0 mm(中框「螺柱支撑面到金焊盘面」实测 1 mm)。
  • 不用挖空:用户实测贴合面上元件全都低于 1 mm,载板保持平板。 (原本担心「挖空会和底层 BAND 那组元件打架」——那组就在手机投影正下方 u 1344 / v 1126,现在不用挖了。)
  • 排线反而更宽裕(绕边下落 5→3 mm):尾插 ≈44.5 ≤ 50、电池 ≈29.6 ≤ 35,§25.16 的路径不用改。
  • ⚠️ 1 mm 立柱不好买(M1.6 尼龙立柱通常 3 mm 起)。做法:螺钉从上穿手机孔 + 1 mm 尼龙垫片 + 穿载板 Φ1.8 孔 + 底面尼龙螺母。垫片可叠。

已落地:

  1. 新封装 PAD-D1.5_MI8-KEY(fp_keypad.py --push,uuid e106d858…):顶层单焊盘 Ø1.5, 在原点、无孔,读回校验过。定径依据:四触点实测最小间距 1.78 mm(不是理想菱形的 1.87) ⇒ 净距 0.28。旧的 POGO-SMD1P_D1.9(Ø1.9)在 1.78 下是负净距,正是那 20 条「重叠(L1)」的根。
  2. 器件 1c8e0ea1… 就地改名换封装:YTC1P-2510-01 D1.9 → MI8-KEY-PAD D1.5, 封装指向新盘,符号没换 ⇒ 原理图一个字没动。

⚠️ 一件必须手工做的:lib_Device.modify 的 property 参数被 API 忽略(名字/封装/描述能改, BOM 字段改不动)。得在客户端里打开这个器件,把 Add into BOM 改成 no、清掉 C5221288 / YIYUAN 料号, 否则 BOM 里会照着买 20 颗用不上的顶针。

⚠️ 工艺(生成不出来,下单时要说):接触盘钢网开口 0(不上锡膏),表面处理 ENIG。 弹片压在锡珠上会接触不良,HASL 反复摩擦会磨穿。

⛔ 还欠什么(下发之后剩下的)

  1. 顶针封装排不下 → 已解决:改成 Ø1.5 焊盘(见上)。但还没走完最后几步: PCB 导入变更(换封装)→ 把 J41~J60 摆到新坐标 → 重布这 20 个网络(原顶针位置的走线全废)。 ⚠️ pcb2_place.py 的本地自检还会报那 20 条重叠 —— 它吃的是 _pcb2_pads.json 缓存里的旧 Ø1.9, 导入变更之后重抓一次封装框才会消。 ⚠️ --push 会重摆全部 218 个元件,风险大;只想摆这 20 个的话得单写一条路径。
  2. J61~J65(EDL 座)离 P*H5 只有 0.92 mm(要 2.2)。二选一:挪五个 J6n 重布 10 条线, 或者干脆不打 H5(每台 6 个孔,够用),后者代价为零。
  3. 孔位挪动后已有铜有 11 处擦边(最深 0.95 mm,全是走线/过孔蹭到),局部挪线可收。
  4. 铺铜没重铺、DRC 没跑。

四个触点谁是 PWR / VUP / VDN / GND 已按用户指认填上(GND 靠机身边缘、PWR 与 GND 正对、 VUP 靠中框那个蓝圈、VDN 在 VUP 正下方),但没做电气验证。 接线前拿万用表对着侧键按下去测通断确认一遍 —— 打偏或接反都是做出板子才发现。

另外开工前拿卡尺复核一个数就够:H1 孔心到四触点菱形中心 ≈ 2.9 mm。量出来是 5 mm 就说明我错了。

对已布线载板的冲击(离线算的;--mech 已下发,铺铜/DRC 还没跑)

35 个孔里 11 个和已有铜冲突,但都很浅(最深 −0.95 mm,全是走线/过孔擦边): P2H5 −0.95(走线 VBUS_EN2 L2)· P2H1 −0.91(过孔 POGO2_VUP)· P2H7 −0.90(走线 VBUS2 L1)… 少打几个孔能减到:不打 H5 → 6 个;不打 H5/H6 → 5 个;不打 H5/H6/H7 → 2 个。 ⇒ 不是重来一版,是十来处局部挪线/挪孔,pcb2_route_sig.py 能收。

⬅️ 还没定 / 还没做

  1. 两处数据存疑(所以用户决定先复测再下发):
    • 戳孔那张在 (63, 3.5) 还有一个没标注的点 —— 可能是第 8 个孔;
    • 下端那两个孔:戳点说距下边 ~1.5 mm,用户尺量 5.5 mm,差 4 mm。
  2. 复测底板已打印:shots/PCB2-measure-sheet.pdf(pcb2_measure_sheet.py)。 干净底板 + 四个实心黑圆基准点(圆心矩形 160 × 110 mm),描轮廓 + 每孔戳一点,拍照回来反算,约 ±0.3 mm。 反算脚本 pcb2_measure_read.py 还没写(照片来了再写;套路见下)。
  3. 复测完才 pcb2_place.py --mech 下发,然后 pcb2_pour.py --push + pcb2_reload.py + drc_read.py。
  4. H5 打不打(等于「6 孔 vs 挪五个 J6n 重布 10 条线」)等复测结果再定。

照片反算这条路是通的(已验证)

用户直接在打印好的 1:1 图第 2 页上描了轮廓,我用 SIFT + 单应把照片和 PyMuPDF 渲染的那一页配准, 印刷的 7 个孔十字圈逐个压准,然后把红笔迹映射回本体 mm。关键几点:

  • 照片取 R 通道当灰度图:红笔迹变白、只剩黑色印刷,配准不受手绘干扰;
  • cv2.SIFT_create() + BFMatcher + findHomography(RANSAC),本例 1029 good / 139 inlier 就够;
  • 反变换后按本体坐标重采样成「1 mm = 20 px」的正视图(r2_rectified.png),量什么都直接读;
  • 描边笔宽约 1.3 mm,取墨迹内沿才是真板边,不然板长会大 1.5 mm。

⚠️ 别拿手绘草图的像素位置当坐标:用户第一张徒手草图,比例和他自己写的尺寸能差 5 mm (「④ 距左边 12 mm」量出来 16.9、「⑦ 距下边 28」量出来 23.4)。草图只能定拓扑,坐标必须来自尺子或戳孔。


板子当前状态(2026-09-06 第二十七轮收尾)

下板 PCB1

项 值
板框 220 × 135,左下角 (−30,−5)(原点不动),定位孔 6 个与载板对齐(pcb1_outline.py)
元件 290(−52:595 窝挪走)
板上铜 3118 线 / 985 孔(route_sig --refresh --ripup --push 之后)
DRC 间距错误 0
DRC 连接性错误 79(上一轮 103)
还欠布通 35 条(K/POGO 那批已不在下板;剩的是 §26.22 的 A/B/C/D/E 老账,子代理的分类报告见 .claude/_pcb1_unrouted_report.md)
铺铜 --verify ✅ 实测和计划对得上
本地摆位检查 ✅ 六项全过(pcb1_env.py 按新载板重生成后,J24/J25 80 针网络逐个对上)

载板 PCB2

项 值
元件 218(+52),全部按 pcb2_place.py v5 表 + 新件重摆并 --verify 回读
定位孔 8 个(原 6 + 顶角 (4,131)/(216,131));尾插槽东端 65→64
死网铜 / 错标签 排母来的 POGO/EDL_EN/HUB_RST 旧线已删;29 孔 / 11 线按接触的铜重标(pcb2_via_relabel.py)
信号布线 pcb2_route_sig.py 整板 123 网布通 115,净距 / 钻孔 0;8 个没布通(+3V3_HUB 去 site1 那支、K3_VDN K5_VDN、EDL_EN2/3/5、VBUS_EN5、+5V J32.40→J32.4),板上只留出线段。清单与两个候选方向见设计文档 §26.23 落地末尾
DRC 间距错误 15(改前基线 115 是封装内焊盘对焊盘;现在剩的是 e2289 那个 GND 过孔压在 VO3_P 线上、J63_3 下面两个无名过孔等重标漏网的)
DRC 连接性错误 506(8 个没布通的 + 铺铜没重铺)
差分对 pcb2_net_conn.py 22/22 连通
⚠️ 还没做 pcb2_pour.py 没重铺(新件的 GND 孔已打)、inner1_verify.py 没跑

⚠️ 每一轮改动之后的固定收尾序列,一步都不能省:

/c/Python314/python -u .claude/pcb1_route_sig.py --refresh --ripup --push
/c/Python314/python .claude/pcb1_pour.py --push --refresh
/c/Python314/python .claude/pcb1_reload.py     # 不重载,DRC 连接性那一支是陈旧的
/c/Python314/python .claude/drc_read.py

⚠️ 一条没做完的:栅格布线器要的出线道是 0.741 mm,不是 0.60

§26.22 的 A-1 把 U6/U7 周围六颗卫星件各挪了 0.30 mm,出线道 0.312/0.315/0.376 → 0.612/0.615/0.676,pcb1_escape_audit 里「几何上出不去」 从 7 个降到 2 个(U6.6=SCL、J25.24 都解决了)。

但 U6.16 / U7.16 还是布不通($6N532 / $6N547,各差 1 条 1.5 mm 的短跳)。 原因是 0.60 这个门槛算漏了栅格量化余量:布线器的判据是 CLR + 半线宽 + QERR = 0.20 + 0.10 + 0.0707 = 0.3707,两边就是 0.741 mm。 0.615 的道理论上塞得下、栅格上塞不下。

⇒ 下次把 pcb1_place.ESCAPE_NUDGE 从 0.30 加到 0.50(东列 0.45), 让三个方向的道宽都 ≥ 0.80。改完照 §26.22 那套善后(挪件会压到邻线和换层孔)。

剩下 46 条布不通的分类(详见 §26.22)

类 条数 处理
A 焊盘几何出不去 2 上面那条:ESCAPE_NUDGE 加到 0.50
B USB 差分对 4 未做。整束 0.7 mm 找不到走廊,要预留走廊或手写几何
C 五路母版系统性缺口 ~14 未做。$*N676/680/698/727,2.4~5.7 mm 短跳在多通道同时失败,在一个通道解决再复制
D I²C / INA_ALERT 末端 ~5 未做。器件脚下顶层口袋里没有合法孔位
E 单发拥堵 ~21 多跑几轮 --ripup(RIP_LIMIT=1 是有意的,见源码注释)

✅ 第二十四轮(2026-09-06,用户拍板):I²C 干线改走 Inner2(§26.20)

SDA/SCL 是全板最长的两个网络(各 14 个焊盘、横跨整板), 既吃布线时间(1516 s)又占着外层最好的通道。用户拍板:走内层,只挪跨板长干线。 新工具 .claude/pcb1_i2c_l3.py(三层迷宫布线 Top / Bottom / Inner2)。

改之前 改之后
I²C 总铜长 596 mm,内层 0 609 mm
Inner2 — 302 mm
顶层 / 底层 596 mm 157 / 150 mm
连通性 SCL 3 片 / SDA 4 片 一模一样

中桥那 13 mm 是唯一留在外层的一段:12V-SW+BR 的中桥(x 61.5~72.6,喂 3.6 A) 是 Inner2 上东西两半唯一不能割的隔断,I²C 过去就必须横着割 ⇒ 只能外层跳。 (上一轮 route_sig 给 SDA 布的就是这个跳法,殊途同归。)

三条这一轮踩到的坑(细节全在 §26.20):

  1. PEN_3V3 决定的不是长度,是平面会不会被切出孤岛。 1.8 的时候两根线在 3V3-SW 里围出 7.2 mm² 孤岛,里面有 R415.2 的 +3V3 焊盘 —— 浮铜检查 / 焊盘覆盖检查 / 连接性 DRC 三样全都不响,只有数连通域看得见。 堵孤岛重绕不收敛(7.2 → 25.4 → 26.8 → 32.5 mm²)。改成 4.0: 干线走 GND 底填,只在器件旁边扎盲端进平面。
  2. expand() 会静默丢掉最后一点上的过孔 —— 引擎 _end_via 的注释早写着,又踩一遍。
  3. own(本网铜放行)要按半线宽内缩,过孔则根本不吃这条放行。

顺带修掉一块和这次无关的浮铜:板子南沿 y 0.59 那条 GND 底填(1312.8 mm²) 在 Inner2 上是孤立的,一个落脚点都没有 —— 它原本靠 x 115116 那条 1.0 mm 缝接出去, 被上一轮信号布线的三个过孔的反焊盘掐断了。新脚本 .claude/pcb1_gnd_stitch.py 在南沿补了 6 个 GND 缝合孔。⚠️ 这一条只有铺铜下发后 verify() 回读实测铜才量得出来, 下发前的 report() 栅格不含别的铺铜,算出来 GND 是一整块。

验收:净距/拐角/孔距 ✅ · 平面完整性 ✅(+12V 1 块 / +3V3 2 块)· pcb1_pour.py --push --verify ✅ 实测和计划对得上 · DRC 里没有一条牵涉 SDA/SCL。


✅ 这一轮 C(2026-09-06):8 A 的源只有 3 个过孔(§26.19)

第一次跑信号布线跑到 35/196 中止 —— 不是布线器的问题,是它报出来的失败把两个上游问题照了出来。

  1. Rs6.2 → +12V 平面只有 3 个 Ø0.3 过孔 ≈ 3~4.5 A,而它要过 8 A。 它是全板 +12V 平面唯一的源。这一条四道验证一条都不管:铺铜断面只量平面本身、 扇出只报「孔放下了没有」、连接性 DRC 只看「连上没有」、逃逸审计只看「出得去吗」。 ⇒ pcb_fanout.py 新增 FORCE_ARRAY(整片阵列钉死,逐个仍走全套净距校核), Rs6.2 钉 12 个孔(≈12 A),摆在焊盘正南那片空地。 规矩:>4 A 的「焊盘 → 平面」转换点,过孔数必须手写。
  2. 顺带把 U13 的 I²C 放了出来。 原来那 3 个孔有两个甩到西边,两根 1 mm 引线 (端帽再往西探 0.5)压死了 U13 南排五个脚唯一的通道;VINSW-TOP 北柱又只差 0.379 mm。 12 个孔改朝南 + 北柱在 y>87 收窄到 x 1.55.3(3.8 mm → 8.4 A), U13 南边变成一整片 x 5.514 / y 91.6~96 的空地。
  3. 扇出不对称又发作了一次:C%06.2 的 GND 孔在 rot 0 的三路往东落到局部 (10.02,27.10)、 在 rot 180 的 CH2/CH3 往西落到 (6.75,27.10),正压在 eFuse 脚 14/15 的出线口上。 已加进 _CH_FORCE_LOCAL。
  4. 剩下 10 个「被围死」不是板上的铜 —— 报出来的挡路物去 pcb-copper.json 里查不到, 是审计自己给邻脚预留的出线段。判据就是这个:查得到才是真障碍。

✅ 这一轮 A(2026-09-06):v11 的两行排法把 8 A 输入链卡死了,已修(§26.17)

§26.16-E 换 XT30 之后把 N-L 块改成两行,摆位的本地检查六项全过, 可是回去重画 §26.11 的顶层宽铜时第一刀就下不去: VIN_RAW 从 J1 出来根本没有 3.49 mm 的路 —— XT30 rot 270 之后 GND 在北针 (13.5,74.5)、 VIN 在南针 (13.5,69.5),而 F1 卧着放和 GND 针同一行,中间又被 D1(17.5,69.5) 堵住, 实测通道只剩 1.674 mm(1 oz 外层约 4.5 A,这里要 8 A)。

这一类错误几何检查一条都报不出来 —— 不重叠、不越框、高度也够。 抓到它的是「照新坐标把顶层宽铜的多边形重推一遍」这个动作本身。 ⇒ 摆位改完不能只看本地检查,必须把 §26.11 那三块多边形重推一次。

三处改动(pcb1_place.py 的 N-L 表):

# 改动 拿回了什么
① F1 卧 → 竖(rot 0 → 90),(17.5,74.5) 不动 F1.1 落到 (17.5, 73.06),正对 J1.2 那片空地
② D1 (17.5,69.5) → (31.0,74.5) rot 270 通道里最后一个障碍搬走,TVS 贴到漏极铜东缘
③ C4/C5/C6 x 25 → 30.5,rot 0 → 180 让出 y 78.85~83.5 那条 4.64 mm 的 VIN_SW 西行干道

顶层三块宽铜重画后实测断面全过:RAW 颈 3.50(要 3.49)、FUSED 5.25、SW 干道 4.75、北柱 5.25 mm。 面积 59 / 30 / 263 mm²,各 1 个连通域,check_top_clearance() 零侵入。

⚠️ 顺带抓到一个静默失效:Rs6.2 一个过孔都没打。 12V-N 为了让开 U13 的 Kelvin 脚,在西边咬了个缺口 (0.5,93.0)-(12.5,101.5), 而 Rs6.2 的焊盘中心 (9.57,93.50) 正落在缺口里 ⇒ 扇出判成「不在铜面上」, 全板唯一的 8 A 源不接电源平面。--push 照样报「失败 0 个」,连接性 DRC 也不响 (+12V 靠别的 55 个焊盘照样连通,只有源那一端是断的)。缺口收成 (0.5,95.6)-(12.5,102.4)。


✅ 这一轮 B(2026-09-06,用户拍板):三组引脚按几何重排(§26.18)

「哪个信号落在哪个物理脚」有三处是自由的,而原来全是按逻辑顺序排的, 和它们在板上要去哪儿一点关系都没有。目标函数用绕开芯片本体的直角最短路 (HPWL 量不出「东边的脚要去西边」有多贵:同一批改动 HPWL 说省 7%,绕障路长说省 15%)。

组 结果 挪了几个
ESP32-S3 U20 22 个信号 1195 → 1020 mm(−15%),绕模块的线 3 根 → 0 12
DAC63204 U6/U7 5 个通道 422 → 352 mm(−17%),一根线都不用新画 4
74HC595 U21/U22/U33 21 个位 1286 → 1141 mm(−11%),每路三键成束 17

合计省 约 390 mm。三处关键决定:

  1. 放开 IO39~IO42(原留给引脚 JTAG)—— 西边从 4 个坑变 8 个。 调试本来就走原生 USB-Serial-JTAG(IO19/IO20 → J22,出厂 eFuse 默认就是它), 板上没有 JTAG 排针。⚠️ 永远别烧 DIS_USB_JTAG,烧了两边都没有。 让出去的只是「USB 起不来时还能进去」这个兜底。 EFUSE_EN1 → IO39、VBUS_EN1 → IO40、EFUSE_EN4 → IO41、VBUS_EN4 → IO42。 空出来的是 IO4 / IO5 / IO7(三个都带 ADC1)+ RXD0/IO44。
  2. DAC 只在已画好线的 5 个通道里置换(U6 管去西半板的 VDAC1/2/4,U7 管去东半板的 VDAC3/5)。 动用 U6.OUT3 / U7.OUT2/3 还能再省 6 mm,但要新画 OUT/FB 两根线,不值。
  3. 595 加了「每路 3 个按键必须落在同一片的连续三个南脚上」的约束 —— 比自由指派多 40 mm, 换来三根线出芯片就是一束。U33(y≈70)从「EDL 专用片」变成「北边那一片」, 接管 CH2/CH3 的六个按键 + HUB_RST,因为它正对 y 87~94 的北排两路。

⛔ U33 现在在 P6-FAN-EDL 那一页上带着 CH2/CH3 的按键网络 —— 页面语义乱了一点, 但三片本来就是一条 24 位链,功能上没区别。

落地时踩到的两条 API

  1. ⚠️⚠️ setState_Net 对 netport 是坏的:调用之后网络名变成空字符串,标签也没了 (和 skill §3b 的 setState_X/Y 同一个毛病)。⇒ 端口只能 删掉重建。
  2. ✅ skill §8b 那句「端口的 dir 读不回来」是错的: getState_Component().name 就是 Netport-BI / Netport-IN / Netport-OUT。 重建前读它原样传回去即可 —— p1_add.py 当年「端口只增不重建」的限制可以解除了。

顺手治了:图面文字叠了好几代

P3 有 44 条 sch_PrimitiveText、P3B 64 条、P6 76 条,大量是同坐标同内容的完全重复 (历代脚本只 create 不 delete),还有互相矛盾的旧说明叠在一起。 四道验证(verify_sch / xpage / port_audit / DRC)一条都不管文字,所以它烂了很久没人发现。 .claude/sch_text.py 整页重写:44→13 / 64→14 / 76→16,顺带改掉换脚后变成假话的几条。

这一轮新增的脚本

文件 用途
pinmap.py ⭐ 三组「逻辑信号 → 物理脚」映射的事实来源 + 落地 + --check 回读对账
sch_text.py 三页图面文字整页重写(删光旧的再写一套)

⚠️ 还欠人眼确认

  • PCB1-v11-NL.png(北左输入链改动前)与改动后的截图 —— 还没截改动后的
  • P3-CONTROL-v2.png / P3B-EXPAND-v2.png / P6-FAN-EDL-v2.png 三张换脚后的原理图
  • 摆位总图 PCB1-v11-place.png(v11 那一版,D1/C4/C5/C6 的新位置还没进图)

上一轮(2026-09-06 第二十二轮):拆线重布 + 两处换件 + 控制块按几何重排(§26.16)

# 改动 依据
1 J1:KF128-5.08-2P → XT30PW-M(C53373776),(8.5, 72.0) rot 270 用户要插拔式;12V 是 8 A,DC-002 那类只有 1 A
2 J26~J30:KF2510-3A(弯针) → 2510-3A(直针)(C2803349) 用户要立式;进深 12.01 → 5.81
3 N-L 块重排 XT30 比 KF128 宽一圈,原位放不下 ⇒ 这一版被第二十三轮推翻,见上面 A
4 控制块:affine() → CTRL 表(退火求解,pcb1_ctrl_place.py) 原来 U21/U22 中心只隔 8.04 mm,中间塞不进一根线
5 22 个附属 R/C:affine() → SAT 表(逐个贴对端引脚) R602/R603 是 USB-C 的 CC 下拉,离 J22 有 77 mm

全板 HPWL 9416 → 8935 mm。摆位大改之后 pcb1_wipe.py --push 把板上全部铜清空过一次 (走线 / 过孔 / 铺铜,元件 / 板框 / 丝印不动),13 个旧账本改名成 .stale。

这一轮沉淀的脚本(仍然有效):

文件 用途
fp_body.py ⭐ 从器件的 3D Model Transform 直接读本体框(--scan / --check)。⚠️ 只给 XY,Z 读不出来
p1p6_conn.py J1 / J26~J30 换件(做到一根导线不动)
pcb1_ctrl_place.py 控制块五颗芯片退火求位置(目标 HPWL)。⚠️ 它写的 _pcb1_ctrl_pos.json 只是参考,pcb1_place.CTRL 是硬编码的事实来源
pcb1_wipe.py 清空板上全部铜(铜层白名单 1/2/15/16)

⛔ 三个要记住的地雷(仍然有效)

  1. pcb1_route_sig.py 的 --net / --limit 试水以前会把计划缓存清成 {}(已修)。
  2. clean() 以前只删得掉 59% 的自己的铜(已修,改成覆盖判据)—— EasyEDA 会合并共线段、在 T 形接点劈线,坐标精确比对认不出来。
  3. 「两个端点钉死 ⇒ 连通性不变」是破的 —— 链会 T 在别的链中段上。 现在先按搭接点切开(409 → 797 条),拆完还要用 pcb1_netlist.analyze() 实算碎片数兜底。

⚠️ 已知偏差(是决定,不是遗漏)

  • 立式风扇座把装配顺序锁死了(用户 2026-09-06 拍板接受):立式插口朝上正对载板底面, 座 + 胶壳约 11.5 mm,13 mm 净空里插拔只剩 1.5 mm ⇒ 必须先插好五根风扇线再合载板。
  • H_FP 里 XT30 的 11.0 和直针座的 9.0 是估计值 —— 3D Model Transform 给不出 Z, 打样前拿实物量。
  • 控制块的通道分配还没改 → 第二十三轮已经做完(§26.18)。
  • POGO2_VUP / POGO2_VDN 离 SW 节点 0.54 / 0.74 mm(§10.3 要「远离」), sw_keepaway() 会一直报,当提示不当错误。

✅ 这一轮(2026-09-06 第二十一轮):五路母版三处整改 + 全链重跑(用户拍板 A+B+C)

设计文档 §26.15。§26.14 抓出的 22 个「几何上没有出路」的引脚,这一轮动手改掉, 改的全是母版(= 五路一起改),改完整条链重跑。

# 改了什么 结果
A SW 不再往南潜:C%05 北移 0.6,SW-run 平着走 y 12.500(1.2 宽),删掉 SW-down 五路 EN 全部解放;SW 节点 9.6 → 8.9 mm / 铜面积 10.9 → 9.4 mm²
B U%8 + Rs%01 + C%13 整体北移 0.8 eFuse 北排那条缝 0.33 → 1.13 mm
C 跟着 B 改的三处:_CH_FORCE_LOCAL['U%__8','1']、F-trunk 北端、K-IN± 相对几何一格没变
— 全链重跑:place → clear → dump → fanout → pour → route_pwr 342 件 / 498 孔 / 15 铜面,功率级 195 段回读 0 条对不上

「几何上出不去」的焊盘:22 → 8(pcb1_escape_audit.py 实测)。 剩下 8 个是和邻脚预留出线段抢地方,不是硬几何 —— 单独布都能通。

⭐ 这一轮踩到的三条

1. 「端帽比端点往外探半个线宽」这一条,一轮里踩了两次。 先是 SW-run 1.5 mm 的西端帽把 EN 封死(在 y=11.5 那一行往西探到 x 11.007, 比端点多探 0.743);改完起点又发现同一个端帽离 U%1.13(BOOT 引脚) 只有 0.080。 ⇒ 凡是宽线的端点,心里都要按「端点 ± w/2 的圆」去想,§26.11 已经写过一次了。

2. 挪 0.1 mm 解决不了拓扑问题。 A 组先试过「SW-run 起点东移 0.35」和「收窄到 0.9」, 两次都卡在 5~20 µm 上 —— 因为 COMP 和 EN 两个脚要并排出来,缺的不是余量是车道。 把 C%05 北移 0.6、让 SW 整条不再下潜,才是解。

3. verify() 只认「同宽」的线会报假阳性。 一段窄线整个落在宽线的圆头端帽里时 EasyEDA 会直接吞掉它 —— 铜一点没少, 回读却报 5 条对不上。判据改成「实测线不窄于计划线,且中心距 ≤ (o_w − p_w)/2」。

4. chamfer_routes(smooth=True) 会挪线(最多 0.15 mm),不只是拿掉铜。 而它的净距校核拿的是切之前的自身路径 ⇒ 两条本轮新画的线会被各自抹到对方身上 (实测 $1N227 vs POGO1_PWR 压成 −0.199,2255 段里 2 段)。 ⇒ 切完再跑一遍 check(),把牵涉到违规的整条退回未切的版本,最多退 4 轮。


✅ 这一轮(2026-09-06 第二十轮):下板信号布线 —— 工具全套建好,⛔ 没下发

# 做了什么 结果
1 新增 pcb1_netlist.py:按实测铜(含回读的铺铜)算全板连通性 还欠 200 个网络 / 384 条连接;顺带发现 GND 其实早就通了
2 新增 pcb1_route_sig.py:下板信号布线器(复用载板栅格引擎,只加不拆) 379 条里布通 284 条(75%),净距 / 钻孔 / 铺铜 三样全 0
3 新增 pcb1_escape_audit.py:逐个焊盘算「能不能走得出去」 563 个待接焊盘里 22 个几何上出不去
4 Kelvin 三条(U13.8/9/10 ↔ Rs6)单独钉死,不许就近扎平面 3 / 3 条,w 0.30
5 USB 差分对「一整束布线 + 平行偏移」 束长 58.1 mm,W 0.28 / S 0.18,对内偏斜 0.00 mm
6 修掉 4 个真 bug(见下) —

⭐⭐ 这一轮最值得记住的四条

1. POLYGON 焊盘的包围盒不是它的形状 —— 又一次。 §26.13 抓的是它的 hole 是假的;这一次是 pad[1], pad[2] 那对宽高。 TPS54622 的散热盘是个带四个缺口的多边形(缺口正是让给 pin 1/7/8/14 的), 按 2.032 × 4.320 的包围盒当障碍,这四个脚整个泡在 GND 铜里: entry_stub 一个出线方向都找不到,BUCK_PG 报「五路全布不通」。 ⇒ POLYGON 焊盘的任何标量属性都不能直接当几何用,只能读 pad[1] 那串点。 现在 pcb1_route_sig.poly_pads() 把 11 个多边形盘按真实轮廓光栅化成矩形条。

2. 网名里带 # 的网络会被引擎的 netof() 拆坏。 引擎用 key.split('#')[0] 从「路径 key」还原网名,而下板有 KEY_OE# 和 IOEXP_INT#。 'IOEXP_INT#' + '#0' 拆回来是 IOEXP_INT ⇒ 校核时它和自己的焊盘被当成异网, 报「线-焊盘 净距 −0.100」,看上去像压线,其实两者本来就该连在一起。 ⇒ 分隔符改成 chr(0)。凡是「用某个字符拼 key」的地方,都要先问一句这个字符会不会出现在数据里。

3. 45° 切角会切进铺铜里 —— seg_slack() 不认铺铜。 切角是往拐角内侧补铜(不是只拿掉铜),而引擎的切角校核只看焊盘 / 过孔 / 走线。 实测 SDA 在 (6.10, 65.50) 被切进 VO1_P 小岛,只剩 0.043。 ⇒ 把铺铜矩形条包装成「假焊盘」一起喂给 chamfer_routes() 和 check()。

4. 0.5 脚距的脚,「放得下」和「栅格上走得通」是两回事。 U8.3(INA226 的 ALERT):西边 1.00 mm 是 GND 扇出孔,东边 1.00 mm 是邻脚的出线段。 0.20 的线需要 1.005 mm(差 5 µm 过不去),0.15 的线只要 0.955 mm —— 可 0.1 mm 栅格上判据要各加一个 QERR 0.0707,变成 1.096 mm,整片不可达。 0.025 mm 栅格的 QERR 只有 0.0177,同一条缝就通了。 ⇒ 新增「细栅格逃逸」:0.1 栅格上出不来的脚,先在 4 mm 内用 0.025 栅格单独走出去。 实测 47 个脚里救回 25 个。

⛔ 剩下 22 个脚是几何上就没有出路,必须动板子

组 焊盘 卡在哪 差多少
A U1.10 U2.10 U3.10 U4.10 U5.10(Buck 的 EN) pcb1_route_pwr 的 SW-run 1.5 mm 铜,圆头端帽从 (20.05,27.10) 往西探到 x 19.30 脚东边净空 0.376,一根 0.2 的线要 0.6
B U14.2/3 U15.2/3/14/15 U16.2/3/14/15 U17.2/3 U18.2/3(eFuse 北排) 上面 INA226 的焊盘边 只隔 0.33 mm,下面自己的散热盘 0.25 mm 要 ≥ 0.6,缺 ~0.7 mm
C U10.3(ALERT)U13.4(SDA) U13.5(SCL) 隔壁 GND 扇出孔正对着出线走廊 5 µm

A 组的解:EN 是每路都有的信号,本来就该进 §10.2 母版 —— 在 pcb1_route_pwr.R 里 加一条 EN 路径,并把 SW-run 的起点往东挪 ~0.35 mm(或前 2 mm 收窄到 1.2)。 收窄反而符合 §10.2-2「SW 铜箔面积压到刚好够载流」。

B 组的解:CH1 那一列从下往上是 C106(顶 43.46) → U14(43.85~47.75) → U8(48.08~53.92) → Rs101(54.27~57.73),缝一律 0.330.39 mm,一点余量都没有。 唯一的空地在 Rs101 顶到通道北界 y 59 之间的 1.27 mm ⇒ 把 U8 + Rs101 整体北移 **0.70.8 mm**(母版改一处 = 五路一起改)。 ⚠️ 这会连带作废 扇出 + 铺铜 + 五路功率级布线(U*.9 → Rs*01.1 的 Kelvin 线也在母版里)。

C 组的解:给那两三个 GND 扇出孔在 pcb_fanout._CH_FORCE_LOCAL 里钉一个偏 0.1~0.2 mm 的位置。

⚠️ 另外 95 条是拥挤,要拆线重布才能再往前

失败集中在控制块(U21U23,x 75107 / y 43~58)和 J24/J25 排针周边; 按网络族数:$14N 9 / $8N 7 / $12N 7 / EFUSE_PG 6 / $10N 6 / INA_ALERT 5 … 现在这个布线器是一遍过、只加不拆(这是有意的:板上已有 691 条线不能被抹掉), 所以先布的把后布的堵死了 —— 这正是商业布线器用 rip-up & reroute(协商式拥塞)解决的那一类。 ⇒ 下一轮要么加拆线重布,要么等 A/B/C 三组改完再看还剩多少。

顺带记两条

  • POGO2_VUP / POGO2_VDN 离 SW 节点只有 0.54 / 0.64 mm(§10.3 要求「远离」)。 已经有 sw_keepaway() 在报,不当错误、只列出来。
  • 8 个 45° 切不动的拐角(转向 89~90°),都在细缝里,chamfer45 试到 0.05 半径仍撞墙。

这一轮的产出(都在 .claude/ 下,没有一个动过板子)

文件 用途
pcb1_netlist.py 按实测铜算连通性:焊盘 / 过孔 / 走线 / 回读的铺铜当图的节点,--todo 列还欠几条,--json 出任务表。⚠️ 判据不能只测焊盘中心那一格:同网通孔盘在回读的环里是个洞
pcb1_route_sig.py 下板信号布线器。--net / --limit / --push / --clean --push / --verify / --refresh。内含 Kelvin 强制表 FORCE、USB 束 + 偏移、细栅格逃逸、铺铜障碍、sw_keepaway()
pcb1_escape_audit.py 逐焊盘的逃逸可达性:两层泛洪 + via_ok() 打通,走不到 4 mm 外就报。--why <焊盘> 列出挡路的铜。⚠️ 判据环不能取自由区最外圈(那一圈 free() 永远是假的)

✅ 这一轮(2026-09-06 第十九轮):五路功率级精调(§26.13)

# 做了什么 结果
1 修 POLYGON 焊盘的假 hole(dump_pads / pcb_fanout / pcb1_parts_dump 三处) 七个散热盘回到扇出名单
2 盘内阵列改成按真实多边形撒点 + 自适应间距 + 补中心孔 U1~U5 0 → 5 个孔;U6/U7 0 → 4;eFuse 2 → 6
3 摆位 v9:L*01 / U*14 / Rs*01 各转 180°,C*05 与三颗输出电容挪位 四条回路的朝向全部理顺
4 新增 pcb1_route_pwr.py:母版 27 条路径 × 5 路 = 210 段,全部顶层 一路 74.8 mm,SW 13.1 → 9.7 mm
5 新增 pcb_fanout._CH_FORCE_LOCAL:5 个焊盘 × 5 路 = 25 个 FORCE_VIA 五路的扇出终于真的一样
6 重跑全链:place → 扇出三步 → 铺铜 → 布线 → DRC 495 孔 / 691 线 / 间距 0 / 物理 0 / 连接性 594

⭐⭐ 这一轮最值得记住的四条

1. 「有孔 = 通孔盘」是错的判据 —— POLYGON 盘的 hole 是假的。 板上 11 个 POLYGON 盘无一例外满足 hole = 0.600 × min(w,h)(U1 的 2.032×4.320 → 1.2192, U6 的 1.70×1.70 → 1.02,J22 的 1.30×0.60 → 0.36),EasyEDA 渲染出来是实心的。 判据应该是层号:layer == 12 才是多层通孔盘。

2. 五路的扇出结果并不相同 —— 明明摆位一模一样。 扇出规划器的候选角度按板坐标排,而 CH2/CH3 整路转了 180°, 同一个焊盘在 CH1 往东扇、在 CH2 就往西扇。母版里算好的走廊于是在 CH2/CH3 上被堵死, 而报出来的是「CH2 的 F-trunk 撞到一个 GND 过孔」——看上去像布线画错了。 ⇒ 关键焊盘按 CH1 的解钉死(FORCE_VIA),钉死之后照样走全套净距校核。

3. DRC 的「物理错误」那一支平时是空的,容易漏看。 0.28/0.25 的线下发之后:间距错误 0、连接性还降了 125 条 —— 而 物理错误·导线 里躺着 50 条「线宽 11mil,应该 ≥ 11.8mil」 (SWNODE/PWR4A 的 strokeWidthMin 都是 0.30)。加宽到 0.30 清零。

4. 回读走线不能按端点比:EasyEDA 会在 T 形接点上把已有的线劈成两条。 西侧那条 +12V 母线下发之后在板上是两条(支线接在中间)。 按端点比报「210 段里 40 段对不上」,而铜一点没少。 ⇒ verify() 用覆盖判据:每 0.2 mm 取一个点,要求被同网同层同线宽的实测线盖住。

⚠️ Kelvin 的那一条:U*8.9 不许有过孔

U*8.9(INA226 的 IN−)已经写进 pcb1_pour.ROUTED_PADS。 让它走 VO*_P 小岛的话,取样点在岛上而不是分流电阻的焊盘上,中间隔着 Rs*01 那三个扇出孔(并联约 0.5 mΩ)。Rs 只有 5 mΩ、满载才 20 mV, 0.5 mΩ × 4 A = 2 mV = 10% 的读数误差,而 DRC 和连通性检查都看不出来 (IN− 和 VO*_P 本来就是同一个网)。pin 8 是 VBUS,照旧进岛。


✅ 上一轮(2026-09-06 第十八轮):F1 → 12 A / R9 → 5 × 2Ω 2512

两件都是用户拍板的选型改动,走完整链路:改原理图 → 导入变更 → 重推摆位 → 重扇出 → 重铺铜 → DRC。详见设计文档 §26.12。

# 做了什么 结果
1 F1 F1206HC10A0TM → F1206HC12A0TM(C310990) 同是 1206,摆位一格没动;封装对象换了(F1206)但焊盘几乎一样
2 R9 立式水泥电阻 → R9A~R9E 5 × 2Ω 2512 串联 10Ω/10W,高 22 → 0.8 mm
3 pcb1_import.py 加 --bom 原来 _p1_sch_bom.json 在仓库里没有生产者
4 pcb_fanout.py 加 guard_not_pushed_twice() 我推重了一次,888 个过孔(见下)
5 P1-INPUT 文字收拾 66 → 14 条,清掉一整代旧说明
6 结果 342 件 / 444 过孔(重复 0)/ 15 块铜面 / DRC 间距 0 + 连接性 725

⚠️ 先纠一条我上一轮写错的

「F1 封装是 1206 但 BOM 写 10A、1206 做不到 10A」—— 错的。 F1206HC10A0TM 本来就是 1206/10 A,BOM 和封装从来没对不上。 换 12 A 的真实理由是降额:8/10 = 80% 超过保险丝常规的 75% 线,且 I²t 只有 15 A²s。

⭐⭐ 这一轮最值得记住的两条

1. pcb_fanout.py --net all --push 不幂等 —— 我连推了两遍。 板上变成 888 个过孔(每个都有同坐标同网的副本),而短线数没变(444): EasyEDA 把共线重叠的新线并进了旧线。连接性 DRC 一个字不报(副本同网,电气上「没错」)。 唯一判据是回读过孔数。现在 --push 前会硬拦;重推必须 --clean → dump_pads.py → --push 一次跑完。

2. H_FP 里那个 9.5 是照封装名 W9.5 抄的,而 R9 是立式件。 RES-TH_L13.5-W9.5-P5.80-D0.7:本体 13.5 长、脚距只有 5.80 ⇒ 它站着 (同族 3D 模型标 H52.0)。那一片 hmax 只有 12.0, 所以「高度预算全过」在它身上假了整整一轮,是用户看出来的,不是检查看出来的。 ⇒ 往 H_FP 加直插件的判据:脚距和本体长度对不上就是立式, 高度不是封装名里的 W,是那个对不上的方向。

⛔ 新增的两个地雷标记

  • p1_add.py 已过期,底稿 p1-base.json 停在 2026-09-04。再推一次会把 R9A~R9E 之间那 6 段导线静默回退。已加硬闸门(要跑得带 --i-know-it-is-stale)。 ⇒ 改完一页就问一遍:还有哪个脚本会重画这一页。
  • 改过原理图必须 pcb1_import.py --bom 再 --verify,否则是拿新 PCB 和旧原理图对账。

截图:PCB1-v8-r9f1.png(R9A~R9E 的 1+2+2 蛇形)/ P1-INPUT-v2.png(原理图)。 还没等到用户回话。


✅ 这一轮(2026-09-06 第十七轮):8 A 顶层宽铜

# 做了什么 结果
1 pcb1_pour.REGIONS 加三块顶层铜面 VINRAW-TOP 46 / VINFUSED-TOP 19 / VINSW-TOP 284 mm²(实测)
2 网名用 pcb1_rules.resolve_auto() 当场反查 $1N223 / $1N227 / $1N236,一个字没写死
3 Grid 分内层 / 外层两套障碍模型 外层连贴片焊盘一起挡;新增 obstacles() 把 438 个过孔 + 441 条走线算进去
4 新校核 check_top_clearance()(不过格子) 当场抓出两处侵入,其中一处栅格看不见
5 新校核 poly_vs_rects() 手写的 24 个点 vs 它该等于的 8 个矩形,0.05 mm 逐格对照
6 下发 + 回读实测铜 15 块铜面全部铺出来,三段各 1 个连通域
7 DRC 间距 0 / 连接性 719(铺顶层铜前是 733)

⭐⭐ 这一轮最值得记住的:线的端点不是铜的端点

北柱东缘写在 x 6.8,Rs6.2 那条 +12V 出线的端点在 x 7.401 —— 看起来隔 0.6 mm。 但那条线 1.0 mm 宽、圆头端帽往外再探 0.5 mm,实际铜到 x 6.901, 真实净距只有 0.101 mm。而 0.25 mm 的栅格上这 0.1 mm 一个格心都落不进去 ⇒ 栅格全绿。真下发下去 EDA 自己会往回缩一点点铜,板子不会报 DRC, 但板上那块铜就不再是计划里那块铜,后面所有断面数字全部作废。

⇒ 凡是手画的铜,都要过一遍 check_top_clearance() 这种真实几何的校核, 栅格只能用来算连通域和断面。

顶层三段长什么样(实测断面,顶层 1 oz)

最窄两处:干道 x=12 处 4.75 mm(10.0 A)、北柱 y=92 处 5.00 mm(10.4 A), 8 A 下 ΔT 分别约 12 / 11 °C。其余各点 8.75~21.5 mm。

截图:PCB1-v7-top8A.png(只开顶层 + 焊盘 + 顶丝印)。还没等到用户回话。

⚠️ 顺带看出来的一件:F1 的封装是 1206,但 BOM 写的是 10 A

FUSE-SMD_L3.2-W1.6-1 = 1206,而 §7 的 BOM 写「保险丝 10A 慢断」。 1206 的贴片保险丝一般做到 5 A 左右就到顶了。8 A 的铜宽本身没问题, 是器件选型和封装对不上。换大封装会动 J1→F1 那一段的摆位 ⇒ 要重推摆位 + 重扇出 + 重铺铜。这一条留给用户拍板。


✅ 这一轮(2026-09-06 第十六轮)做完的

# 做了什么 结果
1 修去耦电容摆位:新增 pcb1_place.DECAP 表 + place_decaps() 7 颗跟着本尊走,最远的储能 3.31 mm,其余 0.76~2.24 mm
2 修 pcb1_parts_dump.py 漏了焊盘自身旋转 8 个元件的 bb 各长 0.260.30 mm(U1U5 / U6 / U7 / U20)
3 新增 pad_clearance_errs():焊盘到焊盘净距 0.2 mm 全板 1171 个焊盘一条不报
4 修 pcb_fanout.TARGET(还停在 §26.8 之前) 删掉 VOn / +5V / 8 A 三段,它们不上 Inner2
5 修 _pour_filter()(指着单板时代的 pcb_pour.py) 改指 pcb1_pour.REGIONS;实测滤掉 17 个铜面外的 +3V3 焊盘
6 扇出规划器补五条(见 §26.9) GND/+12V/+3V3/VOnP 全部失败 0 个
7 扇出下发 438 过孔 + 441 短线,DRC 间距 0
8 实现 pcb1_pour.py --push / --verify 12 块铜面已下发;回读实测铜 24 项全 OK
9 下发前 / 下发后共用同一段 grid_checks() 分两套写只能证明两套代码各自自洽
10 新校核:每块碎片都要有本网过孔落脚(浮铜) 9 组 (网络,层) 的 12 块碎片全部有落脚

⭐⭐ 第 1 条是这一轮最重要的发现:ESP32 的储能电容在 48 mm 外

追「U6.15 为什么扇不出去」追出来的。全板 20 个 IC 的 +3V3 电源脚里 只有 1 个的去耦在 3 mm 以内;C601(22 µF「ESP32 储能」)离 U20.2 48.2 mm, C602(100 nF)45 mm,U20 电源脚周围 12 mm 内一个元件都没有。

根因:控制块是 affine(['P3-CONTROL','P3B-EXPAND'], ...) 按原理图坐标仿射映射摆的, 它保「原理图上挨着的板上也挨着」,保不住「去耦贴着电源脚」; 而 U20 是上面手工单独摆的 —— 芯片被拎出了仿射集合,去耦却留在原地。

⇒ 动控制块布局时先看 DECAP 表,它是这件事的事实来源,check() 里有硬校核。 表的顺序 = 抢位置的顺序,小的高频那颗必须排在大的储能之前(理由见 §26.9)。

⭐⭐ 第 2/3 条:本地检查曾经「全绿而板子是错的」

改完摆位本地六项全过,DRC 却报 4 条间距错误:C607_2 到 U6_13 只有 0.096 mm。 两个原因叠在一起 —— pcb1-parts.json 的包围盒漏了焊盘自身的旋转(QFN 侧边脚是长条焊盘转 90° 用的, U6 的 bb 在 y 上短 0.26 mm);而 check() 只查「包围盒重不重叠」,不重叠不等于够净距。 现在两条都修了,新检查直接照 DRC 那条 0.2 mm 的规则算,量焊盘不量包围盒。

⚠️ 同一套 API 里 getAllPinsByPrimitiveId 的 rotation 是度, pcb_PrimitivePad.getAll() 的是弧度。

下板 PCB1 接下来的顺序

  1. 让用户人眼确认摆位 v9 + 功率级布线(截图已出,见上面「一句话现状」)。 不算硬阻塞,但这一轮动过五路的摆位,值得看一眼。

  2. 设计规则 ✅ §26.7 2. 定铺铜几何 ✅ §26.8

  3. 扇出 ✅ §26.9 / §26.13 4. 铺铜 ✅ §26.10,已下发并回读复核

  4. 8 A 输入链顶层宽铜 ✅ §26.11,已下发并回读复核

  5. 五路功率级精调 ✅ §26.13,已下发并回读复核

  6. 写下板信号布线器 ✅ pcb1_route_sig.py 已建好并跑通 75%(第二十轮), pcb1_route_pwr.py 那 210 段一根没动(这个布线器只加不拆,删除只按坐标删自己画过的)。

  7. A/B/C 三组几何整改 ✅ §26.15,用户拍板 A+B+C,已全链重跑并下发。 全链的正确顺序(动过摆位就照这个走,一步都不能省): pcb1_place --push → pcb1_clear --push → dump_pads → pcb_fanout --net all --push → pcb1_pour --refresh --push → pcb1_route_pwr --push → pcb1_route_sig --push → pcb1_pour --refresh --push(给新过孔补反焊盘)→ pcb1_reload → drc_read。

  8. ⬅️ 下一步:给 pcb1_route_sig.py 加拆线重布(rip-up & reroute),收掉剩下的 91 条。 ⚠️ 直接再跑一遍 pcb1_route_sig.py --refresh --push 也会有进展(它读板上的铜、 只补还缺的),但一遍过的策略已经到顶了 —— 真要清零得能拆别人的线。

    ⚠️ 北左角的顶层现在被 8 A 宽铜占了大半(x 1.528.9 / y 68.693)。 U13 的 I2C、U29、R9 那几条信号得走底层—— 底层整层基本是空的,不占地方。 三条还没布的细线已经有名有姓地写在 pcb1_pour.ROUTED_PADS 里: U13.10(Kelvin VIN+,接北柱顶端那一小截死头)/ DZ1.1 / C1.1(栅极)。

⚠️ 动了铺铜或摆位就要重跑 pcb1_pour.py --push,它自己会先把计划跑绿再下发; 下发完自动 --verify 回读实测铜。只想看不想改板就 --verify(加 --cache 用上次的回读)。

⚠️ 动过摆位就要重跑扇出三步,不能省也不能并: pcb_fanout.py --clean → dump_pads.py → pcb_fanout.py --net all --push。

⚠️ 连接性数字扇出之后会先涨再跌:布线前基线 1145 → 扇出完 1336 → 铺完铜 733。 过孔本身是新对象,铺铜之前每一个都是自己网络里的孤岛。 只看扇出完那一步会以为扇出把事情弄坏了。

⚠️ EasyEDA 客户端这一轮掉线过三次(EDA window ... disconnected)。 掉线时命令可能已经执行完只是回话丢了 —— 别直接重试,先回读板上对象数再决定。

⚠️⚠️ 一条永远别忘的:J24 / J25 的针号是故意错一位的(§26.5,用户已拍板选 A)

p5_btb.py 的 MATE_SWAP = True 让下板这一侧的网络按针号奇偶对调 (两颗封装焊盘编号同手性 + 母座在载板底层、顶视图镜像一次,不改的话下板 VO1_P 会顶在载板 GND 上)。c1_conn.py(载板 C1-CONN)用的是原始表,一个字没动。 ⇒ 判据是网络不是针号:下板 pin(2r−1) 正对载板 pin(2r),差一位是预期的。 每次动完摆位跑 pcb1_place.py --verify 逐个焊盘核。

Inner2 分块的结论(详见设计文档 §26.8)

层 网络 名称 形状
Inner2 +12V 12V-N 北带 y 88~124.5 通板 + 风扇指下伸到 y 66.5,挖掉 U13 那一角
Inner2 +12V 12V-SW+BR 南西块 (0.572, 934) + 中桥 (61.572, 3488),⌐ 形
Inner2 +12V 12V-SE+RC 南东块 (116159.5, 934) + 右通道 (153.5159.5, 3488),L 形
Inner2 +3V3 3V3-SW / 3V3-SE 南部两块,故意不连通(见下)
Inner2 GND GND-FILL-L3 剩余全部
Bottom VOn_P 五块小岛 各约 18 × 13 mm
Inner1 GND GND-L2 整板,只挖 ESP32 天线区

退回走线的四类(都算过,不是省事):+5V 底层 1.2 mm 干线 / VOn 顶层 1.5~2.0 mm / VOn_P 底层小岛 / 8 A 输入链顶层宽铜 ≥3.5 mm。 关键数:内层 0.5 oz 与外层 1 oz 差约 4 倍(8 A 内层要 18.2 mm,外层只要 3.49 mm), 而这块板底层整层是空的 ⇒ 短距离大电流根本不该上内层。

⚠️ +3V3 在 Inner2 上是 2 个连通域,是设计意图不是漏接。 中桥必须纵向穿过 y 41~58.5 那条带,而 +3V3 的南部焊盘横向铺满同一条带 —— 同一层上两个都要连通的 2-D 区域,交叉拓扑上避不开。让 0.5 A 的那个换层:南西 / 南东各自从北边引一条底层干线。

⭐ 这一轮校核当场抓到的一个错:孤岛判定是按铺铜对象算的,不是按网络

第一版把 +12V 写成六个矩形,栅格连通性说「1 个连通域」,看着完全正常。 新加的孤岛规则校核报出来:12V-RC(右通道)里一个 +12V 焊盘都没有 —— 哪怕它和北带紧挨着、铺出来是连续铜,它自己那一块里没有本网焊盘/过孔照样被整块删光 (边框还在、nPoured 是 0)。⇒ 并成三个 L / ⌐ 形多边形,每块各自含风扇座的通孔针。

另外两条:只有通孔焊盘在内层挖反焊盘(贴片对内层透明,板上实测 122 个通孔); 区域间净距不能用包围盒估(L 形的 3V3-SE 包围盒把 12V-FAN 整个罩住, 算出来 0.00 mm 像严重违规,实际相距 0.50 mm)。

下板还欠的几件(都不影响现在动手)

  • C2 / C3 罐高必须 ≤ 11.5 mm(现在按 12.0 建模,H_FP 里那一条)。 载板底面在那块地方是光板,hmax = 12.0,卡着上限过;换一颗矮罐才有余量。§22.3f
  • CH2 的 POGO2_* 三根要横穿 60 mm(信号在 J25、电源在 J24,载板那边决定的,§26.3)。
  • U20 的天线端离下板边只有 1.5 mm,而且上面还盖着载板 —— 这块板的 WiFi 本来就指望不上, 真要用就得把天线引出机箱。
  • 北中块 x 58118 / y 85125 有一块约 60 × 40 的空地(CH2/CH3 的排线车道从这里过), 留给电源面和风扇线,暂时不填。

下板 v6 的布局长什么样(设计文档 §26.3 / §26.4)

 y 125 ┌──────────────────────────────────────────────────────────────┐
       │ P1-INPUT      │  CH2 (北,rot180) │ P1B + P6 + J28 │ CH3 (北,rot180) │
       │ J1/F1/Q1/D1   │  x 30.1~57.3     │ x 58~118       │ x 118.5~145.7   │
       │ C2/C3/R9/U19  │                  │                │                 │
  y 66 ├───────────────┴──────────────────┴────────────────┴─────────────────┤
  62.5 │  ==== J24 (x 5.3~58.7) ====        ==== J25 (x 101.3~154.7) ====     │
  y 59 ├───────────────┬──────────────┬─────────────────┬────────────────────┤
       │ CH1 (南)      │  CH4 (南)    │ P3/P3B 控制块   │  CH5 (南)          │
       │ x 10.6~37.8   │  x 42.3~69.5 │ U20/U6/U21~23   │  x 121.8~149.0     │
   y 0 └───────────────┴──────────────┴─────────────────┴────────────────────┘
   J26/J27 左板边   J29/J30 右板边   J28 顶板边   J22 调试口在右板边

下板新加的工具(.claude/ 下)

文件 用途
pcb1_import.py 下板「从原理图导入变更」:--probe 读全部变更行 / --apply 真的导 / --verify 拿 PCB 元件表和原理图 BOM 逐条对账
pcb1_parts_dump.py 抓 pcb1-parts.json(每件的真实焊盘框 + 每个焊盘的本地坐标)。导入变更之后必须跑
pcb1_env.py 把载板底面折算成下板的高度预算图 pcb1-env.json(zones / lanes / slots / J24-J25 锚点);hmap() 打一张 ASCII 高度地图
pcb1_place.py 下板摆位模型 + 本地六项检查 + --push / --verify(回读焊盘核对插)
pcb1_clear.py 拆掉下板全部铜(导线 / 过孔 / 铺铜边框),保留板框、M3 焊盘、丝印
pcb1_rules.py 下板设计规则:线宽 5 档 / 差分 1 对 / 铺铜全连接;自动命名网按位号+引脚反查。--push / --check
pcb1_place.DECAP 去耦电容跟着本尊走的事实来源(第十六轮加)。表里的电容不参与仿射摆位,芯片摆完之后自动贴到电源脚旁边;check() 里有硬校核
pcb1_place.pad_clearance_errs() 全板焊盘到焊盘净距(0.2 mm,照 DRC 那条规则)。「不重叠」不等于「够净距」
FANOUT_WHY=U6.15 环境变量。打印这个焊盘所有候选孔位卡在哪一道校核上,按次数排序
p1_r9f1.py P1-INPUT 换件(F1 → 12 A / R9 → R9A~R9E)。增量改法:先 create 后 delete、旗删掉重建、坐标全 5 的倍数、引脚坐标断言不过就停手
p1_text_fix.py P1-INPUT 文字去重 + 挪位。幂等(新增块也走「先删再建」),逐条对账最终 14 条
pcb1_import.py --bom 逐页重抓原理图 BOM(_p1_sch_bom.json)。改过原理图先跑它再 --verify,一页一个请求(eda.py 截 20000 字符)
pcb_fanout.guard_not_pushed_twice() 板上已有过孔就拒绝 --push。这脚本只加不删不幂等,推两遍会叠出同坐标副本而短线数和 DRC 都看不出来
pcb1_pour.check_top_clearance() 顶层手画铜面到异网铜的真实几何净距(不过格子)。0.25 mm 栅格漏得掉 0.1 mm 的侵入;走线的圆头端帽比端点还往外探半个线宽
pcb1_pour.poly_vs_rects() 手写的复杂多边形 vs 它该等于的矩形并集,0.05 mm 逐格对照。写错一个坐标,别的检查一个都发现不了
pcb1_pour.obstacles() 板上已有的铜(438 过孔 + 441 走线)。顶层铺铜要躲它们,内层只躲过孔
pcb1_route_pwr.py 五路功率级手工布线的事实来源(§10.2 四条回路)。路径写在 CH1 母版局部坐标里,仿射到五路。不带参数 = 计划 + 五项校核;--push 下发(--clean --push 先删本轮旧线);--verify 按实测铜核覆盖;--ch N 只看一路
pcb1_netlist.py 下板连通性的事实来源:按实测铜(含回读的铺铜)算每个网络还欠几条。--todo / --json / --refresh(第二十轮加)
pcb1_route_sig.py 下板信号布线器(只加不拆)。Kelvin 强制表 / USB 束+偏移 / 细栅格逃逸 / 铺铜当障碍 / sw_keepaway()(第二十轮加)
pcb1_escape_audit.py 逐焊盘的逃逸可达性。「布不通」到底是这一头出不来还是中间挤,只有它分得清(第二十轮加)
pcb1_reload.py 下板专用的客户端重载(DRC 的连接性那一支要 reload 之后才准)。⚠️ 别拿 pcb2_reload.py 当通用工具:那一份写死载板 uuid,而且会把板上全部铺铜边框删掉
pcb_fanout._CH_FORCE_LOCAL 五路通道里必须五路一样的那几个孔(按 CH1 的解钉死再展开)。不钉的话 CH2/CH3 会往反方向扇
pcb1_pour.py 下板铺铜的事实来源:REGIONS 定义每一块。不带参数 = 计划 + 五类校核 + ASCII 分块图;--push = 校核跑绿才下发,完事自动回读复核;--verify(--cache 用上次回读)= 只按实测铜再跑一遍。下发前后走的是同一段 grid_checks()

| pcb2_measure_sheet.py | 小米8 主板 1:1 测量底板(shots/PCB2-measure-sheet.pdf):干净底板 + 四个实心黑圆基准点(圆心矩形 160 × 110 mm)。描轮廓 + 每孔戳一点,拍照回来按基准点做单应反算,约 ±0.3 mm | | pcb2_pdf.py | 载板 1:1 打印用尺寸图 PDF(shots/PCB2-carrier-1to1.pdf)。几何全从客户端实读(板框 / 槽 / 定位孔 / 主板投影),pcb2_place.py 的标称值只用来交叉校验。三页:整板 A4 横向、单站详图两种朝向、坐标表。每页带 100 mm 标尺防打印缩放;--offline 用上次的 dump |

⚠️ pcb_rules.py 是单板时代的,已被 pcb1_rules.py 取代,别再 --push 它。 ⚠️ pcb_fanout.py 仍然是下板在用的(PCB = 7e94…),载板的 pcb2_fanout.py 是它的壳。

⚠️ 载板上只有 6 个 M3 孔,pcb2_place.py 的 MH 表里有 8 个(2026-09-06 出 1:1 图时实读发现)。 缺的是顶边两角 (4, 131) / (216, 131) —— 就是「两板尺寸取大对齐、四角定位孔对齐」那条要求加的那两个, 表改了但 pcb2_place.py --outline 没再跑(M3 孔归 push_outline() 管,不是 --mech)。 要补就跑 --outline(先删 MH* 再重建 8 个,顺带重画板框和 10 条槽), 跑完记得重新 pcb2_clear / DRC:那两个孔的 Ø6 焊盘离主板投影只有 0.34 mm。

⚠️ pcb_place.py / pcb-parts.json / pcb-pads.json 是单板时代的,别再拿来摆下板。

✅ 已拍板并落地:C745 往南挪 2.7 mm(2026-09-06,用户选 A)

C745 从 A 圈上的 (100.05, 61.70) 挪到 (100.0, 59.02)(pcb2_place.hub_layout() 里单列一条, 不再跟着 A 圈的 for 循环走)。GND 脚 (100.0, 58.60) / +3V3 脚 (100.0, 59.44), 扇出孔实际打在正东 (100.87, 58.60),引线 0.65。 pcb2_place --push → pcb2_clear(跨元件净距全过)→ pcb2_fanout --only C745.2 --push → reload → pour → inner1_verify(五块岛仍各 1 连通域)→ DRC 115 间距(基线)+ 332 连接性。

⇒ 载板上除了 J7n 那 65 个 GND 脚(归 bm24_pre() 管),贴片 GND 焊盘已经全部扇出。

pcb_fanout.py 顺手加了 --only 位号[.引脚],...:板上已经有整轮扇出、只想补一两个漏网的时候用, 不会重复下发另外那 247 个。

当初为什么非挪不可(留作记录)
方向 挡住的东西 余量
南 UP_DM 东行段 y=60.60(宽 0.28) 孔要 0.64,只有 0.41
北 自己的 pin1 → 再往北是 U32 南排焊盘 y=63.55 缝 0.73,要 1.0
东 R715 pin2 缝 0.667,要 1.0
西 UP_DM 竖段 x=99.25 / UP_DP 竖段 x=98.75(相距 0.5) 塞不下 Ø0.6 的孔

并到邻近同网焊盘也不行 —— 最近的 GND 是 C743 pin2(2.70 mm 外),中间横着这两条竖段。

(已作废)等用户拍板:C745 的 GND 脚被 UP 差分对堵死

C745(Hub 的 +3V3_HUB 去耦 0402,GND 脚在 (100.05, 61.28))是全板唯一扇不出去的焊盘。 不是搜索不够狠,是几何上真的没有位置:

方向 挡住的东西 余量
南 UP_DM 东行段 y=60.60(宽 0.28) 孔要 0.64,只有 0.68−0.27=0.41
北 自己的 pin1(+3V3_HUB)→ 再往北是 U32 南排焊盘 y=63.55 缝 0.73,要 1.0
东 R715 pin2 缝 0.667,要 1.0
西 UP_DM 竖段 x=99.25 / UP_DP 竖段 x=98.75(相距 0.5) 两条之间塞不下 Ø0.6 的孔;西边那条缝只有 0.01 mm 宽

并到邻近同网焊盘也不行 —— 最近的 GND 是 C743 pin2(2.70 mm 外),中间横着这两条竖段。

三个选项(都要改布局,所以留给用户定):

  • A. C745 转 90° 并东移到 (100.0, 61.7) 一带(脚变成东西向,GND 脚在西)。 实算过:孔打在 (100.0, 62.47) 时离 UP_DM 竖段 0.75、离 U32 pin56 焊盘框 0.15,都过。
  • B. 把 UP_DM / UP_DP 那两条东行段整体往南挪 0.3~0.5 mm(要重推 pcb2_dp_long.py, 下游 J23 那一头也要跟着动)。
  • C. 不管它 —— 那一颗去耦电容的地就只能靠 pcb2_gnd_patch.py 想办法, 想不出来就是一条连接性 DRC,实物上等于这颗电容白焊。不推荐。

✅ 这一轮(2026-09-06 第十三轮):载板 77 个信号网络全部布通并下发,DRC 回到基线

结果
pcb2_route_sig.py 87 个网络 / 459 条路径 / 1030 线 / 210 孔,一个没布通的都没有
本地净距校核 全部通过 ✔
pcb2_gnd_patch --check 板上悬空的 GND 连通域 0 个
inner1_verify 五块岛各 1 主域、3 焊盘 + 11 过孔全在主域,最窄 29.20 / 7.05 / 7.20 / 7.70 / 7.60
pcb2_route_dp(回归) 11 对差分偏斜全部 ✔(最大 4.75 mm)
DRC 115 条 = 全部是封装自带的「贴片焊盘到贴片焊盘」基线;间距 0 新增、连接性 0

⇒ 载板 PCB2 布线完成。 截图 PCB2-v6-routed-all.png / PCB2-v6-hub.png / PCB2-v6-site4.png。

BM24 那一段是整个重写的(v4 的常量在 v5 上全错)

v5 把 J7n 从 rot 180 改成 rot 0 又挪了位置,站内坐标整体差 u' = u − 5.00 / v' = 32.00 − v(v 轴是镜像的)。 照抄映射也不行 —— v 一镜像,「前排外侧那条缝」换到了另一边,而那边站着 R76n/R77n。 v5 的走法(和 v4 反过来,因为后排 25 个脚在 v5 里全是 GND,没有信号脚要往外挤):

  • 前排 5 个 GND(pin 2/8/12/16/24)→ 往内进中央通道,v=35.00 的 0.30 梳子,4 个孔(跳过 u 50.05)
  • 后排 7 个 GND(pin 1/19/23/29/37/41/47)→ 往外,v=33.10 的梳子,5 个孔(那一带一路空到 v 26)
  • pin52 往东平出到 u=59.60 打一个孔
  • THERM / CC1 独占前排外侧:CC1 一根直线上去(R76n 中心特意取 52.233,让 pin1 正好落在 J7n.22 正北); THERM 打两个孔走顶层跨过 CC1

新增 j7_assert():每次跑都拿板上实际焊盘核一遍那一堆手写常量(五个站 × 13 个 GND 脚 + THERM/CC1 + R76n/R77n)。 没有它的时候,坐标失配的表现是「在空地上画了五套梳子」—— 净距校核当然全过(空地不撞人), 只有 DRC 的连接性会含糊地多出几十条。

这一轮修掉的四个真 bug(都在 pcb2_route_sig.py 里)

  1. 搜索窗口边上没有墙。障碍是裁到窗口里再栅格化的,完全落在窗外的铜一格都不标, 于是贴着窗口边走的线看不见它。实测 SCL 的窗口从 x=44.02 起,J31.6 的焊盘框(42.10~43.80) 整个在窗外,布线器就在 (44.10, 67.40) 打了个孔,离那焊盘只有 0.134。 改法:数组往外扩 RIM=1.2 标障碍,再把外面这一圈整体标成占用 —— 可走区域不变,但看得见窗外的铜。
  2. 钻孔间距不分网络,但 viafree 只看异网铜。于是布线器紧挨着 pcb2_dp_long 已经打好的孔又打一个 ((92.500,70.000) 与 (92.400,70.500) 中心只隔 0.510),DRC 报「钻孔到钻孔 8.1mil,应 ≥ 11.8mil」, 净距校核一声不吭。新增 holefree 距离场(所有过孔、不分网络)。
  3. 手写前置段没有一开始就进障碍表。原来只有 GND 的在循环里补进去,THERM/CC1/+3V3 的要等轮到自己 —— 排在前面的网络照样从这些手写坐标上碾过去。
  4. +3V3_HUB 上 pcb2_dp_long 画的三条会被静默删掉。push_sig() 是「本脚本负责的网络整网删掉重画」, 而那三条被 D± 夹着、后面再也挤不进去。改成从同一个事实来源(pcb2_dp_long.build)取回来当前置段, 既让最小生成树知道那些焊盘已经连上,下发时也原样重画。

四段手写坐标(栅格布线器进不去的地方,理由都写在代码注释里)

网络 为什么手写
THERM_n 出阵列后第一次拐弯的 v 只有 0.053 mm 的窗口(下受前排焊盘、上受 *_DPJ 换层孔),而且那一段必须保持 0.11
+3V3_HUB U32.52→U32.46 pin46 在东排被 VBUS_DET/NONREM0 围死,唯一出路是往 QFN 肚子里走(南排焊盘上沿与散热盘之间那条 1.6 mm 死带)
+3V3_HUB C750.1→R716.2 R716.2 是最东那颗 +3V3 焊盘,南北被两个 GND 脚夹住,只能翻顶层斜拉过来
HUB_RST / VBUS_DET R709 与 R712 之间只有 0.66 mm 的缝。0.15 的线走得过去(上下各 0.23),但栅格判据要 0.371 而半缝只有 0.33

一处已知的口子(不是错,是要记住的事)

+3V3_HUB 现在由 pcb2_route_sig.py 下发(含 pcb2_dp_long 那三条)。 ⚠️ 再单独跑 pcb2_dp_long.py --push 会把 route_sig 布的整个 +3V3_HUB 删掉重画(它按网络整删)。 要改差分的话,顺序是 dp_long --push → route_sig --push,不能只跑前一半。 另外 45° 化把那条顶层干线的近直角拐点切成了两个 51° 的转折(sharp_corners 报「切不动」)—— 净距校核过、不是直角,属于可接受;但它意味着板上那条干线的几何已经和 pcb2_dp_long.py 里写的不完全一样。

为此动的两处布局(都验过并重扇出)

  • R76n/R77n:v 37.15 → 38.0,R76n 的 u 顺带改成 52.233。 理由:J7n 前排逃逸带原来只剩 0.58 mm,THERM 和 CC1 一个往东一个往北必然交叉,同层无解, 想让 THERM 翻顶层又打不下孔(要离前排 0.5 ⇒ v ≥ 36.80,又要离 R76n 0.5 ⇒ v ≤ 36.38)。 挪完逃逸带 1.43 mm,两条各走各的。挪之前先用新写的 pcb2_del_box.py 把 R77n.2 的旧扇出孔删掉。
  • C745 往南 2.7 mm(用户拍板,见下)。

新工具

pcb2_del_box.py:删掉指定框里某个网络的线与过孔(板坐标或站内坐标,站内会自动展开到五个位)。 板上的铜是好几个脚本各画各的,挪了元件之后那颗扇出孔就成了没人认领的孤儿 —— 谁的删除逻辑都不管,DRC 只报一条「过孔没连上任何东西」,很容易被当噪声放过。 判据是完全落在框里才删(半进半出一律不动)。

pcb_fanout.py --only 位号[.引脚],...:板上已经有整轮扇出、只想补一两个漏网的时候用。

上一轮(2026-09-06 第十二轮):GND 扇出 + VOn_P 进岛,两步都下发并复核

# 做了什么 结果
1 pcb_fanout.py 补三个缺口(见下);pcb2_fanout.py --push 247 孔 / 122 短线,只有 C745 pin2 扇不出去
2 pcb2_vo_vias.py 扩到 35 个焊盘(补 C8n1.1 与 J8n.1,各 3 孔)并修出线层 55 孔 / 55 线,净距全过、0 个直角
3 pcb2_reload → pcb2_pour --push → _refresh_geo → inner1_verify 五块岛各 1 连通域,3 焊盘 + 11 过孔全在主域;最窄 31.30 / 7.05 / 7.20 / 7.70 / 7.60(下限 7.0)
4 drc_read --pcb2 450 = 间距 115(与基线一模一样,无新增)+ 连接性 335(布线前 519,−184)
5 截图 PCB2-v5-fanout-all.png / PCB2-v5-fanout-site1.png / PCB2-v5-inner1.png

这一轮修掉的四个「一直在但没暴露」的 bug

  • 扇出规划器从来不看板上已有的走线(只看焊盘 / 过孔 / 钻孔)。下板做扇出时板上一根线都没有, 所以从没暴露;载板 v5 是先布了 178 条差分 + 3V3 才扇出的,不修的话过孔会直接压在 D± 上, 而几何验证抓不到(它只查连通性)。现在 EXIST_LAYERS 把 1/2/15/16 层的线全登记成障碍, 线对线分层比、过孔对线不分层(过孔贯穿)。
  • dump_pads.py 有 88 个焊盘认不到位号:pad 坐标是干净的、pin 坐标带 1 µm 噪声, 两边各自 Math.round 到 0.01,落在 .xx5 上的值进了不同的桶。改成 Python 端最近邻配(容差 0.02), 846 个里认到 805(剩下 41 个是螺钉孔和手机定位柱,本来就不属于任何元件)。 这个 bug 会静默传染:J75 pin15/16 认不到主人,「J7n 的 GND 脚整批跳过」就漏掉两个。
  • 第三遍「并到旁边的同网络焊盘」没有长度上限、也不查走线/钻孔,实测把 C745 pin2 一路并到 9.78 mm 外的 J64 pin3(而且那是另一层的焊盘,画出来根本不通)。 现在 LINK_MAX = 3.0,并且要求目标是通孔焊盘或同层。
  • pcb2_vo_vias.py 的出线层写死 L_TOP。v1 时代器件全在顶层没事;v2 之后 VOn_P 的 35 个焊盘 全在底层,照旧会画出 35 条悬在顶层、一头不搭焊盘的线 —— 而且 fits() 拿这个 lay 去筛障碍, 连净距校核都是照着空荡荡的顶层算的,一条都不会报。

两处和上一轮的计划不一样(都是实算之后改的)

  • U32 pin13 的孔位没有钉在 (94.6, 71.4)。 那个位置过孔放得下(西边走廊 x ∈ [94.59, 94.625]), 但引线出不去:pin13 与 pin14 之间只有 0.20 mm,斜着往西北一定切进 pin14 的净距圈; 平着往西的话过孔又躲不开 USB5_DP 的横段(y 69.75,只差 0.5 < 0.64)。 正解是往东打进 QFN 肚子里 —— 引脚环东沿 95.975 到散热盘西沿 97.600 有 1.625 mm 净宽, 自动搜索把 pin13 放在 (96.50, 70.25)、pin19 放在 (97.25, 71.50),离差分车道都在 2 mm 以上。
  • J7n 的 65 个 GND 脚整批不扇了(SKIP_PAD)。它们是 bm24_pre() 手写死的两条梳子 + pin52 单独出线; 让自动扇出去碰的后果实测是「十个必然失败 + pin 1/2/52 三个『成功』的孔正好落在梳子和车道上」, route_sig 还得写个删除框把它们收回去。

一处对设计文档 §10.3 的有意偏离

§10.3 写的是「功率网络每个焊盘打 3 个过孔」。这一轮 VOn_P 只给 J8n.1(这一路 4 A 的唯一去路)和 C8n1.1(2200 µF 主储能)各 3 个, C8n2C8n6 仍是各 1 个 —— 全按 3 个来会往五块岛里再钻 50 个反焊盘, 最窄断面(下限 7.0)扛不住。这五颗只承担高频纹波,单孔 11.5 A 够用。

上一轮(2026-09-06 第十一轮):v5 差分布线全部落地

详见设计文档 §25.16 补 6。 走法表、顶层参考面、45° 斜段规矩都在那里,这里只记做了什么和踩了什么。

# 做了什么 结果
1 pcb2_dp_long.py 整个重写(v3 存 obsolete/):pair 1 直西、pair 2 正北、pair 3 绕 Hub 北边东行、pair 4 整段顶层、pair 5 绕北边再南下跨 UP、UP 底层直东 本地净距全过
2 SITE_J 按 v5 站内坐标重写:DMJ 底层 45° 斜进、DPJ 翻顶层跳过去(两排之间只有 1.515 mm,孔只能落在 y 36.805~37.31) 五站同构
3 pin5 / pin10 两条 +3V3 车道 + 顶层干线一起画(被 D± 夹住,后面的布线器进不去);C747 → (88.2, 71.5)、C748 → (92.5, 71.2) pcb2_place --push、pcb2_clear 过
4 VO4 岛主体东界 89 → 87(顶层段不许跨岛边);topref 检查 0 处跨界 reload → pour → inner1_verify 五块全 ✅
5 --push:182 线 / 26 孔;DRC 634 = 间距 115(基线)+ 连接性 519 无新增间距错误

⚠️ 这一轮踩到的

  • 0.5 脚距的车道之间打不了孔(孔要离两边车道各 0.645)。出 QFN 的次序只能是「最外侧先拐走,邻居走了才打孔」, pair 4 的两个孔为此上下错 0.9、DP4 先抬 0.6 再打。想改走法先在纸上按这条排一遍。
  • chamfer() 不能对 45° 拐再切一刀:切出来的 22.5° 小段向内挪 0.1,实测把 DP4 抬升段切进 pin10 车道净距圈。已改成转向角 ≤ 46° 不切。
  • 上一版脚本里的 U32 坐标(x 105.55)是 v1 的,U32 早搬到 (100, 68) 了 —— 改布局后所有写死坐标的布线脚本都要重推,别只改一处。
  • 同宽共线的两段下发后被 EDA 合并成一条(182 → 178),但不同宽的共线段(BM24 出线 0.11 / 0.20 / 0.28 三档)保留 —— 换挡点安全。

✅ 这一轮(2026-09-06 第八轮):载板 v4 —— 尾插槽到 J1401 正下方,J7n 到主板下方远处

详见设计文档 §25.16「2026-09-06 补 3」。 用户指令:尾插槽放在主板尾插接口下方;载板尾插座放在主板下方远处、50 mm 排线能到的地方;连接时排线走载板背面。

# 做了什么 结果
1 SLOTS['tail'] → u 4565 / v 1.44.4(J1401 正下方);TAIL_LANE u 44.662.6 / v 4.438.3 不放元件(check() 新增一条) 本地检查过
2 J7n → (53.5, 37.0) rot 0;U5n (49.8, 41.5, rot 180) / R76n (52.8, 39.15) / R77n (55.8, 39.15) 挪到 J7n 北边;J6n (62.5, 41);行 D 西移 4.5 离 H1 4.0 / H3 2.9,pcb2_clear 816 焊盘净距 ≥ 0.20
3 --outline --push --verify --heights 槽 10/10;J71 回读 pin51(VBUS) 西端 (52.47)、pin4/6 北排西端 (54.65/55.0, 121.06)、奇数排南 ✔
4 pcb2_reload → pcb2_pour --push → _refresh_geo → inner1_verify 五块岛各 1 连通域、焊盘全在主域,最窄 7.05 / 7.20 / 7.70 / 7.60 / 20.85(下限 7.0)
5 截图 PCB2-v4-bottom.png / PCB2-v4-ch1.png 自己看过:槽在 J1401 下、J7n 在远端、U5n 在北 —— 还要用户确认

为什么 J7n 从 rot 180 改成 rot 0(不是拍脑袋)

排线只绕 u 轴弯(接口边一个 U 形回折 + 槽里一个台阶),不在面内转 ⇒ J7n 的触点图案是 J1401 图案的平移; J1401 的 pin1/2 端(G1)在 +u ⇒ J7n 的 pin1/2 端要在 −u;封装 pin51 在本地 −X、底层翻层不改 x ⇒ rot 0。 v3b 的 rot 180 把 VBUS 端放到了东边,和 §24.3「VBUS 落到 −X 端」矛盾。翻面次数(回折 1 次、台阶 0 次)算出来公头朝 +z,正好插朝 −z 的 J7n。 剩下唯一没定的是封装奇/偶排分配(§24.3 镜像位),错了只改焊盘编号,D± 换到南排西端。

上一轮(2026-09-06 第七轮):载板 v3 + C8n1 换矮封装(槽位置已被 v4 取代)

详见设计文档 §25.16 末尾「2026-09-06 补」。

# 做了什么 结果
1 槽挪到主板远端边(v 45.5~48.5),手机位内移 2.5,器件全部收进主板下方 本地检查 / 净距 / 镜像回读全过
2 pcb2_pour.py ISLANDS v3:走廊主体 + 主板下方整条带,VO2/VO4 为顶层差分收边 inner1_check / inner1_verify 五块全 ✅(最窄 7.05)
3 C8n1 → Rubycon 6.3TPV2200M10X10.5(C3828985):个人库建器件、c4_site.py 五页重画、PCB 导入 80+80 极性丝印核过(PCB2-v3-c801.png)
4 为差分让位挪 C747/C748、U31 组、U34/C760、R721/722、R717/718 见 pcb2_place.py 注释
5 新工具 pcb2_reload.py(重载客户端解卡)、pcb2_snapshot.py

⚠️ 这一轮踩到的

  • lib_Device.create() 返回 undefined 但器件已建出来;copy() 从系统库复制返回 null 且真没建; 个人库内 copy() 可用;给个人库器件 modify(association.footprint = 系统库封装) 会把封装复制进个人库(新 uuid)。 建完一律 search + get 核对,别看返回值(skill §9y)。
  • 铺铜「重建所有」在大量 API 改动之后必卡(本轮三次),重载后一次就通 ⇒ pcb2_reload.py 然后 --push。
  • pcb2_place.py --push 的 modify() 里 layer 和 x/y/rot 分两次下发(一次下发 rot 偶发不生效)。

上一轮(2026-09-06 第六轮):载板 v2 —— 全部到底层 + 穿线槽(已被 v3 部分取代)

详见设计文档 §25.16。 用户两条指令:正面器件全放背面并在背面重新布局;排线通过板内细长槽穿到载板。 中途补充:器件可以放到主板投影下方(底层),方便布线。

# 做了什么 结果
1 pcb2_snapshot.py:先把 v1 全部铜/元件抓成 _pcb2_snap_topside.json 161 件 / 1586 线 / 416 孔 / 7 铺铜,可回滚
2 实测底层镜像语义(U51/J71/R761 翻到底层读焊盘再翻回来) 本地 y 翻 → 再转 rot;两脚件 pin1 方向不变,SOT23/BM24 南北排对调
3 pcb2_place.py 重写成 v2 顶层 20 / 底层 141;槽 10 条;C8n1 卧倒 keep-out;新增「压槽 / 螺钉头 / 顶层只许顶针」三项检查
4 --clean-copper --outline --mech --push --verify --heights 清 1416 线 + 416 孔 + 7 铺铜(另有 16 条漏网线第二遍才删掉);槽建 10/10;镜像回读全 ✔
5 pcb2_clear.py 跨元件净距全部 ≥ 0.20
6 pcb2-bottom-heights.json 底层 141 件高度图 + 10 条槽,给下板布局避让

⚠️ 这一轮踩到的三条(都写进 skill)

  • 底层元件的焊盘位置不能推:翻层不是绕 y 轴镜像,是本地 y 翻。J23 用顶层那版的 rot 90 放到底层, 口会朝板内;改 rot 270 并把 R717/R718 的 y 对调,--verify 回读核过。
  • 挖槽区域 = pcb_PrimitiveFill.create(12, polygon)(layer 12 = MULTI)。跑道形用两段 90° 弧, 和板框一样逆时针、正角度。
  • 批量 delete() 一遍删不干净:1416 条按 getAll() 删完,回读还剩 16 条(旧 BM24 GND 梳子, 正好压在新槽上)。删除循环要跑到剩余为 0 再收。

上一轮(2026-09-06 第五轮):载板收尾 —— 已被 v2 整体作废,只留作历史

详见设计文档 §25.15。

# 做了什么 结果
1 .claude/pcb2_vo_vias.py:25 个贴片电容各打一个过孔下到 Inner1 的岛 25 线 / 25 孔,全部一段直线,连接性 −31
2 .claude/pcb2_gnd_patch.py:自己算出悬空的 GND 连通域再补孔 5 个域(C749.2/R710.2/U32.19/J23.A1B12/J23.B1A12)→ 6 线 / 5 孔,连接性 −10
3 .claude/pcb2_npth.py:从铺铜反焊盘反解封装自带的非金属化孔 J23 里两个 Ø0.600 @ (213.660, 59.100) / (213.660, 64.900)
4 inner1_verify.py 加了过孔校核 五块岛 5 焊盘 + 5 过孔全在主域,最窄断面 7.20/9.10/10.15/7.35/7.60(下限 7.0)
5 pcb2_gnd_patch.py --check 板上悬空 GND 连通域 0 个
6 DRC 125 = 同封装内 115(与基线一模一样)+ 连接性 10 + 间距 0 + 物理 0

⚠️⚠️ 这一轮最值得记住的:封装自带的孔,API 一个都读不出来

J23 的封装里有两个 Ø0.60 非金属化定位孔,正卡在 A1B12/B1A12 两个 GND 焊盘的外侧角上。 pcb_PrimitivePad.getAll() 里没有、pcb_PrimitiveRegion/pcb_PrimitiveFill 是空的、 getPrimitiveByPrimitiveId('e55') 返回空、getPrimitiveAtPoint() 只回最上面那条线。 只有 DRC(「挖槽区域 到 导线」)和内层铺铜的反焊盘看得见它们。 ⇒ pcb2_npth.py 拟合铺铜环里的圆弧,用「已知过孔的反焊盘半径 − 过孔半径」标定净距 (实测 0.3583,234 个样本,离散度 0.005),再反推孔径。

另外三条

  • 净距校核只看铜,钻孔是另一条规则。 第一版打的孔落在同网 J23_4 的焊盘里, 校核全过,DRC 报「钻孔 到 钻孔 距离 0mil」。同网的通孔焊盘/过孔都要按 DRILL=0.33 再判一遍。
  • 金属化的槽孔焊盘和非金属化孔待遇完全不同:线可以压在焊盘自己的孔上, 不能压在非金属化孔上。混在一起判的话,J23.1/J23.4 这种槽孔焊盘连碰都碰不得 (焊盘上任何一点离自己的槽都不足 0.80 mm)。
  • 只加不删的下发必须记账。 pcb2_gnd_patch 把自己画的坐标存 _pcb2_gnd_patch.json, 下次先删再画。不记账实测多出一对旧坐标的过孔,而且新画的细线被 EasyEDA 并进旧的粗线里。

✅ 这一轮(2026-09-06 第四轮):栅格迷宫布线器,载板剩下的 77 个网络

详见设计文档 §25.14。 事实来源是 .claude/pcb2_route_sig.py(BM24 周围的车道 是手写死的几何,其余交给布线器)。

# 做了什么 结果
1 新写布线器 pcb2_route_sig.py 栅格 0.1/0.05/0.025 三档 + scipy 稀疏图 Dijkstra + 45° 整形;产出过同一份 check()
2 77 个网络 431 条路径 / 991 段导线 / 160 个过孔,净距校核全过
3 BM24 的 65 个 GND 脚(§25.8 的待办) 两条梳子 + 12 个过孔/站,中央通道那 1.64 mm 是这一轮才用上的
4 抓到并修掉 R76n 装反 rot 0 → 180。不改的话五个站的 VBUSn 全布不通
5 +5V 线宽 0.80 → 0.60 §25.10 说了段电流 ≤0.6 A;0.80 在走廊里挤不过去
6 PWR1A 的 strokeWidthMin 0.25 → 0.15 +3V3_HUB 要穿 Hub 卫星圈 0.659 mm 的缝;载流够(420 mA / 0.15 mm 可载 ~0.8 A)
7 布完线重新铺铜 不铺的话 DRC 多出 445 条「铺铜区域到过孔/钻孔」(新过孔没有反焊盘)
8 DRC 166 = 同封装内 115(与 §25.8 基线一模一样)+ 连接性 51 + 间距 0 + 物理 0
9 45° 化(用户拍板) sharp_corners / chamfer45 / smooth_micro 三个工具放在 pcb2_route_dp.py,两个脚本共用。差分对 10 → 0、信号线 72 → 0,DRC 不变

⚠️⚠️ 这一轮最值得记住的一条:expand() 会静默丢掉路径末端的过孔

pcb2_route_dp.expand() 打过孔的条件里有 i + 1 < n - 1 —— 最后一个点被显式跳过。 于是「走到目标正上方再换层落下去」这种路径,过孔没了,两层的铜各画各的、中间断开。

三道校核全都抓不到:净距校核只看撞没撞(断开当然不撞)、 inner1_verify 只看铜面、DRC 的连接性又要 location.reload() 才刷新。

踩到两次:布线器自己的路径断了 5 个网络;手写的 bm24_pre() 断了 50 个 GND 过孔 (DRC 里 GND 55 条,补上之后掉到 10)。 ⇒ _end_via():末点上有换层就补一个重复点,所有路径(含手写的)都要过这一道。

另外三条(都写在 §25.14 里)

  • 障碍要先胀 QERR 再栅格化,否则距离场高估 0.08 mm,净距校核在下发前一刻才炸。
  • location.reload() 会被「离开该页面」确认框挡下来(文档有未保存改动), 点「留在该页面」= 根本没重载,客户端仍卡在后台任务上, 于是 铺铜管理器… 点了没反应。重载前先 save()。
  • 先给所有焊盘留好出线段再开始布线(escape routing 先行), 否则先布的网络会把后布的网络出焊盘那 0.6 mm 占掉。

✅ 这一轮(2026-09-06):载板 Inner1 分割电源面

详见设计文档 §25.11。 事实来源是 .claude/pcb2_pour.py 的 ISLANDS,改铜面改那里再 --push。

# 做了什么 结果
1 五块 VOn_P 岛 + GND 底填落到 Inner1 优先级 L15 VO1<VO2<VO3<VO4<VO5<GND(岛先占地盘)
2 每条边界都按具体那颗排母针的反焊盘卡出来 9 条边界逐条列在 §25.11 的表里;最窄的颈是 VO3 的 7.40 mm(被 J32.27/J32.35 夹住)
3 新脚本 inner1_check.py(下发前预测) 按 ISLANDS 多边形 + 异网焊盘/过孔反焊盘栅格化,五块全过
4 新脚本 inner1_verify.py(下发后实测) 读回 EDA 真吐出来的铜再栅格化:五块各 1 个连通域、5/5 本网通孔焊盘在主域,最窄断面 7.20/7.60/7.70/11.70/12.90 mm
5 新脚本 inner1_diffref.py(§10.1) 顶层 5 段 *J 差分下方都只有 GND-INNER1 一块铜、覆盖 100% —— 这条约束从「可以接受」升级成「已实测满足」
6 查出铺铜默认是十字花连接 铜面把自己的焊盘挖空只留 4 条 0.254 mm 辐条 ⇒ 校核量到「0.3 mm」。给 VOn_P 挂 PLANE-DIRECT(connectMode:1)后铜面是一整块。见下
7 pcb2_rules.py 收编该规则 新增 PLANE_RULE / PLANE_NETS,--push 建条目 + 指派,--check 十三项全过
8 新脚本 shot_pcb_layers.py 只显示指定铜层截图(可带区域),截完自动恢复图层可见性
9 DRC 499 = 同封装内间距 115(与基线一模一样)+ 连接性 384 + 间距错误 0。连接性从 396 掉到 384,正是五块岛接上的那 12 处

截图 PCB2-inner1.png(整板)/ PCB2-inner1-left.png / PCB2-inner1-right.png(走廊两半, 能看清五块岛的轮廓和两条颈从排母针缝里穿下去/上来)。

⚠️ 这一轮最值得记住的一条:铺铜的「段数」不能当连通性判据

getState_PourFills() 把一块铜面吐成十几个 fill,那多半是切块(相邻块共边)不是断开。 「17 段」既可能是一整块被切碎、也可能真碎成 17 座孤岛,段数分辨不出来。 唯一靠得住的是把所有环画进同一张栅格再做连通域 —— 这就是 inner1_verify.py。 而且十字花连接时那十几个 fill 里有一部分是 fill:false 的描边辐条(不是填充区), 只画填充区会得到假的断路。两个坑叠在一起,skill §9j-2 有完整实测表。

⚠️ 另一条:优先级的步长是 2,不是 1

同层建 6 块铜面,回读的优先级是 1,3,5,7,9,11(同层只有一块时是 1)。 所以脚本里只能断言相对次序,不能断言「第 k 块 == k」—— pcb2_pour.py 第一版就是后者,白报了一次「优先级和计划对不上」。已改成断言次序。

✅ 这一轮(2026-09-05 夜):换件实施全部落地

详见设计文档 §22.3f 末尾的实施清单 + 新的 §25.9。 上一轮那三条硬约束消掉了两条。

# 做了什么 结果
1 实测两颗新符号的引脚几何 公母完全一样:第 r 行 = pin 2r−1(左, cx−30)/ 2r(右, cx+30),第 1 行 y = cy+95
2 p5_btb.py 换件 器件换 C55020601;引脚公式 r/41−r → 2r−1/2r;ROW0 100 → 95;补上「坐标必须是 5 的倍数」断言
3 c1_conn.py 换件 器件换 C5307344;引脚公式和 ROW0(95) 都不用动
4 P5-BTB / C1-CONN 重生成 verify_sch 两页 PASS(C1-CONN 要带 --nc c1-nets-nc.json,U34.3 是有意悬空)· xpage 0 漏接 · port_audit 下板 513 / 载板 330 可疑 0
5 §22.2 逐针表重写 两块板共用一张表(VO1_P 两边都是 1/3/5 脚),旧的「两套编号」警告整节作废
6 载板 PCB 从原理图导入变更 只有 14 条:C1-CONN 那 7 个元件各一条「移除+增加」(整页重画 ⇒ UUID 全新)。「包含设计规则」没勾,11 条规则原样保留
7 五个 Ø10 电解整体上移 2.5 mm BAND['c1'] 的 v:−11.0 → −8.0。见下,这是换件逼出来的
8 载板重摆 / 重扇 / 重铺 161 元件本地四项过 · pcb2_clear.py 816 焊盘过 · GND 扇出 210 孔(和换件前一样)· Inner2 重铺 213 段
9 DRC 511 = 同封装内间距 115(和基线一模一样)+ 连接性 396(没布线)+ 其它 0

截图 PCB2-carrier-v4.png / P5-BTB.png / C1-CONN.png。

⚠️ 换成通孔件逼出来的连锁改动:五个大电解必须上移

直插排母 = 顶层也占位,而 J31/J32 的焊盘区高 4.24 mm。 原来的走廊里,上排三站的罐底 y=68.25、下排两站的罐顶 y=66.95,中间只剩 1.30 mm —— 怎么摆都塞不进去(贴片排母时代它只在底层,所以从来没露出来过)。

罐体在服务带里 u 5.75~18.25 这一段是独占的(行 1/2/3 的件都从 u=21 起), 所以 v 可以一直收到「不进手机投影」为止(v ≤ −6.25)。取 v = −8.0(和行 2 齐平):

罐体边 → 手机板边 3.00 mm(keep-out 边 1.75)
keep-out 边 → J31/J32 焊盘 1.63(上排)/ 1.43(下排)mm
实测可行区间 v ∈ [−9.0, −6.75] 本地四项全过,取中间偏保守

这一轮踩到的三个坑(都已写死在脚本里)

  1. pcb2-parts.json 不能在元件摆好之后随便重抓。 焊盘的 x/y 跟着元件转, 但 pad 数组里的 w/h 不转(方向记在 pad.rotation 上)。 摆好之后直接抓会得到「位置转了、尺寸没转」的四不像包围盒, pcb2_place.box() 再按 rot 翻一次 ⇒ 全错:实测凭空报出 12 条假问题 (手排的 0402 互相重叠、J23 越框)。 ⇒ 新脚本 .claude/pcb2_parts_dump.py 把焊盘位置反转回元件本地系再存, 和「刚导入、全是 rot=0」时抓的那一份等价。
  2. pcb_fanout.clean() 在载板上是破坏性的。 它删的是「layer !== 11 的所有导线」—— 下板除板框外没有别的线所以没事,载板上却有 丝印 50 + 装配 100 + 文档 20 = 170 条 会被一起删光,而且 DRC 和几何检查一条都不报。 ⇒ pcb2_fanout.py 里另写了 clean2(),只删过孔和信号层(1/2/15/16) 的导线, 并回读断言 3/9/13 三层还在。 ⚠️ 板框是 layer 11 上的 pcb_PrimitivePolyline,不是 Line —— 「剩余导线里有没有 layer 11」不能当板框还在的证据(第一版断言就是这么写错的)。
  3. 通孔焊盘不需要扇出:pcb_fanout.one_net() 的 todo 本来就带 not p['hole']。 所以 SKIP_HDR 直接关掉即可,J31/J32 那 80 个脚自己就是过孔, §25.8 里「33 个 GND 脚扇不出去」这条自动消失。

⛔ 还剩的一条硬约束(另两条已解决)

1. C2~C6 压在 J24 位置上 / 3. 两板之间没有立柱位 —— 这两条是下板的事, 留到下板重排接口区时一起处理(下面「下一步」第 1 件)。

C2/C3 重选时罐高必须 ≤ 11.5 mm。现在的 C22383480 商城一个参数都没标, §22.3d 那个「约 12 mm」是估的。⚠️ 别换成聚合物(低 ESR 会让输入滤波欠阻尼,同 §6.2 的理)。

✅ 追加一轮(2026-09-05 夜):排母逐针分配按几何重排

铺 Inner1 之前摸底,摸出 §22.2 那张「定稿」表和载板几何对不上,用户拍板改(方案 A)。 全过程见设计文档 §25.10,新的逐针表在 §22.2(事实来源仍是 .claude/p5_btb.py)。

硬冲突:排母南排前 16 位全是 GND 焊盘(Ø1.70 / 间距 2.54 ⇒ 缝里只剩 0.44 mm 铜网), 而 4 A 按 IPC 内层 0.5 oz 要 6.97 mm ⇒ 4 A 的电源面穿不过排母的地针行。 09-04 那版偏偏把 VO4_P/VO5_P 放在 J32 的北排,它们的站却在排母南边。

新规矩:北排喂北边的站,南排喂南边的站,一路都不许跨行。 J31 正对 site1(北)+site4(南),J32 正对 site3(北)+site5(南),site2 取 J31 北排最右三针。

09-04 09-05
五路 VOn_P 最远曼哈顿 ~120 mm(VO4 要绕排母端头钻手机 2 底下) ≤42 mm,全部同侧
+5V 3 针,Inner1 上要一条 150 mm 的横贯脊 10 针分散取电,段电流 ≤0.6 A ⇒ 顶层走线即可,不占内层
GND 33 针 26 针(0.77 A/针)
Inner1 规划 5 岛 + +5V + +3V3_HUB + 底填 5 块 VOn_P + GND 底填

网络表一个都没变,只是落点变了。五路的「正对面 3 针 GND」(§15.4)一条不少, p5_btb.py 的 plan() 逐行断言。

原理图画法改了一处:GND 也走 netport(不再画地母线)—— 重排后 GND 散成若干段,每段配母线 + 地旗会让旗掉进相邻行的端口里(行距 10 / 旗高 15~20)。

验证:verify_sch 两页 PASS · xpage 0 漏接 · port_audit 下板 544 / 载板 361 可疑 0 · 载板导入变更 65 条全是 J31/J32 的「修改网络」(无元件增删)· PCB 上 80 个焊盘的网络和逐针表逐行比对一致 · 重扇 210 孔 + Inner2 重铺 185 段 · DRC 518 = 同封装内 115(与基线一致)+ 连接性 403 + 其它 0。

✅ 这一轮(2026-09-06 第二轮):布线开工,连接器侧 5 对差分完成

详见设计文档 §25.12。 事实来源 .claude/pcb2_route_dp.py(SITE_J 是路径,同文件里带本地校核)。

# 做了什么 结果
1 抓到并改掉一个摆位错误:U5n 装反 USBLC6-2SC6 是直通器件,朝连接器那一排原来背对着 J7n。BAND['u5'] rot 0→180。SOT-23-6 对称,6 个焊盘落在完全相同的位置,只是引脚编号对调 ⇒ 无新间距冲突
2 跑完整的返工流程 重摆 161 元件 → pcb2_clear 816 焊盘过 → GND 扇出 clean/dump/push 210 孔 → 重铺铜 → inner1_verify + inner1_diffref 全过,各项数字与改之前完全一致
3 判定自动布线器在载板上不可用 配置逐条回读断言(所选网络 22 个 / 移除已有 / 只勾顶底层)之后跑:只画出 J23 自己那两条 A6↔B6/A7↔B7,全板 0 过孔,30 秒"跑完"。pcb2_route_diff.py 连同 --rollback 留着,别再试
4 新工具 pcb2_route_dp.py 路径按站内坐标写一遍映射到五站;本地校核线-焊盘/线-过孔/线-线/过孔-×4,一般 0.20、对内 0.15;--corridors 扫底层横向走廊
5 5 对 USBn_DPJ/DMJ 布完 DP 全程顶层 5.05 mm;DM 两端次序相反必须交叉,两个过孔沉底层穿过去,9.19 mm,偏斜 4.14(容差 5)。60 段导线 + 10 过孔
6 出线宽度按几何算死 BM24 焊盘阵列里 0.11 mm(上限算出来是 0.1197,取 0.12 会差 0.00015 卡线);出阵列后两线还隔 0.35 时 0.20;分开后 0.28。DIFF['strokeWidthMin'] 随之降到 0.10
7 DRC 479 = 同封装内 115(与基线一致)+ 连接性 364 + 间距错误 0

截图 PCB2-dp-site1.png(能看清 DPJ 走顶层、DMJ 沉到底层从它底下穿过去)。

⚠️ 加完线和过孔一定要重铺铜再看 DRC

不重铺时刷出 40 条间距错误(「到过孔」20 +「到钻孔」20)—— 那是铺铜还不知道新过孔、没让开它们。重铺之后 GND-INNER1 环数 217→227、 INNER2 330→340,正好是 10 个新过孔的反焊盘,40 条全消失。

✅ 这一轮(2026-09-06 第三轮):6 对长差分布完,载板差分对收工

详见设计文档 §25.13。 事实来源 .claude/pcb2_dp_long.py(坐标 + 逐条理由)。

决定全局走法的是 x 112~116 那道隘口:Hub 北边的 C751(1206,底层) 加 C815 的 GND 扇出孔,把北高速掐成「底层低窗 0.535 mm(1 对)」+「底层高窗」+ 「顶层窗口 0.58 mm(1 对,C751 在底层所以顶层是通的)」; 南边被 Hub 的 A 圈 0402 封死(只剩 y≈56 一条缝)。 三对东行的只能一对一个窗口,而南北次序被 U32 的引脚次序锁死 —— 正好对得上,不用额外交叉。 pair4 是全板唯一要横穿其它对的一对,一出焊盘就上顶层南下。

新脚本 / 新用法:

/c/Python314/python .claude/pcb2_net_conn.py            # 按实际铜算连通域(不看 DRC 面板)
/c/Python314/python .claude/inner1_diffref.py --routes  # §10.1 沿实际走线逐点查参考面
/c/Python314/python .claude/_refresh_geo.py             # 刷 inner1_* 三个脚本的输入缓存

⚠️ 三个这一轮踩到的坑

  1. drc_read.py 不加 --pcb2 跑的是下板。 第一次读到 649 条全是下板的网络。
  2. DRC 的连接性结果在程序化建线后是陈旧的,location.reload() 才刷新。 刚下发报 403(每个网络还报 2~7 条"断开了连接"),重载后是 338 —— 比布线前少 26,正合预期。 间距错误那一支倒是即时的。
  3. 45° 切角会把线甩进旁边焊盘的净距圈。 chamfer() 的 r 从 0.6 降到 0.35, 焊盘阵列附近还要多插一个共线点把切角压到 0.15~0.2。

下一步

# 做什么 备注
1 收尾 Hub 卫星圈那三段(+3V3_HUB 2 段 / VBUS_UP 1) 要用户拍板:方案 ① R711/R716/R720 从半径 8.0 挪到 9.5(这三颗的 GND 扇出要重做;挪完走 §25.12 那套返工流程:pcb2_place --push → pcb2_route_dp --dump → pcb2_route_sig --push → pcb2_gnd_patch --push → pcb2_pour --push → reload → DRC)。SCL/POGO5_VDN 已通,U34 已挪。见 §25.15 末尾
2 下板 PCB 导入变更 + 接口区重排 删了 15 个连接器 + 53 个 7xx 器件,新增 J24J30 / U33。J24 中心 (32, 62.5)、本体 53.34×5.08 会压到 C2C6,必须挪;C2/C3 顺便重选(罐高 ≤11.5)。J24/J25 的 40 个焊盘按 pcb2-header-pads.json 减 (30,5) 摆,别按引脚号推理
3 让用户截图确认载板布线 已截 PCB2-vo-vias-site1.png(VO1 站的电容打孔)/ PCB2-gnd-j23.png(J23 那两个屏蔽脚)/ PCB2-u34-j31.png(U34 新位置)。⚠️ EasyEDA 窗口必须在前台,在后台时 document.hidden = true、画布不渲染,getCurrentRenderedAreaImage() 只回 88 字节的空 PNG

⚠️ 2026-09-05 从实物照片新看出来的两条(都还没处理)

1. 原厂排线长约 85~90 mm,只给了 3 mm 水平距离 → ✅ 用户 2026-09-05 拍板:不管,装配时折叠。 照片上排线从主板下边缘一路走到尾插小板(板上印 MAIN + 箭头), 按主板宽 67.05 mm 标定,两个公头相距约 87 mm,而载板的 J7n 在 v = −3.0、 离手机板边只有 3 mm ⇒ 有 80 mm 以上的余量。 用户原话「排线长度不用管,我到时折叠一下」 —— 所以 J7n 不为此挪位, 也不为此改 §13.5 的「台阶式下折」。留一条给装配:折叠的那一坨会占手机上方的高度, 定压板高度(h_up)时要把它算进去。

2. J7n 并没有像注释说的那样「正对 J1401」,横向差 3.9 mm。 mi8_contacts.json(卡尺校核过)记的 J1401 中心是 u = 53.58(长轴沿 u,占 47.4659.71), 而 pcb2_place.BAND['j7'] 是 u = 57.5。 不致命(软板本来就有余量、而且照片上原厂排线自己就是歪的), 但不能直接改成 53.58 —— 那会和同一行的 j6(EDL 座,占 u 39.848.2)撞上 0.7 mm, 要连着 j6 / c3 一起重排。和上面第 1 条一起决定。

✅ BM24 的镜像位 2026-09-05 已实测(§24.3):结论是「不用改」。 用户量出「G1 在右端 / 靠板边那一排是奇数排」,照片确认排线从板边直出, 所以载板 +Y 排(朝手机那一排)= 奇数排 ⇒ ODD_ROW_TOP = True 是对的; 顺带第二位(贴片方向)也自洽:VBUS 端落在 −X = 手机左端,和「52=GND / 51=VBUS」对上。 打样前已经没有会导致重打板的未知量了。

⚠️ 但同一批照片带出两条新的机械问题,见下一节。


上一轮:排母选型(2026-09-05,用户授权「你来定选型」)—— 已实施

详见设计文档 §22.3f。 两个占位到此全部消灭。

型号 立创 关键参数
下板 J24/J25 公头 AZ-PZ2.54-V11-2*20P(宝讯) C55020601 塑高 2.5、配合针长 6、端接针长 3;3 A/针、镀金、直插
载板 J31/J32 母座 PC104-2*20(洲日) C5307344 塑高 10.5、行距 2.54、2.5 A/针、直插

对插 = 2.5 + 10.5 = 13.0 mm(塑料对塑料定位),满足 §22.3d 的 ≥13 mm。 把库里「2.54 间距 + 标了塑高」的排母全扫了一遍,10.5 mm 这一档只有 PC/104 一族, 其余全是 8.3~8.5(对插只有 11 mm,够不到),而 2.54 双排排针的塑高全是 2.5,没法从公头补。

改成插装是比高度更重要的收获:80 个通孔脚本身就是过孔, 直接连 Inner1/Inner2 —— §25.8 里被迫跳过的「J31/J32 那 33 个 GND 脚扇不出去」自动消失。

(这一节原来的「三条硬约束」与「编号体系已实测」已经实施完,结论搬到文首「这一轮」和设计文档 §22.3f / §25.9。)

开工前必读(按顺序)

  1. 五路直供电源板设计.md §22.7 —— 两个 Board 各有哪些页、位号怎么分、验证结果。
  2. §22.2(J24/J25 逐针表 + J31/J32 编号对照)、§22.4 末尾(EDL 驱动与载板开关)、 §22.6(风扇 + TACH 分压)、§13.5(USB 最终走方案 D:原厂主板-尾插排线 + 载板上同型号 BM24 座;那里也留着已作废的方案 A / C 记录,和「买一块尾插小板当量具」的测法)。
  3. .claude/skills/easyeda-sch-geometry —— §9j-2 是最新一轮(Inner1)加的: 铺铜的焊盘连接方式 connectMode 藏在 DRC 规则里,默认十字花会让几何校核报假断路; §9j 第 5 条补了「优先级步长是 2 不是 1」。 §9m / §9n / §9o 是换件那一轮加的: 包围盒不能在元件摆好之后重抓 / 通用 clean() 会删掉丝印 / 「从原理图导入变更」怎么读全。 §9j~§9l 是铺铜那三条,§6b / §6c / §6d 是「几何量必须读回、不能推」的血泪条目。

上一轮(2026-09-05):布线前置三件 + 一个必须记住的事实

全部见设计文档 §25.7。 这一轮一根走线都没画(用户拍板停在排母上,见上)。

# 做了什么 结果
1 载板设计规则整套下发 .claude/pcb2_rules.py --check 十一项全过
2 11 对差分对(不是 7 对) 5 长段 + 5 短段 *J + UPSTREAM
3 Inner2 整板地平面 一块 GND-INNER2,重建后 81 块填充,已存盘(.claude/pcb2_pour.py)
4 两个中跨 M3 (76.6, 80.1) / (148.1, 80.1) §25.6 ⑤ 那条待办关闭;pcb2_clear.py 816 焊盘全过
5 查清排母占位件是 SMD、排距 4.80 成了眼下的路障,见上
6 解决了「铺铜优先级设不上」 优先级 = 同层内的创建顺序,先建的赢;对照实验见 skill §9j
7 GND 扇出 .claude/pcb2_fanout.py 210 个过孔落盘(全 GND,Ø0.610/0.305),出线 105 顶 + 21 底
8 查出 BM24 的一个硬约束 五颗 J7n 的 65 个 GND 脚里 51 个打不下过孔(0.35 间距),见 §25.8
9 DRC 基线 全部 551 = 同封装内间距 115(封装自带)+ 连接性 436(没布线)+ 间距错误 0

⚠️ 看 DRC 只看「间距错误」那一支

「同封装内间距错误」那 115 条是 J71~J75(0.170 mm) / U32(0.125) / J23(0.200) 封装自带的引脚间距,安全间距规则 0.2 mm 比这几颗连接器自己的脚距还严,必然报, 三个数都在嘉立创四层工艺下限 0.1 mm 之上、可以打。 别去调低全局安全间距来「修」它们,那是把全板保护一起放掉。成因逐条见 §25.8 末尾。

为什么差分对是 11 对:载板上每一路 D± 被 ESD 器件 U5n 切成两段 —— 长段 USBn_DP/DM(U32 → U5n,走底层)+ 短段 USBn_DPJ/DMJ(U5n → J7n,约 5 mm,走顶层)。 两段都要建对,否则短段会按普通信号布、在 ESD 那头留一截不受控阻抗。

中跨孔为什么放在手机之间的缝下(y≈80)而不是走廊正中(y≈67.5): ① 要支撑的是 y 85.5~130 那一排手机,80.1 离它 5 mm、67.5 差 18 mm; ② 走廊正中被 C811/C831 两个 Ø12.5 电解罐夹着,只剩 6.9 mm 高的缝, 净间距 2.90 mm;缝下那两处是 5.28 mm。 ⚠️ 再往上会被顶针 J48/J52(y 87.96)逼住,y=83 掉到 2.41、y=84 只剩 1.41。

这一轮踩到的四条 EasyEDA 坑(skill §9j~§9l)

  1. 铺铜优先级是「数字小的先铺、占地盘」 —— 对话框底部自己写着。 下板 pcb_pour.py 里 GND=1 / 电源岛=5 是反的(那边铜面从不重叠所以没出事), 抄到载板之前必须翻过来(Inner1 是分割电源面 + GND 底填,全靠优先级互相裁)。
  2. create() 的 pourPriority 参数不生效 —— 优先级 = 同层内的创建顺序。 ⚠️ modify() 补设不生效还会把客户端弄卡死。 ✅ 对照实验已做,规则确认:先建的赢。电源岛先建、GND 底填最后建。 pcb2_pour.py 的 plan() 顺序就是优先级,--push 会回读断言。
  3. 「重建所有」点了没反应 = 客户端卡在后台任务上(窗口底部一条 0% 进度条), location.reload() 后一次就通。⚠️ 重载会丢掉重载前建的铺铜边框,即使 save 过 —— 所以「建边框 → 重建 → 确认 → save」必须一气呵成。
  4. 每个下发脚本必须自己 openDocument:这一轮因为上一条命令开了 PCB1, 把下板的 14 块铜面重铺并存了盘(无损失,但两边返回值都看着正常)。

另记一条正常行为:preserveSilos=false 时区域内没有本网焊盘/过孔,整块会被当孤岛删光 (边框还在、填充是 0)。所以扇出必须排在铺铜之前。

上上轮(2026-09-05 凌晨):载板切 4 层 + 布线前四处返工

全部见设计文档 §25.6。 这一轮没加新东西,是把上一版摆位里四处会咬人的地方修掉 —— 四处全部是「原理图侧看不出来、包围盒检查也过」的那一类。

# 改了什么 为什么
1 载板 2 层 → 4 层 11 对 USB HS 要 90 Ω,两层板 1.5 mm 介质做不出来;20 A 没有内层电源面;两面都是元件面
2 J23 左口袋 → 右口袋 §25.2 当初只核了下游。上游 J23(x5)→Hub(x110) 曼哈顿 140 mm,手册上限 152。挪完上游 104 / 最远下游 113
3 U31 (25,42) → (105,71) §22.7 写了「就近 Hub 供电是 PCB 摆位约束」,上一版没落实,离 90 mm。现在离 U32.pin64 8.62 mm
4 Hub 26 件网格粗排 → 逐脚对位手排 见下
5 M3 孔 (8,127)/(212,127) 挪走 压在 CH1/CH3 手机本体投影里,其中一个和定位孔 P1H2 重叠 0.93 mm

⚠️ 载板的层角色和下板是镜像的:载板 Inner1 = 电源面 / Inner2 = 完整地平面, 因为 U32 在底层,所有长距离 D± 都从底层出发,底层的参考面是 Inner2。 反过来的话底层 D± 就以分割的电源面为参考。嘉立创四层 1.6 mm 叠层上下对称, 所以 W0.28 / S0.18 照搬,不用重算。

Hub 手排真正多做的事是让出三条通道(网格排法做不到):西侧扇出(10 根下游 D±)、 南侧竖井(XTAL + UP_D±,宽 2.16 mm)、晶振窝。实测对位距离: RBIAS 1.43 / VDD18PLL 1.43 / 各 +3V3 1.462.30 / 配置电阻 1.43.2 / 晶振 4.955.76(是通道的代价,不是疏忽,负载电容贴端子 1.51.8 已做到)。

新写的检查:.claude/pcb2_clear.py(焊盘级净间距)

/c/Python314/python .claude/pcb2_clear.py

按焊盘真实形状两两算净间距,分「同封装内」(只报负值)/「跨元件」(判据 0.2 mm)。 M3 孔那个错误只有它抓得到 —— 自由焊盘不属于任何元件, pcb2_place.py 的包围盒检查根本不看它们(成因写进 skill §9i)。

写它踩到两个 API 陷阱,都写进 skill 了:

  • §9g 焊盘归属只能靠 primitiveId 前缀 —— getState_ParentComponentPrimitiveId 在 getAll() 回来的对象上不存在,try/catch 包住会静默地把所有焊盘当自由焊盘。
  • §9h ⚠️ 焊盘的 rotation 是弧度、元件的是角度。按角度判会把所有旋转过的 矩形焊盘长短边认反 —— J23 的 12 个信号焊盘被误报成两两重叠 0.3 mm(真实 +0.2)。

本轮验证结果(全过)

pcb2_place.py 本地四项 + M3 孔断言      ✔ 161 元件(顶 133 / 底 28)
pcb2_clear.py 跨元件净间距 >= 0.2 mm    ✔ 814 个焊盘
pcb2_clear.py 同封装内负间距            ✔ 0 对
铜层数                                  ✔ 2 -> 4(id 1/15/16/2)

截图 PCB2-carrier-v2.png。

上一轮(2026-09-04 晚)做完的两件

  1. ✅ 顶针占位型号换掉了:C9900001338 → YTC1P-2510-01(立创 C5221288), 配 3.0 mm 立柱(原来文档写的 2.0 mm 买不到,见 §22.3e 的整族对照表)。 c4_site.py 已改,C4-SITE1~5 五页重生成,六项验证 + 跨页对账 + port_audit 全过。
  2. ✅ C1-CONN 抓到并修好一处真断线:GND 旗写在 y=362,旗被吸附到 360、导线端点留在 362, 旗压在导线中段 → J31 pin1 的地是断的。verify_sch / DRC / 跨页对账全过, 只有 port_audit.py 抓得到。已在 c1_conn.py / c4_site.py 加「坐标必须是 5 的倍数」断言, 成因写回 skill §8a-2b。⚠️ 上一轮记的「可疑 0」不能当数,每轮都要重跑。

上一轮(2026-09-04 深夜):USB 定为方案 D,载板改造已完成

方案 D(设计文档 §13.5):手机 J1401 ──原厂「主板→尾插」排线── 载板上一颗同型号 BM24-50DS 座 ──> 载板上的 USB 电路。 用户拍板:天线自己另买插主板上,麦克风 / 震动马达不要,「只要能开机正常运行系统就行」。

载板改造做完了,三道验证全绿(verify_sch 每页 PASS / xpage 0 漏接 / port_audit 载板 330 个可疑 0、下板 513 个可疑 0):

页 改了什么 脚本
C5-J1401A / B(新) 五颗 J71~J75 BM24-50DS 座(C424561),A 放 CH13、B 放 CH45 .claude/c5_j1401.py
C5-AUX(新) 每路 U5n USBLC6 ESD + R76n 56k(CC1→VBUS) + R77n 100k(口温→GND) .claude/c5_aux.py
C3-USB(改) 删掉 J17~J21 五个 1.25mm 出线座;D± 不再经过这一页,只留 VBUS 负载开关 .claude/c3_usb.py

USB 链路现在是:C2-HUB 的 U32 → C5-AUX 的 U5n → C5-J1401 的 J7n → 原厂排线 → 手机 J1401。 NC 清单 .claude/c5-nets-nc.json(每颗座子 34 个悬空脚,含必须悬空的 CC2)。 截图:C3-USB.png / C5-J1401A.png / C5-AUX.png,都看过。

调试串口(pin24 TX / pin29 RX)故意没引:1.8V 电平接 ESP32 要加电平转换, 且 pin29 夹在两个地脚中间会把排版挤垮。要用是一次改板。

上一轮(2026-09-04 深夜):Board3 画完又作废 —— USB 方案改回 A(后又改成 D)

先按方案 C 画完了 Board3(J1401 转接小板),当晚用户确认「用排线连接」之后 改回方案 A:装原厂尾插小板 + 一根改端子的 USB-A 转 C 线。 Board3 / phonerail-usbtap / PCB3-USBTAP 已从工程删除, 生成脚本挪到 .claude/obsolete/t1_usbtap.py。理由对照表见设计文档 §13.5。

方案 A 当时是最优的,但用户随后提出「直接用原厂排线连主板和载板」—— 那就是方案 D,比 A 更紧凑、电路全在载板上好改。方案 A 已作废, 它唯一的优势(保留天线馈点)被「用户自己另买天线」抵消了。

作废但仍有效的四条结论留在 §23:立创 BM24 封装编号是错的(同时佐证了 §13.4 的引脚表)、 BM24 只有 10 次插拔寿命、Rp/Rd 分压核算、以及两条 EasyEDA API 经验(skill §8b / §8c)。

上一轮(2026-09-04 深夜):BM24-50DS 封装自建完成

下一步四件里的第 2 件做完了。 器件在个人库:BM24-50DS/2-0.35V(51) HRS-PIN (771dbf002b714bb79cde47cf86aa0fb1,封装 940f093c1578426aa81e91ebf0bfbd76), c5_j1401.py 已改用它,C5-J1401A/B 重生成,三道验证全绿 (verify_sch 两页 PASS / xpage 0 漏接 / port_audit 载板 330 个可疑 0)。 生成脚本 .claude/fp_bm24.py,封装截图 FP-BM24.png。逐项尺寸与来源写在设计文档 §24。

几何不是外推、也不是抄立创:从 Hirose 官方样本(D47680_en)的 PDF 矢量图里 逐段反算的,50 芯的 A/B/C 表直接可用。信号焊盘 0.18 × 0.48 @ 0.35 间距、两排 y = ±1.06、 电源焊盘 0.49 × 0.76 @ x = ±6.03。

⚠️⚠️ 但是多出一件必须做的事:镜像位实测(§24.3)

原来文档里写「两种候选正好差一个 180° 旋转,转贴片方向就能切换」——这句是错的, 候选有 4 个:2(1 脚在哪一端)× 2(奇数脚在哪一排)。 前一位靠旋转能纠,后一位是镜像,错了要重打载板(D+ 会落到 45 脚 SPKR_OUT_P)。

BM24 的 DS 座是完全对称件(Hirose 样本注 2:no polarity,本体两端与上下两排全一样), 所以「哪一排是奇数排」查图纸查不到 —— 它是小米8 那块板的布局事实,只能量。

量法(两个探点,零成本零风险,打样前必做):

  1. 两个电源触点(焊盘大好戳)分别对主板地量电阻,~0 Ω 的是 G1 = 1/2 脚那一端。
  2. 从 G1 端数,两排各自的第 2 个信号触点对地量:通的那排是奇数排(pin3 = GND; 偶数排第 2 个是 pin4 = VREG_L19A_3P0,不通)。

结果若是「奇数排在 −Y」,把 fp_bm24.py 的 ODD_ROW_TOP 改成 False 重跑一遍即可。

上一轮(2026-09-04 深夜):载板 PCB 从零布起

板框 220 × 135(R5 圆角),五块主板 3+2 双排面对面、第二排居中(用户拍板)。 原理图 161 个元件全部导入并摆好,本地四项检查全过(位号齐全 / 不越框 / 分层重叠 / 顶层不压主板投影)。 逐项见设计文档 §25,脚本 .claude/pcb2_place.py,截图 PCB2-carrier-v1.png(全板)/ PCB2-site1.png(CH1 位)/ PCB2-j23.png。

已落盘的东西:板框 1 条闭合多段线、载板自己 4 个 M3、35 个手机定位孔(NPTH Φ2.5)、 5 个手机位丝印外框 + 标签、161 个元件(顶层 133 / 底层 28)。

⚠️ 抓到一个会让侧键永久短路的封装错误(§25.3)

立创给顶针 C5221288 的封装焊盘是 Ø2.5,而小米8 四个侧键触点最近的一对 (J601↔J604)只隔 2.217 mm —— 两个焊盘直接叠在一起,音量+ 会被永久按住。 顶针法兰其实只有 Ø2.0,是立创把焊盘开大了。原理图侧完全看不出来。

已自建 POGO-SMD1P_D1.9(焊盘 Ø1.9,净间距 0.317 mm),c4_site.py 改用新器件, C4-SITE1~5 重生成、三道验证全绿、PCB 重新导入。

三条布局上的硬决定

  1. J31/J32 排母在底层(下板公头朝上,载板母座必须朝下)。 「第 r 行对第 r 行」不靠脑补:--push 会把载板侧 80 个焊盘坐标导出到 .claude/pcb2-header-pads.json,下板照数值摆即可(下板目标 = 导出值 − (30,5))。
  2. USB Hub 也在底层、压在通道正中 (110,67) —— 放左右口袋的话最远那台 D± 要走 200+ mm,超 USB2517I 手册的 152 mm 上限;放中间后最长 92 mm。 ⚠️ 布下板时别让 C2/C3(12 mm 电解)撞上载板底面的元件。
  3. 35 个定位孔做 NPTH,立柱必须用尼龙 —— 金属化 + 金属立柱 = 五台手机的地 在载板上并成一根,4 A 回流会绕开专用 P− 针,违反 §6.4。

载板这一版还欠的(详见 §25.5)

✅ J23 已定稿(rot=270、x=5.134,壳体前端面与板边齐平)· ⬜ Hub 那 26 个件是网格粗排、布线前要手调 · ⬜ 设计规则 / 铺铜 / 布线一根没做 · ⬜ 主板 7 个定位孔的孔径没量

上一轮写的「下一步四件」(第 1/2/5 件已完成,剩下的搬到文首)

# 做什么 备注
1 ✅ 排母选型 + 换件实施全部完成(2026-09-05 夜,见文首)
2 ✅ 自建 BM24-50DS 封装 + 镜像位实测(§24.3)—— 全部完成,结论是当前的 ODD_ROW_TOP = True 就对
3 下板 PCB 导入变更 删了 15 个连接器 + 53 个 7xx 器件,新增 J24~J30 / U33,接口区要重排,最后统一重铺铜。⚠️ 下板的铜面 2026-09-05 被误重铺过一次(无损失,本来就要重做)
4 🟡 载板 PCB —— 摆位 + 规则 + 差分对 + Inner2 地平面 + 6 个 M3 + GND 扇出 + 换件重摆/重扇/重铺 + Inner1 分割电源面都已完成。⬜ 只剩 布线
5 ✅ 载板上半部分的安装点 —— 已加两个中跨 M3 (76.6, 80.1) / (148.1, 80.1),§25.7 ③

改完照例:verify_sch.py(当前页)+ 跨页对账 + port_audit.py (载板加 --board Board2),三个都干净了再让用户看截图。 动 PCB 的话再加一条 pcb2_clear.py(焊盘级净间距,自由焊盘只有它管)。

载板布线的下一步,按顺序

  1. ✅ 换设计规则 —— .claude/pcb2_rules.py --push,--check 十一项全过(§25.7 ①)。
  2. ✅ 建差分对 —— 11 对(5 长 + 5 短 *J + UPSTREAM),短段也建了对。
  3. ✅ Inner2 整板 GND —— 一块 GND-INNER2,81 块填充,已存盘(.claude/pcb2_pour.py --push)。
  4. ✅ GND 扇出 —— .claude/pcb2_fanout.py,换件后已 clean → dump → push 重来一遍, 210 个过孔。J31/J32 那 33 个 GND 脚不再需要扇出(通孔件自己就是过孔, one_net() 的 todo 带 not p['hole'])。 ⬜ 只剩五颗 BM24 的 51 个 GND 脚(过孔塞不进 0.35 间距,只能逃逸布线,§25.8)。
  5. ✅ Inner1 分割电源面 —— 2026-09-06 铺完(五块 VOn_P 岛 + GND 底填, §25.11 / pcb2_pour.py 的 ISLANDS)。三个校核脚本全过: inner1_check.py(下发前预测)/ inner1_verify.py(下发后实测,最窄颈 7.20 mm)/ inner1_diffref.py(§10.1 参考面)。 ⇒ +5V / +3V3_HUB / VOn_P 的扇出现在可以做了(之前扇过去全是悬空过孔)。
  6. 🟡 布线 —— 先 D±(长的先走),再电源,最后杂散信号。 ⚠️ BM24 的逃逸布线单独排一项:前排往南、后排往北,同排横向穿不过去(净距 0.17 mm)。

Inner1 分割电源面的规划(⚠️ 这是 09-05 的规划稿;落地结果与实测见设计文档 §25.11)

层角色:Inner1 = 电源面(分割),Inner2 = 完整地平面(§25.6 ①,和下板是镜像的)。

只做三类岛,其余全部 GND 底填(不是原来写的「+5V / +3V3_HUB / VO15_P / VBUS15」 十二块)——理由是内层是 0.5 oz,按 IPC-2221 内层算 4 A 要 6.97 mm 净宽、 2.8 A 要 4.3 mm,而两排手机之间的走廊(y 49.66~85.5)只有 35.8 mm 高:

岛 网络 为什么要 最小颈宽
5 块 VO1_P~VO5_P 每路 4 A,这是上四层的头号理由 ≥7.0 mm
底填 GND 剩余全部,优先级数字给最大 —

⚠️ 2026-09-05 夜简化:+5V 和 +3V3_HUB 两块岛取消了(§25.10)。 排母逐针分配按几何重排之后,+5V 从 3 针变成 10 针、分散在两颗排母的南北四处, 每个入口只喂本站的 U2n + C70n、段电流 ≤0.6 A ⇒ 顶层 0.5~0.8 mm 走线就够, 内层不用给它留地方。+3V3_HUB(420 mA、局部)同理。 ⇒ Inner1 只剩「五块 VOn_P + GND 底填」,五路 4 A 各走各的,互不挤。

VBUS1~5 不做岛:每口 0.5 A、走线只在本服务带内约 18 mm,PWR1A(0.6 mm) 走线足够。 额外好处:顶层那 5 段 *J 差分(U5n→J7n,5 mm)下方的 Inner1 就是完整的 GND 底填, 参考面比分割的电源面干净得多(§25.7 ① 那条警告因此自动消解)。

走廊里已知的障碍(Inner1 只怕直插焊盘,不怕表贴件): C801/C811/C821 三个 Ø12.5 电解在 y 68.2580.75,C831/C841 在 y 54.4166.91, 两个新中跨 M3 的 Ø6 焊盘在 (76.6, 80.1) / (148.1, 80.1), 外加 J31/J32 的 80 个通孔焊盘(坐标已定稿:J31 x 37.86986.129 / J32 x 133.869182.129, 两行 y = 66.23 / 68.77,见 pcb2-header-pads.json)。 ⚠️ 2.54 间距的通孔在内层会把铜面打成筛子(Ø1.7 焊盘 + 0.2 净距 ⇒ 相邻反焊盘之间只剩 0.44 mm 网), 电源岛的颈宽要绕开这两条带,别指望从排母底下横穿过去。

还欠用户的五件事

  1. BM24 的镜像位实测 → ✅ 2026-09-05 已完成,结论「不用改」(§24.3)。 可选的零成本复核(还没做):奇数排上从 G1 端数,位置 17-18-19 与 22-23-24 各自三连导通 (SPKR_OUT_N 33/35/37 和 SPKR_OUT_P 43/45/47),偶数排没有这种三连。 0b. 量排线两个公头之间的距离 → ✅ 用户拍板不管,装配时折叠(余量约 80 mm,会占手机上方高度)。
  2. h_up:主板正面(朝上那面)最高元件高出板面多少。只影响压板高度与整机层高。
  3. ✅ 排针/排母选型已定(§22.3f):C55020601 + C5307344,对插 13.0 mm。顶针已定稿。 ⬜ 到货要核两件:母座触头镀层(商城只写了「黄铜」,没写镀层)、 对插后实测板间距是否真的 13.0 mm(确认是塑料贴合、不是针先顶死)。 2b. 首件上电前量一次:VBUSn 对 GND 的电阻(确认电源触点 G1/G2 没接反,§13.5)。 负载开关默认关断,所以这一步零风险、零成本。
  4. 顶针首件必验:±1 mm 公差链对 1.2~2.2 mm 的侧键触点是危险的,而且 选定的顶针冠径只有 Ø0.9 球头、又是贴片不可调,所以首件实测是唯一对策(§22.3c)。
  5. 用卡尺重量那 7 个定位孔:±1 mm 里有 ±0.5 来自「孔位是从照片提的」, 量一遍能直接砍掉一半误差 —— 这是眼下性价比最高的一项。

这一轮做了什么(2026-09-04 傍晚,载板画完)

载板 8 页

C1-CONN(排母 J31/J32 + U31 5V→3.3V + U34 LM75BD 温度芯片 0x48)、 C2-HUB、C3-USB、C4-SITE1~5(每台手机一页:P+/P− 焊盘 + §6.3 电容组 + 4 根侧键顶针 + EDL 座/模拟开关/下拉/去耦)。逐页内容与位号见 §22.7。

手机位一台一页,和下板「一路一页」保持同一结构;五页由 .claude/c4_site.py 同一段代码生成,改一处五页一起变。

这一轮自己发现并补上的两个洞

  1. UP_VBUS_DET 在载板上没有源头。 §22.2 给它留了一根排针,但 J23 的 VBUS 是 5V, 排针那头是 3.3V 域的 IO44。在 C2-HUB 上补了 R721 10k / R722 20k 分压(3.33V)。
  2. 载板上原来没有 3.3V。 排针只送 +5V,而 Hub、温度芯片、五颗 EDL 开关都要 3.3V。 U31 从下板搬过来后画在 C1-CONN(+5V 进来的那一页);§9.5 要求的「就近 Hub 供电」 是 PCB 摆位约束,与分页无关,已在文档里说清楚。

两个明知不对、故意先放着的占位型号(⚠️ 顶针那条已于当晚解决,见文首)

占位 差在哪
顶针 C9900001338 ✅已换 自由高 9.0 mm,§22.3e 要的是工作高 2.0 / 自由高 3.03.5 / 冠径 Ø1.52
排母 C9900025309 高度没按 13 mm 净空选;而且编号体系和公头不同(§22.2 有对照表)

放占位是为了让网表先连通、能跑完整验证;两个都在 §22.7 里单列了一节,打样前必换。

⚠ 一个必须记住的编号陷阱

下板公头(DIP 式)第 r 行 = 引脚 r / 41−r;载板母座(IDC 奇偶式)第 r 行 = 引脚 2r−1 / 2r。 只能按行对,不能按引脚号对(VO1_P 在下板是 1/2/3 脚,在载板是 1/3/5 脚)。 原理图这边不会错 —— c1_conn.py 直接 import p5_btb.py 的行表,两边同一个事实来源。 风险在 PCB:焊盘编号 + 摆放镜像决定第 r 行会不会真对上第 r 行,布板时要实打实核一次。

新踩的两条 EDA 坑(已写进 skill §6c / §6d)

  1. 同一颗电解、同样 create(270),在两个图页里 pin1 方向相反。 方向表一张都别信, 极性元件放完必须断言(c4_site.py 里断言「电解 pin1 的 y 必须大于 pin2」)。
  2. 红黑两个测试点的引脚偏移不一样(−20 / −10),按名义值算线会画出斜线, 而 create() 对斜线直接抛 create failed! 并让整批下发中途断掉。 现在下发前统一断言「横平竖直且非零长度」。

状态一览

阶段 状态
方案设计 ✅ 定稿 v1.6(五路直供电源板设计.md)
小米8 侧接口引脚定义 ✅ 已从原理图逐脚追踪(设计文档 §13)
器件选型 ✅ 全部立创有货,UUID 缓存在 .claude/eda-parts.json
P1-INPUT ✅ 补完输入侧 INA226(U13+Rs6+C11,§19),六项验证全过,需重新看一眼
P2-CHANNEL(CH1) ✅ 已删 J2/J7,侧键漏极改挂 POGO1_* 端口,六项全过
P2B-CH2 ~ P2E-CH5 ✅ 同样删了 J3J6 / J8J11,改挂 POGO2~5_*,各自六项全过
P3-CONTROL ✅ 画完,六项验证全过,等用户看截图
P3B-EXPAND ✅ 画完,六项验证全过,等用户看截图
P4-USB ➡️ 已整页搬到 Board2 载板,改名 C3-USB;下板已删
P4B-HUB ➡️ 已整页搬到 Board2 载板,改名 C2-HUB;下板已删。⬜ 还要在载板上补 U31
P1B-5V ✅ 已摘掉 U31 + C736/C737(跟 Hub 一起上载板),现在是纯 12V→5V,9 元件,六项全过
P5-BTB(原 P5-HARNESS) ✅ 整页重画:J24/J25 两条 2×20,47 信号 + 33 地,六项全过(截图 P5-BTB.png)
P6-FAN-EDL ✅ 新建:U33 第三片 595(EDL_EN15)+ 风扇低边驱动 + J26J30,六项全过(截图 P6-FAN-EDL.png)
跨页网络对账 ✅ 下板 11 页 0 漏接 / 0 待补;✅ 载板 62 个网络 0 漏接
port_audit.py ✅ 下板 513 个、载板 260 个,可疑 0 个(载板加 --board Board2)。⚠️ 2026-09-04 晚重跑时载板曾报 1 个(C1-CONN 的 GND 旗),已修 —— 每轮都要重跑,别信上一轮的数
Board2 载板原理图 ✅ 8 页全部画完:C1-CONN / C2-HUB / C3-USB / C4-SITE1~5,六项验证 + 跨页对账 + port_audit 全过
Board2 载板 PCB 🟡 4 层 / 220×135 板框 / 35 个定位孔 / 6 个 M3(含 2 个中跨)/ 161 个元件已摆完(§25、§25.6、§25.7);⬜ 未布线
Board2 载板 GND 扇出 🟡 210 个过孔已落盘(§25.8),脚本 pcb2_fanout.py;⬜ 差 J31/J32 的 33 个(等换件)+ BM24 的 51 个(要逃逸布线)
Board2 载板 设计规则 ✅ 整套已下发,pcb2_rules.py --check 十三项全过(四层能力集 / PWR4A·PWR5V·PWR1A / 11 对差分 / USBPAIR 0.15 / 12 个网络指派 / PLANE-DIRECT 铺铜全连接 + 5 个网络指派)
Board2 载板 铺铜 ✅ 两层内层都铺完:Inner2 整板 GND + Inner1 五块 VOn_P 岛 + GND 底填(§25.11)。脚本 pcb2_pour.py(ISLANDS 是事实来源,--verify 回读)+ 三个校核脚本 inner1_check / inner1_verify / inner1_diffref
BM24-50DS 封装 ✅ 自建完成(§24),个人库器件 771dbf00…,几何来自 Hirose 官方样本。⬜ 镜像位待实测(§24.3)
PCB 转板 ⚠️ 已过期:原理图这一轮删了 15 个连接器 + 53 个 7xx 器件、新增 J24~J30/U33,还没导入变更
PCB 板框 ✅ 160 × 125 mm,四角 R5 圆角 + 4 个 M3 金属化定位孔(接 GND)
PCB 布局 🟡 第五遍:8 A 输入链整条重排(见下),等用户看 PCB-layout-v5.png
设计规则 ✅ 整套已下发,--check 七项全 OK(能力集 / PWR4A / PWR12V / 差分对 / 0.2 mm 间距 / 7 对 / 12 个指派)。上一轮说的「只能手动」是错的,见下
PCB 板框 ⚠️ 改成一条闭合 polyline 了(原来的 4 直线 + 4 圆弧自动布线器不认,见下)
设计规则 ✅ 等长容差 5 mm;新增 USBPAIR 0.15 mm,只挂在每一对自己的两个网络之间
铺铜 ✅ 19 块(新增 +5V / +3V3_HUB / +3V3×2),三条低压轨的 91 个焊盘已扇出
7 对 USB 差分 ✅ 6 / 7 布通(USB1~5 + UPSTREAM)。⬜ 只剩 ESP32 调试口 USB_DP/DM
PCB 布线 🟡 1208 过孔 / 5814 导线已落盘(save 后重开验证过)。⚠️ 铺铜是旧填充、接口区马上要大改 —— 先别继续布
Kelvin / 灌铜网络的漏网焊盘 ⬜ 要手工布:INA226 的 VIN±、五路泄放电阻 R*19、输入侧 U13 —— 5 mΩ 分流器满量程才 20 mV,多 1 mΩ 走线就是 20% 误差
GND 漏网焊盘 ⬜ 20 个(459 个扇出过孔之外的)
丝印整理 ⬜ 位号大面积压字,打样前要单独跑一遍
原理图 SCL 断线 ✅ U13 的 SCL 压在导线中段、根本没接上,已修(设计文档 §19.4)。新增全工程扫描 .claude/port_audit.py
板载 USB Hub 改造 ✅ 原理图 + PCB 布局都做完了
两板架构(2026-09-04 拍板) ✅ 设计文档 §22 定稿;✅ 下板原理图改造已做完(§22.7);⬜ 载板 C1-CONN 未画
上板(载板) ✅ 原理图 8 页画完;⬜ PCB 还是空板;✅ 顶针已定稿 YTC1P-2510-01;⬜ 只剩排母占位(§22.7)
散热 ✅ 定稿 + 电路已画(P6-FAN-EDL:Q6/D2/J26~J30 + TACH 分压)。⬜ 风扇型号未定(若选 4 线风扇,§22.6 那段 MOS+分压可整段删)
小米8 主板机械数据 ✅ 全部到手:44.66×67.09 实测、7 个定位孔 ±0.5、触点 ±0.3(§22.3)。数据在 .claude/mi8_mech.json / mi8_contacts.json
EDL(9008) 控制口 ✅ 两侧都画完:下板 U33(第三片 595)→ EDL_EN1~5 → 排针 → 载板 U41U45(TS5A3157DBVR)+ J61J65(1.25mm 2P)+ R741~R745 下拉

下板原理图 11 页(顺序已排): P1-INPUT / P1B-5V / P2-CHANNEL / P2B-CH2 / P2C-CH3 / P2D-CH4 / P2E-CH5 / P3-CONTROL / P3B-EXPAND / P5-BTB / P6-FAN-EDL。

载板原理图 8 页(顺序已排): C1-CONN / C2-HUB / C3-USB / C4-SITE1~5。

截图:12 页里已截 10 页(缺 P2B/P2C/P2D,它们是 P2 的机械复制)—— P1-INPUT.png、P1B-5V.png、P2-CHANNEL.png、P2E-CH5.png、P3-CONTROL.png、 P3B-EXPAND.png、P4-USB.png、P4B-HUB.png、P5-HARNESS.png,PCB 最新是 PCB-layout-v4.png。 (上一轮那三张空白 PNG 是渲染没出来,不是图元缺失;这一轮重截,都是好的。)

这一轮做了什么(2026-09-04,方案转向)

一、自动布线(转向前完成的部分)

见下面「自动布线终于跑起来了」那节。结果保留着:1208 过孔 / 5814 导线, 6/7 对 USB 差分布通,间距/物理错误 0。但接口区马上要大改,别在这上面继续投入。

三条最贵的教训(每条都卡了很久):

  1. 板框必须是一条闭合 polyline,不能是 4 直线 + 4 圆弧。 自动布线器取板框走 fu(),只扫 polygon 管理器;独立线/弧不在那个表里 → getFilterBoard() 返回空 → 弹「请先绘制板框!」然后静默退出。 正确写法:pcb_MathPolygon.createPolygon(SRC) → pcb_PrimitivePolyline.create('', 11, poly, 5)。 ⚠️ 直接把源数组喂给 create() 会抛「无法创建多边形图元」而且真的没建 —— 跟铺铜那条「抛了也算成功」方向相反。
  2. 布线跑的时候什么都别碰。不要 save()、不要 pcb_Drc.check()、 连 getAll() 这种只读调用都别做。被打断时 routerResult() 里 state!==1 会直接丢弃结果 —— 画布上看得见、getAll() 数得到, 但没进文档模型。判据只有一个:save 后重新 openDocument 再数一次。
  3. 差分对要一次统筹全部 7 对。一对一对布会互相堵死(保留 模式下先布的成了障碍)。 正确姿势:全选 7 对的焊盘 + 「已有导线/过孔 = 移除」+「布线网络 = 所选网络」。

其它:重载客户端会清空自动布线对话框的设置;DRC 面板刷不出来时走 设计 → 清除错误 → 设计 → 检查DRC。

二、方案转向:两块板(用户 2026-09-04 拍板)

用户的原始需求分四次说清楚:

  1. 「做成两块板,五路 usb 移到上板,排针对接,上板加五块主板定位」
  2. 「进入 9008 的控制接口也接入」
  3. 「与主板连接的 usbhub 移到上板,不用排针,排针只需要把下板的电源部分接上来」
  4. 「侧键做顶针,EDL 做 2 针 1.25mm 座子我自己接线,VBUS 通断走排针」
  5. 「不要散热垫,加风扇」→「侧面两侧各 2 个小风扇吸入,顶部 1 个大风扇排出」→「风扇装 3D 打印外壳上」

完整规格写在设计文档 §22,这里不重复。

三、⭐ 小米8 主板机械数据:一直就在我们自己的 PDF 里

旧结论「PDF 是电气原理图、不含机械尺寸、只能实测」是错的。 小米8原理图/Xiaomi_Mi_8_Schematic.pdf 共 46 页, 前 2 页是正反面位号图(含完整板框轮廓 + 全部位号),后 44 页才是原理图。 当初只按「原理图」去读,get_text() 一看全是位号就跳过了,从没渲染出来看一眼。

拿到的数据(方法与精度分档见 §22.3a / §22.3b):

  • 比例尺 17.0 pt/mm(从 464 个 0201 封装反推,卡尺验证 0.7%)
  • 主板 44.66 × 67.09 mm(卡尺实测)
  • 7 个定位孔(从用户标红框的照片提取,±0.5 mm)
  • EDL / 尾插 / 侧键触点(从器件本体图元提取,±0.3 mm,并被两个卡尺读数交叉验证)

四、⚠️ 这一轮我判错了四次,都是同一个毛病

没有实物对照就从矢量图元硬推。 记下来,别再犯:

判错 错因
「主板没有定位孔」 轮廓用「逐行取最外侧墨点」提取,这方法看不见板内的孔和挖空
「定位孔就是图纸上那 7 个圆」 抓到的是测试点和圆焊盘。摆成横向和用户照片一对就露馅
把两个卡尺读数当成「板厚」 用户说的是侧键到板边的距离
把 7.61 读成 1.76 卡尺屏是旋转的,数字看反了

规矩:卡尺读数先跟用户确认再落档;从图纸推的几何结论, 必须有一个实物基准交叉验证才能写进文档。

五、EDL(9008) 电路(原理图 Sheet 17,比维修站的付费短接图可靠)

VREG_S4A_1P8 ──[R1204 1.8kΩ 0201]── TP1204   ← 0.75 mm 测试点
FORCED_USB_BOOT ──┬── TP1205                  ← 0.75 mm 测试点
                  └── CR1209 PESD3V3V1BCSF ── GND

短接两点 = 把 FORCED_USB_BOOT 拉到 1.8V → 进 9008。 串了 1.8 kΩ,短路电流只有 1 mA,一颗模拟开关就够。详见 §22.4。


这一轮:自动布线终于跑起来了(2026-09-04)

⚠️⚠️ 头号发现:板框必须是一条闭合多段线,不能是 4 直线 + 4 圆弧

布线 → 自动布线... → 运行 静默失败:对话框正常关闭、一根导线不画、 console 没有任何异常、也没有网络请求。三次尝试(React onClick / 原生 dispatchEvent / 等 90 秒)现象完全一样。

顺着客户端 bundle 追下去才找到:

pcb_ManufactureData.getAutoRouteJsonFile()
  → ui.js  getAutoRouteJson()  → windowBridge.rpcCall('getNewAutoRouteJson', …)
  → pcb.js vP.autoRouting()    → createPangolinJson()
  → getFilterBoard() → fu() → []        ← 就是这里
  → 弹 "Please draw a board outline first!" 然后 return undefined

fu() 的判据是(pcb.js 反编译):

[Ni.getInstance().getAllItem(), tn…, Yi…]      // 只有 polygon 管理器
  .forEach(t => t.forEach(e => {
     !e.isEmpty && (e.type == "Board Outline" || e.type == "Line" && e.layerId == 11) && o.push(e)
  }))

它只扫多段线(polygon)管理器。我们的板框是 pcb_PrimitiveLine.create() × 4 + pcb_PrimitiveArc.create() × 4, 八个独立图元,压根不在那个表里 —— 于是 fu() 恒返回空。

这个错极难往板框上想,因为除了自动布线,板框在别处全都是好的: 渲染正常、铺铜按它的几何铺好了、DRC/ERC 全过、Gerber 也认。 同一个 fu() 还卡着 getDsnFile() 和 getAutoRouteJsonFile() (两个都返回 undefined,等于 freerouting 那条路也被堵死了)。

正确姿势(两步,缺一不可):

const poly = await eda.pcb_MathPolygon.createPolygon(SRC);   // ⚠️ 必须先过这一步
await eda.pcb_PrimitivePolyline.create('', 11, poly, 5);

⚠️ 把 TPCB_PolygonSourceArray 直接喂给 pcb_PrimitivePolyline.create() 会抛「错误:无法创建多边形图元」,而且是真的没建(getAll() 仍是 0)—— 注意这跟铺铜那条「create() 抛了也算成功」的经验方向正好相反,别混用。

pcb_place.py 的 push_outline() 已经改成这个写法(新增 outline_poly())。

一路排查中间踩到的三件事

  1. pcb_PrimitiveArc.getAll() / getAllPrimitiveId() 在本工程恒返回 0。 排查时我以为四个 R5 圆角不存在,又补建了四段 —— 于是 layer 11 上现在有 8 段重叠圆弧 + 4 条重叠直线(几何完全重合,Gerber 上是同一条路径描两遍)。 它们不在 fu() 的表里,不影响布线;但删不掉(枚举不到就没有 primitiveId), 要清只能在客户端里手删,或者隐藏其它层后框选 layer 11 全删再重建 polyline。
  2. 桥接的 WS 请求 30 秒硬超时,eda.py 的 timeout= 只管 HTTP 那一段。 JS 里 sleep 超过 30 s 一律拿到 Request … timed out after 30000ms。 要等就分多次短调用轮询。
  3. 客户端重连会换 windowId。curl http://127.0.0.1:49620/eda-windows 可以确认 当前只有一个窗口 —— 排查「是不是打到另一个窗口去了」时先看这个,别猜。

抓静默失败的通用手法(这轮就是靠它破的)

页面里挂三层钩子,再触发一次,就能看见吞掉的错误:

// ① console 全接管 ② window.onerror / unhandledrejection ③ fetch / XHR / Worker
// ④ 最关键:postMessage —— 父窗口与编辑器 iframe 之间的 rpc 都走这里
window.addEventListener('message', e => { …记录 topic 与 message… }, true);
document.querySelector('iframe.editframe').contentWindow
        .addEventListener('message', …, true);

回包里 {"topic":"getNewAutoRouteJson_MSG_BUS_RPC_64600","type":"push"} 没有 message 字段 —— 一眼就能断定是 iframe 里的处理函数返回了 undefined, 而不是参数没传对或者按钮没点上。

再配合 console.warn 里打 new Error().stack,能直接定位到 ui.js:1819 的 getAutoRouteJson,然后把 pro-ui/…/js/ui.js(17 MB)和 pro-pcb/…/js/pcb.js(10 MB)下下来 grep —— 这两个包是公开 CDN,随便下。

自动布线的设置(对话框里的状态是持久化的,改一次就记住)

项 取值 为什么
布线网络 所有网络
布线拐角 45°
已有导线/过孔 保留 708 个扇出过孔和平面层不能被冲掉
效果优先级 完成度优先
布线图层 只勾顶层 + 底层 内层1 是完整地平面、内层2 是电源平面,不许被信号线割开
忽略网络 15 个 GND(459 个扇出过孔已经接到地平面了,让它布会刷出一堆顶层地线)+ 7 对差分的 14 个网络(USB1~5_DP/DM、UP_DP/DM、USB_DP/DM,打算之后按对单独布)

对话框是 React 的,忽略网络 那个下拉可以直接从 fiber 上取 onChange 逐个灌:

const p = /* 从 [data-id^="selectOptionKey_"] 往上找带 {list, onChange} 的 fiber */;
p.onChange('GND');            // 每加一个 list 就少一个,要重新取 fiber

⚠️ 自动布线的结果不到最后不落盘 —— 中途被打断就白跑

第二趟(补差分对)跑完,getAll() 报 6196 条导线、几何上逐段查过 USB1_DP 从 U32.2 一路连到 J17.3、并查集只有一个连通块, 但 DRC 咬定这两个焊盘断开。pcb_Document.save() 之后 重载客户端,板子回到了第一趟的 1095 过孔 / 3455 导线 —— 第二趟一根线都没进文档。

原因在 routerResult():

routerResult(t){ if(this.state!==1){ console.error("Auto Routing is stop!", this.state); return; } … }

布线结果是一批一批推回来的,state!==1(被打断)时直接丢掉。 画布上看得见、getAll() 数得到,但那只是 historyGroup 里的预览,没有进文档模型 —— 所以 DRC 是对的,错的是我。

判据只有一个:save 之后重新 openDocument,再数一次导线/过孔。 数字对不上就是没落盘。别信 getAll(),也别信画布。

跑的时候不要碰客户端:不要 save()、不要 pcb_Drc.check()、 连 pcb_PrimitiveVia.getAll() 这种只读调用都先别做 —— 第二趟就是这么被打断的 (第一趟跑满 ~50 分钟、进度条自己消失,结果就落盘了)。 盯进度只读 DOM 文本:document.body.innerText 里那行 自动布线中...(NN%)。

顺带两条

  • 重载客户端会把自动布线对话框的设置清空(忽略网络清零、四个布线图层全勾回来)。 重载之后必须重新配一遍再跑。
  • DRC 面板刷不出来时走菜单:设计 → 清除错误(全部 (0))→ 设计 → 检查DRC。 面板上那个「刷新」按钮不是 DRC 的,pcb_Drc.check() 单独调有时也不刷新。

局部返工:把 7 对 USB 差分抢回通道(2026-09-04,用户拍板)

第一趟自动布线之后,7 对差分只布上 2 对(USB1/USB2),其余 5 对 连「所选网络」单独重跑都一根线不画 —— 板子已经被普通信号线占满了。 用户拍板走「局部返工」而不是整体回滚。四步:

① 先改规则(pcb_rules.py --push),否则布完还是一堆假违规:

改动 从 到 为什么
differentailPairLenTolerMax 0.5 mm 5 mm USB 2.0 HS 对内偏斜预算 ≈100 ps ≈ 15 mm;实测偏差 1.6~1.9 mm ≈ 10 ps。为凑 0.5 去绕蛇形线是拿串扰换数字
新增安全间距条目 USBPAIR — 0.15 mm 只通过 overwriteNetByNetRules 挂在每一对自己的两个网络之间;对其它网络仍是 0.2 mm

USBPAIR 那条不能用全表抬下限的循环扫到(会被抬回 0.2),代码里单独跳过了。

net-by-net 的条目形状文档没写,是从 pcb.js 的 getBetweenNetDataItem() 反出来的:

{net1:{type:'net',name:'USB1_DP'}, net2:{type:'net',name:'USB1_DM'},
 rule:'USBPAIR', copperRuleName:null, planeRuleName:null}

⚠️ overwriteNetByNetRules 是整表覆盖,要先 getNetByNetRules() 读回来再合并。 改完 DRC 172 → 166,4 条对内间距 + 2 条等长违规一起消失。

② 三条低压轨改成铺铜(铜面 15 → 19 块):

铜面 网络 层 范围 盖住
5V-L3 +5V L3 x 11151 / y 5.514 10 / 16
3V3HUB-L3 +3V3_HUB L3 x 59111 / y 1531 21 / 21
3V3-CTRL-L3 +3V3 L3 x 95.5150.5 / y 33.567 合计 45 / 61
3V3-CH-L3 +3V3 L3 x 5155 / y 100.3124 同上

都塞在 12V 那三条带子之间的空地里,互相与 12V 都留 ≥0.5 mm。 3V3-CTRL-L3 的右边界 150.5 是被 ESP32 天线禁铺区(x≥151)逼的。

这带出两个工具改动:

  • pcb_pour.audit() 要按网络做并集。+3V3 是两块铜面,逐块判「焊盘在不在里面」 会让每一块都把另一块盖住的焊盘报成「意外漏掉」。
  • 新增 POUR_PARTIAL = ('+5V', '+3V3'):这两条轨的铜面故意只盖一部分, 漏在外面的焊盘靠走线接,不算错。

③ pcb_fanout.py 只对「落在自己铜面里」的焊盘扇出(新增 _pour_filter())。 在没有铜的地方打一个下到 L3 的过孔 = 一个悬空过孔,DRC 反而多一条断连。 三条轨一共扇出 91 个过孔,5 个焊盘周围太挤放不下,留给走线。

④ 局部返工的选取判据(_snap_before.json 是基准):

删掉「布线器加的」且满足下面任一条的**整个网络**的线与过孔:
  a) 网络已经改成铺铜(+5V / +3V3 / +3V3_HUB)
  b) 该网络在 y < 28 mm 的下带里有走线,且不是 GND/12V/VOn 这类扇出网络、也不是差分对

结果:34 个网络 / 678 条导线 / 68 个过孔,只占布线器产物的 18%。 整网删、不留断头,剩下的 80% 一根没动。

⚠️ 差分对必须一次统筹全部 7 对,一对一对布会互相堵死

下带清空之后,选中 5 对跑「所选网络」,只布上 USB4/USB5;再跑一次, 一根线都不加。原因是 保留 模式下先布好的那两对成了后面的障碍 —— USB3 与上游 UP 要横穿它们,而下带就那么宽。

正确姿势是把 7 对全选中 + 「已有导线/过孔 = 移除」,让布线器把已布的两对 一起拆掉重排。clearRoute(false, undefined, this.selectNets) 只清选中的网络, 不会波及别处(pcb.js 里确认过)。一趟下来 6 / 7 对布通(USB1~5 + UPSTREAM), 只剩 ESP32 那条调试口 USB_DP/DM。

顺带澄清一条之前的误判:「所选网络」模式是好的。早先它「跑完却一根线不画」 不是模式坏了,是那时候板子上真的没路了 —— 日志里 routingRange:1、选中 24 个焊盘 都对,弹的也是「自动布线已完成」而不是报错。

布线跑完之后要做的(按顺序)

  1. DRC —— python .claude/drc_read.py。看「间距错误」和「物理错误」两支, 连接性错误剩多少 = 还有多少没布上。
  2. 七对差分单独布(USB1~5_DP/DM、UP_DP/DM、USB_DP/DM)。 规则里已经建好 7 对、W 0.28 / S 0.18 / 等长容差 0.5 mm。 ⚠️ 顺序上其实应该先布差分再自动布线——这一轮是为了先验证布线器能不能跑才反过来的。 如果跑完发现差分对已经无路可走,就按快照回滚,改成「先差分后自动」重跑。
  3. 重新铺铜。底层(layer 2)上有 VO1~5_P 五块铜面,自动布线在底层加了信号线, 铜面要重建才会让开它们:工具 → 铺铜管理器... → 重建所有 (pcb_pour.py --rebuild 里已经把这一串打好了;rebuildCopperRegion() API 是坏的)。
  4. 丝印整理 —— 位号大面积压字,打样前要单独跑一遍。
  5. Gerber 预览复核板框闭合 + 四个 M3 定位孔。

跑之前先存了个快照(.claude/_snap_before.json)

布线前把 708 个过孔 + 575 条导线的 primitiveId 全存了下来。 万一自动布线的结果不能要,删掉不在这张表里的线和过孔就能精确回滚, 比 清除布线(会连扇出一起清)干净得多。


上一轮:开始布线(2026-09-03)

顺序是「先把面铺好,再拉线」—— 平面定了,信号线的参考面和过孔位置才有依据。

1. 先抓到一个真的断线:U13 的 SCL 没接上 ⚠️

追 12V 母线的网络时发现 PCB 里 U13.2 / U13.5 挂在一个只有两个焊盘的 $1N257 上,而 SCL 网络 11 个焊盘里没有 U13。输入侧检流那片 INA226 是死的。

根因不是漏画线,是 SCL 端口的引脚压在一条导线的中段而不是端点 (端口在 (920,340),导线是 [920,320 → 920,570])。EasyEDA 只在端点认连接。 同一页的 SDA 端口恰好落在端点上,所以 SDA 是好的 —— 两个端口在图上一模一样。

DRC / ERC / verify_sch --nets / 跨页对账四道全过,因为 verify_sch 的判据是「引脚落在某条导线段上」,中段搭接满足这一条。

新增 .claude/port_audit.py:全工程 572 个旗/端口逐个判「是不是某条导线段的端点」。 修之前 1 个不合格(就是它),修完 0 个。写进 easyeda-sch-geometry §8a-2 与设计文档 §19.4。

写这个检查时踩了个自伤的坑:第一版按折线解析导线,572 个里报出 197 个可疑, 其中一大片是工作正常的 GND 旗。getState_Line() 返回的是端点对(skill §5), 按折线解析会凭空造出斜线段。误报到这个程度的检查等于没有检查。

2. 8 A 输入链整条重排

原来这一串是仿射映射甩出来的,J1(x5.8) → F1(x46.6) → Q1(x53) → 体电容(x66/75) → Rs6(x84.6~90.7):8 A 要从 x5.8 走到 x90.7,85 mm,其中 $1N223(J1→F1) 单独一段就 42 mm。8 A 在 0.5 oz 内层要 ΔT=20 °C 得 18 mm 宽铜面 (IPC-2221 内层按外层 50% 取,面积翻倍),band B 一共才 36 mm 高,塞不下四条。

而且这一串在 y 上来回折(F1.2 在 y60.08,Q1 源极在 y62.85,中间还夹着 $1N236 在 y60.56 的一个焊盘),L3 上按 y 切开分块,切线最窄处只剩 0.2 mm。

改成 J1 右手边一条走廊 x 11.546 / y 49.568.5(栅极网络放 J1 上方口袋):

之前 现在
$1N223 J1→F1 42 mm 6.9 mm
8 A 段的横跨 x 5.8 ~ 90.7 x 12 ~ 40
分叉点 x 90.7 x ≈ 40(Rs6 下游)
母线最大截面电流 8 A 全程 顶带 ≤ 6.4 A(往左喂 CH1、往右喂 CH2~5)

顺带把 13.7 × 9.7 的立式泄放电阻 R9 也钉死了 —— 仿射区右缩之后它被推到 x95, 本体框伸到 x102 压进 P3 区。relax() 只夹中心坐标、不看外框,这类件必须手钉。

3. 平面层(.claude/pcb_pour.py)

铜面 层 网络 范围
GND-L2 L2 GND 整板(板框内缩 0.5,挖 ESP32 天线区)
VIN-RAW-L3 L3 $1N223 x 314 / y 45.555.5
VIN-FUSED-L3 L3 $1N227 x 14.619 / y 49.556.5
VIN-SW-L3 L3 $1N236 L 形,x 14.435 / y 4966.5
12V-L3 L3 +12V 下带 x14.695 y3348.3 + 中带 x38.595 + 顶带全板 y67.581.9
VO1~5-L3 L3 VOn 每路 30 mm 宽 / y 82.5~99.5
VO1~5_P-L4 L4 VOn_P 每路 30 mm 宽 / y 110~116.2,带一条下伸臂到分流电阻

分界线是焊盘边定的不是整数:+12V 顶边 81.9 卡在 C102 那排 1210 焊盘(y80.281.7); VOn 底边 82.5 是因为 C107 pin1 是 rot90 的 1210,焊盘跨到 y83.0784.55 —— 按「元件中心在 y85.4」算会把它漏在两块铜面之间的缝里。

U8~U13 的 0.5 mm 间距 Kelvin 脚一律不进铜面(U13.10 是 VIN+、U13.9 是 VIN−, 相距 0.5 mm,任何分界线都塞不进去),走顶层细线,本来也不载流。 pcb_pour.py 里 audit() 会把「本网焊盘在铜面外」的逐条列出来, EXPECT_OUT 是白名单,漏在名单外的就要查。

4. 铺铜 API 的三个坑(都实测过)

  1. rebuildCopperRegion() 是坏的,四种写法都抛 Cannot read properties of undefined (reading 'length')。 唯一能填充的路子是 GUI:工具 → 铺铜管理器... → 重建所有 → 确认, 已用 React 的 onClick 打进 pcb_pour.py。判据是 nPoured 不是 nPour。
  2. 多边形必须显式闭合(末尾把首点再写一遍)。文档说「首尾不重合会自动重合」 对铺铜边框不成立:不闭合就直接不建。board_poly() 一直能用是因为 它最后那段圆弧正好回到起点。
  3. create() 成功了也会抛 错误:无法创建覆铜边框图元。返回值和 try/catch 都不能当判据,只能建完 getAll() 按名字核对 —— 而且名字会被转成大写。

排查时分不清 1 和 2 的办法:不闭合的矩形建完 getAll() 数量不变,闭合的 +1, 两者抛一模一样的异常。

5. GND 扇出:改布局之后必须按这个顺序重跑

python .claude/pcb_fanout.py --clean      # 1. 先清空板上的过孔与走线
python .claude/dump_pads.py               # 2. 再重新抓障碍表
python .claude/pcb_fanout.py --push       # 3. 最后规划 + 下发

--clean --push 写成一条会用旧的 pcb-copper.json:清是清干净了, 规划器看的还是那 459 个已经不存在的过孔,把它们当障碍绕 —— 实测 459 → 292 个过孔、五路 Buck 的 pin2 全扇不出去、27 个散热盘只打进 4 个孔。 反过来先 dump 再 --clean --push 更糟,板上会剩两套(747 个)。

正确顺序的结果(15 个网络一趟跑完):708 个过孔 / 0 个失败, 其中 GND 459、+12V 128、输入链三条 48、五路 VOn 各 17、五路 VOn_P 各 8。

6b. 出线宽度必须按焊盘算,不能按网络算

第一版给功率网络一刀切 1.0 mm(= 线宽规则的最小值),结果 29 个焊盘扇不出去: +12V 上挂着 R104 那种 0402 分压抽头,焊盘只有 0.57 × 0.54 mm, 1.0 mm 的线比焊盘还宽。退回 0.3 mm 硬扇出去,又被判 「线宽 11.8 mil,应在 39.4 ~ 393.7 mil」—— 34 条物理错误。

两处一起改才对:

  1. PWR4A / PWR12V 的最小值 1.0 → 0.3(默认值仍是 1.5 / 2.0)。 这就是 §10.1 那条「载流靠灌铜保证,不靠 DRC」再走一步。
  2. 出线宽度按焊盘短边 × 0.9 取,夹到 [0.3, 1.0];过孔数按焊盘短边 ≥ 0.8 mm 给 3 个、 否则 1 个 —— 0402 抽头本来就不载功率电流,多打过孔只会把平面打成筛子。

6c. 扇出的障碍模型漏掉的两件事(DRC 抓出来的)

现象 根因
10 条「导线到过孔 3.4 mil,应 ≥ 7.9 mil」 板上已有的过孔只按钻孔半径 0.15 登记进 holes,没按焊盘半径 0.3 登记进 grid。差的正好是 0.15 mm
1 条「导线到导线 6.2 mil」 出线之间的间距压根没查过 —— 原来只查了线对焊盘、线对钻孔

两条都补进 pcb_fanout.py 了(trk_grid + seg_seg_dist)。 补完重跑:间距错误 0、物理错误 0。

6. 顺手发现的两条 EasyEDA 路子

  • 设计 → 从原理图导入变更(Alt+I) 能用 DOM 打开并读出变更表, 逐条列 U13:2 $1N257 → SCL。改完原理图必须走这一步,PCB 才会跟着变。
  • 布线 → 扇出布线... / 自动布线... / 差分对布线 / 等长调节 都在菜单里。 信号线那 400 多条不必自己写布线器,下一轮直接驱动它。

最新一轮:设计规则不用手动了(2026-09-03 深夜,收尾)

上一轮结论「规则配置只能在 GUI 里手建」是错的。原因是参数形状给错了, 现在 python .claude/pcb_rules.py --push 一条命令把五项全下发,--check 七项全 OK:

[OK] 四层板能力集已就位(最小线宽 0.102 / 过孔 0.30-0.20,含 0.5oz 内层条目)
[OK] 线宽规则 PWR12V 存在      [OK] 线宽规则 PWR4A 存在
[OK] 差分对几何 W 0.280 / S 0.180 mm
[OK] 安全间距下限 0.2000 mm
[OK] 差分对 7 对: DEBUG UPSTREAM USB1 USB2 USB3 USB4 USB5
[OK] 线宽指派 12 个网络

当初为什么以为不行 —— 两个坑叠在一起

  1. overwriteCurrentRuleConfiguration(x) 要 config 本体,不要 {name, config} 包装。 传包装返回 undefined 且静默无效;传本体返回 true。 (saveRuleConfiguration(cfg, name, allowOverwrite) 确实是三个参数 —— 上一轮只传了一个 —— 但补齐了照样返回 false。它是真的不能用, 好在我们并不需要存命名配置。)

  2. 两种 config 的形状不一样,混用会被默默吃掉。

    来源 线宽 安全间距表
    getRuleConfiguration('<系统能力集>') 平:form.strokeWidthDefault entry.table
    getCurrentRuleConfiguration() 嵌套:form.data['1'].defaultValue entry.tables['1'].content

    正确姿势:拿 getCurrentRuleConfiguration().config 改,原形状送回去。 系统能力集只当数据源用(那张内层 0.5 oz 间距表是从它身上抄的)。

「切到多层板能力集」其实只是五个字段

把两个能力集逐值 diff 过:只差 3 个数 + 2 条默认不启用的条目,其余 300 多个数完全一致。

项 Two Layers Multiple Layers
Track/copperThickness1oz 最小线宽 0.127 0.102
过孔外径最小 0.5 0.3
过孔内径最小 0.3 0.2
Track/copperThickness0.5oz 无 有(min 0.089,isSetDefault=false)
Safe Spacing/copperThicknessInner0.5oz 无 有(isSetDefault=false)

也就是说,能力集之间的差别全是放宽限制,两层板那套只是更保守。 真正必要的是最小线宽 0.102(U32 那个 0.5 mm 间距 QFN-64 的扇出要用)。

⚠️ 下发之后千万别做的一件事

别再去 GUI 的 设计 → 设计规则… 里动“配置”下拉框。 换一下就是整套重置:PWR4A / PWR12V 两条线宽规则会被冲掉, 12 个功率网络的指派立刻变回悬空(DRC 不报错,线宽也就不生效 —— 查不出来的那种坏)。 要改规则就改 pcb_rules.py 再 --push。

配置名下发后会自动变成 “自定义配置”,这是对的 —— 说明它已经从系统能力集上摘下来了。

顺带踩到的一个坑:mm 值会被换算成 mil 再换回来

写进去 0.2,回读是 0.19999959999999997(差 4e-7 mm)。 任何几何值都不能做精确比较,--check 的容差已放到 1e-4。 (第一版容差 1e-9,结果安全间距那项在明明写对的情况下报红。)

DRC 结果读得回来(.claude/drc_read.py),而且真抓到了 9 个错

先说一句:本轮早些时候写的「DRC 结果读不回来」是错的,已推翻。

pcb_Drc.check() 确实只返回一个 boolean,40 多个方法里没有结果 getter, 也没有 DRC 标记层可读。但 DRC 面板在 DOM 里是挂载着的 (跟设计规则对话框不一样,那个窗口不前台就不挂载)。三个手法:

  1. 左边分类树要用 React 的 onClick 点 —— 从元素上找 __reactProps$*, 一路往上找到带 onClick 的那层直接调。el.click() 不管用,不点就不选中, 右边明细区一直是「空列表」。
  2. 「清除错误」那个反过来,它是原生 <button class="i18n">,没有 React props, el.click() 直接生效。同一个面板上两种按钮混着,别一条路走到黑。
  3. 明细表 innerText 尾巴上粘着分页组件的一大段 CSS,碰到 input::-webkit 截断。

跑法:/c/Python314/python .claude/drc_read.py(--no-check 只读不重跑)。

那一片黄叉:是 check() 画的,不是板子坏了

跑完 check(),每个未布线焊盘都盖一个黄叉,没布线时 1200+ 个, 满屏都是,看着像出了大事。分类是:

全部 (1273) = 间距错误 (9)  +  连接性错误 (1264)
                 ↑真错误          ↑还没布线,预期之内

只看「间距错误」那一支。 看完用面板上的「清除错误」清掉。

9 个真错误,两类,都已修掉(1273 → 1264)

① 贴片焊盘到贴片焊盘 ×5 —— 五路的 Cin 挨太近,差 10 µm

C%01 / C%02(Buck 输入去耦,§10.2-1 手调的热回路)两片焊盘只剩 0.1905 mm,安全间距要 0.2 mm,差 10 µm,五路一起报。 这是把全局间距从 0.152 抬到 0.2 mm 的直接后果 —— 老规则下它是过的。

改法:pcb_place.py 里 C%02 的 y 从 14.9 挪到 14.95,缝变 0.2405 mm。 0.05 mm 对约 4 mm 周长的热回路可以忽略。模型和板子都已改(五路各挪一颗)。

② 钻孔到钻孔 ×4 —— 四个 M3 定位孔各有一个重影

四个角上各有两个 MHn 焊盘,焊盘一样大(6.0 mm),钻孔一个 3.2 mm(对的), 另一个 0.32 mm —— 正好差 10 倍,当初建孔时 mm/mil 滑了一位, 改对之后旧的那 4 个没删。两个孔完全同心,距离 0 mil。 已删掉 4 个 0.32 mm 的,MHn 从 8 个回到 4 个,全是 3.2 mm / GND。

⚠️ 这两类本地几何检查一个都抓不到 —— pcb_place.py 的 check() 只看 本体框重叠和越框,不看焊盘间距,更不看钻孔。布局改完要跑一次 drc_read.py。

布线时如果 U32 扇出被 0.2 mm 间距卡住

0.5 mm 间距 QFN 出线,相邻焊盘到走线边缘算下来约 0.233 mm, 比 0.2 mm 只多 33 µm,能过但很紧。真被刷屏了不要去改全局间距 —— pcb_Drc.getRegionRules / overwriteRegionRules 可以在 U32 周围开一块局部规则区。


最新一轮:缩板 + 定位孔 + 设计规则(2026-09-03 深夜)

1. 板框 200×150 → 160×125,四角 R5 + 4 个 M3 定位孔

用户拍板的尺寸。定位孔 M3 金属化接 GND:孔 Φ3.2 / 焊盘 Φ6.0 / 禁布 Φ6.5, 孔心 (5,5)(155,5)(5,120)(155,120),与圆角同心。

⚠️ 四角定位孔和 band A 的连接器是几何上打架的,5 × 32 = 160 正好铺满板宽, CH1 / CH5 的顶角必然被连接器本体压住。解开靠两条(都写在 pcb_place.py 的 CH1 表里):

  1. J7~J11(按键出线)是立式座,本来就不需要板边 —— 挪进通道内部 (25.5, 31), 顶边只留 J2~J6 一个 12 mm 宽的右角式座;
  2. J2 摆在通道中央(local x=15,本体 x 9~21),左右各让出 9 mm。

功率链那一段(local y 3~42)一个件都没动 —— §10.2 的四条关键回路是手调出来的, 缩板不该动它们。按键驱动那 9 个件从「J2 右边一横排」改成右列 x 24/28 的纵排。

check() 里新加了定位孔禁布区这一项。孔不是元件,原来那两项检查都看不见它。

2. 又是本体框:J2 离板边差了 4 mm

FP_BODY 里 Micro-Fit 的本体我上一轮手量成 12 × 13.5 mm,渲染图上重量是 9.8 × 10.1 (对得上 43650 两位右角式的 8.6 × 10.1)。偏大 4 mm 的后果是 check() 以为贴着板边、 实际差 4 mm。已改成 (-4.95, -9.0, 4.95, 1.4) 并把 J2 挪到 local y=49 顶住 y=125。

这是 FP_BODY 第二次出错了(第一次是 U20 压出板框)。手量的本体框都要复核一遍, 偏大是安全方向(连接器缩进去),偏小是危险方向(本体悬空出框)。

3. Hub 挪到板中央 —— D± 最长砍掉一半

原来 U32 在最左边(x=31),到 CH5 要走 112 mm。挪到板中央 x=79 之后:

USB1 USB2 USB3 USB4 USB5 上游 J23
D± 长度 64 32 0 32 64 mm 16 mm

⚠️ U32 正上方就是 CH3 的负载开关块,所以去耦不能像原来那样在正下方排一横排 —— 改成上面一排 4 颗 + 左边一列 3 颗。

4. 设计规则与差分对(.claude/pcb_rules.py)

API 能做的已经做了:

  • 7 对差分对:USB1~5(Hub → 五路出线座)、UPSTREAM(J23 → Hub)、DEBUG(J22 → ESP32)。
  • 12 个功率网络指派线宽规则:VO1VO5 / VO1_PVO5_P → PWR4A;+12V / +5V → PWR12V。

API 做不了的(当时这么写的,2026-09-03 深夜已推翻,见下一节):

saveRuleConfiguration / overwriteCurrentRuleConfiguration 全部返回 false 或 undefined,什么也不做。 —— 是参数形状给错了,不是 API 不行。

5. 外层铜厚:2 oz → 1 oz(用户 2026-09-03 拍板)

理由:U32 是 0.5 mm 间距的 QFN-64(2 oz 外层最小线宽/间距 0.2 mm 起,扇出很吃力, 1 oz 是 0.102 mm),而且 90 Ω 的几何随铜厚变(2 oz 下同样 W/S 只剩 83 Ω)。 设计文档 §10.1 已改写,附了完整的载流表。

这条改动连带否掉了一个数:原来 PWR12V 定的 2.5 mm,在 1 oz 下 8 A 温升 35 °C,不能用。已改成:

规则 值 说明
PWR4A 默认 1.5 / 最小 0.3 / 最大 6.0 mm 4 A 温升 16.7 °C ✓;最小值 2026-09-03 由 1.0 降到 0.3,见设计文档 §10.1 的补注
PWR12V 默认 2.0 / 最小 0.3 / 最大 10.0 mm 只给分支(每路 1.6 A);0402 抽头引不出 1.0 mm 的线

⚠️ 12V 总干那 8 A 不许当走线画 —— 1 oz 下要 3.49 mm 才够 20 °C。 必须走灌铜:Inner2-PWR(0.5 oz)上要 ≥18 mm 宽,或顶层灌铜 ≥3.5 mm。 没把 PWR12V 的最小值设成 3.5 mm,是因为 +12V 上还有一堆进 0402/SOT-23 焊盘的 短支线,卡 3.5 会刷出几百条假 DRC 错误,规则就没人看了。 载流靠灌铜保证,不靠 DRC。

6. 还有一条要用户确认

  • 90 Ω 的 W/S 必须拿板厂叠层复核。0.28/0.18 是按嘉立创四层 1.6 mm 标准叠层 (h=0.2104 mm、εr≈4.3、1 oz 外层)的微带近似算的,叠层一变就得重算。

上一轮(2026-09-03 晚)做了什么

新增了三个脚本(都在 .claude/):

脚本 干什么
refresh_cache.py 重抓 pcb-parts.json / pcb-pads.json / sch-xy.json。改完原理图 + importChanges 之后必跑,否则 pcb_place.py 看到的还是旧元件表
shot_pcb.py 截整块 PCB(shot.py 只能截原理图页,对 PCB 会报错)
shot_pcb_region.py <out> <x0> <y0> <x1> <y1> 截 PCB 的指定 mm 区域。方向错误只有放大了才看得出来,整板图上一个 SOT-23-6 才几个像素

1. PCB 导入终于点下去了:357 → 391 元件

上一轮卡在「importChanges() 弹了确认框但点不了」(屏幕会话拿不到,CopyFromScreen 报 handle invalid)。这轮换了个办法绕过去:桥接执行的 JS 能拿到主窗口的 document, 所以弹窗可以直接用 DOM 点:

[...document.querySelectorAll('button')].filter(b => b.innerText.trim()==='应用修改')[0].click();

弹窗本身在 div[class^=lc_modal_dialog__body] 里,变更表是虚拟滚动的, DOM 里只有可见的那几行(想抓全表得滚动收集,实测容易撞 30 s 桥接超时,没必要)。

结果 357 → 391(新增 39、删掉 5 个 USB-A)。上一轮预估的 386 少算了几个。

做了一次逐位号对账(.claude/refresh_cache.py 顺带的能力): 原理图进 PCB 的 391 个 ↔ PCB 的 391 个,重号 0 / 漏 0 / 多余 0, C801~C806 正确地没进来。

2. 【C】带按板载 Hub 重排(.claude/pcb_place.py)

USB-A 腾出来的 75 mm 板边给了 Hub。现在是:

x  2..28  【C1】12V→5V(U30 + L3 + Cin/Cout)—— 靠左板边,离 J1 的 12V 近
x 30..62  【C2】板载 Hub:J23 USB-C 在下板边 / U32 QFN-64 / Y1 / U31 3V3 LDO
x 64..198 【C3】五路 TPS22918 + J17~J21 出线,节距 27 mm

D± 全程留在这一带里,不纵穿五路 Buck 的电感与 SW 铜箔(§15.3)。 Hub 那 27 个件全部手排:仿射映射会把 strap 电阻甩进 U32 与晶振之间那条 1.8 mm 的缝里,而两个落在同一点的器件在推挤迭代里是对称的、永远分不开 (R719/R720 就卡在那儿,relax() 跑 3000 轮也出不来)。

3. 截图看出来的四个真错误(几何检查全都查不到)

# 错误 为什么本地检查抓不到
1 U30 的 Cin 摆在了封装的另一侧,热回路绕整个封装 检查只管重叠/越框,不管「哪个焊盘挨着哪个」
2 五路负载开关的四个卫星件全部左右镜像(CIN/ON 该在右、COUT/CT 该在左) 同上,而且五路一起错
3 J22 调试口开口朝板内 封装本体外形 API 取不到,box() 用的是对称框
4 U20 ESP32 模组整块压出右板框 2.7 mm 同上 —— 模组原点在焊盘形心,天线端往外还有 7 mm

根因是同一个:rot=0 时 SOT-23-6 的 1/2/3 在右列、4/5/6 在左列, 和画原理图时的直觉相反。现在是照着焊盘坐标读回来写死在注释里的,不是猜的:

U30 / U24~U28 (SOT-23-6, rot=0)
  右列 自下而上  pin1(-0.94) / pin2(0) / pin3(+0.96)
  左列 自下而上  pin6(-0.94) / pin5(0) / pin4(+0.96)

3 和 4 已经在 FP_BODY 里补了两条手量的不对称本体框(USB-C、ESP32 模组), 以后再动这两个器件,check() 就能自己拦住了。

读回焊盘框有个坑:getState_Pad() 给的 w/h 是未旋转的, 所以对 rot≠0 的元件,我那段「px−w/2」算出来的框是错的(J22 的 x/y 跨度和 同封装的 J23 对不上就是这么发现的)。要拿准确的框,得先把元件转回 rot=0 读一遍再转回去。 pcb_place.py 的 FP_PAD 只收 rot==0 的实例,正是为了避开这一条。

4. P4B-HUB 的压字

6 行注释横穿 J23 的符号(几何验证不看文字)。注释挪到 U32 右下的空白带 (sch (460, 210~300))—— 一次挪过头压到标题栏上,又上移了 30。

每次重画 P4B,importChanges 都会报 4 行 J23/R717/R718 修改网络: CC1/CC2 是没有旗/端口的页内两脚网络,w('CC1', …) 那个名字不会落到网表上, 每次重画都重新分配一个 $15Nxxx。是噪声不是错误,已按提示应用。 想根治就给这两条网络各加一对端口。


前四轮做了什么(都在 2026-09-03)

0. P5-HARNESS(新增,原理图 10 页画齐了)

线束页,§20。四个分区:【A】三条线束的板端↔手机端连接图、【B】手机侧电容组(真电路)、 【C】五路对照表、【D】五条红字警告。除【B】外全是非电气图元 (矩形 / 折线 / 富文本),不参与网表。模型在 .claude/p5_harness.py。

手机侧电容组 C801~C806 是离板件:addIntoBom=false / addIntoPcb=false, 读回确认过。只画了 CH1 一套,CH2~CH5 位号见页上的【C】表。

顺手补了一个真缺口:VOn_P(Rs01 之后 → J.1 / Q*02 / INA226 的 VIN−、VBUS) 原来是页内一段没名字的网络,P5 的电容组挂不上去。已在五页各加一个 VOn_P 端口((960,745),dir='OUT',从 J2 那根横线上引 50 长支线), 五页重推 + 六项验证全过。

⚠️ VO2_PVO5_P 全工程只有 1 个实例(P5 只画了 CH1 一套电容组), 这不是漏接。.claude/xpage.py 新增 NAMING_ONLY 集合把它们列为例外, 哪天补画 CH2CH5 的电容组就删掉。


前三轮

1. 五路复制(新增 P2B~P2E 四页)

用 dmt_Schematic.copySchematicPage 从 P2 复制,再由 .claude/p2gen.py(模型)+ .claude/p2push.py(下发)按通道号改写位号 / 网络名 / 地址跳线。

复制时发现母版上有两个真问题,五页一起修了:

  1. K1_VUP 端口其实没连上。 它的引脚坐标和 R122.1 完全重合, 靠一段零长度导线"连着"。几何验证在旧 P2 上不报,是因为那段零长度线 恰好落在引脚上;真按这个网表打样,那一路音量+ 很可能是断的。 三个按键端口 + VDAC1 现在统一改走向上 40 的短支线,末端才放端口。
  2. 两处标签压字:VDAC1 与 K1_PWR 的标签面对面撞在一起(渲染成 VDACK1_PWR); J7 的地旗文字压在 Q105 的位号上(渲染成 G5)5)。

⚠️ 所以 CH1 也重画了一遍(元件没动,只重画了导线 / 旗 / 端口 / 文字)。 用户之前确认过的是旧版 P2,这一页要重新看一眼。

2. INA226 地址按布线难度重排(§8.2)

INA226 的 A1/A0 各能接 GND / VS / SDA / SCL,但只用 GND 和 VS 只能凑出 4 个地址 (0x40/0x41/0x44/0x45),全板要 6 个,必然有两片得把地址脚搭到 I²C 线上。 原来的顺排(0x40~0x44)会让 CH3 接 SDA、CH4 接 SCL,而 A0 脚上下正好夹着 Alert 和 SDA 两根横线,往 SCL 拉必然跨线。

CH1 CH2 CH3 CH4 CH5 U13(输入侧)
A1 GND GND VS VS GND GND
A0 GND VS GND VS SDA SCL
地址 0x40 0x41 0x44 0x45 0x42 0x43

这样五个通道里只有 CH5 需要搭 SDA(0 交叉),剩下那个 SCL 落到 U13 头上—— 它画在 P1 的空白区,布线自由。 代价:通道号不再等于地址低位,固件里要查表,不能算。

3. P1 补输入侧检流(§19)

U13 INA226 + Rs6 5mΩ + C11,量 12V 母线,地址 0x43(A1=GND / A0=SCL)。 体电容在 Rs6 上游,所以量到的是负载电流;满板约 8A → 40mV(满量程 81.92mV)。 U13 转了 90°,VIN+/VIN−/VBUS/GND/VS+ 五个脚朝上正对分流电阻,I²C 朝下走空白区。

又挖出一个同类真错误:P1 上 BLD_REF/BLD_A 两个端口的引脚坐标和 U29(TL431) 的 REF / 阴极完全重合,靠零长度导线"连着"。已挪开 40 并补真导线。 这类错误几何验证抓不到(零长度段会让"引脚落在导线上"通过),只有重画那一页才暴露—— 规律已写进 skill §8a。

4. 之前:P4-USB(§18)

五路 TPS22918 负载开关,一路一列,D±/GND 直通、只切 VBUS,全页 0 处交叉。 ON 下拉 / CT 摆率 100pF / QOD 短接 VOUT / CIN>COUT 四条按 datasheet 定稿。

⚠️ TPS22918 的引脚号:datasheet 引脚图的文字被排版拆开后读起来像 1 VOUT / 2 QOD / …,和 Pin Functions 表相反。按文字坐标还原后确认表是对的: 1 VIN / 2 GND / 3 ON / 4 CT / 5 QOD / 6 VOUT。接反了图上看不出来。

✅ 板载 USB Hub(2026-09-03 做完原理图部分)

外部只留一根 USB 线。用户拍板:USB2517I 单片 7 口 + USB-C 上游口。 硬件配置逐条对 datasheet 复核,写在设计文档 §21;5V 轨与 LDO 见 §9.4 / §9.5。

最终方案

项 结论
Hub U32 = USB2517I-JZX(C478081,QFN-64);口 1~5 给手机,口 6/7 用 10k 上拉禁用
配置 CFG_SEL[2:0]=000 → 内部默认配置 + strap 使能;寄存器 06h 默认 9Bh、bit7=1 = 自供电
上游口 J23 USB-C(UFP),CC1/CC2 各 5.1k 下拉,不做 PD
下行 VBUS 板上 5V 轨(U30 = 第二片 LMR51430,12V→4.98V),经 U24~U28 由 ESP32 通断
Hub 的 3.3V U31 AMS1117-3.3 专供(Hub 满配约 420mA,挂不上 U19 那条 0.5A 控制轨)
Hub 复位 U22.Q7(74HC595 唯一那个空着的输出)→ HUB_RST,R716 10k 上拉
删除 J12~J16 五个 USB-A 母座

三个 datasheet 陷阱(都已避开,细节在 §21)

  1. 别下错手册:Microchip 站上 00001692C.pdf 是 USB251xB 系列(另一世代)。 我们这颗要看 2517.pdf(SMSC USB2517 Rev 2.8)。
  2. 引脚图和引脚表打架(RBIAS 图上读作 64、表里是 63)。 按坐标还原引脚图后确认表是对的 —— 和 §18 的 TPS22918 同一个套路。 库里的符号也与表一致,三方对上了才动笔。
  3. 必须是自供电。声明成总线供电,主机按每口 100mA 做功率预算, 而手机描述符要 500mA → 主机拒绝配置,手机根本枚举不上。 靠 CFG_SEL=000 走内部默认值拿到自供电,不需要 EEPROM。

验证结果

页 结果
P4B-HUB 27 元件 / 112 导线段,六项全过,0 处交叉,32 个脚在 NC 清单
P1B-5V 12 元件 / 44 导线段,六项全过,2 处合法交叉(+12V×SW5V、SW5V×CB5V)
P4-USB 30 元件 / 180 导线段,六项全过,0 交叉
P3B-EXPAND 重推(加 HUB_RST),六项全过
跨页对账 0 个漏接(12 页);+5V/+3V3_HUB/HUB_RST/USB1~5_D± 全部两端对上

顺手修了一个遗留真错误:P3B 的 C611(DAC 的 CAP 旁路 2.2µF)在页面上被挪动过, 位置与 p3b_nets.py 的接线对不上,两个脚都是悬空的。上一轮没重跑 P3B 的验证所以没发现。 已按 skill §8d 用 rot=270 摆回横放(这颗 0805 传 rot=0 反而是竖的)。

这一页为什么满是网络端口

U32 是 65 脚 QFN,33 脚在左、32 脚在右,脚间距只有 10。外围器件直接连脚必然打结。 做法:每个用到的脚只引一小段 stub,末端放网络端口当页内标号, 器件按功能分块摆在四周,块间靠端口名对上。 netport 约 10 单位高,在 10 的脚间距上排一列不重叠(skill §8b-2);netflag 有 20 高,放不下。 J23 的 USB-C 正反脚也用端口重名并联,省掉了 Type-C 固有的一堆交叉(P3 的 J22 当年是靠绕线解的)。

这三件收尾都已经做完(2026-09-03 晚)

  • PCB 导入已应用(357 → 391),逐位号对账通过。
  • 【C】带已按 Hub 重排并下发,391 个全部在框内、无重叠。
  • 三张空白截图已重截,都是好的。

⭐⭐ 第二件事:主板载板(2026-09-03 用户提出,尚未动手)

需求原话:「底板是现在设计的电源板,另外做一块板子,按照主板定位孔设计, 将 5 块主板固定在这块板子上,固定主板的板子直接对插到我们现在设计的这块板子」。

即两块板叠起来:

        ┌─────────────────────────────────────┐
        │ 载板:按小米8 主板定位孔打孔,装 5 块主板 │
        │  每台的 P+/P− 焊盘 + §6.3 电容组       │
        │  侧键触点(顶针?)+ USB              │
        └───────────┬─────────────────────────┘
                    │ 板对板对插(电源 4A×5 + 信号约 30 根)
        ┌───────────┴─────────────────────────┐
        │ 底板:现在这块 200×150 五路电源板      │
        └─────────────────────────────────────┘

这是好事,但先说清楚它推翻了什么

推翻的:

  • §15.4 的线束基本消失 —— 现在图上的 J2J6(Micro-Fit)/ J7J11 / J17~J21 共 15 个连接器,会被板对板连接器取代。
  • P5-HARNESS 那页要重写(它现在画的是线束)。
  • PCB 布局的连接器分边决策作废。我 2026-09-03 排的「上边=电源+按键、 下边=USB 子系统」是为了线束走向和 D± 不穿功率区;改成板对板之后, 接口位置由机械叠层决定,不是由「线从哪出」决定。【A】【C】两带都要重排。

变好的(值得高兴的一条):

  • §6「最容易翻车的地方」大幅缓解。 原方案是 20 cm 线缆 ≈ 300 nH, 射频突发 2 A/µs 时压降 0.6 V。改成板对板后回路电感掉到大约 30~50 nH 量级, 同样 di/dt 下压降约 0.1 V。粗算 L=50nH / C=2200µF: f_res 从 6.2 kHz 升到约 15 kHz,Z0 从 11.7 mΩ 降到约 4.8 mΩ, Q = 4.8/(走线+触点 R + ESR 60mΩ) ≈ 0.07,比原来还过阻尼。 → §6.1 / §6.2 的数要重算;C801 用普通铝电解做阻尼这条依然成立(更保险了)。
  • §6.4「大电容必须在排线端」自动满足 —— 电容组直接放在载板上 P+/P− 焊盘旁边, 离得比原来更近,而且是贴片工艺,不用手焊线束。

✅ 头号阻塞项已解除(2026-09-04):机械数据一直就在我们手里

下面这段旧结论是错的,留着当教训:曾经断定 「Xiaomi_Mi_8_Schematic.pdf 是电气原理图,不含任何机械尺寸,只能实测」。

实际上那份 PDF 共 46 页,前 2 页是正反面位号图(含完整板框轮廓 + 全部位号), 后 44 页才是原理图。当初只按「原理图」去读,get_text() 一看全是位号就跳过了, 从没渲染出来看一眼 —— 一张图看 5 秒就能推翻的结论,靠翻文字翻了一整轮。

已经从中反推出:比例尺 17.0 pt/mm、主板 ≈ 45.0 × 66.8 mm、 J600 / J1401 / TP1204 / TP1205 / J601~J604 的坐标与所在面。方法与数据见设计文档 §22.3。

⚠️ 仍需用户做一件事:用卡尺核一次主板长宽。45 mm 对 74.8 mm 的机身偏窄, 怕比例尺有系统性偏差;后面所有孔位都按这个尺子换算。

六个待定项

  1. 电池那一路怎么接。 §13.5 已经论证过:必须保留原厂 BM3E 排线 (立创只有母座 5050040812,没有对插的公座;而且 BATT_ID 可能是 1-wire 认证 IC,复制不了)。所以载板上做 P+/P− 焊盘, 排线一头焊在载板、另一头插回主板 J600。 ⚠️ §13.5 那条「别焊错边」的坑原样保留 —— 只是从焊线束变成焊到载板焊盘。
  2. 侧键触点怎么接。 J601~J604 是主板上的弹片触点。 载板上用**弹簧顶针(pogo pin)**顶上去最干净,但要求机械对位准 (定位孔正好提供这个基准)。前提是主板装上去时触点那一面朝着载板 —— 要先确认第 2 项。
  3. USB 怎么从尾插小板引到载板。 USB-C 母座在原厂尾插小板上, 小板靠 FPC 插主板 J1401。最省事:一根短 USB 线从小板 USB-C 到载板上的连接器。 要不要把尾插小板也固定在载板上,一起定。
  4. 板对板连接器选型 —— 这条最难。 每台 2 根电源(4A)+ 4 根按键 + 4 根 USB = 10 根, 五台共 50 根,其中电源合计要过 20 A。单个信号 BTB 扛不住。 现实做法是电源与信号分开:电源用大电流连接器或多针并联, 信号用普通 BTB;D± 那 5 对还要满足 §15.3 的 90Ω 差分。
  5. 叠层间距(立柱高度)。 底板上现在有一批高个子: C2/C3 470µF 直插电解(约 1012 mm)、R9 水泥电阻(约 9 mm)、 ~~J12J16 USB-A 母座~~(Hub 改造后已全部删除,板上最高的连接器变成 J23 USB-C,约 3.2 mm)、 U20 ESP32 模组。要么立柱够高,要么把这些挪到不被载板压住的区域。
  6. 和「板载 USB Hub」那件事的关系。 两件事都动 USB 接口,建议一起设计: Hub 改造让外部只剩一根线,载板改造让内部不用线束 —— 合起来才是 「一根 USB + 一根 12V 进来,五台手机插上去就行」。

建议的推进顺序

  1. 先量主板(上表 1~4)。拿不到机械数据,载板一步都走不了。
  2. 量的同时可以并行做 Hub 改造(那件事不依赖机械数据)。
  3. 机械数据到手后:定叠层与板对板连接器 → 画载板外形与孔位 → 反过来改底板的接口区布局。
  4. 底板的功率部分(P1、五路 Buck、控制)不受影响,不用重画。 受影响的只有「接口怎么出板」这一层。

改完要同步的地方

位置 改什么
设计文档 §6.1 / §6.2 线缆电感 300nH → 板对板约 50nH,压降与 Q 重算
设计文档 §6.3 / §6.4 电容组从「装在排线端」改成「装在载板上 P+/P− 焊盘旁」
设计文档 §13.3 侧键从飞线改成顶针(如果定了顶针方案)
设计文档 §15.4 线束表基本作废,改成板对板接口定义表
设计文档 新增一节 载板:机械、孔位、叠层、板对板连接器
原理图 P5-HARNESS 从「线束页」改成「载板接口页」
原理图 P2 系列 / P4 J2J6 / J7J11 / J17~J21 换成板对板连接器的一部分
PCB 接口区重排;载板是第二块 PCB(EasyEDA 里新建一个 Board)

PCB(2026-09-03 转板完成)

PCB1(uuid 7e9415a16a909c51)挂在 Board1 下,关联原理图 c63aa0a04d951314。

项 结果
铜层 pcb_Layer.setTheNumberOfCopperLayers(4) → 顶层 / Inner1-GND / Inner2-PWR / 底层
元件 357 个,与原理图逐个对上,无重号
网络 227 个
日志 致命错误 0 / 错误 0 / 警告 0
P5 的 C801~C806 ✅ 没有进来(addIntoPcb=false 生效)

转板要知道的三件事

  1. pcb_Document.importChanges() 返回 true 只表示「弹窗开了」,不代表导入完成。 它会弹一个「确认导入信息」对话框列出全部变更,要点右下角的 「应用修改」 才真的写进去。 API 里没有确认这一步的接口,canvas 截图也拍不到弹窗(截图只有画布)—— 要看弹窗得截整屏(System.Drawing 的 CopyFromScreen)。
  2. 导入前先 sch_Document.save(),否则 API 改的内容可能还没落盘。
  3. 「元件 $Ixx 唯一 ID 与其他元件重复」那 578 条提示是良性的。 来源是 P2B~P2E 由 copySchematicPage 复制而来、内部唯一 ID 撞车, EasyEDA 生成网表时自动重分配。担心的是「每次重分配结果不同 → 以后改原理图会把布局冲掉」, 已实测排除:第二次 importChanges 日志是「无变更,原理图与 PCB 已经匹配!」, 说明重分配是确定性的,后续是增量更新。

PCB 布局(2026-09-03;第一遍见下,第二遍见开头「本轮」那节)

板框 200 × 150 mm,原点在左下角,Y 向上,API 单位 mil(sys_Unit.getFrontendDataUnit())。 模型在 .claude/pcb_place.py(--push 下发)。 截图:PCB-layout-v1.png(第一遍,357 元件)/ PCB-layout-v2.png(当前,391 元件)。

分区(2026-09-03 用户拍板板框尺寸 + 连接器边)

y=150 ── 上边:每路 J2~J6 电源 Micro-Fit + J7~J11 按键 4P(去手机)
  【A】五路功率级   y 86~150,每路 40 mm 宽,同方向横排(§10.3)
y= 86 ──
  【B】P1 输入(左 x0~96) │ P3/P3B 控制(右 x96~200)   y 42~86
y= 42 ──
  【C】USB 子系统   y 0~42(**已按板载 Hub 重排**,见开头「本轮」第 2 节)
y=  0 ── 下边:唯一的外部 USB 口 J23 在这条边上

左短边 = J1 12V 接线端子;右短边 = J22 调试 USB-C + U20 ESP32(天线朝板边)。

⚠️ 与用户选择的一处偏离(已知,等确认)

用户选的是「手机的三根线束全部同点出板」。但 J17~J21 放上边的话, D± 必须纵穿五路 Buck 的电感与 SW 铜箔,这是 §15.3 明令禁止的 (比线长本身严重得多)。所以实际排的是:

  • 上边 = 每路的电源 + 按键(两根线束同点出板)
  • 下边 = 整个 USB 子系统,J12(上游)与 J17(去手机)相距约 19 mm,D± 全程留在 USB 带里

要恢复成「三根同点」就把 J17~J21 挪到上边,代价是 D± 穿功率区。

怎么排的

  • 五路功率级是显式手排(pcb_place.py 的 CH1 表),自下而上: 反馈/补偿 → Buck(Cin 贴 VIN/GND)→ 电感/输出 → eFuse → 分流+INA226(Kelvin)→ 连接器。 按键驱动放右列,远离 SW。CH1 排好后按 40 mm 节距平移复制五份(§10.3 同方向)。
  • P1 / P3 / P3B 按原理图坐标仿射映射进各自分区,再跑分离迭代消重叠—— 原理图上的功能分组画图时就想清楚了,直接当粗排初值。
  • 本地检查:重叠 0、越框 0。

踩到的三个坑

  1. 封装本体不一定以原点对称。 USB-A(KH-AF90ZJ)本体从原点往 −Y 伸 10.7 mm、 往 +Y 只有 1 mm。按对称尺寸算,第一次放 y=8 时本体伸出板框 2.7 mm, 而本地检查(用对称框)查不出来,是截图看出来的。已改到 y=11.5。
  2. API 取不到封装的本体外形。 pcb_PrimitiveLine.getAll() 只返回板框那 4 条, 元件内部的丝印 / 外形图元不外露(和原理图里 sch_PrimitivePin.getAll() 对元件实例 返回空是同一个套路)。能读回来的只有焊盘: component.getState_Pads() 给的是 {primitiveId, net, padNumber}, 要用 pcb_PrimitiveComponent.getAllPinsByPrimitiveId(id) 才拿得到真实坐标, 焊盘尺寸在 pad.getState_Pad() = ["RECT", w, h, r]。 所以 pcb_place.py 的占位框 = 标称本体框 ∪ 真实焊盘框, 板边连接器另有一张手量的 FP_BODY。「本体伸出板框」只能靠截图人眼看。
  3. zoomToRegion 的参数是 (left, right, top, bottom),不是 (x0,y0,x1,y1)。 传错了不报错,只是没缩放。

下一步(按顺序)

  1. 人眼确认 PCB-layout-v4.png,尤其下面「等确认」那几条。
  2. 设计规则 已全部下发(pcb_rules.py --push,--check 七项全 OK)。 Hub 到五路的 D± 最长约 140 mm(U32 在 x=45、CH5 在 x=185), USB 2.0 HS 走得了,但必须是受控阻抗,且五对要等长成对。
  3. 精调关键回路(§10.2):五路 Buck 的 Cin 热回路 / SW 铜箔 / FB 走线 / Kelvin 取样, 调好再重新复制五份;U30 那一路的 SW 只能走 x 13.9~15.4 那条 1.5 mm 的缝 (VIN 与 GND 夹着 SW 是这个封装的固有形状,绕不开)。
  4. U31(SOT-223,耗散约 0.71 W)与 U32 的散热铺铜。
  5. 灌铜(Inner1-GND 整片不许割断;Inner2-PWR 走 12V + 5V + 五路输出)→ 布线 → DRC。

等用户确认的事(几何验证证明不了)

  • 板边连接器的朝向。都是从渲染图上量出来的推断,要对着连接器图纸核一眼—— 接反了板子做出来插不进去:
    • J2~J6 Molex 43650 右角式,转 180° 让开口朝上板边(未改动)。
    • J23 / J22 USB-C:焊盘在 +Y、壳体往 −Y 伸 5.2 mm,也就是 rot=0 时开口朝 −Y。 J23 rot=0 开口朝下板边;J22 这轮从 270 改成了 90(原来开口朝板内)。
    • J1 KF128:丝印上两个大箭头 = 进线方向,rot=0 时朝 +Y(板内), 这轮改成了 90,进线朝左板边。箭头到底是不是进线方向,请核一眼。
    • U20 ESP32 模组:这轮从 x=186 挪到 183.2,让整块模组(含天线端)不出框。 如果你本来就想让天线伸出板外,那要挪回去并把板框改成带缺口的形状。
  • 丝印位号大面积压字(EasyEDA 自动生成的),布线前要单独跑一遍丝印整理。

打样前的阻塞项(都不需要电源板,可并行做)

  1. 排线旁路验证(设计文档 §13.7)—— 确认焊在 P+/P− 而非 B+/B−。 判定性测试是第 3 步:可调电源升到 4.40V,BTB 座电压必须跟随且不断开。
  2. 侧键触点定位(§13.3)—— 万用表 5 分钟,拍照存档。带电验证那一步是安全闸门。
  3. 环路补偿实测(§9.2)—— 图上按 fco≈18kHz 放了 R106/C110/C111,三个位置都留了焊盘。

已知遗留

  • 上游用的是 USB-A 母座(§15.1 定的),所以板到 Hub 之间是一根 A 公 - A 公线。 这不是标准 USB 用法;好处是物理上插不了充电头(VBUS 一旦被抬到 9V, TPS22918 的绝对最大 6V 会当场炸)。要换 USB-B / Type-C 就趁现在,改 5 个连接器而已。
  • C7(P1 上 U19 的输入去耦)只有 2.2 µF,datasheet 要求高频去耦 ≥4.7 µF。 建议改成 10 µF/50V 0805(C440198,BOM 里已有)。不改也能工作, 但 PCB 上必须把 C7 贴到引脚旁。§9.3
  • 电感 Isat 略低于目标:SPM6530T-4R7M 是 Isat 6.2A,原定 ≥7A。 正常峰值 4.61A、eFuse 限流工况 5.08A 都够;但 Buck 高侧限流 datasheet 是 8/11/14A,打嗝保护触发时电感会短暂深度饱和。首板要确认发热与饱和表现。
  • eFuse 最坏容差下可能到 4.519V 才跳,超过 1S 上限 4.45V 约 60mV。已接受,理由见 §5。
  • PCA9555 的 4 个备用脚直接接地(不串电阻),依赖"上电默认为输入"。 固件严禁把 Port0[7] / Port1[7:5] 配成输出。
  • U29 泄放的 R9 是突发额定(10Ω/5W,13.3V 下 17.7W), 持续故障靠固件监控输入电压兜底:>13.0V 关全部 VBUS,>13.8V 关全部 EFUSE_EN。
  • 交叉点:P2 系列各 6 处、P3 有 1 处(USB-C 的 D± 交叉,Type-C 固有)、P4 是 0 处。 几何验证确认互不相连。
  • 手机侧 C802/C803 用了 MLCC 代替聚合物(§6.3)。立创基础库里没有 100µF/6.3V 聚合物, 改用 100µF/6.3V X5R 1206(C15008)。3.9V 偏压下每颗只剩约 40 µF, 中频段储能从 200 µF 掉到约 80 µF。C801 的 2200 µF 才是主力,估计影响不大, 但这是个待用户拍板的偏离:要严格照原方案就得单独采购聚合物。
  • C801 是选型陷阱:必须买普通铝电解(C49256788,ESR 数十 mΩ)。 同尺寸的聚合物 C53446724(ESR 12 mΩ)会让线缆 LC 的 Q 从 0.19 回到 4.5 而振铃。 这一条已写成 P5 页上的红字。

沉淀下来的工具(.claude/ 下)

文件 用途
eda.py 跑任意 JS 到桥接:python .claude/eda.py x.js。HTTP 500 会带出中文错误原文
place.py / place4.py 批量放置元件;place4.py 支持 mirror(位置表 p4_pos.py)
fetch_pins.py 导出某页的真实引脚坐标成 pins.json
build.py 本地几何检查 + 分批推送(--push),用于 P3/P3B/P4
p2gen.py 五路母版模型:几何来自 p2-ch1.json,按通道号改位号 / 网络名 / 地址跳线
p2push.py python .claude/p2push.py <n> <pageUuid> --temp|--final|--push
p2-ch1.json CH1 页的几何快照(导线 / 旗 / 元件),是五页的唯一事实来源
shot.py 截图某页存 PNG —— 画完先自己看一眼,能省掉大部分返工
xpage.py 跨页网络对账,找"全工程只有一个实例"的网络
drc_read.py 跑 DRC 并把结果读回来(DOM 抓 DRC 面板)。布局一改就跑,本地几何检查抓不到焊盘/钻孔间距
p3_nets.py / p3b_nets.py / p4_nets.py 三页的网表源码(改图改这里,再跑 build.py)
p4b_nets.py + p4b_pos.py P4B-HUB:USB2517I 那一页的位置表与网表
p1b_nets.py P1B-5V:12V→5V + Hub 专用 LDO
p1_add.py P1 的增量:导线/旗/文字整页重画(底稿 p1-base.json),端口只增不重建
p5_harness.py P5 整页模型 + 下发(含文字越界 / 压标题栏的本地自检)
pcb_place.py PCB 布局模型 + 下发(分区表 + CH1 手排 + 仿射映射 + 重叠/越框自检)
pcb-parts.json PCB 侧 357 个元件的位号 / 封装 / 坐标(--push 前要重新读)
pcb-pads.json 每个元件的真实焊盘外框(封装不对称时靠它,见上面的坑 1)
sch-xy.json 每页元件的原理图坐标,仿射映射的输入
eda-parts.json 器件 + 工程/图纸/全部 10 页的 UUID

⚠️ P2 早期的 p2-nets.py / p2-pos.py 等四个文件已和页面对不上(位号还是改号前的 C1/R1,端口位置也是旧的),已挪到 .claude/obsolete/,别 import。 五路的唯一事实来源是 p2-ch1.json + p2gen.py。

位号改名要分两趟

copySchematicPage 会把复制页的位号自动改成不撞车的(C11~C24、U2/U3/U4……), 而这些恰好就是别的通道要用的号。一趟改名会撞号被 EasyEDA 悄悄改掉, 所以 p2push.py 先把所有页都改成 Z<n>T<i> 临时名(--temp), 再统一改成目标名(--final)。

新会话开工检查清单

  1. 当前状态:
    • 🔴 R2 刀片 PCB 正在推进中,见 刀片式方案规划-R2.md §25(2026-09-08 M5): 器件已全部翻到手机主板的另一面(A 面只留 10 件:K1~K4/J5/J4/C60/J2/J3/J1), 功率级改成手排,§7.2 四条关键回路全部达标(loop_check.py 是新的机器判据)。 布线 747 段 / 280 过孔,DRC 间距 0 / 物理 0,Buck 的 SW/BOOT/FB 都已接上。 ⛔ 还欠:13 个网络没接完(全是信号类,清单 .claude/r2/_blade_open_nets.txt; 这个数和之前记的「29 处」不可比,因为布线器原来在六个地方静默断线,见 R2 §25.8/25.10)、 铺铜不落盘(reload 后只剩 2 块,待查)。
    • ✅ 板载 USB Hub 的原理图部分做完了(12 页,跨页 0 漏接)。 还欠:把新器件导进 PCB + 【C】带重排 + 人眼看三张新图的截图。
    • ⭐⭐ 主板载板(5 块主板固定在一块载板上,对插到底板)—— 卡在「没有小米8 主板的机械数据」,只能等用户实测。
  2. 桥接在不在:/c/Python314/python .claude/eda.py 随便跑一段 JS;不通就按下面「环境」起服务。
  3. 页面 UUID 全在 .claude/eda-parts.json 的 pages 里(12 页都有),不用重新枚举。
  4. 动手前读 easyeda-sch-geometry skill,尤其 §8a(零长度导线)、§8b(端口 dir 决定标签朝向)、 §2(旗的 rot 语义,四种组合都实测过了)。
  5. 还欠人眼确认的页:P1-INPUT、P2-CHANNEL、P3-CONTROL、P3B-EXPAND、P5-HARNESS, 以及这轮新改/新增的 P4-USB、P4B-HUB、P1B-5V (P2B~P2E 与 P2-CHANNEL 完全同构,看一页即可)。 ⚠️ 截图 API 有时会抓到另一个窗口(返回的 windowId 会变),拍出来只有元件没有导线。 遇到就换个时机重截,或直接在客户端里看 —— 先用 API 读回图元数量确认数据在不在,再判断。
  6. 改完任何一页,跑 verify_sch.py(当前页)+ xpage.py(全工程),两个都要干净。

环境

  • EasyEDA 桥接:node C:/Users/35138/.claude/skills/easyeda-api/scripts/bridge-server.mjs 后台运行,端口 49620~49629 自动选。客户端里要装 run-api-gateway.eext 扩展。
  • Python:/c/Python314/python(Windows 路径写 C:\Python314\python.exe)。 已装 pymupdf —— datasheet 可以直接下下来用 fitz 抽文字(get_text("words") 带坐标,用来还原被排版拆散的引脚图,P4 就是靠这个确认 TPS22918 引脚号的)。
  • 验证:/c/Python314/python .claude/skills/easyeda-sch-geometry/scripts/verify_sch.py --nets (跑的是当前打开的那一页,所以要先 openDocument)。
  • 清空上下文后,先看 CLAUDE.md,再看这份,然后读 easyeda-sch-geometry skill。