0hmX/mt41k512m8da-107-it-p-footprints

This code defines the mechanical and physical layout of a Micron digital ball grid array (FBGA) package with 78 balls, including pin positions, footprint geometry, PCB pads, and silkscreen outlines for surface-mount integration.

Version
0.1.1
License
unset
Stars
1

scripts/verify.ts

import { strict as assert } from 'node:assert'
import { readFileSync, writeFileSync } from 'node:fs'
const circuit = JSON.parse(readFileSync('dist/examples/preview/circuit.json','utf8'))
const spec = JSON.parse(readFileSync('evidence/micron-mechanical.json','utf8'))
const pads = circuit.filter((x:any)=>x.type==='pcb_smtpad')
const ports = circuit.filter((x:any)=>x.type==='pcb_port')
const sources = circuit.filter((x:any)=>x.type==='source_port')
const ki = readFileSync('evidence/kicad-grid-reference.kicad_mod','utf8')
const k = [...ki.matchAll(/\(pad (\w+) smd circle \(at ([\d.-]+) ([\d.-]+)\) \(size ([\d.]+) ([\d.]+)\)/g)]
assert.equal(pads.length,78); assert.equal(k.length,78)
const names=new Set<string>()
for(const pad of pads){
 const pp=ports.find((p:any)=>p.pcb_port_id===pad.pcb_port_id)
 const sp=sources.find((p:any)=>p.source_port_id===pp.source_port_id)
 const name=sp.name; names.add(name)
 const kr=k.find(m=>m[1]===name); assert(kr,`Missing KiCad ball ${name}`)
 assert(Math.abs(pad.x-Number(kr[2]))<1e-9)
 assert(Math.abs(pad.y+Number(kr[3]))<1e-9,'KiCad Y-down normalized to tscircuit Y-up')
 assert(Math.abs(pad.radius*2-Number(kr[4]))<1e-9)
 const row=spec.rows.indexOf(name.slice(0,1)); const col=Number(name.slice(1))
 assert(spec.columns.includes(col)); assert(row>=0)
 assert(Math.abs(pad.x-(col-5)*spec.pitch)<1e-9)
 assert(Math.abs(pad.y-(6-row)*spec.pitch)<1e-9)
 assert.equal(pad.layer,'top'); assert.equal(pad.shape,'circle')
}
assert.equal(names.size,78)
assert.equal(Math.max(...pads.map((p:any)=>p.x))-Math.min(...pads.map((p:any)=>p.x)),spec.xSpan)
assert.equal(Math.max(...pads.map((p:any)=>p.y))-Math.min(...pads.map((p:any)=>p.y)),spec.ySpan)
let minGap=Infinity
for(let i=0;i<78;i++)for(let j=i+1;j<78;j++) minGap=Math.min(minGap,Math.hypot(pads[i].x-pads[j].x,pads[i].y-pads[j].y)-pads[i].radius-pads[j].radius)
assert(minGap>0.379999)
const raw=readFileSync('evidence/original-jlc-import.tsx','utf8')
const original=[...raw.matchAll(/<smtpad portHints=\{\["pin(\d+)"\]\} pcbX="([\d.-]+)mm" pcbY="([\d.-]+)mm" radius="([\d.]+)mm"/g)]
assert.equal(original.length,78)
let maxDelta=0
for(const m of original){
 const src=sources.find((s:any)=>s.pin_number===Number(m[1]));const pp=ports.find((p:any)=>p.source_port_id===src.source_port_id);const pad=pads.find((p:any)=>p.pcb_port_id===pp.pcb_port_id)
 maxDelta=Math.max(maxDelta,Math.abs(pad.x-Number(m[2])),Math.abs(pad.y-Number(m[3])))
}
const report={result:'PASS',compiledPads:78,uniqueBallNames:78,micronGrid:'78/78 match',kicadCentersAndDiameters:'78/78 match (Y axis normalized)',originalImportMaxAxisDeltaMm:maxDelta,originalPadDiameterMm:Number(original[0][4])*2,newPadDiameterMm:0.42,minCopperGapMm:minGap,scope:'Geometry and physical ball identity only; no electrical, assembly-process, or DDR routing validation.'}
writeFileSync('evidence/verification.json',JSON.stringify(report,null,2)+'\n')
console.log(report)