95 行
4.6 KiB
Python
95 行
4.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
u"""D16:SDA6 / SCL6(CH224A ↔ TCA9548A 通道 5)为什么布不通 —— 在布线器同一张网格上做泛洪(服务器跑)。
|
||
|
||
DCDC_ROUTE_SERVER=1 /root/pypy/bin/pypy3 .claude/r2/d16_reach.py [NET ...] # 默认 SDA6 SCL6
|
||
|
||
障碍 = 板上快照(_board_for_nets.json)+ _d16_route.out.json 里本轮新增的铜,建图口径与 route_one 相同
|
||
(route_base._build:L1 西侧 12V 大铜挡线、PD_L1 窗口、NPTH / 天线净空、内层线挡孔)。
|
||
从本网每只焊盘的引出格点出发,两层 + 换层孔泛洪:报每只焊盘能到的格点数 / 外框、两头碰不碰得上;
|
||
碰不上时列出「围住它的那一圈」上是哪些网(格点归属),好决定拆谁、或者改原理图。
|
||
"""
|
||
import collections
|
||
import io
|
||
import json
|
||
import os
|
||
import sys
|
||
|
||
if os.environ.get('DCDC_ROUTE_SERVER') != '1':
|
||
sys.exit(u'!! 布线运算只在服务器上跑(DCDC_ROUTE_SERVER=1),见 CLAUDE.md')
|
||
if '--grid' not in sys.argv:
|
||
sys.argv += ['--grid', '0.125']
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
sys.path.insert(0, os.path.dirname(HERE))
|
||
sys.path.insert(0, HERE)
|
||
import route_nets as N # noqa: E402
|
||
|
||
R = N.R
|
||
RT = R.RT
|
||
|
||
|
||
def main():
|
||
nets = [a for a in sys.argv[1:] if not a.startswith('--') and a not in ('0.125',)] or ['SDA6', 'SCL6']
|
||
b = json.load(io.open(os.path.join(HERE, '_board_for_nets.json'), encoding='utf-8'))
|
||
segs = [tuple(s[:7]) for s in b['segs']]
|
||
vias = [tuple(v[:3]) for v in b['vias']]
|
||
p = os.path.join(HERE, '_d16_route.out.json')
|
||
if os.path.exists(p):
|
||
r = json.load(io.open(p, encoding='utf-8'))
|
||
segs += [tuple(s[:7]) for s in r['segs']]
|
||
vias += [tuple(v[:3]) for v in r['vias']]
|
||
pads = R.load_pads()
|
||
N.INNER[:] = [s for s in segs if s[0] in (15, 16)]
|
||
obst = [s for s in segs if s[0] in (1, 2)]
|
||
w = 0.2
|
||
halfw = w / 2.0 + RT.CLR
|
||
g, gt, others = R.grid_now(pads, halfw, obst, vias, [])
|
||
inv = {v: k for k, v in g._id.items()} # router.Grid.nid:网名 → 1..250;255 = 多网争用
|
||
inv[255] = u'#多网'
|
||
out = {}
|
||
for net in nets:
|
||
ps = [q for q in pads if q[7] == net and not str(q[1]).endswith(R.TH_SUFFIX)]
|
||
regions = []
|
||
for q in ps:
|
||
starts = RT.pad_nodes(g, q, halfw, others.get(q[6], []), w / 2.0, gt)
|
||
seen = set()
|
||
dq = collections.deque()
|
||
for nd in starts:
|
||
n = (nd[0], nd[1], nd[2])
|
||
if g.free(n[0], n[1], n[2], net):
|
||
seen.add(n)
|
||
dq.append(n)
|
||
border = collections.Counter()
|
||
while dq:
|
||
l, i, j = dq.popleft()
|
||
for di, dj in ((1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (1, -1), (-1, 1), (-1, -1)):
|
||
n = (l, i + di, j + dj)
|
||
if n in seen:
|
||
continue
|
||
if g.free(l, i + di, j + dj, net):
|
||
seen.add(n)
|
||
dq.append(n)
|
||
elif 0 <= i + di < RT.NX and 0 <= j + dj < RT.NY:
|
||
border[(l, g.own[l][g.idx(i + di, j + dj)])] += 1
|
||
o = RT.LAY[1] if l == RT.LAY[0] else RT.LAY[0]
|
||
n = (o, i, j)
|
||
if n not in seen and g.free(o, i, j, net) and g.free_via(i, j, net):
|
||
seen.add(n)
|
||
dq.append(n)
|
||
xs = [RT.wx(n[1]) for n in seen]
|
||
ys = [RT.wy(n[2]) for n in seen]
|
||
box = (round(min(xs), 2), round(min(ys), 2), round(max(xs), 2), round(max(ys), 2)) if seen else None
|
||
top = [(l, inv.get(k, k), c) for (l, k), c in border.most_common(12)]
|
||
regions.append({'pad': '%s.%s' % (q[0], q[1]), 'starts': len(starts), 'cells': len(seen),
|
||
'box': box, 'border': top, 'set': seen})
|
||
print(u'%s %s.%s:引出格点 %d,能到 %d 格,外框 %s;边界上最多的归属 %s'
|
||
% (net, q[0], q[1], len(starts), len(seen), box, top[:8]))
|
||
meet = len(regions) >= 2 and bool(regions[0]['set'] & regions[1]['set'])
|
||
print(u'%s:两头%s' % (net, u'碰得上(该布得通,查 route_one)' if meet else u'碰不上 —— 一头被围死'))
|
||
out[net] = [{k: v for k, v in rg.items() if k != 'set'} for rg in regions] + [{'meet': meet}]
|
||
io.open(os.path.join(HERE, '_d16_reach.out.json'), 'w', encoding='utf-8').write(
|
||
json.dumps(out, ensure_ascii=False, default=str))
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|