0hmX/am3352
This code suite comprises TypeScript scripts that analyze, verify, and assemble complex DDR memory interface hardware, focusing on physical routing, via and pad placement, electrical clearance, and physical constraints, often involving precise geometric calculations and consistent provenance tracking.
- Version
- 1.0.5
- License
- unset
- Stars
- 0
scripts/validate.ts
import { assertRouteAngles } from './check-route-angles'
import assert from 'node:assert/strict'
import{readFileSync,writeFileSync}from'node:fs'
import{FIXED_SUPPLY_DROPS}from'../src/fixed-supply-drops'
import{AM3352_PLANE_DROPS,AM3352_SIGNAL_CONNECTIONS}from'../src/layout'
import{decouplingCaps}from'../src/DecouplingNetwork'
import{AM3352_PINS}from'../src/pin-map'
import{getAM3352TracePaths}from'../src/saved-paths'
import{assertLayoutProfile}from'../src/profiles'
const profile=process.argv[2]??'native'
assertLayoutProfile(profile)
const am3352TracePaths=getAM3352TracePaths(profile)
const json=JSON.parse(readFileSync(`src/generated/${profile}.circuit.json`,'utf8'))
const fixture=JSON.parse(readFileSync(`scripts/fixtures/${profile}.input.json`,'utf8'))
assertRouteAngles(am3352TracePaths)
assertRouteAngles(fixture.input.traces)
const result=JSON.parse(readFileSync(`dist/solver/${profile==='native'?'six-layer':profile}-output.json`,'utf8'))
assert.ok(result.output.validation.valid&&result.drc.valid)
assert.equal(am3352TracePaths.length,313)
const actualConnections=[...am3352TracePaths.map(p=>p.connection),...FIXED_SUPPLY_DROPS.map(d=>`U1.${d.ball}`)].sort()
const expectedConnections=[...AM3352_PLANE_DROPS,...AM3352_SIGNAL_CONNECTIONS].map(d=>`U1.${AM3352_PINS[d.pinNumber-1]!.ballName}`).sort()
assert.deepEqual(actualConnections,expectedConnections,'Every original signal and supply ball must have exactly one saved or fixed path')
for(const d of FIXED_SUPPLY_DROPS){
const trace=json.find((e:any)=>e.type==='source_trace'&&e.name===`FIXED_DROP_${d.ball}`)
const net=json.find((e:any)=>e.type==='source_net'&&e.name===d.net)
assert.ok(trace?.connected_source_net_ids.includes(net?.source_net_id),`${d.ball} fixed drop is on the wrong supply`)
}
const planes=new Set(fixture.input.buses.filter((b:any)=>b.termination?.type==='plane').flatMap((b:any)=>b.connectionNames))
const traces=json.filter((e:any)=>e.type==='pcb_trace')
const geometry=(r:any[])=>r.map(p=>[p.route_type,p.x.toFixed(6),p.y.toFixed(6),p.layer??p.from_layer,p.to_layer??'',p.width??p.via_diameter??''])
const same=(a:any,b:any)=>JSON.stringify(geometry(a))===JSON.stringify(geometry(b))
for(const t of fixture.input.traces)assert.ok(traces.some((r:any)=>same(r.route,t.route)),`Fixed capacitor copper changed: ${t.pcb_trace_id}`)
for(const p of am3352TracePaths)assert.ok(traces.some((t:any)=>same(t.route,p.route)),`Saved path was lost or changed: ${p.connection}`)
let edgeCount=0
for(const t of result.output.fanoutTraces){
if(planes.has(t.connection_name))continue
const end=t.route.at(-1)
assert.ok(Math.abs(Math.max(Math.abs(end.x),Math.abs(end.y))-8.5)<1e-6,`Not at 1 mm padded BGA boundary: ${t.connection_name}`)
edgeCount++
}
for(const [connection,edge] of Object.entries(fixture.requestedExits??{})){
const end=result.output.fanoutTraces.find((t:any)=>t.connection_name===connection).route.at(-1)
const value=edge==='left'?end.x+8.5:edge==='right'?end.x-8.5:edge==='top'?end.y-8.5:end.y+8.5
assert.ok(Math.abs(value)<1e-6,`${connection} missed requested ${edge} exit`)
}
assert.equal(edgeCount,201)
const pairReports=fixture.input.differentialPairs.map((p:any)=>{
const routes=p.connectionNames.map((n:string)=>result.output.fanoutTraces.find((t:any)=>t.connection_name===n).route)
const names=routes.map((r:any[])=>AM3352_PINS.find(p=>Math.hypot(p.x-r[0].x,p.y-r[0].y)<1e-6)!.name)
const lengths=routes.map((r:any[])=>r.slice(1).reduce((n:number,b:any,i:number)=>n+Math.hypot(b.x-r[i].x,b.y-r[i].y),0))
const skew=Math.abs(lengths[0]-lengths[1]);assert.ok(skew<=p.lengthTolerance+1e-6)
const spans=routes.map((r:any[])=>r.filter(q=>q.route_type==='via').map(q=>[q.from_layer,q.to_layer]))
assert.deepEqual(spans[0],spans[1],`Asymmetric pair via spans: ${names}`)
assert.equal(routes[0].at(-1).layer,routes[1].at(-1).layer)
return {signals:names,copperLengthsMm:lengths,skewMm:skew,toleranceMm:p.lengthTolerance,viaSpans:spans[0],exitLayer:routes[0].at(-1).layer}
})
writeFileSync(`src/generated/${profile}.metrics.json`,JSON.stringify({solver:'@tscircuit/fanout-solver',revision:'3af2f35ee2bbd715059b725900c0f7f71e64bdd8',validation:result.output.validation,drc:result.drc,layers:6,viaType:"through-hole",viaPadMm:.4,viaDrillMm:.2,capacitorsOnBottom:true,capacitorBoundaryMm:17,paddingMm:1,packageSizeMm:15,fanoutSizeMm:17,edgeConnections:edgeCount,savedPaths:313,capacitors:decouplingCaps.length,fixedSupplyConnections:FIXED_SUPPLY_DROPS.length,fixedTraces:fixture.input.traces.length,coalescedVias:result.coalescedVias??[],pairs:pairReports},null,2)+'\n')
console.log('201 signals reach the 1 mm padded BGA boundary; all saved/fixed routes preserved; five pairs pass skew and via-span checks')