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-ram-power-via-reuse.ts

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'
export function sharedViaApproaches(start:any,end:{x:number;y:number}){
 const dx=end.x-start.x,dy=end.y-start.y,ax=Math.abs(dx),ay=Math.abs(dy),diagonal=Math.min(ax,ay),sx=Math.sign(dx),sy=Math.sign(dy),length=Math.max(ax,ay)+(Math.SQRT2-1)*diagonal
 if(length>1.524+1e-8)return[]
 const mids=[{x:start.x+sx*diagonal,y:start.y+sy*diagonal},{x:end.x-sx*diagonal,y:end.y-sy*diagonal}]
 return mids.map(mid=>[{...start},...([mid,end].filter((p,i,a)=>Math.hypot(p.x-(i?a[i-1]!:start).x,p.y-(i?a[i-1]!:start).y)>1e-8).map(p=>({route_type:'wire',x:p.x,y:p.y,layer:'top',width:start.width})))])
}
if(import.meta.main){
 const [dirArg,previousArg,capArg,outArg]=process.argv.slice(2);if(!dirArg||!previousArg||!capArg)throw Error('Usage: capture-dir accepted-power-directory caps-directory [output-directory]')
 const dir=resolve(dirArg),previous=resolve(previousArg),capDir=resolve(capArg),out=resolve(outArg??`${dir}/power-via-reuse`),read=(p:string)=>JSON.parse(readFileSync(p,'utf8')),hash=(v:any)=>createHash('sha256').update(JSON.stringify(v)).digest('hex'),{captureHash,snapshot}=verifyDdrCapture(dir)
 const prior=read(`${previous}/accepted-candidate.json`),checkpoint=read(`${previous}/dogbone-checkpoint.json`),original=read(`${dir}/unrouted.circuit.json`),placements=read(`${dir}/capture-report.json`).placements,input=read(`${dir}/merged-routing-input.json`),host=read(`${dir}/data-exact-width/candidate.physical.json`),planes=read(`${previous}/accepted-reference-planes.json`),capRaw=read(`${capDir}/candidate.circuit.json`)
 if(prior.captureHash!==captureHash||host.captureHash!==captureHash||host.layerSpace!=='physical'||hash(host.traces)!==checkpoint.provenance.hostTracesSha256||hash(capRaw)!==checkpoint.provenance.capCircuitSha256)throw Error('Capture/host/cap provenance changed')
 const idMap=new Map(capRaw.map((e:any)=>[e[`${e.type}_id`],`power_plan_caps_${e[`${e.type}_id`]}`])),remap=(v:any):any=>Array.isArray(v)?v.map(remap):v&&typeof v==='object'?Object.fromEntries(Object.entries(v).map(([k,x])=>[k,remap(x)])):typeof v==='string'?idMap.get(v)??v:v,caps=remap(capRaw)
 const accepted=[...prior.replacements],attempts:any[]=[],changes:any[]=[],usage=new Map<string,string[]>(),transform=(p:any,byte:number)=>{const q=placements[byte],a=q.pcbRotation*Math.PI/180;return{x:q.pcbX+p.x*Math.cos(a)-p.y*Math.sin(a),y:q.pcbY+p.x*Math.sin(a)+p.y*Math.cos(a)}}
 const vias:any[]=snapshot.pathsByByte.flatMap((paths:any[],byte:number)=>snapshot.terminalsByByte[byte].filter((t:any)=>/^VDD(?:Q)?_|^VSS(?:Q)?_/.test(t.terminal)).flatMap((t:any)=>{const p=paths.find((p:any)=>p.connection===`U1.ball_${t.ball}`),v=p.route.find((p:any)=>p.route_type==='via');if(!v)return[];const xy=transform(v,byte),physical=original.find((e:any)=>e.type==='pcb_via'&&Math.hypot(e.x-xy.x,e.y-xy.y)<1e-6);if(!physical)throw Error('Missing actual full-depth via');const key=physical.pcb_via_id;usage.set(key,[...(usage.get(key)??[]),`${byte}:${t.ball}`]);return[{...xy,key,byte,terminal:t.terminal,ball:t.ball}]}))
 const check=(routes:any[])=>{const removed=new Set(routes.map(r=>r.pcb_trace_id)),base=[...original.filter((e:any)=>e.type!=='pcb_copper_pour'&&!removed.has(e.pcb_trace_id)),...caps,...planes,...prior.supportConnections];return verifyDdrSystemCopper(base,[...host.traces,...routes],input.connections,prior.aliases)}
 const initial=check(accepted);if(initial.errors.length||initial.violations.length||!initial.angles.valid||initial.joinedBends.length)throw Error('Prior candidate no longer passes combined check')
 for(const row of prior.rejected){
  const originalTrace=original.find((e:any)=>e.pcb_trace_id===row.pcbTraceId),start=originalTrace.route[0],ground=row.terminal.startsWith('VSS'),candidateVias=vias.filter(v=>v.byte===row.byte&&v.terminal.startsWith(ground?'VSS':'VDD')).map(v=>({...v,lowerBound:Math.max(Math.abs(v.x-start.x),Math.abs(v.y-start.y))+(Math.SQRT2-1)*Math.min(Math.abs(v.x-start.x),Math.abs(v.y-start.y))})).sort((a,b)=>a.lowerBound-b.lowerBound)
  let solved=false
  for(const via of candidateVias){if(via.lowerBound>1.524+1e-8)break;if((usage.get(via.key)?.length??0)>=2){attempts.push({byte:row.byte,terminal:row.terminal,via:via.key,status:'two-ball-sharing-limit'});continue}
   for(const route of sharedViaApproaches(start,via)){
    const candidate={...originalTrace,route,trace_length:via.lowerBound,connectsTo:[...(originalTrace.connectsTo??[]).filter((id:string)=>!id.startsWith('pcb_breakout_point_')),via.key]},report=check([...accepted,candidate]),pass=!report.errors.length&&!report.violations.length&&report.angles.valid&&!report.joinedBends.length
    attempts.push({byte:row.byte,terminal:row.terminal,viaTerminal:via.terminal,viaId:via.key,lengthMm:via.lowerBound,pass,errors:report.errors,violations:report.violations.filter((v:any)=>v.aShape.traceId===candidate.pcb_trace_id||v.bShape.traceId===candidate.pcb_trace_id).map((v:any)=>({layer:v.layer,gapMm:v.gapMm,aTraceId:v.aShape.traceId,bTraceId:v.bShape.traceId,aObjectId:v.aShape.join??v.aShape.traceId,bObjectId:v.bShape.join??v.bShape.traceId,aNet:v.aNet,bNet:v.bNet})),joinedBends:report.joinedBends})
    if(pass){accepted.push(candidate);usage.get(via.key)!.push(`${row.byte}:${row.ball}`);changes.push({...row,reusedViaId:via.key,reusedViaTerminal:via.terminal,lengthMm:via.lowerBound,servedBalls:[...usage.get(via.key)!],route});solved=true;break}
   }
   if(solved)break
  }
 }
 const report=check(accepted),unresolved=prior.rejected.filter((r:any)=>!changes.some(c=>c.pcbTraceId===r.pcbTraceId))
 mkdirSync(out,{recursive:true});writeFileSync(`${out}/candidate.json`,JSON.stringify({captureHash,hostTracesSha256:hash(host.traces),replacements:accepted,aliases:prior.aliases,supportConnections:prior.supportConnections,previousChanges:prior.changes,reuseChanges:changes,unresolved,scope:'At most two RAM balls per reused through via. Explicit supply assignments retained. No added barrel, no supply-plane or PDN continuity signoff.'},null,2));writeFileSync(`${out}/attempts.json`,JSON.stringify(attempts,null,2));writeFileSync(`${out}/physical-report.json`,JSON.stringify({captureHash,hostTracesSha256:hash(host.traces),...report},null,2));writeFileSync(`${out}/reference-planes.json`,JSON.stringify(planes,null,2));console.log(JSON.stringify({prior:prior.replacements.length,newReuse:changes.length,total:accepted.length,unresolved:unresolved.length,errors:report.errors.length,violations:report.violations.length,angles:report.angles.valid,joined:report.joinedBends.length}))
}