# -*- 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()