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/route-ddr-system-worker.ts

import {readFileSync,writeFileSync} from 'node:fs'
import {resolve} from 'node:path'
import {verifyDdrCapture} from './ddr-capture-provenance'
import {AutoroutingPipelineSolver4,AutoroutingPipelineSolver6,AutoroutingPipelineSolver7_MultiGraph,AutoroutingPipelineSolver9_PreloadedTraceGraph} from '@tscircuit/capacity-autorouter'
const directory=resolve(process.argv[2]??'dist/ddr-system/outward_north')
const corridorMode=process.argv.includes('--corridors')
const input=JSON.parse(readFileSync(resolve(directory,corridorMode?'corridor-routing-input.json':'routing-input.json'),'utf8'))
if(corridorMode){const {captureHash}=verifyDdrCapture(directory);if(input.__ddrCaptureHash!==captureHash)throw Error('Corridors belong to another capture')}
// Shared CPU exit points identify shared electrical nets; preserve every RAM
// terminal while consolidating duplicate source branches into one routed net.
const owner=new Map<string,number>()
const parent=input.connections.map((_:any,i:number)=>i)
const find=(i:number):number=>parent[i]===i?i:parent[i]=find(parent[i])
for(const [i,c] of input.connections.entries())for(const p of c.pointsToConnect){
 const key=p.pointId??`${p.x},${p.y},${p.layer}`
 if(owner.has(key)){const a=find(i),b=find(owner.get(key)!);if(a!==b)parent[a]=b}else owner.set(key,i)
}
const groups=new Map<number,any[]>()
for(const [i,c] of input.connections.entries()){const k=find(i);groups.set(k,[...(groups.get(k)??[]),c])}
const alias=new Map<string,string>()
const connections=[...groups.values()].map(g=>{
 const name=g[0].name
 for(const c of g)alias.set(c.name,name)
 const points=new Map(g.flatMap(c=>c.pointsToConnect.map((p:any)=>[p.pointId??`${p.x},${p.y},${p.layer}`,p])))
 return {...g[0],name,rootConnectionName:name,mergedConnectionNames:g.map(c=>c.name),pointsToConnect:[...points.values()]}
})
if(connections.length!==50||input.connections.length!==78||connections.filter(c=>c.pointsToConnect.length===3).length!==28)throw Error('Incomplete two-x8 signal topology')
for(const o of input.obstacles)o.connectedTo=[...new Set([...o.connectedTo,...o.connectedTo.map((n:string)=>alias.get(n)).filter(Boolean)])]
const obstaclesOnly=process.argv.includes('--obstacles-only')
// The runtime already emits fixed and saved copper as obstacles. In this mode
// avoid rebuilding its electrical topology; retain every geometric obstacle.
let normalized={...input,connections,allowJumpers:false,allowViaInPad:false,buses:[{busId:'DDR_HOST',connectionNames:connections.map(c=>c.name),allowedLayers:['top','inner2','inner4','inner6']}],minTraceToPadEdgeClearance:.1016,minPadEdgeToPadEdgeClearance:.1016,minViaEdgeToPadEdgeClearance:.1016,minViaPadDiameter:.4572,minViaHoleDiameter:.254,...(obstaclesOnly?{traces:[],obstacles:input.obstacles.map((o:any,i:number)=>({...o,netIsAssignable:false,offBoardConnectsTo:[],connectedTo:[...new Set(o.connectedTo.map((n:string)=>alias.get(n)).filter(Boolean)),`obstacle:${i}`]}))}:{})}
const projected=process.argv.includes('--project-signal-layers')
const physicalToVirtual:Record<string,string>={top:'top',inner2:'inner1',inner4:'inner2',inner6:'bottom'}
const virtualToPhysical=Object.fromEntries(Object.entries(physicalToVirtual).map(([p,v])=>[v,p]))
if(projected){
 if(!obstaclesOnly||input.obstacles.filter((o:any)=>o.connectedTo.some((n:string)=>n.startsWith('solid_module_'))).length!==3)throw Error('Signal layer projection requires three enclosing solid module keepouts')
 normalized={...normalized,layerCount:4,connections:normalized.connections.map((c:any)=>({...c,pointsToConnect:c.pointsToConnect.map((p:any)=>{if(!physicalToVirtual[p.layer])throw Error('Unexpected DDR terminal layer');return {...p,layer:physicalToVirtual[p.layer]}})})),obstacles:normalized.obstacles.map((o:any)=>({...o,layers:[...new Set(o.layers.map((l:string)=>physicalToVirtual[l]).filter(Boolean))]})).filter((o:any)=>o.layers.length),buses:[{busId:'DDR_HOST',connectionNames:connections.map(c=>c.name),allowedLayers:Object.values(physicalToVirtual)}]}
}
writeFileSync(resolve(directory,'merged-routing-input.json'),JSON.stringify(normalized,null,2)+'\n')
if(process.argv.includes('--prepare-only'))process.exit(0)
console.log(`Full interface: ${input.connections.length} branches -> ${connections.length} nets; ${connections.filter(c=>c.pointsToConnect.length===3).length} shared two-RAM nets`)
const pipeline=process.argv.find(a=>a.startsWith('--pipeline='))?.slice(11)??'4'
const standard=pipeline==='4'?AutoroutingPipelineSolver4:pipeline==='6'?AutoroutingPipelineSolver6:pipeline==='7'?AutoroutingPipelineSolver7_MultiGraph:undefined
if(!standard)throw Error('Choose a supported via-generating pipeline: 4,6,7')
const Solver=obstaclesOnly?standard:AutoroutingPipelineSolver9_PreloadedTraceGraph
const effort=Number(process.argv.find(a=>a.startsWith('--effort='))?.slice(9)??1)
const maxNodeDimension=Number(process.argv.find(a=>a.startsWith('--node-size='))?.slice(12)??16)
const solver:any=new Solver(normalized,{effort,maxNodeDimension})
let iterations=0,last=Date.now()
const pathIterations=Number(process.argv.find(a=>a.startsWith('--path-iterations='))?.slice(18)??100000)
const start=Date.now(),limit=Number(process.argv[3]??120)*1000
while(!solver.solved&&!solver.failed&&Date.now()-start<limit){
 if(solver.portPointPathingSolver)solver.portPointPathingSolver.MAX_ITERATIONS=pathIterations
 // Give asynchronous solver initialization/cache effects a chance to settle.
 if(iterations%256===0)await Bun.sleep(0)
 solver.step();iterations++
 if(Date.now()-last>10000){console.log(JSON.stringify({iterations,elapsedSeconds:(Date.now()-start)/1000,phase:solver.getCurrentPhase(),stats:(solver as any).stats}));last=Date.now()}
}
const graphMetrics=(solver.portPointPathingSolver as any)?.getSolveGraphBenchmarkMetrics?.()
const status=solver.solved?'routed-unvalidated':solver.failed?'routing-failed':'diagnostic-time-limit'
writeFileSync(resolve(directory,'router-report.json'),JSON.stringify({status,captureHash:input.__ddrCaptureHash,pipeline,graphMetrics,iterations,elapsedSeconds:(Date.now()-start)/1000,error:solver.error,phase:solver.getCurrentPhase(),effort,maxNodeDimension,pathIterations,netCount:connections.length,branchCount:input.connections.length},null,2)+'\n')
if(!solver.solved){console.log(status,solver.error);process.exit(1)}
const routes=solver.getOutputSimplifiedPcbTraces().map((t:any)=>({...t,route:t.route.map((q:any)=>q.route_type==='wire'?{...q,layer:projected?virtualToPhysical[q.layer]!:q.layer}:q.route_type==='via'?{...q,from_layer:projected?virtualToPhysical[q.from_layer]!:q.from_layer,to_layer:projected?virtualToPhysical[q.to_layer]!:q.to_layer,via_diameter:.4572,via_hole_diameter:.254}:q)}))
writeFileSync(resolve(directory,'host-routes.json'),JSON.stringify(routes,null,2)+'\n')
console.log(status)