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-e9-via-reuse.ts
/** Bounded existing-winding E9→G8 power-via reuse; all original geometry reserved. */
import{readFileSync,writeFileSync,mkdirSync}from'node:fs'
import{resolve}from'node:path'
import{createHash}from'node:crypto'
import{supportCopperObstacles}from'./ddr-support-routing-context'
import{verifyDdrSystemCopper,auditDdrTraceJunctions}from'./check-ddr-system-copper'
import{auditRouteAngles}from'./check-route-angles'
import{refreshDdrPlaneContacts}from'./ddr-plane-contact-audit'
const [parentArg,outArg]=process.argv.slice(2),dir=resolve(parentArg!),out=resolve(outArg!),hash=(x:any)=>createHash('sha256').update(JSON.stringify(x)).digest('hex'),bound:Record<string,string>={},read=(p:string)=>{p=resolve(p);bound[p]=createHash('sha256').update(readFileSync(p)).digest('hex');return JSON.parse(readFileSync(p,'utf8'))},parent=read(`${dir}/candidate.circuit.json`),host=read(`${dir}/host-bundle.json`),input=read(`${dir}/routing-input.json`),prior=read(`${dir}/report.json`),power=read(`${dir}/ram-power-launch-audit.json`),oldAttempts=read('dist/ddr-system/host-taps-54af3e346b5e/power-via-reuse/attempts.json'),same=(a:any,b:any)=>Math.hypot(a.x-b.x,a.y-b.y)<1e-7
if(prior.candidateCircuitSha256!==hash(parent)||host.traces.length!==19)throw Error('Expected bound nineteen-host parent')
let child=structuredClone(parent);const attempts:any[]=[],replacements:any[]=[],{routeViaMinimalWindingAlternativesSteps}=await import(resolve('node_modules/@tscircuit/fanout-solver/lib/route-via-minimal-winding.ts'))
for(const byte of [0,1]){const name=`RAM${byte}_E9_reuse`,before=child.find((e:any)=>e.type==='pcb_trace'&&e.pcb_trace_id===`saved_fanout_pcb_group_${byte?5:3}_65`),start=before.route[0],pad=child.find((e:any)=>e.type==='pcb_smtpad'&&e.pcb_port_id===start.start_pcb_port_id),g8=child.find((e:any)=>e.type==='pcb_trace'&&e.pcb_trace_id===`saved_fanout_pcb_group_${byte?5:3}_54`),via=g8.route.find((p:any)=>p.route_type==='via'),existing=child.find((e:any)=>e.type==='pcb_via'&&same(e,via)),contact=prior.planeAudit.find((p:any)=>p.layer==='inner3').viaContacts.find((v:any)=>same(v,via)&&v.annulusSamplesInPlane===32),users=power.rows.filter((r:any)=>r.planeContact&&r.connectionVia&&same(r.connectionVia,via)),join=child.find((e:any)=>e.type==='source_trace'&&e.source_trace_id===`planned_ram${byte}_E9_supply`)
if(!pad||!same(pad,start)||!contact||users.length!==1||users[0].ball!=='G8'||join.connected_source_net_ids[0]!=='source_net_352')throw Error('Reuse ownership/annulus/usage invalid')
const target={x:via.x,y:via.y,layer:'top'},sourcePoint={x:start.x,y:start.y,layer:'top',pcb_port_id:start.start_pcb_port_id},connection={name,pointsToConnect:[sourcePoint,target]},fixed=child.filter((e:any)=>e.pcb_trace_id!==before.pcb_trace_id),obstacles=fixed.filter((e:any)=>['pcb_trace','pcb_via','pcb_smtpad'].includes(e.type)).flatMap((e:any)=>{let projected=e;if(e===g8){projected=structuredClone(g8);const vi=projected.route.findIndex((p:any)=>p.route_type==='via'),last=projected.route[vi-1],prev=projected.route[vi-2],d=Math.hypot(last.x-prev.x,last.y-prev.y);if(d<.25)throw Error('G8 terminal projection too short');projected.route=projected.route.slice(0,vi);projected.route[vi-1]={...last,x:last.x+.25*(prev.x-last.x)/d,y:last.y+.25*(prev.y-last.y)/d}}return supportCopperObstacles([projected]).map((o:any)=>({...o,...((e.type==='pcb_smtpad'&&e.shape==='circle')||e.type==='pcb_via'||o.obstacleId.startsWith('fixed_via_')||o.obstacleId.endsWith('_cap')?{shape:'circle'}:{}),connectedTo:[e===pad||e===existing?name:`reserved:${e[`${e.type}_id`]}`]}))}),sourceObstacle=obstacles.find((o:any)=>o.connectedTo[0]===name&&same(o.center,start));if(!sourceObstacle)throw Error('Missing source selector')
const bounds={minX:start.x-1.1,maxX:target.x+1.1,minY:start.y-.9,maxY:target.y+.9},pc={connection,connectionIndex:0,sourcePoint,sourcePointIndex:0,sourceLayer:'top',sourceObstacle,targetPoint:target},bus={busId:name,direction:'top',exitEdge:'top',termination:{type:'boundary'},connections:[pc],componentId:'actual-E9-pad',componentObstacles:[sourceObstacle],componentBounds:bounds,sharedBoundary:bounds,xCoordinates:[start.x],yCoordinates:[start.y],pitchX:.8,pitchY:.8},params={srj:{...input,bounds,connections:[connection],obstacles,traces:[]},bus,targetLayer:'top',terminals:[{connection:pc,viaPoint:sourcePoint,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:.02,maximumRouteOrderAttempts:1,maximumSearchStates:300000,heuristicWeight:1,sourceEscapePaths:new Map([[0,[sourcePoint,sourcePoint]]])},steps=routeViaMinimalWindingAlternativesSteps(params as any,1),started=Date.now(),progress:any[]=[];let results:any[]=[],finished=false
while(Date.now()-started<55000){const s=steps.next();if(s.done){results=s.value;finished=true;break}progress.push({...s.value,visualization:undefined})}if(!finished)steps.return([])
const trial:any={byte,target,existingViaId:existing?.pcb_via_id,users:users.map((u:any)=>u.ball),nominalLowerBoundMm:Math.max(Math.abs(target.x-start.x),Math.abs(target.y-start.y))+(Math.SQRT2-1)*Math.min(Math.abs(target.x-start.x),Math.abs(target.y-start.y)),bounds,finished,elapsedSeconds:(Date.now()-started)/1000,progress,raw:results,accepted:false}
if(results[0]?.length===1){const route=results[0][0].trace.route;if(route.some((p:any)=>p.route_type!=='wire'||p.layer!=='top')||!same(route[0],start)||!same(route.at(-1),target))throw Error('Solver changed endpoint/layer');route[0]={...route[0],start_pcb_port_id:start.start_pcb_port_id};const lengthMm=route.slice(1).reduce((n:number,p:any,i:number)=>n+Math.hypot(p.x-route[i].x,p.y-route[i].y),0),after={...before,route,connectsTo:[before.source_trace_id,start.start_pcb_port_id,existing.pcb_via_id]};delete after.connection_name;const fresh=[...host.traces,after],ids=new Set(fresh.map((t:any)=>t.pcb_trace_id)),proposed=child.map((e:any)=>e.type==='pcb_trace'&&e.pcb_trace_id===before.pcb_trace_id?after:e),physical=verifyDdrSystemCopper(proposed.filter((e:any)=>!ids.has(e.pcb_trace_id)),fresh,input.connections,{},50,{reportSameNetContacts:true}),angles=auditRouteAngles(proposed.filter((e:any)=>e.type==='pcb_trace')),junctions=auditDdrTraceJunctions(proposed,input.connections);Object.assign(trial,{lengthMm,physical,angles,junctions,after});if(lengthMm<=1.524+1e-8&&!physical.errors.length&&!physical.violations.length&&angles.valid&&junctions.valid){trial.accepted=true;child=proposed;replacements.push({id:`pcb_trace:${before.pcb_trace_id}`,before,after,beforeSha256:hash(before),afterSha256:hash(after)})}}
attempts.push(trial)}
const planeAudit=refreshDdrPlaneContacts(prior.planeAudit,child.filter((e:any)=>e.type==='pcb_copper_pour'),child),report={captureHash:prior.captureHash,parentCircuitSha256:hash(parent),candidateCircuitSha256:hash(child),sourceHashes:bound,oldAttempts:oldAttempts.filter((a:any)=>a.terminal==='VDDQ_E9'),attempts,planeAudit,hostsPreserved:host.traces.every((t:any)=>child.some((e:any)=>hash(e)===hash(t))),scope:'Bounded existing winding reuse of actual G8 power barrel for E9, at most two RAM pads per via, .12 wire/.1016 clearance. Planes and all hosts unchanged. No accepted route without <=1.524mm plus full physical/angles/junction checks; same-net contacts recorded for review.'}
for(const[p,h]of Object.entries(bound))if(createHash('sha256').update(readFileSync(p)).digest('hex')!==h)throw Error('Input changed');mkdirSync(out,{recursive:true});for(const[n,v]of Object.entries({'report.json':report,'candidate.circuit.json':child,'original-copper-replacements.json':{parentCircuitSha256:hash(parent),candidateCircuitSha256:hash(child),replacements}}))writeFileSync(`${out}/${n}`,JSON.stringify(v,null,2));console.log(JSON.stringify(attempts.map(a=>({byte:a.byte,accepted:a.accepted,finished:a.finished,lengthMm:a.lengthMm,states:a.progress.at(-1)?.expandedStateCount,violations:a.physical?.violations.length}))))