R2 底板改 4 层(D15):规则 / 几何 / 背面摆位 / 4 层布线与铺铜,已下发
- 规则:铜层 2→4,差分 0.28/0.18,间距 0.15,过孔 0.3/0.2(rules_base) - 几何:98×98 板框 + 4×M3,槽阵居中,L1 12V 大铜 + 指头(pcb_base) - 摆位:五插槽留 L1,其余 133 件翻到 L4;背面镜像规则实测;Hub 放走廊 2、 东移 2.5 mm;扇出圈 / 小件间距 1.5(去耦例外)/ 9 A 过孔阵禁区(place_base) - 叠层按用户决定改为 L1 槽面 / L2 整层 +3V3 / L3 整层 GND / L4 器件面 - 布线:D± 七对按构造布通(USB3 掉头、USB1/2 北去掉头、USB4/5 东绕、 J23 桥),+3V3 / GND / +12V 平面网每脚一孔,0.125 网格 + PathFinder 开关(route_base、remote_route.ps1);剩 37 个网待手工补 - 铺铜:4 层重写,入口链自南向北切段 + 断面校核(pour_base); 已下发布线 / 缝合孔 / 铜面轮廓,填铜未成功(待查) - 工具:plot_base / plot_routes / shot_base,board_check 通孔两层都算 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0168Bu6SEx3cXXCJLkdnE6zB
这个提交包含在:
@@ -0,0 +1,266 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""R2 刀片 **B4-EDGE** 页:PCI-E x4 金手指 + PRSNT# 短接 + D± 退路座。
|
||||
|
||||
/c/Python314/python .claude/r2/b4_edge.py # 只算 + 本地自检
|
||||
/c/Python314/python .claude/r2/b4_edge.py --push # 清页 + 重画(幂等)
|
||||
|
||||
M2 的最后一页(前四页见 b1_power / b2_ctrl / b3_phone / b3b_usb)。
|
||||
|
||||
## T4 已核(2026-09-08):针位表对着 PCI-E CEM 逐脚核过,方案文档 §5.2 有两处是错的
|
||||
|
||||
| 针 | 方案文档(凭记忆写的) | CEM 实际 | 后果 |
|
||||
|---|---|---|---|
|
||||
| **B3** | 「RSVD,拿来做 +5V」 | **+12V**(B1/B2/B3 三针都是) | 刀片误插进真 PC:主板把 12 V 灌进刀片的 +5V 轨,U5 TPS22918(6V abs max)与手机 VBUS 全灭 |
|
||||
| B12 | 「RSVD」 | CEM 1.x = RSVD,2.0 起 = CLKREQ# | 不能当自由针用(真主板上有 3.3V 上拉、真卡上是开漏输出)|
|
||||
|
||||
⇒ **+5V 改挂 A19 / A32** —— 这两针在 CEM 1.x~5.0 每一版里都是 RSVD,
|
||||
真主板上不接、真卡上也不接,是两个方向都安全的仅有选择。
|
||||
代价:两针都在 A 排,图上是两个同名 `+5V` 端口(见下面 ALLOW_SPLIT)。
|
||||
|
||||
其余针位与 §5.2 一致:+12V=B1/B2/B3/A2/A3、+3V3=B8/A9/A10、SCL=B5(SMCLK)、
|
||||
SDA=B6(SMDAT)、INT#=B11(WAKE#)、EFUSE_EN=A11(PERST#)、D±=B14/B15(PET0,两侧 B13/B16 天然夹地)、
|
||||
PRSNT#=A1↔B31(x4 全长在位;B17 那个是 x1 用的,本板当地用)。
|
||||
|
||||
**VDAC 定在 A16**(PERp0):上下两针 A15 / A17 都是地,是全连接器夹得最干净的一个位置。
|
||||
方案文档原来写「预留差分对 A16/A17」—— 那对差分在刀片上没有对端,留着也用不了,
|
||||
让给需要屏蔽的模拟量更划算。
|
||||
|
||||
## 悬空的 6 根针(`b4-nc.json`)
|
||||
|
||||
`A5~A8`(JTAG TCK/TDI/TDO/TMS)、`B9`(JTAG TRST#)、**`B10`(+3.3Vaux)**。
|
||||
⚠ B10 是必须悬空的那一根:真卡插进底板槽时,底板若在这针上给了 5V 会直接打穿它的 aux 域。
|
||||
|
||||
其余用不上的针(B12 CLKREQ# / B17 PRSNT#2(x1) / B30 PWRBRK# / 全部 lane 针)一律接地:
|
||||
两个方向都无害(主板侧要么开漏、要么 AC 耦合),换来 39 根地针的回流面积。
|
||||
|
||||
## 版图:地母线甩到最左/最右,信号端口排在母线与引脚之间
|
||||
|
||||
抄 `b3b_usb` 的 J4(50 脚 BM24)那一套 —— 地是长横线一路拉到母线,信号是短横线接端口,
|
||||
两者 y 不同所以永不交叉。竖线只在同一网络的几针之间用,且都躲在母线之外的空白区。
|
||||
|
||||
## ⚠ ALLOW_SPLIT 放宽了,补一层更硬的判据
|
||||
|
||||
连接器页天然靠同名端口/旗成网,不是靠导线:`GND`(三段梳子)、`+12V`(A/B 两排)、
|
||||
`+3V3`(A/B 两排)、`+5V`(A19/A32 两针)、`USB_DP/DM`(金手指 + 退路座)都是分块的。
|
||||
放宽 ALLOW_SPLIT 会让「漏连」这条检查失效,所以本脚本额外做一件事:
|
||||
逐脚断言「这根针踩着的那段导线,声明的网络名 == PIN_NET 里写的」(`check_pin_nets`)。
|
||||
少接一根、接错一根都会当场报出来。
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HERE)
|
||||
from schcheck import Sheet, by_rel # noqa: E402
|
||||
|
||||
PAGE, NAME = 'c8f10dbd1286853b', 'B4-EDGE'
|
||||
|
||||
EDGE, WV4 = 'PCIE-X4-EDGE-64P', 'C225106'
|
||||
|
||||
# ── 版面常量 ─────────────────────────────────────────────────────────
|
||||
J1 = (620, 470) # 金手指符号中心;A 排引脚 x=585,B 排 x=655
|
||||
AX, BX = J1[0] - 35, J1[0] + 35
|
||||
ABUS, BBUS = 340, 900 # 左/右地母线
|
||||
APORT, BPORT = 490, 760 # 信号端口列
|
||||
J2 = (1000, 700) # D± 退路座(1.25mm 4P),引脚朝左
|
||||
|
||||
|
||||
def row(n):
|
||||
u"""金手指第 n 号针的 y(A/B 两排同高,pitch 10)。"""
|
||||
return J1[1] + 155 - 10 * (n - 1)
|
||||
|
||||
|
||||
# ── 64 根针的网络分配(T4 已核,出处见表头)──────────────────────────
|
||||
PIN_NET = {}
|
||||
for _n, _net in [(1, 'PRSNT#'), (2, '+12V'), (3, '+12V'), (4, 'GND'),
|
||||
(5, 'NC'), (6, 'NC'), (7, 'NC'), (8, 'NC'),
|
||||
(9, '+3V3'), (10, '+3V3'), (11, 'EFUSE_EN'),
|
||||
(12, 'GND'), (13, 'GND'), (14, 'GND'), (15, 'GND'),
|
||||
(16, 'VDAC'), (17, 'GND'), (18, 'GND'), (19, '+5V'),
|
||||
(32, '+5V')]:
|
||||
PIN_NET['A%d' % _n] = _net
|
||||
for _n in range(20, 32): # A20~A31 全部接地
|
||||
PIN_NET['A%d' % _n] = 'GND'
|
||||
for _n, _net in [(1, '+12V'), (2, '+12V'), (3, '+12V'), (4, 'GND'),
|
||||
(5, 'SCL'), (6, 'SDA'), (7, 'GND'), (8, '+3V3'),
|
||||
(9, 'NC'), (10, 'NC'), (11, 'INT#'),
|
||||
(12, 'GND'), (13, 'GND'), (14, 'USB_DP'), (15, 'USB_DM'),
|
||||
(31, 'PRSNT#'), (32, 'GND')]:
|
||||
PIN_NET['B%d' % _n] = _net
|
||||
for _n in range(16, 31): # B16~B30 全部接地
|
||||
PIN_NET['B%d' % _n] = 'GND'
|
||||
assert len(PIN_NET) == 64, len(PIN_NET)
|
||||
|
||||
NC = {'J1': [p for p, n in PIN_NET.items() if n == 'NC']}
|
||||
assert sorted(NC['J1']) == sorted(['A5', 'A6', 'A7', 'A8', 'B9', 'B10']), NC['J1']
|
||||
|
||||
# 地母线要按「连续成段」切:中间隔着信号针的地方断开,每一段自带一面地旗。
|
||||
AGND = [n for n in range(1, 33) if PIN_NET['A%d' % n] == 'GND']
|
||||
BGND_MAIN = [n for n in range(1, 33)
|
||||
if PIN_NET['B%d' % n] == 'GND' and n != 32]
|
||||
|
||||
# 同名端口/旗成网、导线上天然分块的网络(判据换成 check_pin_nets,见表头)
|
||||
ALLOW_SPLIT = set(['GND', '+12V', '+3V3', '+5V', 'USB_DP', 'USB_DM'])
|
||||
|
||||
EDGE_REL = {}
|
||||
for _n in range(1, 33):
|
||||
EDGE_REL['A%d' % _n] = (-35, 155 - 10 * (_n - 1))
|
||||
EDGE_REL['B%d' % _n] = (35, 155 - 10 * (_n - 1))
|
||||
# A1251WV-S-4P rot 0 实测:1~4 自上而下、5/6 是两个固定脚,全部朝左出
|
||||
WV4_REL = {'1': (-25, 15), '2': (-25, 5), '3': (-25, -5), '4': (-25, -15),
|
||||
'5': (-25, 35), '6': (-25, -35)}
|
||||
|
||||
|
||||
def placement():
|
||||
return [['J1', EDGE, J1[0], J1[1], 0],
|
||||
['J2', WV4, J2[0], J2[1], 0]]
|
||||
|
||||
|
||||
def expect():
|
||||
return {'J1': by_rel(J1[0], J1[1], EDGE_REL),
|
||||
'J2': by_rel(J2[0], J2[1], WV4_REL)}
|
||||
|
||||
|
||||
def build():
|
||||
s = Sheet(PAGE, NAME)
|
||||
|
||||
# ── 1. 地:左右两把梳子 ──────────────────────────────────────────
|
||||
for n in AGND:
|
||||
s.w('GND', (AX, row(n)), (ABUS, row(n)))
|
||||
s.w('GND', (ABUS, row(AGND[0])), (ABUS, 625)) # 母线往上甩出去挂旗
|
||||
s.w('GND', (ABUS, row(AGND[0])), (ABUS, row(AGND[-1])))
|
||||
s.gnd(ABUS, 625, 180)
|
||||
|
||||
for n in BGND_MAIN:
|
||||
s.w('GND', (BX, row(n)), (BBUS, row(n)))
|
||||
# ⚠ 母线在 B8(+3V3) 那一行故意断开:B9/B10 悬空、这一段是全页唯一
|
||||
# 能让 +3V3 横穿到母线以东挂电源旗的通道(否则右半边被梳子封死)。
|
||||
s.w('GND', (BBUS, row(4)), (BBUS, 625))
|
||||
s.w('GND', (BBUS, row(4)), (BBUS, row(7)))
|
||||
s.gnd(BBUS, 625, 180)
|
||||
s.w('GND', (BBUS, row(12)), (BBUS, row(30)))
|
||||
s.w('GND', (BBUS, row(30)), (BBUS, 310))
|
||||
s.gnd(BBUS, 310, 0)
|
||||
# B32 单独一根(下面就是 PRSNT# 的过桥,梳子伸不下去)
|
||||
s.w('GND', (BX, row(32)), (BX, 290))
|
||||
s.gnd(BX, 290, 0)
|
||||
|
||||
# ── 2. +12V:A2/A3 与 B1/B2/B3,各自并起来出一个端口 ─────────────
|
||||
s.w('+12V', (AX, row(2)), (520, row(2)))
|
||||
s.w('+12V', (AX, row(3)), (520, row(3)))
|
||||
s.w('+12V', (520, 650), (520, row(3)))
|
||||
s.w('+12V', (520, 650), (470, 650))
|
||||
s.port('OUT', '+12V', 470, 650, 0)
|
||||
for n in (1, 2, 3):
|
||||
s.w('+12V', (BX, row(n)), (720, row(n)))
|
||||
s.w('+12V', (720, row(1)), (720, row(3)))
|
||||
s.w('+12V', (720, row(1)), (720, 660))
|
||||
s.w('+12V', (720, 660), (790, 660))
|
||||
s.port('OUT', '+12V', 790, 660, 180)
|
||||
|
||||
# ── 3. +3V3:A9/A10 一面旗,B8 横穿母线缺口到东边一面旗 ──────────
|
||||
s.w('+3V3', (AX, row(9)), (450, row(9)))
|
||||
s.w('+3V3', (AX, row(10)), (450, row(10)))
|
||||
s.w('+3V3', (450, row(9)), (450, row(10)))
|
||||
s.w('+3V3', (450, row(9)), (450, 570))
|
||||
s.pwr('+3V3', 450, 570, 0)
|
||||
s.w('+3V3', (BX, row(8)), (1000, row(8)))
|
||||
s.w('+3V3', (1000, row(8)), (1000, 580))
|
||||
s.pwr('+3V3', 1000, 580, 0)
|
||||
|
||||
# ── 4. +5V:A19 / A32 两根 RSVD 针,各一个同名端口 ───────────────
|
||||
for n in (19, 32):
|
||||
s.w('+5V', (AX, row(n)), (APORT, row(n)))
|
||||
s.port('OUT', '+5V', APORT, row(n), 0)
|
||||
|
||||
# ── 5. A 排信号 ─────────────────────────────────────────────────
|
||||
s.w('EFUSE_EN', (AX, row(11)), (APORT, row(11)))
|
||||
s.port('OUT', 'EFUSE_EN', APORT, row(11), 0)
|
||||
s.w('VDAC', (AX, row(16)), (APORT, row(16)))
|
||||
s.port('OUT', 'VDAC', APORT, row(16), 0)
|
||||
|
||||
# ── 6. B 排信号 ─────────────────────────────────────────────────
|
||||
for n, net, d in ((5, 'SCL', 'BI'), (6, 'SDA', 'BI'), (11, 'INT#', 'IN'),
|
||||
(14, 'USB_DP', 'BI'), (15, 'USB_DM', 'BI')):
|
||||
s.w(net, (BX, row(n)), (BPORT, row(n)))
|
||||
s.port(d, net, BPORT, row(n), 180)
|
||||
|
||||
# ── 7. PRSNT#:A1 ↔ B31,从符号上方绕到下方(x4 全长在位)────────
|
||||
s.w('PRSNT#', (AX, row(1)), (AX, 680))
|
||||
s.w('PRSNT#', (AX, 680), (290, 680))
|
||||
s.w('PRSNT#', (290, 680), (290, 245))
|
||||
s.w('PRSNT#', (290, 245), (700, 245))
|
||||
s.w('PRSNT#', (700, 245), (700, row(31)))
|
||||
s.w('PRSNT#', (700, row(31)), (BX, row(31)))
|
||||
# 这个端口在刀片上没有对端(PRSNT# 只在金手指内部短接),纯粹为了给网络起名 ——
|
||||
# 不放的话 EasyEDA 会给它一个 $4N... 默认名,PCB 侧脚本按名字就认不出来。
|
||||
# xpage.py 的 NAMING_ONLY 里已加了它。
|
||||
s.w('PRSNT#', (620, 245), (620, 215))
|
||||
s.port('OUT', 'PRSNT#', 620, 215, 0)
|
||||
|
||||
# ── 8. J2 D± 退路座(1=+5V 2=DP 3=DM 4/5/6=GND)──────────────────
|
||||
jx, jy = J2
|
||||
for dy, net, d in ((15, '+5V', 'OUT'), (5, 'USB_DP', 'BI'), (-5, 'USB_DM', 'BI')):
|
||||
s.w(net, (jx - 25, jy + dy), (890, jy + dy))
|
||||
s.port(d, net, 890, jy + dy, 0)
|
||||
for dy in (35, -15, -35): # 4 = 地,5/6 = 固定脚
|
||||
s.w('GND', (jx - 25, jy + dy), (1030, jy + dy))
|
||||
s.w('GND', (1030, jy + 35), (1030, jy - 35))
|
||||
s.w('GND', (1030, jy + 35), (1030, jy + 60))
|
||||
s.gnd(1030, jy + 60, 180)
|
||||
|
||||
# ── 9. 文字 ─────────────────────────────────────────────────────
|
||||
s.txt(45, 795, u'【B4-EDGE】PCI-E x4 金手指(只借机械与针位语义,不是 PCIe)+ D± 退路座', 14)
|
||||
s.txt(45, 230, u'⚠ NOT PCIe —— 底板插槽旁必须丝印「PhoneRail blade only」。', 11)
|
||||
s.txt(45, 212, u'T4 已核 CEM 引脚表:B1/B2/B3 都是 +12V(不是 RSVD),', 10)
|
||||
s.txt(45, 194, u' 所以 +5V 挪到 A19 / A32 —— 每一版 CEM 里它俩都是 RSVD。', 10)
|
||||
s.txt(45, 176, u'⚠ B10 = +3.3Vaux 必须悬空;B12/B17/B30 与全部 lane 针接地。', 10)
|
||||
s.txt(45, 158, u'PRSNT# = A1 ↔ B31 短接(x4 全长在位判据)。', 10)
|
||||
s.txt(45, 140, u'J2 = D± 退路座:PCB 上金手指到 J2 之间留割断位(D12)。', 10)
|
||||
return s
|
||||
|
||||
|
||||
def check_pin_nets(s):
|
||||
u"""逐脚断言「针踩着的那段导线声明的网络」== PIN_NET。ALLOW_SPLIT 放宽之后靠这条兜底。"""
|
||||
from schcheck import _on
|
||||
errs = []
|
||||
pins = dict((p, (AX if p[0] == 'A' else BX, row(int(p[1:]))))
|
||||
for p in PIN_NET)
|
||||
for p, xy in sorted(pins.items()):
|
||||
want = PIN_NET[p]
|
||||
got = sorted(set(seg[2] for seg in s.W if _on(xy, seg[0], seg[1])))
|
||||
if want == 'NC':
|
||||
if got:
|
||||
errs.append(u'%s 应当悬空,却踩在 %s 上' % (p, got))
|
||||
elif got != [want]:
|
||||
errs.append(u'%s 应当是 %s,实际踩着 %s' % (p, want, got or u'(空)'))
|
||||
return errs
|
||||
|
||||
|
||||
def main():
|
||||
push = '--push' in sys.argv
|
||||
place = placement()
|
||||
exp = expect()
|
||||
s = build()
|
||||
s.assert_grid()
|
||||
pins = dict((d, dict((k, tuple(v)) for k, v in want.items()))
|
||||
for d, want in exp.items())
|
||||
errs = s.netcheck(pins, NC, ALLOW_SPLIT) + check_pin_nets(s)
|
||||
ngnd = len([1 for p, n in PIN_NET.items() if n == 'GND'])
|
||||
print(u'金手指 64 针:地 %d / 悬空 %d / 有名信号 %d'
|
||||
% (ngnd, len(NC['J1']), 64 - ngnd - len(NC['J1'])))
|
||||
s.report(len(place), errs)
|
||||
with io.open(os.path.join(HERE, 'b4-nc.json'), 'w', encoding='utf-8') as f:
|
||||
f.write(json.dumps(NC, ensure_ascii=False, indent=1))
|
||||
if not push:
|
||||
return
|
||||
s.clear()
|
||||
s.place(place, exp)
|
||||
s.push_geometry()
|
||||
print(u'接着跑 verify_sch.py --nets --nc .claude/r2/b4-nc.json')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
在新工单中引用
屏蔽一个用户