astra/am3352-sbc
A 4-layer AM3352 single-board computer with 128‑MiB DDR3, PMIC power regulation, USB‑C 5‑V PD input, dual USB host ports, HDMI output with ESD protection, microSD/UART/JTAG interfaces, addressable RGB indicators, and a PWM-driven buzzer.
- Version
- 0.1.19
- License
- unset
- Stars
- 0
scripts/audit-peripheral-candidate.ts
import {readFileSync,writeFileSync} from 'node:fs';
import {createHash} from 'node:crypto';
import {checkPcbTraceSelfShorts} from '@tscircuit/checks';
import {pairLengthReports,exteriorPairSpacingReports,type Trace} from '@tscircuit/bus-lanes-solver';
const raw=readFileSync(process.argv[2]??'output/board-pairs.circuit.json','utf8'),cj:any[]=JSON.parse(raw);
const native=JSON.parse(readFileSync('output/peripheral-signal-first.srj.json','utf8'));
const originals:any[]=JSON.parse(readFileSync('output/peripheral-signal-first.audit.json','utf8'));
const originalNames=new Map(originals.filter(e=>e.type==='source_trace').map(e=>[e.source_trace_id,e.name]));
const sources=cj.filter(e=>e.type==='source_trace'&&native.connections.some((c:any)=>originalNames.get(c.name)===e.name));
const ids=new Set(sources.map(e=>e.source_trace_id)),ports=new Set(sources.flatMap(e=>e.connected_source_port_ids));
const pcbPorts=new Set(cj.filter(e=>e.type==='pcb_port'&&ports.has(e.source_port_id)).map(e=>e.pcb_port_id));
const rename=new Map(sources.map(e=>[e.source_trace_id,native.connections.find((c:any)=>originalNames.get(c.name)===e.name).name]));
const traces:Trace[]=cj.filter(e=>e.type==='pcb_trace'&&ids.has(e.source_trace_id)).map(e=>({...e,connection_name:rename.get(e.source_trace_id)}));
const expected:Trace[]=JSON.parse(readFileSync('output/peripheral-complete.traces.json','utf8'));
const normalize=(route:any[])=>route.map(p=>Object.fromEntries(Object.entries(p).filter(([k])=>!['start_pcb_port_id','end_pcb_port_id','layers','pcb_port_id','pointId'].includes(k)).sort(([a],[b])=>a.localeCompare(b)))).filter((p,i,a)=>!i||JSON.stringify(p)!==JSON.stringify(a[i-1]));
const lost=expected.filter(t=>!traces.some(actual=>actual.connection_name===t.connection_name&&JSON.stringify(normalize(actual.route))===JSON.stringify(normalize(t.route)))).map(t=>t.connection_name);
const errors=cj.filter(e=>e.type.endsWith('_error'));
const connectivity=errors.filter(e=>e.type==='pcb_port_not_connected_error'&&e.pcb_port_ids?.some((p:string)=>pcbPorts.has(p))||e.type==='pcb_trace_missing_error'&&ids.has(e.source_trace_id));
const copperErrors=errors.filter(e=>!['pcb_port_not_connected_error','pcb_trace_missing_error','pcb_bus_length_skew_error'].includes(e.type)&&!(e.type==='pcb_trace_error'&&e.message.includes('has dangling endpoint')));
const pairs=pairLengthReports(native,traces),coupling=exteriorPairSpacingReports(native,traces);
const selfShorts=checkPcbTraceSelfShorts(cj);
const report={selfShorts,circuitSha256:createHash('sha256').update(raw).digest('hex'),traces:traces.length,expected:24,expectedPorts:pcbPorts.size,geometryPreserved:lost.length===0,lost,connectivityErrors:connectivity,copperErrors,pairs,coupling,pass:selfShorts.length===0&&traces.length===24&&lost.length===0&&!connectivity.length&&!copperErrors.length&&pairs.length===12&&pairs.every(p=>p.matched)&&coupling.length===12&&coupling.every(p=>p.applicable&&p.matched),wholeBoardComplete:false,fabricationReady:false};
writeFileSync(process.argv[3]??'output/peripheral-native-audit.json',JSON.stringify(report,null,2));console.log(JSON.stringify({...report,pairs:undefined,coupling:undefined}));if(!report.pass)process.exitCode=1;