krishnax12/t113-minimal-linux
Allwinner T113-S3 dual-Cortex-A7 SoC with integrated 128MB DDR3, multi-rail decoupling, 24MHz and 32.768kHz oscillators, filtering, and exposed test points.
- Version
- 0.1.1
- License
- unset
- Stars
- 0
scripts/add-pin-escapes.py
"""Preserve short fine-pitch power/drill escapes from the reviewed placement."""
import json, math, sys
from pathlib import Path
from routing_geometry import Nets, audit
source, destination = map(Path, sys.argv[1:3])
circuit = json.loads(source.read_text())
nets = Nets(circuit)
components = {c['name']:c['source_component_id'] for c in nets.of('source_component')}
root_subcircuit = nets.of('pcb_board')[0]['subcircuit_id']
added = []
def port(selector):
name, number = selector
source_port = next(p for p in nets.of('source_port') if p['source_component_id']==components[name] and p.get('pin_number')==number)
pcb_port = next(p for p in nets.of('pcb_port') if p.get('source_port_id')==source_port['source_port_id'])
return {**pcb_port,'source_port_id':source_port['source_port_id']}
def route(spec):
start = port(spec['from'])
end = port(spec['to']) if spec.get('to') else None
if nets.source(start['source_port_id'])!=spec['net'] or (end and nets.source(end['source_port_id'])!=spec['net']):raise ValueError('Escape would join different nets: '+spec['name'])
points = spec['points']
if math.hypot(start['x']-points[0][0], start['y']-points[0][1])>.001:raise ValueError('Source placement changed: '+spec['name'])
if end and math.hypot(end['x']-points[-1][0],end['y']-points[-1][1])>.001:raise ValueError('Destination placement changed: '+spec['name'])
index = sum(r['type']=='pcb_trace' for r in added)
source_trace_id = 'source_trace_escape_'+str(index)
metadata = {'source_net_id':nets.ids[spec['net']],'subcircuit_id':root_subcircuit}
copper_route = []
layer = 'top'
for x,y,width in points:
if isinstance(width,str):
copper_route.append({'route_type':'via','x':x,'y':y,'from_layer':layer,'to_layer':width})
added.append({'type':'pcb_via','pcb_via_id':'pcb_via_escape_'+str(len(added)),'x':x,'y':y,'outer_diameter':.6,'hole_diameter':.3,'layers':['top','inner1','inner2','bottom'],'from_layer':'top','to_layer':'bottom',**metadata})
layer = width
else:copper_route.append({'route_type':'wire','x':x,'y':y,'width':width,'layer':layer})
copper_route[0]['start_pcb_port_id']=start['pcb_port_id']
if end:copper_route[-1]['end_pcb_port_id']=end['pcb_port_id']
added.append({'type':'source_trace','source_trace_id':source_trace_id,'connected_source_port_ids':[start['source_port_id']]+([end['source_port_id']] if end else []),'connected_source_net_ids':[metadata['source_net_id']],'subcircuit_id':root_subcircuit,'display_name':spec['name'],'min_trace_thickness':min(p['width'] for p in copper_route if p['route_type']=='wire')})
added.append({'type':'pcb_trace','pcb_trace_id':'pcb_trace_escape_'+str(index),'source_trace_id':source_trace_id,'route':copper_route,**metadata})
route({'name':'CORE_PIN46','net':'V0V9','from':('U_SOC',46),'to':('C_SOC_46',1),'points':[(7.629906,-.999998,.15),(8.7,-.999998,.15),(9.73,-1.2,.15),(9.73,-1.2,'bottom'),(9.73,-1.2,.3),(10.01,-1,.3)]})
route({'name':'DDR_ZQ_ESCAPE','net':'DDR_ZQ','from':('U_SOC',47),'points':[(7.629906,-.599948,.15),(9.954906,-.674948,.15),(10.179906,-.899948,.15),(10.354906,-1.374948,.15),(10.354906,-2.299948,.15),(10.354906,-2.299948,'bottom'),(10.354906,-2.299948,.15)]})
route({'name':'FEL_LOWER_VBUS','net':'USB0_VBUS','from':('J_USB_FEL',25),'to':('C_FEL_ESD',1),'points':[(-61.87497245,5.599827,.3),(-61.8,5.599827,.3),(-61.8,5.599827,'bottom'),(-61.8,5.599827,.6),(-59.7,3.349827,.6),(-59.55,3.199827,.6),(-59.2,3.099827,.6),(-58.85,3.149827,.6),(-58.45,3.499827,.6),(-58,4.029677,.6),(-58,4.029677,.8),(-53.825,8.9456,.8),(-53.825,8.9456,'top'),(-53.825,8.9456,.3),(-53.825,8,.3)]})
route({'name':'FEL_ESD_VBUS','net':'USB0_VBUS','from':('U_ESD_FEL',5),'to':('C_FEL_ESD',1),'points':[(-55.850904,8,.25),(-55.850904,8,'bottom'),(-55.850904,8,.3),(-54.125,8.7,.3),(-53.825,8.9456,.3),(-53.825,8.9456,'top'),(-53.825,8.9456,.3),(-53.825,8,.3)]})
# Shared physical barrel: keep one drill per net/location, without changing routes.
seen = {(round(v['x'],6),round(v['y'],6),v.get('source_net_id')) for v in circuit if v['type']=='pcb_via'}
unique=[]
for record in added:
if record['type']=='pcb_via':
key=(round(record['x'],6),round(record['y'],6),record['source_net_id'])
if key in seen:continue
seen.add(key)
unique.append(record)
circuit += unique
destination.write_text(json.dumps(circuit,indent=2)+'\n')
report=audit(circuit)
destination.with_suffix('.escape-check.json').write_text(json.dumps(report,indent=2)+'\n')
print('Added',sum(r['type']=='pcb_trace' for r in unique),'escapes; violations',report['violationCounts'])
if report['violations']:sys.exit(1)