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/plan-ddr-d3-actual-transition.ts

/** Bounded CPU-side layer-transition candidates for a declared data connection.
 * Conventional full-depth barrel and explicit voids on every reference plane. */
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 {classifyHostFixedContact} from './audit-ddr-host-fixed-contacts'
import {auditHostSelfContacts} from './audit-ddr-host-self-contacts'
import {planDdrGroundAntipads} from './ddr-ground-plane-antipads'
const [capture,integrated,signalName,out,sideArg,westArg,shapeArg,rangeArg]=process.argv.slice(2)
if(!capture||!integrated||!signalName||!out)throw Error('Usage: capture integrated-dir CPU-signal output')
const signs=sideArg===undefined?[-1,1]:sideArg==='above'?[1]:sideArg==='below'?[-1]:[]
if(!signs.length)throw Error('Transition side must be above or below')
if(rangeArg!==undefined&&!['ram-side','ram-side-low'].includes(rangeArg))throw Error('Unknown candidate range')
if(shapeArg!==undefined&&shapeArg!=='outside-turn'&&shapeArg!=='single-turn')throw Error('Unknown transition shape')
const westMm=westArg===undefined?.2:Number(westArg);if(!Number.isFinite(westMm)||westMm<.2||westMm>1.2)throw Error('West launch must be .2–1.2mm')
const read=(p:string)=>JSON.parse(readFileSync(p,'utf8')),hash=(x:any)=>createHash('sha256').update(JSON.stringify(x)).digest('hex')
const {captureHash}=verifyDdrCapture(capture),circuit=read(resolve(integrated,'candidate.circuit.json')),host=read(resolve(integrated,'host-bundle.json')),report=read(resolve(integrated,'report.json')),input=read(resolve(integrated,'routing-input.json')),map=read(resolve(capture,'signal-map.json'))
const inputPaths=['candidate.circuit.json','report.json','host-bundle.json','routing-input.json'].map(p=>resolve(integrated,p)),fileHash=(p:string)=>createHash('sha256').update(readFileSync(p)).digest('hex'),inputHashes=Object.fromEntries(inputPaths.map(p=>[p,fileHash(p)]))
if(host.captureHash!==captureHash||host.layerSpace!=='physical'||report.captureHash!==captureHash||hash(circuit)!==report.candidateCircuitSha256)throw Error('Integrated provenance mismatch')
const entry=map.find((m:any)=>m.cpuSignal===signalName),connection=input.connections.find((c:any)=>c.source_trace_id===entry?.sourceTraceId)
if(!connection||connection.pointsToConnect.length!==2)throw Error('Expected two-point data connection')
if(host.traces.some((t:any)=>t.connection_name===connection.name))throw Error('Data net already has host routing')
for(const p of connection.pointsToConnect){const e=circuit.find((e:any)=>e.type==='pcb_breakout_point'&&e.pcb_breakout_point_id===p.pointId);if(!e||e.x!==p.x||e.y!==p.y||e.layer!==p.layer)throw Error('Authoritative terminal mismatch')}
const [ram,cpu]=[...connection.pointsToConnect].sort((a:any,b:any)=>a.x-b.x)
const planar=(route:any[])=>route.reduce((n:number,b:any,i:number)=>{const a=route[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),escape=(p:any)=>{const e=circuit.find((e:any)=>e.type==='pcb_breakout_point'&&e.pcb_breakout_point_id===p.pointId),ts=circuit.filter((t:any)=>t.type==='pcb_trace'&&t.source_trace_id===e?.source_trace_id&&t.route.at(-1)?.layer===p.layer&&Math.hypot(t.route.at(-1).x-p.x,t.route.at(-1).y-p.y)<1e-7);if(ts.length!==1)throw Error('Ambiguous actual saved escape');return planar(ts[0].route)},pairConnections=input.connections.filter((c:any)=>c.ramByte===connection.ramByte&&['DQS_P','DQS_N'].includes(c.ramTerminal));if(pairConnections.length!==2)throw Error('Missing current byte strobe reference')
const nominalPath=resolve(capture,'../../ddr-integrated-ten/dqlm-audit.json'),nominalAudit=read(nominalPath);inputHashes[nominalPath]=fileHash(nominalPath);if(nominalAudit.captureHash!==captureHash)throw Error('Nominal reference capture differs');const nominalMm=nominalAudit.bytes.find((b:any)=>b.byte===connection.ramByte)?.dqlmPairedDataMaximumMm;if(!Number.isFinite(nominalMm))throw Error('Missing byte nominal')
const currentPairMean=pairConnections.reduce((n:number,c:any)=>{const t=host.traces.find((t:any)=>t.connection_name===c.name);if(!t)throw Error('Missing actual strobe host');return n+planar(t.route)+c.pointsToConnect.reduce((n:number,p:any)=>n+escape(p),0)},0)/2,baseBudget={ramEscapeMm:escape(ram),cpuEscapeMm:escape(cpu),nominalMm,targetPlanarMm:currentPairMean}
if(cpu.layer===ram.layer||!['inner2','inner4','inner6'].includes(ram.layer))throw Error('Expected a DDR byte-layer transition')
const planes=circuit.filter((e:any)=>e.type==='pcb_copper_pour'),ids=new Set(host.traces.map((t:any)=>t.pcb_trace_id)),fixed=circuit.filter((e:any)=>e.type!=='pcb_copper_pour'&&!ids.has(e.pcb_trace_id)),attempts:any[]=[],accepted:any[]=[]
mkdirSync(out,{recursive:true})
const started=Date.now(),ramSide=rangeArg==='ram-side'||rangeArg==='ram-side-low',dxValues=ramSide?[-5.5,-5.3,-5.1,-4.9].map(x=>cpu.x-x):[.4,.6,.8,1,1.2,1.4,1.6,1.8,2,2.4],dyValues=ramSide?(rangeArg==='ram-side-low'?[-11.5,-11.75,-12,-12.25,-12.5]:[-8,-8.2,-8.4,-8.6,-8.8]).map(y=>Math.abs(cpu.y-y)):(shapeArg==='outside-turn'?[.4,.8,1.2,1.6,2,2.4,2.8,3.2]:[.4,.8,1.2,1.6,2])
search:for(const sign of signs)for(const dx of dxValues)for(const dy of dyValues){
 if(Date.now()-started>55000)break search
 if(dx<=westMm)continue
 const collar={x:cpu.x-westMm,y:cpu.y},via={x:cpu.x-dx,y:cpu.y+sign*dy},ax=collar.x-via.x,ay=Math.abs(via.y-collar.y),m=Math.min(ax,ay),corner={x:collar.x-m,y:collar.y+sign*m}
 const first={x:collar.x-.2,y:collar.y+sign*.2},last={x:first.x,y:via.y-sign*(first.x-via.x)}
 if(shapeArg==='outside-turn'&&(first.x<=via.x||sign*(last.y-first.y)<-1e-8))continue
 const points=(shapeArg==='outside-turn'?[cpu,collar,first,last,via]:[cpu,collar,corner,via]).filter((p,i,a)=>!i||Math.hypot(p.x-a[i-1].x,p.y-a[i-1].y)>1e-8)
 const trace={type:'pcb_trace',pcb_trace_id:`planned_transition_${signalName}`,source_trace_id:connection.source_trace_id,connection_name:connection.name,route:[...points.map(p=>({route_type:'wire',x:p.x,y:p.y,layer:cpu.layer,width:.12})),{route_type:'via',...via,from_layer:cpu.layer,to_layer:ram.layer,via_diameter:.4572,via_hole_diameter:.254},{route_type:'wire',...via,layer:ram.layer,width:.12}]}
 const conductor=verifyDdrSystemCopper(fixed,[...host.traces,trace],input.connections,{})
 if(conductor.errors.length||conductor.violations.length||!conductor.angles.valid||conductor.joinedBends.length){const barrelOnly=ramSide?verifyDdrSystemCopper(fixed,[...host.traces,{...trace,route:[{route_type:'wire',...via,layer:cpu.layer,width:.12},...trace.route.slice(-2)]}],input.connections,{}):null;if(barrelOnly&&!barrelOnly.errors.length&&!barrelOnly.violations.length){const barrelTrace={...trace,route:[{route_type:'wire',...via,layer:cpu.layer,width:.12},...trace.route.slice(-2)]},landingPlanes=planDdrGroundAntipads(planes,[{...via,net:signalName}]),landingPhysical=verifyDdrSystemCopper([...fixed,...landingPlanes.planes],[...host.traces,barrelTrace],input.connections,{},50,{reportSameNetContacts:true}),unintended=(landingPhysical.sameNetContacts??[]).filter((v:any)=>v.aShape.traceId===trace.pcb_trace_id||v.bShape.traceId===trace.pcb_trace_id);if(!landingPhysical.errors.length&&!landingPhysical.violations.length&&!unintended.length)writeFileSync(resolve(out,'via-only-candidate.json'),JSON.stringify({captureHash,parentCircuitSha256:hash(circuit),inputHashes,connection,via,trace:barrelTrace,planes:landingPlanes.planes,addedAntipads:landingPlanes.added,physical:landingPhysical,scope:'Isolated full-depth via landing only. No CPU or RAM connection claimed; the CPU prefix failed. All prior host paths retained.'},null,2))}attempts.push({via,barrelOnlyConductorErrors:barrelOnly?.errors.length,barrelOnlyConductorViolations:barrelOnly?.violations.length,dx,dy,sign,stage:'conductor-prefilter',errors:conductor.errors.length,violations:conductor.violations.length,angles:conductor.angles.violations.length,joins:conductor.joinedBends.length,firstViolations:conductor.violations.slice(0,3)});continue}
 let plan:any;try{plan=planDdrGroundAntipads(planes,[{...via,net:signalName}])}catch(error){attempts.push({dx,dy,sign,reason:String(error)});continue}
 const physical=verifyDdrSystemCopper([...fixed,...plan.planes],[...host.traces,trace],input.connections,{},50,{reportSameNetContacts:true})
 const same=(a:any,b:any)=>Math.hypot(a.x-b.x,a.y-b.y)<1e-7,contacts=(physical.sameNetContacts??[]).filter((v:any)=>v.aShape.traceId===trace.pcb_trace_id||v.bShape.traceId===trace.pcb_trace_id).map((v:any)=>classifyHostFixedContact(v,trace)),cpuExit=circuit.find((e:any)=>e.type==='pcb_breakout_point'&&e.pcb_breakout_point_id===cpu.pointId),terminalOnly=contacts.length===1&&contacts.every((v:any)=>{const t=fixed.find((e:any)=>e.type==='pcb_trace'&&e.pcb_trace_id===v.fixedTraceId);return v.classification!=='unintended-interior-contact'&&t?.source_trace_id===cpuExit.source_trace_id&&t.route.at(-1)?.layer===cpu.layer&&same(t.route.at(-1),cpu)&&v.hostShape.layer===cpu.layer&&[v.hostShape.a,v.hostShape.b].some((p:any)=>same(p,cpu))}),selfContacts=auditHostSelfContacts([...host.traces,trace])
 const clean=terminalOnly&&selfContacts.valid&&!physical.errors.length&&!physical.violations.length&&physical.angles.valid&&!physical.joinedBends.length&&report.physical.connectivity.filter((c:any)=>c.connected).every((c:any)=>physical.connectivity.some(n=>n.name===c.name&&n.connected))
 attempts.push({dx,dy,sign,clean,errors:physical.errors.length,violations:physical.violations.length,angles:physical.angles.violations.length,joins:physical.joinedBends.length})
 if(clean){const transitionMm=points.reduce((sum:number,p:any,i:number)=>sum+(i?Math.hypot(p.x-points[i-1].x,p.y-points[i-1].y):0),0),dxRam=Math.abs(via.x-ram.x),dyRam=Math.abs(via.y-ram.y),remainingOctileMm=Math.max(dxRam,dyRam)+(Math.SQRT2-1)*Math.min(dxRam,dyRam),budget={...baseBudget,transitionMm,remainingOctileMm,optimisticTotalMm:baseBudget.ramEscapeMm+baseBudget.cpuEscapeMm+transitionMm+remainingOctileMm};accepted.push({terminalOnly,contacts,selfContacts,budget,trace,sourceEscapePoints:points,via,source:cpu,target:ram,addedAntipads:plan.added});writeFileSync(resolve(out,'planes.json'),JSON.stringify(plan.planes,null,2));writeFileSync(resolve(out,'physical-report.json'),JSON.stringify({captureHash,...physical},null,2));break search}
}
for(const[p,h]of Object.entries(inputHashes))if(fileHash(p)!==h)throw Error('Transition parent changed')
writeFileSync(resolve(out,'transition-plan.json'),JSON.stringify({captureHash,inputHashes,elapsedMs:Date.now()-started,parentCircuitSha256:hash(circuit),hostTracesSha256:hash(host.traces),connection,accepted,attempts,candidateRange:rangeArg??'cpu-side',shape:shapeArg??'single-turn',westMm,transitionSide:sideArg??'either',scope:'Verified partial layer transition; RAM connection still needs routing. No complete-net or timing claim.'},null,2));console.log(JSON.stringify({accepted:accepted.length,attempts:attempts.length,via:accepted[0]?.via}));if(!accepted.length)process.exitCode=1