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-d3-actual-transition.ts
/** Existing winding solver with a preverified layer transition. */
import {readFileSync,writeFileSync,mkdirSync} 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'
import {terminalOnlyFixedObstacles} from './ddr-terminal-copper-obstacles'
import {supportCopperObstacles} from './ddr-support-routing-context'
import {auditCompositeHostTopology} from './ddr-composite-routing-context'
import {auditRouteAngles} from './check-route-angles'
import {auditDdrTraceJunctions} from './check-ddr-system-copper'
import {checkHostTerminalContract} from './route-ddr-sequential'
const {routeViaMinimalWindingAlternativesSteps}=await import(resolve('node_modules/@tscircuit/fanout-solver/lib/route-via-minimal-winding.ts'))
const [capture,integrated,transitionDir,out,stateArg,dq0Dir]=process.argv.slice(2).filter(arg=>!arg.startsWith('--source-collar='))
const sourceCollarMm=Number(process.argv.find(arg=>arg.startsWith('--source-collar='))?.split('=')[1]??.2)
if(!Number.isFinite(sourceCollarMm)||sourceCollarMm<.05||sourceCollarMm>.2)throw Error('Explicit source collar must be .05–.2mm')
const maximumSearchStates=stateArg===undefined?100000:Number(stateArg)
if(!Number.isInteger(maximumSearchStates)||maximumSearchStates<1000||maximumSearchStates>1000000)throw Error('Search state budget must be1000–1000000')
if(!capture||!integrated||!transitionDir||!out)throw Error('Usage: capture integrated transition-plan-directory output')
const inputHashes=new Map<string,string>(),fileHash=(p:string)=>createHash('sha256').update(readFileSync(p)).digest('hex'),read=(p:string)=>{p=resolve(p);inputHashes.set(p,fileHash(p));return JSON.parse(readFileSync(p,'utf8'))},hash=(x:any)=>createHash('sha256').update(JSON.stringify(x)).digest('hex')
const {captureHash}=verifyDdrCapture(capture),original=read(resolve(capture,'unrouted.circuit.json')),host=read(resolve(integrated,'host-bundle.json')),circuit=read(resolve(integrated,'candidate.circuit.json')),plan=read(resolve(transitionDir,'transition-plan.json')),planes=read(resolve(transitionDir,'planes.json')),input=read(resolve(integrated,'routing-input.json'))
if(plan.captureHash!==captureHash||plan.parentCircuitSha256!==hash(circuit)||plan.hostTracesSha256!==hash(host.traces)||plan.accepted.length!==1)throw Error('Transition provenance mismatch')
const t=plan.accepted[0],connection=plan.connection
if(hash(input.connections.find((c:any)=>c.name===connection.name))!==hash(connection))throw Error('Updated endpoint contract differs')
for(const trace of host.traces)if(hash(circuit.find((e:any)=>e.type==='pcb_trace'&&e.pcb_trace_id===trace.pcb_trace_id))!==hash(trace))throw Error('Actual retained host differs')
let baseCircuit=circuit,baseHost=host,dq0Replacement:any=null
if(dq0Dir){const m=read(resolve(dq0Dir,'original-copper-replacements.json'));if(m.captureHash!==captureHash||m.replacements.length!==1)throw Error('Unexpected DQ0 manifest');const r=m.replacements[0];if(r.before.pcb_trace_id!=='source_trace_575_0'||hash(circuit.find((e:any)=>e.type==='pcb_trace'&&e.pcb_trace_id===r.before.pcb_trace_id))!==hash(r.before))throw Error('DQ0 prior differs');baseCircuit=circuit.map((e:any)=>e.type==='pcb_trace'&&e.pcb_trace_id===r.before.pcb_trace_id?r.after:e);baseHost={...host,traces:host.traces.map((e:any)=>e.pcb_trace_id===r.before.pcb_trace_id?r.after:e)};dq0Replacement=m}
const ids=new Set(baseHost.traces.map((t:any)=>t.pcb_trace_id)),fixed=[...baseCircuit.filter((e:any)=>e.type!=='pcb_copper_pour'&&!ids.has(e.pcb_trace_id)),...planes]
const baseline=verifyDdrSystemCopper(fixed,[...baseHost.traces,t.trace],input.connections,{})
if(baseline.errors.length||baseline.violations.length||!baseline.angles.valid||baseline.joinedBends.length)throw Error('Transition no longer physically valid')
const projection=terminalOnlyFixedObstacles(fixed,connection),transitionObstacles=supportCopperObstacles([t.trace]).flatMap((o:any)=>{const viaEnd=Math.hypot(o.center.x-t.via.x,o.center.y-t.via.y)<1e-7&&(o.obstacleId.startsWith('fixed_via_')||o.obstacleId.endsWith('_cap'));return viaEnd?[{...o,shape:'circle',layers:o.layers.filter((l:string)=>l!==t.target.layer),connectedTo:['reserved:transition']},{...o,shape:'circle',layers:o.layers.filter((l:string)=>l===t.target.layer),connectedTo:[connection.name]}].filter(o=>o.layers.length):[{...o,connectedTo:['reserved:transition']}]})
const srj:any={...input,traces:baseHost.traces,obstacles:[...projection.obstacles,...supportCopperObstacles(baseHost.traces).map((o:any)=>({...o,...(o.obstacleId.startsWith('fixed_via_')||o.obstacleId.endsWith('_cap')?{shape:'circle'}:{}),connectedTo:['reserved:existing-host']})),...transitionObstacles]}
// Start at the RAM escape so its actual outward tangent is preserved. End at
// the verified CPU via, where the layer transition terminates the planar run.
const source=t.target,target={...t.via,layer:t.target.layer}
const ramExit=baseCircuit.find((e:any)=>e.type==='pcb_breakout_point'&&e.pcb_breakout_point_id===source.pointId)
const ramTrace=baseCircuit.find((e:any)=>e.type==='pcb_trace'&&e.source_trace_id===ramExit?.source_trace_id&&e.route.at(-1)?.layer===source.layer&&Math.hypot(e.route.at(-1).x-source.x,e.route.at(-1).y-source.y)<1e-7)
if(!ramTrace)throw Error('Missing exact RAM escape copper')
const prev=[...ramTrace.route].reverse().find((p:any)=>p.route_type==='wire'&&p.layer===source.layer&&Math.hypot(p.x-source.x,p.y-source.y)>1e-7)
if(!prev)throw Error('Missing RAM escape tangent')
const len=Math.hypot(source.x-prev.x,source.y-prev.y),collar={x:source.x+sourceCollarMm*(source.x-prev.x)/len,y:source.y+sourceCollarMm*(source.y-prev.y)/len}
const sourceObstacle=srj.obstacles.find((o:any)=>o.connectedTo.includes(connection.name)&&o.layers.includes(source.layer)&&Math.abs(o.center.x-source.x)<=o.width/2+1e-7&&Math.abs(o.center.y-source.y)<=o.height/2+1e-7)
if(!sourceObstacle)throw Error('Missing real source obstacle')
const prepared:any={connection,connectionIndex:0,sourcePoint:{...source,pcb_port_id:source.pointId},sourcePointIndex:connection.pointsToConnect.findIndex((p:any)=>p.pointId===source.pointId),sourceLayer:source.layer,sourceObstacle,targetPoint:target}
const bus:any={busId:connection.name,direction:'right',exitEdge:'right',termination:{type:'boundary'},connections:[prepared],componentId:'actual-RAM-DDR-exit',componentObstacles:[sourceObstacle],componentBounds:input.bounds,sharedBoundary:{...input.bounds,maxX:Math.max(4,t.source.x)},xCoordinates:[source.x],yCoordinates:[source.y],pitchX:.8,pitchY:.8}
const params:any={srj,bus,targetLayer:target.layer,terminals:[{connection:prepared,viaPoint:collar,exitPoint:target}],acceptedPlans:[],layerNames:['top',...Array.from({length:8},(_,i)=>`inner${i+1}`),'bottom'],traceWidth:.12,viaDiameter:.4572,viaHoleDiameter:.254,clearance:.1016,allowBlindAndBuriedVias:false,allowSourceLayerRouting:true,gridStep:.1,maximumRouteOrderAttempts:1,maximumSearchStates,heuristicWeight:1.2,sourceEscapePaths:new Map([[0,[source,collar]]])}
mkdirSync(out,{recursive:true});const write=(name:string,value:any)=>writeFileSync(resolve(out,name),JSON.stringify(value,null,2)+'\n'),started=Date.now(),steps=routeViaMinimalWindingAlternativesSteps(params,1),progress:any[]=[];let result:any=[];write('input-evidence.json',{captureHash,inputHashes:Object.fromEntries(inputHashes),dq0Replacement,terminalProjection:projection.witnesses,transitionObstacleScope:'Transition reserved except its exact inner4 via landing. All original fixed same-net copper reserved except true CPU/RAM collars.'})
while(true){if(Date.now()-started>55000){steps.return([]);break}const step=steps.next();if(step.done){result=step.value;break}progress.push({...step.value,visualization:undefined});write('progress.json',progress)}
const reversedTransition=[...t.trace.route].reverse().map((p:any)=>p.route_type==='via'?{...p,from_layer:p.to_layer,to_layer:p.from_layer}:p)
const raw=(result[0]??[]);if(raw.some((p:any)=>p.trace.route.some((q:any)=>q.route_type!=='wire'||q.layer!==target.layer)))throw Error('Unexpected solver via or layer')
const traces=raw.map((p:any)=>({...p.trace,route:[...p.trace.route,...reversedTransition],source_trace_id:connection.source_trace_id,connectsTo:connection.pointsToConnect.map((p:any)=>p.pointId)})),terminal=checkHostTerminalContract(traces,[connection]),physical=verifyDdrSystemCopper(fixed,[...baseHost.traces,...traces],input.connections,{})
const candidate=[...fixed,...baseHost.traces,...traces],allHosts=[...baseHost.traces,...traces],topology=auditCompositeHostTopology(candidate,allHosts,input.connections),allAngles=auditRouteAngles(candidate.filter((e:any)=>e.type==='pcb_trace')),allJunctions=auditDdrTraceJunctions(candidate,input.connections),allTerminal=checkHostTerminalContract(allHosts,input.connections.filter((c:any)=>allHosts.some((t:any)=>t.connection_name===c.name)))
const planar=(r:any[])=>r.reduce((n:number,b:any,i:number)=>{const a=r[i-1];return n+(a?.route_type==='wire'&&b.route_type==='wire'&&a.layer===b.layer?Math.hypot(a.x-b.x,a.y-b.y):0)},0),fullPlanarMm=traces.length?planar(traces[0].route)+t.budget.ramEscapeMm+t.budget.cpuEscapeMm:null
const accepted=topology.valid&&allAngles.valid&&allJunctions.valid&&allTerminal.valid&&physical.connectivity.filter(c=>c.connected).length===baseHost.traces.length+1&&fullPlanarMm!==null&&fullPlanarMm<=t.budget.nominalMm+1e-7&&traces.length===1&&terminal.valid&&!physical.errors.length&&!physical.violations.length&&physical.angles.valid&&!physical.joinedBends.length&&physical.connectivity.some(c=>c.name===connection.name&&c.connected)
for(const[p,h]of inputHashes)if(fileHash(p)!==h)throw Error('D3 routing input changed');write('raw-traces.json',traces)
write('report.json',{captureHash,accepted,fullPlanarMm,targetPlanarMm:t.budget.targetPlanarMm??28.423158,nominalMm:t.budget.nominalMm,topology,allAngles,allJunctions,allTerminal,dq0Replacement,inputHashes:Object.fromEntries(inputHashes),terminal,physical,source,collar,sourceCollarMm,target,planningRegion:bus.sharedBoundary,transitionPlanSha256:hash(plan),maximumSearchStates,elapsedSeconds:(Date.now()-started)/1000,scope:'Layer-transition routing attempt; full interface timing and remaining nets unverified.'})
if(accepted){write('candidate.circuit.json',candidate);write('host-bundle.json',{captureHash,layerSpace:'physical',traces:allHosts})}
console.log(JSON.stringify({accepted,alternatives:result.length,errors:physical.errors.length,violations:physical.violations.length,angles:physical.angles.violations.length,joinedBends:physical.joinedBends.length}));if(!accepted)process.exitCode=1