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}))
}