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-current-c1-power.ts
/** Isolated coupled current-adapter/C1 power experiment. External RAM is read-only. */
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,auditDdrTraceJunctions} from './check-ddr-system-copper'
import {auditRouteAngles} from './check-route-angles'
import {mergeDdrSignalAntipads} from './merge-ddr-signal-antipads'
const [parentArg,outArg,offsetArg]=process.argv.slice(2),parentDir=resolve(parentArg!),out=resolve(outArg!),ram=resolve('/Users/ankan/Documents/Codex/2026-09-10/what-x20/outputs/mt41k512m8da-107-it-p-fanout'),hash=(v:any)=>createHash('sha256').update(JSON.stringify(v)).digest('hex'),sources:Record<string,string>={},read=(p:string)=>{p=resolve(p);sources[p]=createHash('sha256').update(readFileSync(p)).digest('hex');return JSON.parse(readFileSync(p,'utf8'))},parent=read(`${parentDir}/candidate.circuit.json`),host=read(`${parentDir}/host-bundle.json`),input=read(`${parentDir}/routing-input.json`),native=read(`${ram}/src/generated/bus_grouped.trace-paths.json`),{captureHash,snapshot}=verifyDdrCapture(resolve('dist/ddr-system/host-taps-54af3e346b5e'))
if(host.captureHash!==captureHash||host.traces.length!==18)throw Error('Expected current eighteen-host parent')
for(const name of ['am3352-host.ts','am3352-power-escapes.ts','am3352-trunk-taps.ts'])sources[`${ram}/src/${name}`]=createHash('sha256').update(readFileSync(`${ram}/src/${name}`)).digest('hex')
const {adaptRamPathsToHost}=await import(resolve(ram,'src/am3352-host.ts')),patches:any[]=[],newVias:any[]=[],details:any[]=[],freshIds=new Set<string>(host.traces.map((t:any)=>t.pcb_trace_id));let child=structuredClone(parent)
function replace(before:any,after:any,reason:string){const index=child.findIndex((e:any)=>e.type===before.type&&e[`${e.type}_id`]===before[`${before.type}_id`]);if(index<0)throw Error('Missing replacement');child[index]=after;patches.push({id:`${before.type}:${before[`${before.type}_id`]}`,before,after,beforeSha256:hash(before),afterSha256:hash(after),reason})}
for(const byte of [0,1]){
const paths=adaptRamPathsToHost(native,byte?'am3352-byte1':'am3352-byte0'),place=snapshot.placements[byte]
for(const ball of ['E1','E2','E3']){const p=paths.find((p:any)=>p.connection===`U1.ball_${ball}`);if(!p)throw Error('Missing adapter ball');const sp=child.find((e:any)=>e.type==='source_port'&&e.source_component_id===`source_component_${byte?84:83}`&&e.name===`ball_${ball}`),exit=child.find((e:any)=>e.type==='pcb_breakout_point'&&e.source_port_id===sp.source_port_id),before=child.find((e:any)=>e.type==='pcb_trace'&&e.source_trace_id===exit.source_trace_id),after=structuredClone(before);after.route=p.route.map((v:any)=>({...v,x:place.pcbX-v.x,y:place.pcbY-v.y}));after.route[0].start_pcb_port_id=before.route[0].start_pcb_port_id;replace(before,after,`Current source adapter RAM${byte}/${ball}`);freshIds.add(after.pcb_trace_id);const end=after.route.at(-1);replace(exit,{...exit,x:end.x,y:end.y,layer:end.layer},'Current adapter terminal');newVias.push(...after.route.filter((p:any)=>p.route_type==='via').map((p:any)=>({...p,ball,byte,net:ball==='E2'?'DDR_1V5':'signal'})));details.push({byte,ball,traceId:after.pcb_trace_id,via:after.route.find((p:any)=>p.route_type==='via')})}
const before=child.find((e:any)=>e.type==='pcb_trace'&&e.pcb_trace_id===`saved_fanout_pcb_group_${byte?5:3}_2`),start=before.route[0],pad=child.find((e:any)=>e.type==='pcb_smtpad'&&e.pcb_port_id===start.start_pcb_port_id)
if(!pad||Math.hypot(pad.x-start.x,pad.y-start.y)>1e-8)throw Error('C1 launch is not source-owned pad center')
const offset=Number(offsetArg??'.4');if(!Number.isFinite(offset)||offset<=0||Math.hypot(offset,offset)>1.524)throw Error('Invalid launch offset');const via={x:pad.x+offset,y:pad.y+offset},after={...before,connectsTo:before.connectsTo.filter((s:string)=>!s.startsWith('pcb_via_')&&!s.startsWith('pcb_breakout_point_')),route:[start,{route_type:'wire',...via,layer:'top',width:.12},{route_type:'via',...via,from_layer:'top',to_layer:'inner3',via_diameter:.4572,via_hole_diameter:.254}]};delete after.connection_name;replace(before,after,`Dedicated C1 pad +${offset},+${offset} mm through-via supply drop`);freshIds.add(after.pcb_trace_id);newVias.push({...via,byte,ball:'C1',net:'DDR_1V5'});details.push({byte,ball:'C1',traceId:after.pcb_trace_id,pad:{x:pad.x,y:pad.y},via,padToViaMm:Math.hypot(offset,offset),railJoin:child.find((e:any)=>e.type==='source_trace'&&e.source_trace_id===`planned_ram${byte}_C1_supply`)})
}
// Old emitted barrels belong to replaced traces and must not survive as ghost copper.
const replacedIds=new Set(patches.filter(p=>p.before.type==='pcb_trace').map(p=>p.before.pcb_trace_id)),removals=child.filter((e:any)=>e.type==='pcb_via'&&replacedIds.has(e.pcb_trace_id));child=child.filter((e:any)=>!removals.includes(e))
const fresh=child.filter((e:any)=>e.type==='pcb_trace'&&freshIds.has(e.pcb_trace_id)),withoutPlanes=child.filter((e:any)=>e.type!=='pcb_copper_pour'&&!freshIds.has(e.pcb_trace_id)),conductor=verifyDdrSystemCopper(withoutPlanes,fresh,input.connections,{},50,{reportSameNetContacts:true})
let planeError:string|undefined
try {for(const before of child.filter((e:any)=>e.type==='pcb_copper_pour')){const vias=newVias.filter(v=>before.layer!=='inner3'||v.net!=='DDR_1V5'),plan=mergeDdrSignalAntipads([before],vias,.1016);if(hash(before)!==hash(plan.planes[0]))replace(before,plan.planes[0],'Foreign current-adapter and C1 through-via antipads')}}catch(e){planeError=String(e)}
const physical=verifyDdrSystemCopper(child.filter((e:any)=>!freshIds.has(e.pcb_trace_id)),fresh,input.connections,{},50,{reportSameNetContacts:true}),angles=auditRouteAngles(child.filter((e:any)=>e.type==='pcb_trace')),junctions=auditDdrTraceJunctions(child,input.connections),report={captureHash,parentDirectory:parentDir,parentCandidateSha256:hash(parent),candidateSha256:hash(child),sourceHashes:sources,details,newVias,conductor,planeError,physical,angles,junctions,hostsPreserved:host.traces.every((t:any)=>child.some((e:any)=>hash(e)===hash(t))),scope:'Bounded isolated E1/E2/E3 adapter migration plus dedicated C1 launch experiment. Eight trace updates, six terminal updates; original hosts unchanged, so E3 byte0 host timing becomes overlong until separately retuned. Bottom power/VREF fresh-layer policy errors are reported, not waived. No accepted-board or external edits.'}
for(const[p,h]of Object.entries(sources))if(createHash('sha256').update(readFileSync(p)).digest('hex')!==h)throw Error('Read source changed')
mkdirSync(out,{recursive:true});for(const[n,v]of Object.entries({'candidate.circuit.json':child,'report.json':report,'original-copper-replacements.json':{captureHash,parentCircuitSha256:hash(parent),candidateCircuitSha256:hash(child),replacements:patches,removals:removals.map((before:any)=>({before,beforeSha256:hash(before)}))}}))writeFileSync(resolve(out,n),JSON.stringify(v,null,2));console.log(JSON.stringify({details,conductorErrors:conductor.errors,conductorViolations:conductor.violations.length,planeError,physicalErrors:physical.errors,physicalViolations:physical.violations.length,first:physical.violations.slice(0,3),angles:angles.valid,junctions:junctions.valid,hostsPreserved:report.hostsPreserved}))