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/assemble-ddr-dq5-dm1.ts
/** Exact shared-parent co-design merge; full physical checks before publishing. */
import{readFileSync,writeFileSync,mkdirSync}from'node:fs'
import{resolve}from'node:path'
import{createHash}from'node:crypto'
import{verifyDdrCapture}from'./ddr-capture-provenance'
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'
import{refreshDdrPlaneContacts}from'./ddr-plane-contact-audit'
import{fanoutTracePath}from'@tscircuit/props'
function buildFacingRamCache(snapshot:any,replacements:Array<{ball:string,trace:any}>){
if(replacements.length!==5||new Set(replacements.map(r=>r.ball)).size!==5)throw Error('Expected five revised RAM0 paths')
const placement=snapshot.placements[0];if(placement.pcbRotation!==180||snapshot.pathsByByte[0].length!==72)throw Error('Expected frozen 72-path rotated RAM0')
const paths=structuredClone(snapshot.pathsByByte[0]);for(const {ball,trace}of replacements){const i=paths.findIndex((p:any)=>p.connection===`U1.ball_${ball}`);if(i<0)throw Error('Missing changed RAM ball');paths[i]={...paths[i],route:trace.route.map((p:any)=>p.route_type==='wire'?{route_type:'wire',x:placement.pcbX-p.x,y:placement.pcbY-p.y,layer:p.layer,width:p.width}:{route_type:'via',x:placement.pcbX-p.x,y:placement.pcbY-p.y,from_layer:p.from_layer,to_layer:p.to_layer,via_diameter:p.via_diameter,via_hole_diameter:p.via_hole_diameter})}}
for(let i=0;i<paths.length;i++){fanoutTracePath.parse(paths[i]);if(!replacements.some(r=>paths[i].connection===`U1.ball_${r.ball}`)&&hash(paths[i])!==hash(snapshot.pathsByByte[0][i]))throw Error('Unrelated RAM cache path changed')}
if(!auditRouteAngles(paths).valid)throw Error('Cache angles fail')
const bounds=(ps:any[])=>{const p=ps.flatMap(p=>p.route);return{minX:Math.min(...p.map(p=>p.x)),maxX:Math.max(...p.map(p=>p.x)),minY:Math.min(...p.map(p=>p.y)),maxY:Math.max(...p.map(p=>p.y))}},before=bounds(snapshot.pathsByByte[0]),after=bounds(paths)
if(after.minX<before.minX-1e-7||after.maxX>before.maxX+1e-7||after.minY<before.minY-1e-7||after.maxY>before.maxY+1e-7)throw Error('RAM envelope grew')
return{paths,before,after}
}
const[capture,dqDir,dmDir,out]=process.argv.slice(2),bound:Record<string,string>={},fh=(p:string)=>createHash('sha256').update(readFileSync(p)).digest('hex'),read=(p:string)=>{p=resolve(p);bound[p]=fh(p);return JSON.parse(readFileSync(p,'utf8'))},hash=(x:any)=>createHash('sha256').update(JSON.stringify(x)).digest('hex'),key=(e:any)=>`${e.type}:${e[`${e.type}_id`]}`
const{captureHash,snapshot}=verifyDdrCapture(capture),dq=read(resolve(dqDir,'candidate.circuit.json')),dm=read(resolve(dmDir,'candidate.circuit.json')),a=read(resolve(dqDir,'original-copper-replacements.json')),b=read(resolve(dmDir,'original-copper-replacements.json')),parent=read(resolve(a.parentDirectory,'candidate.circuit.json')),prior=read(resolve(dmDir,'report.json')),dqHost=read(resolve(dqDir,'host-bundle.json')),dmHost=read(resolve(dmDir,'host-bundle.json')),input=read(resolve(dqDir,'routing-input.json'))
const replay=(base:any[],m:any,skipIdentity=false)=>{const map=new Map(base.map(e=>[key(e),e])),seen=new Set<string>();for(const w of m.replacements){const k=key(w.before);if(skipIdentity&&hash(w.before)===hash(w.after))continue;if(seen.has(k)||hash(map.get(k))!==hash(w.before))throw Error('Replacement does not match source');seen.add(k);map.delete(k);if(k!==key(w.after)&&map.has(key(w.after)))throw Error('Duplicate replacement identity');map.set(key(w.after),w.after)}for(const e of m.removals??[]){const k=key(e);if(hash(map.get(k))!==hash(e))throw Error('Removal differs');map.delete(k)}for(const e of m.additions??[]){if(map.has(key(e)))throw Error('Addition collides');map.set(key(e),e)}return[...map.values()]},equal=(x:any[],y:any[])=>hash(x.map(hash).sort())===hash(y.map(hash).sort())
if([a,b,prior,dqHost,dmHost].some(v=>v.captureHash!==captureHash)||a.parentCircuitSha256!==hash(parent)||b.parentCircuitSha256!==hash(parent)||a.candidateCircuitSha256!==hash(dq)||b.candidateCircuitSha256!==hash(dm)||!equal(replay(parent,a),dq)||!equal(replay(parent,b),dm))throw Error('Shared-parent provenance mismatch')
const candidate=replay(dq,b,true),retired=new Set(b.removals.map((e:any)=>e.pcb_trace_id)),traces=[...dqHost.traces.filter((t:any)=>!retired.has(t.pcb_trace_id)),...b.additions],host={captureHash,layerSpace:'physical',traces},connections=input.connections.map((c:any)=>({...c,pointsToConnect:c.pointsToConnect.map((p:any)=>{const e=candidate.find((e:any)=>e.pcb_breakout_point_id===p.pointId);if(!e)throw Error('Missing endpoint');return{...p,x:e.x,y:e.y,layer:e.layer}})}))
for(const t of traces)if(hash(candidate.find((e:any)=>e.type==='pcb_trace'&&e.pcb_trace_id===t.pcb_trace_id))!==hash(t))throw Error('Host differs')
const topology=auditCompositeHostTopology(candidate,traces,connections),angles=auditRouteAngles(candidate.filter((e:any)=>e.type==='pcb_trace')),junctions=auditDdrTraceJunctions(candidate,connections),terminals=checkHostTerminalContract(traces,connections.filter((c:any)=>traces.some((t:any)=>t.connection_name===c.name))),planes=candidate.filter((e:any)=>e.type==='pcb_copper_pour'),planeAudit=refreshDdrPlaneContacts(prior.planeAudit,planes,candidate)
mkdirSync(out,{recursive:true});const write=(n:string,x:any)=>writeFileSync(resolve(out,n),JSON.stringify(x,null,2)+'\n');if(!topology.valid||!angles.valid||!junctions.valid||!terminals.valid||topology.physical.connectivity.filter(c=>c.connected).length!==13){write('failed-merge.json',{topology,angles,junctions,terminals});throw Error('Combined13 failed')}
const oldContract=read(resolve(dmDir,'exit-contract.json')),ballNames=[...oldContract.changedBalls,'E8'],replacements=ballNames.map((ball:string)=>{const port=candidate.find((e:any)=>e.type==='source_port'&&e.name===`ball_${ball}`&&e.source_component_id==='source_component_83'),exit=candidate.find((e:any)=>e.type==='pcb_breakout_point'&&e.source_port_id===port?.source_port_id),trace=candidate.find((e:any)=>e.type==='pcb_trace'&&e.source_trace_id===exit?.source_trace_id);if(!trace)throw Error('Missing RAM0 source');return{ball,trace}}),cache=buildFacingRamCache(snapshot,replacements),t=replacements.at(-1)!.trace,end=t.route.at(-1),prev=[...t.route].reverse().find((p:any)=>p.route_type==='wire'&&p.layer===end.layer&&Math.hypot(p.x-end.x,p.y-end.y)>1e-7),d=Math.hypot(end.x-prev.x,end.y-prev.y),world={x:(end.x-prev.x)/d,y:(end.y-prev.y)/d},placement=snapshot.placements[0],dq5Contract={ball:'E8',terminal:'DQ5',worldSide:'right',localSide:'left',worldExit:{x:end.x,y:end.y,layer:end.layer},localExit:{x:placement.pcbX-end.x,y:placement.pcbY-end.y,layer:end.layer},tangent:{world,local:{x:-world.x,y:-world.y}},physicalViaSpan:'all ten layers',traceWidthMm:.12}
if(!auditRouteAngles(cache.paths).valid)throw Error('RAM cache angles fail')
const lengths=traces.map((t:any)=>{const planar=(r:any[])=>r.slice(1).reduce((n:number,b:any,i:number)=>{const a=r[i];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),c=connections.find((c:any)=>c.name===t.connection_name);return{net:t.connection_name,byte:c.ramByte,terminal:c.ramTerminal,fullPlanarMm:planar(t.route)+c.pointsToConnect.reduce((n:number,p:any)=>{const e=candidate.find((e:any)=>e.pcb_breakout_point_id===p.pointId),r=candidate.find((e:any)=>e.type==='pcb_trace'&&e.source_trace_id===candidate.find((x:any)=>x.pcb_breakout_point_id===p.pointId)?.source_trace_id);if(!r)throw Error('Missing length escape');return n+planar(r.route)},0)}})
const report={...prior,accepted:true,captureHash,candidateCircuitSha256:hash(candidate),supportCircuitSha256:hash(candidate),hostTracesSha256:hash(traces),hostBundleSha256:hash(host),connectedHostCount:13,physical:topology.physical,topology,terminalContract:terminals,fullCandidateAngles:angles,fixedJunctionAudit:junctions,planeAudit,inputFileHashes:bound,lengths,scope:'Thirteen connected geometric hosts with DM1 and DQ5 replacements; DQ2/DQ4 and other byte lengths are not matched. DQ6, remaining interface, four power launches and electrical qualification incomplete.'}
write('candidate.circuit.json',candidate);write('host-bundle.json',host);write('routing-input.json',{...input,connections,traces,obstacles:undefined});write('report.json',report);write('ram-byte0-facing-progress.trace-paths.json',cache.paths);write('exit-contract.json',{...oldContract,candidateCircuitSha256:hash(candidate),ramCacheSha256:hash(cache.paths),changedBalls:ballNames,unchangedPaths:72-ballNames.length,newCenterlineBounds:cache.after,exits:[...oldContract.exits,dq5Contract],scope:report.scope});for(const f of['ram-byte1-facing-progress.trace-paths.json','ram-byte1-exit-contract.json'])write(f,read(resolve(dmDir,f)));write('source-provenance.json',{captureHash,inputHashes:bound,sharedParentSha256:hash(parent),dqManifest:a,dmManifest:b})
for(const s of['audit-ddr-global-power-launches.ts','audit-ddr-host-reference-coverage.ts']){const p=Bun.spawn(['bun',`scripts/${s}`,resolve(out),resolve(capture)],{stdout:'inherit',stderr:'inherit'});if(await p.exited)throw Error(`Audit failed ${s}`)}
const power=read(resolve(out,'ram-power-launch-audit.json'));if(power.launchQualified!==56)throw Error('Power changed');for(const[p,h]of Object.entries(bound))if(fh(p)!==h)throw Error('Bound input changed');write('report.json',{...report,launchQualified:56,totalRamPowerPads:60,launchAuditSha256:hash(power)});console.log(JSON.stringify({hosts:13,power:56,lengths}))