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/capture-ddr-system.tsx

/** Capture the complete two-x8 host routing problem. This is an unrouted draft,
 * deliberately not a successful board or DDR compliance assertion. */
import {readFileSync,writeFileSync,mkdirSync} from 'node:fs'
import {resolve} from 'node:path'
import {createHash} from 'node:crypto'
import {Fragment} from 'react'
import {RootCircuit,type GenericLocalAutorouter,type SimpleRouteJson} from '@tscircuit/core'
import {AM3352Module,getAM3352TracePaths} from '../src/index'
import {DdrMemoryAdapter,adaptDdrMemoryPaths,getDdrMemoryConnections,DDR_MEMORY_SIX_LAYER_MAP,type DdrMemoryPin} from '../src/ddr-memory'
const base=process.argv[2]
if(!base)throw Error('Usage: bun scripts/capture-ddr-system.tsx /path/to/memory [memory-profile]')
const profile=process.argv[3]??'outward_north'
const swapped=process.argv.includes('--swap-bytes')
const byteY=(byte:number)=>(byte===0?1:-1)*(swapped?-8:8)
const pins=JSON.parse(readFileSync(resolve(base,'src/pin-map.json'),'utf8')) as Array<DdrMemoryPin&{x:number;y:number;radius:number}>
const sourceText=readFileSync(resolve(base,`src/generated/${profile}.trace-paths.json`),'utf8')
const paths=JSON.parse(sourceText)
const boundary=profile==='bus_grouped'?JSON.parse(readFileSync(resolve(base,'src/generated/bus_grouped.boundary.json'),'utf8')):undefined
if(profile==='bus_grouped')for(const terminal of ['DQ0','DQS_P','CK_P','A0']) {
 const pin=pins.find(p=>p.terminal===terminal)!
 const end=paths.find((p:any)=>p.connection===`U1.ball_${pin.ball}`).route.at(-1)
 if(end.layer!==(['DQ0','DQS_P'].includes(terminal)?'inner2':'inner3'))throw Error('RAM bus_grouped layer declarations and cache disagree; regenerate memory first')
}
const supportSourceHash=createHash('sha256').update(['src/DecouplingNetwork.tsx','src/additional-decoupling.json','src/fixed-supply-drops.ts','src/layout.ts','src/generated/fixed-cap-elbows.json'].map(file=>readFileSync(file,'utf8')).join('\n')).digest('hex')
const captureHash=createHash('sha256').update(JSON.stringify({paths,pins,boundary,supportSourceHash,cpuPaths:getAM3352TracePaths('ddr_left'),ramY:[byteY(0),byteY(1)],ramWidth:profile==='bus_grouped'?.1168:null})).digest('hex')
const runDir=resolve('dist/ddr-system',profile+(swapped?'-swapped':'')+'-'+captureHash.slice(0,12))
mkdirSync(runDir,{recursive:true})
writeFileSync(resolve(runDir,'memory-source-snapshot.json'),JSON.stringify({profile,captureHash,placements:[0,1].map(byte=>({x:-12,y:byteY(byte),rotation:180})),sha256:createHash('sha256').update(sourceText).digest('hex'),paths,pins,boundary},null,2)+'\n')
const capture=async(input:SimpleRouteJson):Promise<GenericLocalAutorouter>=>{
 const signalInput={...input,connections:input.connections.filter(c=>Boolean(c.source_trace_id))}
 writeFileSync(resolve(runDir,'routing-input.json'),JSON.stringify(signalInput,null,2)+'\n')
 writeFileSync(resolve(runDir,'full-routing-input.json'),JSON.stringify(input,null,2)+'\n')
 const handlers:Record<string,Array<(e:any)=>void>>={complete:[],error:[],progress:[]}
 return {input,isRouting:false,start(){queueMicrotask(()=>handlers.complete.forEach(h=>h({type:'complete',traces:[]})))},stop(){},on(e,h){handlers[e].push(h)},solveSync(){return []}}
}
const root=new RootCircuit()
root.add(<board width={80} height={60} layers={10} schematicDisabled autorouter={{algorithmFn:capture}} minPadEdgeToPadEdgeClearance={.0762} minTraceToPadEdgeClearance={.0762} minViaEdgeToPadEdgeClearance={.0762} minTraceWidth={.1016} defaultTraceWidth={.12} minViaPadDiameter={.4572} minViaHoleDiameter={.254} allowBlindAndBuriedVias={false}>
 <AM3352Module name="CPU" layoutProfile="ddr_left" pcbX={12}/>
 {([0,1] as const).map(byte=><DdrMemoryAdapter key={byte} name={`RAM${byte}`} traceWidthMm={profile==='bus_grouped'?.1168:undefined} layerMap={profile==='bus_grouped'?DDR_MEMORY_SIX_LAYER_MAP:undefined} boundary={boundary} paths={paths} pins={pins} pcbX={-12} pcbY={byteY(byte)} pcbRotation={180}
 connections={getDdrMemoryConnections('CPU',byte)} footprint={<footprint>{pins.map(p=><Fragment key={p.ball}><smtpad portHints={[`pin${p.pin}`]} pcbX={p.x} pcbY={p.y} radius={p.radius} shape="circle"/></Fragment>)}</footprint>}/>)}
</board>)
await root.renderUntilSettled()
const json=root.getCircuitJson()
writeFileSync(resolve(runDir,'unrouted.circuit.json'),JSON.stringify(json,null,2)+'\n')
const input=JSON.parse(readFileSync(resolve(runDir,'routing-input.json'),'utf8'))
const externals=json.filter((e:any)=>e.type==='source_trace'&&/^RAM[01]_.*_external$/.test(e.name??''))
const expected=[...getAM3352TracePaths('ddr_left').map(p=>({...p,route:p.route.map(q=>({...q,x:Number(q.x)+12,y:Number(q.y)}))})),...([0,1] as const).flatMap(byte=>adaptDdrMemoryPaths(paths,profile==='bus_grouped'?DDR_MEMORY_SIX_LAYER_MAP:undefined,profile==='bus_grouped'?.1168:undefined).map(p=>({...p,route:p.route.map(q=>({...q,x:-12-Number(q.x),y:byteY(byte)-Number(q.y)}))}))) ]
const rendered=json.filter((e:any)=>e.type==='pcb_trace')
for(const path of expected)if(!rendered.some((t:any)=>t.route.length===path.route.length&&t.route.every((q:any,i:number)=>{const p:any=path.route[i];return q.route_type===p.route_type&&Math.hypot(q.x-p.x,q.y-p.y)<1e-7&&(q.route_type==='wire'?q.layer===p.layer&&Math.abs(q.width-p.width)<1e-8:q.from_layer===p.from_layer&&q.to_layer===p.to_layer)})))throw Error(`Missing or altered cached route ${path.connection}`)
const signalNames=externals.map((e:any)=>{
 const [,byte,terminal]=e.name.match(/^RAM([01])_(.*)_external$/)
 const ports=e.connected_source_port_ids.map((id:string)=>json.find((p:any)=>p.type==='source_port'&&p.source_port_id===id)) as any[]
 const cpu=ports.find(p=>p.port_hints?.some((h:string)=>h.startsWith('DDR_')))
 const ram=ports.find(p=>p.port_hints?.includes(`SIG_${terminal}`))
 const signal=cpu?.port_hints.find((h:string)=>h.startsWith('DDR_'))
 const expected=getDdrMemoryConnections('CPU',Number(byte) as 0|1)[terminal]
 if(!cpu||!ram||expected!==`.CPU .U1 > .${signal}`)throw Error(`Actual source ports disagree with DDR wiring for ${e.name}`)
 return {sourceTraceId:e.source_trace_id,ramByte:Number(byte),ramTerminal:terminal,ramBall:ram.name,cpuSignal:signal,cpuBall:cpu.name,cpuTarget:expected}
})
if(new Set(signalNames.map(s=>s.cpuSignal)).size!==50)throw Error('Expected all50 distinct DDR/reset CPU nets')
writeFileSync(resolve(runDir,'signal-map.json'),JSON.stringify(signalNames,null,2)+'\n')
const report={cachedPathsPreserved:expected.length,status:'unrouted-draft',memoryProfile:profile,expectedExternalBranches:78,declaredExternalBranches:externals.length,routingConnections:input.connections.length,terminalCounts:input.connections.map((c:any)=>({name:c.name,terminals:c.pointsToConnect.length})),emittedErrors:json.filter(e=>e.type.endsWith('_error')),scope:'Complete two-byte signal declarations, not routed copper. RAM power, VREF, ZQ, termination and full timing remain required.'}
writeFileSync(resolve(runDir,'capture-report.json'),JSON.stringify(report,null,2)+'\n')
if(externals.length!==78)throw Error(`Expected78 shared/byte branches, saw${externals.length}`)
writeFileSync(resolve(runDir,'provenance.json'),JSON.stringify({captureHash,memorySourceSha256:createHash('sha256').update(sourceText).digest('hex'),files:Object.fromEntries(['unrouted.circuit.json','memory-source-snapshot.json','signal-map.json','routing-input.json'].map(name=>[name,createHash('sha256').update(readFileSync(resolve(runDir,name))).digest('hex')]))},null,2)+'\n')
console.log(`${runDir}: ${externals.length} declaredbranches, ${input.connections.length} routingconnections, ${report.emittedErrors.length} draft errors`)