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/diagnose-ddr-router.ts

/** Isolate router failures without removing foreign copper/keepouts.
 * Each solver runs in a child process with a hard external 20-second watchdog. */
import {readFileSync,writeFileSync,mkdirSync} from 'node:fs'
import {resolve} from 'node:path'
import * as pipelines from '@tscircuit/capacity-autorouter'
const directory=resolve(process.argv[2]??'dist/ddr-system/bus_grouped')
const out=resolve(directory,'router-isolation')
mkdirSync(out,{recursive:true})
const write=(file:string,data:unknown)=>writeFileSync(resolve(out,file),JSON.stringify(data,null,2)+'\n')
const read=(file:string)=>JSON.parse(readFileSync(resolve(directory,file),'utf8'))
const cases:Record<string,(signal:string)=>boolean>={
 dq0:s=>s==='DDR_D0',dq1:s=>s==='DDR_D1',dq8:s=>s==='DDR_D8',synthetic:()=>false,
 byte0:s=>/^DDR_D[0-7]$/.test(s)||['DDR_DQM0','DDR_DQS0','DDR_DQSn0'].includes(s),
 clock:s=>['DDR_CK','DDR_CKn'].includes(s),address0:s=>s==='DDR_A0',
}
function prepared(caseName:string,projected:boolean){
 const input=read('corridor-routing-input.json'),map=read('signal-map.json')
 if(caseName==='synthetic'){
  const points=[{x:-2,y:0,layer:'top',pointId:'synthetic-a'},{x:2,y:0,layer:'inner2',pointId:'synthetic-b'}]
  if(projected)points[1]!.layer='inner1'
  return {normalized:{...input,bounds:{minX:-10,maxX:10,minY:-10,maxY:10},layerCount:projected?4:10,obstacles:[],traces:[],connections:[{name:'synthetic-layer-transition',source_trace_id:'synthetic-layer-transition',pointsToConnect:points}]},metadata:{caseName,projected,originalBranches:0,allMergedNetCount:1,selectedNetCount:1,selectedNames:['synthetic-layer-transition'],selectedTerminalCounts:[2],obstacleCount:0,solidBoxes:0,scope:'Separate artificial solver capability test; does not remove any real-board obstacle'}}
 }
 const owner=new Map<string,number>(),parent=input.connections.map((_:any,i:number)=>i)
 const find=(i:number):number=>parent[i]===i?i:parent[i]=find(parent[i])
 const pointKey=(p:any)=>p.pointId??`${p.x},${p.y},${p.layer}`
 for(const [i,c] of input.connections.entries())for(const p of c.pointsToConnect){const key=pointKey(p);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 merged=[...groups.values()].map(g=>{
  const name=g[0].name
  for(const c of g)alias.set(c.name,name)
  return {...g[0],name,rootConnectionName:name,mergedConnectionNames:g.map(c=>c.name),pointsToConnect:[...new Map(g.flatMap(c=>c.pointsToConnect.map((p:any)=>[pointKey(p),p]))).values()]}
 })
 const desired=new Set(map.filter((m:any)=>cases[caseName]!(m.cpuSignal??m.cpuTarget.split('.').at(-1))).map((m:any)=>alias.get(m.sourceTraceId)))
 const connections=merged.filter(c=>desired.has(c.name))
 if(!connections.length)throw Error(`No matching input connections for ${caseName}`)
 const physicalToVirtual:Record<string,string>={top:'top',inner2:'inner1',inner4:'inner2',inner6:'bottom'}
 const obstacles=input.obstacles.map((o:any)=>({...o,connectedTo:[...new Set(o.connectedTo.map((n:string)=>alias.get(n)??n))],netIsAssignable:false,offBoardConnectsTo:[]}))
 const normalized={...input,connections,obstacles,traces:[],buses:[{busId:'DDR_DIAGNOSTIC',connectionNames:connections.map(c=>c.name),allowedLayers:Object.keys(physicalToVirtual)}],minTraceToPadEdgeClearance:.1016,minPadEdgeToPadEdgeClearance:.1016,minViaEdgeToPadEdgeClearance:.1016,minViaPadDiameter:.4572,minViaHoleDiameter:.254}
 if(projected){
  normalized.layerCount=4
  normalized.connections=connections.map(c=>({...c,pointsToConnect:c.pointsToConnect.map((p:any)=>{if(!physicalToVirtual[p.layer])throw Error(`Unknown signal layer ${p.layer}`);return {...p,layer:physicalToVirtual[p.layer]}})}))
  normalized.obstacles=obstacles.map((o:any)=>({...o,layers:[...new Set(o.layers.map((l:string)=>physicalToVirtual[l]).filter(Boolean))]}))
  if(normalized.obstacles.some((o:any)=>!o.layers.length))throw Error('Projection would drop an obstacle; refusing diagnostic')
  normalized.buses[0]!.allowedLayers=Object.values(physicalToVirtual)
 }
 return {normalized,metadata:{caseName,projected,originalBranches:input.connections.length,allMergedNetCount:merged.length,selectedNetCount:connections.length,selectedNames:connections.map(c=>c.name),selectedTerminalCounts:connections.map(c=>c.pointsToConnect.length),obstacleCount:obstacles.length,solidBoxes:obstacles.filter((o:any)=>o.connectedTo.some((n:string)=>n.startsWith('solid_module_'))).length}}
}
if(process.argv[3]==='--child'){
 const caseName=process.argv[4]!,projected=process.argv[5]==='4',solverName=process.argv[6]??'AutoroutingPipelineSolver8',id=`${caseName}-${projected?'4':'10'}layers${process.argv[6]?`-${solverName}`:''}`
 const {normalized,metadata}=prepared(caseName,projected)
 write(`${id}.input.json`,normalized)
 const Solver=(pipelines as any)[solverName]; if(!Solver)throw Error(`Unavailable solver ${solverName}`)
 const start=Date.now(),solver=new Solver(normalized,{effort:1})
 let iterations=0,phase=''
 while(!solver.solved&&!solver.failed){
  if(solver.portPointPathingSolver)solver.portPointPathingSolver.MAX_ITERATIONS=1000000
  const current=solver.getCurrentPhase()
  if(current!==phase){phase=current;write(`${id}.json`,{...metadata,status:'running',phase,iterations,elapsedSeconds:(Date.now()-start)/1000,routedNetCount:0})}
  solver.step();iterations++
  if(solverName.includes('Networked')&&iterations%100===0)await Bun.sleep(1)
 }
 const routes=solver.solved?solver.getOutputSimplifiedPcbTraces():[]
 if(solver.solved)write(`${id}.routes.json`,routes)
 write(`${id}.json`,{...metadata,status:solver.solved?'routed-unvalidated':'routing-failed',phase:solver.getCurrentPhase(),error:solver.error,iterations,elapsedSeconds:(Date.now()-start)/1000,routedTraceCount:routes.length,routedNetCount:solver.solved?metadata.selectedNetCount:0,scope:'Router isolation only; no geometry, timing or DDR electrical acceptance'})
}else if(process.argv[3]==='--alternates'){
 const names=['AutoroutingPipelineSolver4','AutoroutingPipelineSolver6','AutoroutingPipelineSolver7_MultiGraph','AutoroutingPipelineSolver9_Networked','AutoroutingPipelineSolver11_Simplification']
 const results=[]
 for(const solverName of names){
  const id=`synthetic-4layers-${solverName}`
  if(solverName==='AutoroutingPipelineSolver11_Simplification'){
   const result={solverName,status:'not-a-missing-connection-router',routedNetCount:0,reason:'Installed source states Pipeline11 only cleans existing traces and never routes missing connections'}
   write(`${id}.json`,result);results.push(result);console.log(JSON.stringify(result));write('alternate-summary.json',{status:'synthetic-capability-only',results});continue
  }
  if(!(pipelines as any)[solverName]){results.push({solverName,status:'not-exported'});continue}
  const start=Date.now()
  const child=Bun.spawn(['bun',new URL(import.meta.url).pathname,directory,'--child','synthetic','4',solverName],{stdout:'ignore',stderr:'pipe'})
  let timedOut=false;const timer=setTimeout(()=>{timedOut=true;child.kill('SIGKILL')},10000)
  const exit=await child.exited;clearTimeout(timer)
  const stderr=await new Response(child.stderr).text()
  let result:any
  try{result=JSON.parse(readFileSync(resolve(out,`${id}.json`),'utf8'))}catch{result={status:'child-failed'}}
  result={...result,solverName}
  if(timedOut)result={...result,status:'external-time-limit',elapsedSeconds:(Date.now()-start)/1000,routedNetCount:0}
  if(exit!==0&&!timedOut)result={...result,status:'child-failed',exit,stderr}
  write(`${id}.json`,result);results.push(result);console.log(JSON.stringify(result))
  write('alternate-summary.json',{status:'synthetic-capability-only',results})
 }
}else{
 const selected=process.argv[3]?process.argv[3].split(','):Object.keys(cases)
 for(const name of selected)if(!cases[name])throw Error(`Unknown diagnostic case ${name}`)
 const override=process.argv.find(a=>a.startsWith('--pipeline='))?.slice(11)
 const results:any[]=[]
 for(const name of selected)for(const layers of ['4','10']){
  const id=`${name}-${layers}layers${override?`-${override}`:''}`,start=Date.now()
  const child=Bun.spawn(['bun',new URL(import.meta.url).pathname,directory,'--child',name,layers,...(override?[override]:[])],{stdout:'ignore',stderr:'pipe'})
  let timedOut=false
  const timer=setTimeout(()=>{timedOut=true;child.kill('SIGKILL')},20000)
  const exit=await child.exited;clearTimeout(timer)
  const stderr=await new Response(child.stderr).text()
  let result:any
  try{result=JSON.parse(readFileSync(resolve(out,`${id}.json`),'utf8'))}catch{result={caseName:name,projected:layers==='4',status:'child-failed',routedNetCount:0}}
  if(timedOut)result={...result,status:'external-time-limit',elapsedSeconds:(Date.now()-start)/1000}
  if(exit!==0&&!timedOut)result={...result,status:'child-failed',exit,stderr}
  write(`${id}.json`,result);results.push(result)
  console.log(JSON.stringify(result))
  write('summary.json',{status:'diagnostic-not-board-acceptance',results})
 }
}