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/audit-ddr-right-exit-budget.ts

/** Read-only proposed-exit budgets and bounded via-site legality probes. */
import {readFileSync,writeFileSync} from 'node:fs'
import {resolve} from 'node:path'
import {createHash} from 'node:crypto'
import {verifyDdrCapture} from './ddr-capture-provenance'
import {verifyDdrSystemCopper} from './check-ddr-system-copper'
const dir=resolve(process.argv[2]??'dist/ddr-system/host-taps-54af3e346b5e'),out=resolve(process.argv[3]??'dist/ddr-integrated-ten/right-exit-budget.json')
const read=(p:string)=>JSON.parse(readFileSync(p,'utf8')),{captureHash}=verifyDdrCapture(dir),original=read(resolve(dir,'unrouted.circuit.json')),connections=read(resolve(dir,'merged-routing-input.json')).connections,floors=read('dist/ddr-integrated-ten/dqlm-audit.json')
if(floors.captureHash!==captureHash)throw Error('Stale length-floor audit')
const rightX=-6.040077,octile=(a:any,b:any)=>{const x=Math.abs(a.x-b.x),y=Math.abs(a.y-b.y);return Math.max(x,y)+(Math.SQRT2-1)*Math.min(x,y)}
const planar=(route:any[])=>route.reduce((n,p,i)=>{const q=route[i-1];return n+(q?.route_type==='wire'&&p.route_type==='wire'&&q.layer===p.layer?Math.hypot(p.x-q.x,p.y-q.y):0)},0)
const bytes:any[]=[]
for(const byte of floors.bytes){
 const rows:any[]=[]
 for(const member of byte.members.filter((m:any)=>byte.nominalUnreachableMembers.includes(m.terminal))){
  const trace=original.find((e:any)=>e.pcb_trace_id===member.ram.savedTraceId),viaIndex=trace.route.findIndex((p:any)=>p.route_type==='via')
  if(viaIndex<0)throw Error('No saved RAM escape via')
  const oldVia=trace.route[viaIndex],prefixMm=planar(trace.route.slice(0,viaIndex)),fixed=original.filter((e:any)=>!(e.type==='pcb_trace'&&e.pcb_trace_id===trace.pcb_trace_id)&&!(e.type==='pcb_via'&&e.pcb_trace_id===trace.pcb_trace_id))
  const budget=(exitY:number,prefix:number,via:any)=>{
   const exit={x:rightX,y:exitY,layer:member.ram.exit.layer},hostLowerMm=octile(exit,member.cpu.exit),remainingRamEscapeMm=byte.dqlmPairedDataMaximumMm-member.cpu.fixedPlanarMm-hostLowerMm,ramLowerMm=prefix+octile(via,exit)
   return {exit,hostLowerMm,remainingRamEscapeMm,ramEscapeLowerMm:ramLowerMm,optimisticMarginMm:remainingRamEscapeMm-ramLowerMm,totalLowerMm:member.cpu.fixedPlanarMm+hostLowerMm+ramLowerMm}
  }
  const candidates=[{label:'retained-existing-via',x:oldVia.x,y:oldVia.y},...[-1,1].flatMap(sx=>[-1,1].map(sy=>({label:`adjacent-interstice-${sx>0?'east':'west'}-${sy>0?'north':'south'}`,x:member.ram.x+sx*.4,y:member.ram.y+sy*.4})))].filter((p,i,a)=>a.findIndex(q=>Math.hypot(p.x-q.x,p.y-q.y)<1e-6)===i)
  const probes=candidates.map(p=>{
   const proposed={type:'pcb_trace',pcb_trace_id:trace.pcb_trace_id,source_trace_id:trace.source_trace_id,connection_name:member.name,route:[{route_type:'wire',x:member.ram.x,y:member.ram.y,layer:'top',width:.12},{route_type:'wire',x:p.x,y:p.y,layer:'top',width:.12},{route_type:'via',x:p.x,y:p.y,from_layer:'top',to_layer:member.ram.exit.layer,via_diameter:.4572,via_hole_diameter:.254},{route_type:'wire',x:p.x,y:p.y,layer:member.ram.exit.layer,width:.12}]}
   const physical=verifyDdrSystemCopper(fixed,[proposed],connections),violations=physical.violations.map((v:any)=>({layer:v.layer,gapMm:v.gapMm,a:v.aShape.traceId??v.a,b:v.bShape.traceId??v.b})),legMm=Math.hypot(p.x-member.ram.x,p.y-member.ram.y)
   return {...p,legMm,legalAgainstCurrentFixed:!physical.errors.length&&!violations.length&&physical.angles.valid&&!physical.joinedBends.length,errors:physical.errors,violations,angles:physical.angles.violations,joinedBends:physical.joinedBends,budgetAtViaY:budget(p.y,legMm,p),budgetAtOldExitY:budget(member.ram.exit.y,legMm,p)}
  })
  rows.push({terminal:member.terminal,name:member.name,ball:member.ram,nominalMm:byte.dqlmPairedDataMaximumMm,cpuFixedMm:member.cpu.fixedPlanarMm,cpuExit:member.cpu.exit,retainedVia:{x:oldVia.x,y:oldVia.y,prefixMm},moveOnlyExitKeepOldY:budget(member.ram.exit.y,prefixMm,oldVia),redirectExistingViaAtItsY:budget(oldVia.y,prefixMm,oldVia),viaProbes:probes,earliestLegalProbedSites:probes.filter(p=>p.legalAgainstCurrentFixed).sort((a,b)=>a.legMm-b.legMm).map(p=>({label:p.label,x:p.x,y:p.y,legMm:p.legMm}))})
 }
 const ramOrder=[...rows].sort((a,b)=>a.retainedVia.y-b.retainedVia.y||a.cpuExit.y-b.cpuExit.y).map(r=>({terminal:r.terminal,viaY:r.retainedVia.y,viaX:r.retainedVia.x})),cpuOrder=[...rows].sort((a,b)=>a.cpuExit.y-b.cpuExit.y).map(r=>({terminal:r.terminal,y:r.cpuExit.y,layer:r.cpuExit.layer})),inversions:any[]=[]
 for(let i=0;i<rows.length;i++)for(let j=i+1;j<rows.length;j++){const a=rows[i],b=rows[j];if((a.retainedVia.y-b.retainedVia.y)*(a.cpuExit.y-b.cpuExit.y)<-1e-8)inversions.push({a:a.terminal,b:b.terminal,cpuLayers:[a.cpuExit.layer,b.cpuExit.layer]})}
 const laneRows:any[]=[]
 for(const viaY of [...new Set(rows.map(r=>r.retainedVia.y))].sort((a,b)=>a-b)){const tied=rows.filter(r=>Math.abs(r.retainedVia.y-viaY)<1e-7).sort((a,b)=>a.cpuExit.y-b.cpuExit.y),slots=tied.map(r=>r.ball.exit.y).sort((a,b)=>a-b);tied.forEach((r,i)=>laneRows.push({terminal:r.terminal,x:rightX,y:slots[i],layer:r.ball.exit.layer,sourceViaY:viaY,cpuExitY:r.cpuExit.y}))}
 const existingRight=byte.members.filter((m:any)=>m.ram.exit.faces.includes('right')).map((m:any)=>({terminal:m.terminal,x:m.ram.exit.x,y:m.ram.exit.y})),slotConflicts=laneRows.flatMap(slot=>existingRight.filter((p:any)=>Math.abs(p.y-slot.y)<.2216-1e-7).map((p:any)=>({proposed:slot.terminal,existing:p.terminal,y:slot.y,separationMm:Math.abs(p.y-slot.y)})))
 bytes.push({targetOrderOnly:laneRows.map(r=>r.terminal),unadjustedRightFaceSlotCandidates:laneRows,existingRightFacePorts:existingRight,unadjustedSlotConflicts:slotConflicts,slotStatus:'Ordering guidance only: existing right-face even-DQ/DQS ports must also be repacked or coordinated; old left-face Y values cannot be copied blindly.',minimumInternalCandidateSlotSpacingMm:Math.min(...laneRows.slice(1).map((r,i)=>r.y-laneRows[i].y)),byte:byte.byte,nominalMm:byte.dqlmPairedDataMaximumMm,rows,recommendedRightExitBottomToTopOrder:ramOrder,cpuExitBottomToTopOrder:cpuOrder,ramToCpuOrderInversions:inversions,orderingScope:'Preserving RAM via order minimizes monotone same-layer crossings inside the memory escape. CPU order inversions require coordinated CPU fanout/transition/lane assignment; scalar ordering alone does not prove routability.'})
}
const report={captureHash,originalSha256:createHash('sha256').update(readFileSync(resolve(dir,'unrouted.circuit.json'))).digest('hex'),rightExitX:rightX,bytes,scope:'No source or routing mutation. All length budgets are obstacle-free lower bounds with fixed CPU escapes; no via propagation delay or terminal-turn allowance. Via probes replace only their own original RAM escape for a read-only full-ten-layer clearance test. Four neighboring +/-0.4mm interstices plus existing site are a bounded candidate family, NOT a continuous earliest-via optimizer. Original reference-pour holes remain unchanged, so a new site needing an antipad is correctly not legal against this frozen board.'}
writeFileSync(out,JSON.stringify(report,null,2)+'\n');console.log(JSON.stringify(bytes.map(b=>({byte:b.byte,rows:b.rows.map((r:any)=>({terminal:r.terminal,oldYBudget:r.moveOnlyExitKeepOldY.remainingRamEscapeMm,retainedViaFloor:r.moveOnlyExitKeepOldY.ramEscapeLowerMm,margin:r.moveOnlyExitKeepOldY.optimisticMarginMm,legalViaSites:r.earliestLegalProbedSites})),ramOrder:b.recommendedRightExitBottomToTopOrder,cpuOrder:b.cpuExitBottomToTopOrder,inversions:b.ramToCpuOrderInversions})),null,2))