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/audit-ddr-dqlm.ts

/** Read-only nominal-length audit using actual source-owned package ball pads. */
import {readFileSync,writeFileSync,existsSync} from 'node:fs'
import {resolve,dirname} from 'node:path'
import {createHash} from 'node:crypto'
import {verifyDdrCapture} from './ddr-capture-provenance'
const [dir,out,auditArgument]=process.argv.slice(2)
if(!dir||!out)throw Error('Usage: capture-directory output-json [independent-host-audit.json]')
const read=(p:string)=>JSON.parse(readFileSync(p,'utf8')),{captureHash}=verifyDdrCapture(dir),original=read(resolve(dir,'unrouted.circuit.json')),input=read(resolve(dir,'routing-input.json')),auditPath=resolve(auditArgument??'dist/ddr-integrated-nine/independent-host-audit.json'),actual=read(auditPath)
const {getSourceNetOwnerForAudit}=await import(resolve('/Users/ankan/Documents/Codex/2026-09-10/what-x20/outputs/mt41k512m8da-107-it-p-fanout/scripts/audit-am3352-obstacle-ownership.ts'))
const owner=getSourceNetOwnerForAudit(original),rows:any[]=[]
const shaFile=(p:string)=>createHash('sha256').update(readFileSync(p)).digest('hex')
const integratedBundle=read(resolve(dirname(auditPath),'host-bundle.json'))
if(integratedBundle.captureHash!==captureHash)throw Error('Compared integrated host bundle belongs to another capture')
const planar=(route:any[])=>route.reduce((n,p,i)=>{const q=route[i-1];return n+(p.route_type==='wire'&&q?.route_type==='wire'&&p.layer===q.layer?Math.hypot(p.x-q.x,p.y-q.y):0)},0)

for(const c of input.connections.filter((c:any)=>/^DQ[0-7]$/.test(c.ramTerminal)||['DM','DQS_P','DQS_N'].includes(c.ramTerminal))){
 const expected=owner(c.source_trace_id??c.name)
 if(!expected||c.pointsToConnect.length!==2)throw Error('Ambiguous byte net')
 const balls=c.pointsToConnect.map((p:any)=>{
  const exit=original.find((e:any)=>e.type==='pcb_breakout_point'&&e.pcb_breakout_point_id===p.pointId)
  if(!exit||owner(exit.pcb_breakout_point_id)!==expected)throw Error('Breakout/source ownership mismatch')
  const traces=original.filter((e:any)=>e.type==='pcb_trace'&&e.source_trace_id===exit.source_trace_id&&e.route.some((q:any)=>q.route_type==='wire'&&q.layer===p.layer&&Math.hypot(q.x-p.x,q.y-p.y)<1e-7))
  if(traces.length!==1)throw Error('Ambiguous saved source path')
  const ball=traces[0].route.find((p:any)=>p.route_type==='wire')
  const pads=original.filter((e:any)=>e.type==='pcb_smtpad'&&e.layer===ball.layer&&Math.hypot(e.x-ball.x,e.y-ball.y)<1e-7&&owner(e.pcb_smtpad_id)===expected)
  if(pads.length!==1)throw Error('Actual source-owned ball pad not unique')
  const saved=traces[0],last=saved.route.at(-1)
  if(last.route_type!=='wire'||last.layer!==p.layer||Math.hypot(last.x-p.x,last.y-p.y)>1e-7)throw Error('Frozen exit is not the saved path endpoint')
  const before=[...saved.route].reverse().find((q:any)=>q.route_type==='wire'&&q.layer===p.layer&&Math.hypot(q.x-p.x,q.y-p.y)>1e-7)
  const group=original.find((e:any)=>e.type==='pcb_group'&&/^RAM[01]$/.test(e.name??'')&&e.subcircuit_id===exit.subcircuit_id)
  const faceDistances=group?{left:Math.abs(p.x-(group.center.x-group.width/2)),right:Math.abs(p.x-(group.center.x+group.width/2)),top:Math.abs(p.y-(group.center.y+group.height/2)),bottom:Math.abs(p.y-(group.center.y-group.height/2))}:null
  const exitFaces=faceDistances?Object.entries(faceDistances).filter(([_,distance])=>distance<1e-6).map(([side])=>side):[]
  return {exit:{x:p.x,y:p.y,layer:p.layer,faces:exitFaces,groupName:group?.name,tangentDegrees:before?Math.atan2(p.y-before.y,p.x-before.x)*180/Math.PI:null},fixedPlanarMm:planar(saved.route),x:pads[0].x,y:pads[0].y,padId:pads[0].pcb_smtpad_id,sourcePortId:pads[0].source_port_id,breakoutId:p.pointId,savedTraceId:traces[0].pcb_trace_id}
 }).sort((a:any,b:any)=>a.x-b.x)
 const [ram,cpu]=balls,dx=Math.abs(cpu.x-ram.x),dy=Math.abs(cpu.y-ram.y)
 const hostDx=Math.abs(cpu.exit.x-ram.exit.x),hostDy=Math.abs(cpu.exit.y-ram.exit.y),hostOctilinearLowerBoundMm=Math.max(hostDx,hostDy)+(Math.SQRT2-1)*Math.min(hostDx,hostDy),fixedPlanarMm=cpu.fixedPlanarMm+ram.fixedPlanarMm
 rows.push({hostOctilinearLowerBoundMm,fixedPlanarMm,totalPlanarLowerBoundMm:fixedPlanarMm+hostOctilinearLowerBoundMm,byte:c.ramByte,terminal:c.ramTerminal,name:c.name,ram,cpu,dxMm:dx,dyMm:dy,manhattanMm:dx+dy})
}
const bytes=[0,1].map(byte=>{
 const members=rows.filter(r=>r.byte===byte);if(members.length!==11)throw Error(`Missing byte members ${byte}`)
 const data=members.filter(r=>r.terminal==='DM'||/^DQ[0-7]$/.test(r.terminal)),dqNominalMm=Math.max(...data.map(r=>r.manhattanMm)),includingStrobesMm=Math.max(...members.map(r=>r.manhattanMm))
 const axisEnvelopeMm=Math.max(...data.map(r=>r.dxMm))+Math.max(...data.map(r=>r.dyMm))
 const floors=members.map(r=>({terminal:r.terminal,name:r.name,ramExit:r.ram.exit,cpuExit:r.cpu.exit,ramFixedMm:r.ram.fixedPlanarMm,cpuFixedMm:r.cpu.fixedPlanarMm,hostOctilinearLowerBoundMm:r.hostOctilinearLowerBoundMm,totalPlanarLowerBoundMm:r.totalPlanarLowerBoundMm,excessOverNominalMm:r.totalPlanarLowerBoundMm-dqNominalMm,excessOverAxisEnvelopeMm:r.totalPlanarLowerBoundMm-axisEnvelopeMm,nominalUnreachableWithFrozenExits:r.totalPlanarLowerBoundMm>dqNominalMm+1e-7}))
 return {byte,frozenExitFloors:floors,nominalUnreachableMembers:floors.filter(f=>f.nominalUnreachableWithFrozenExits).map(f=>f.terminal),dqlmPairedDataMaximumMm:dqNominalMm,dqlmIncludingStrobesMm:includingStrobesMm,independentAxisEnvelopeMm:Math.max(...data.map(r=>r.dxMm))+Math.max(...data.map(r=>r.dyMm)),limitingDataTerminals:data.filter(r=>Math.abs(r.manhattanMm-dqNominalMm)<1e-7).map(r=>r.terminal),members,measuredRoutes:actual.lengths.filter((r:any)=>r.byte===byte&&Number.isFinite(r.fullPlanarMm)&&members.some(m=>m.terminal===r.terminal)).map((r:any)=>({...r,deltaFromDqlmMm:r.fullPlanarMm-dqNominalMm}))}
})
let referenceFloorComparison:any=null
const floorPath=resolve(dir,'length-floor-report.json')
if(existsSync(floorPath)){
 const reference=read(floorPath),fixedHash=shaFile(resolve(dir,'unrouted.circuit.json')),forcedInputHash=shaFile(resolve(dir,'forced-trunk-routing-input.json'))
 if(reference.fixedSha256!==fixedHash||reference.inputSha256!==forcedInputHash)throw Error('Reference floor report has stale provenance')
 const differences=rows.map(r=>{const f=reference.bytes.find((b:any)=>b.byte===r.byte)?.nets.find((n:any)=>n.terminal===r.terminal);if(!f)throw Error('Missing reference floor member');return {byte:r.byte,terminal:r.terminal,differenceMm:r.totalPlanarLowerBoundMm-f.totalPlanarLowerBoundMm}})
 if(differences.some(d=>Math.abs(d.differenceMm)>1e-6))throw Error('Independent floor differs from provenance-bound reference')
 referenceFloorComparison={fixedHash,forcedInputHash,referenceSha256:shaFile(floorPath),all22IndependentlyReproduced:true,maxDifferenceMm:Math.max(...differences.map(d=>Math.abs(d.differenceMm)))}
}
const report={captureHash,actualAuditPath:auditPath,integratedHostBundleSha256:shaFile(resolve(dirname(auditPath),'host-bundle.json')),referenceFloorComparison,lowerBoundScope:'Actual frozen CPU and RAM planar escape lengths plus obstacle-free octilinear distance between exact exits. Does not count new/fixed via propagation delay, exit-tangent restrictions, physical obstacles, or additional matching. Exceeding nominal already at this optimistic floor proves the current frozen exits cannot meet that nominal budget through host-route optimization alone.',source:'TI SPRS717L, section7.7.2.3.6.2, Figure7-67, Table7-69, printedpage188',sourceUrl:'https://www.ti.com/lit/ds/symlink/am3352.pdf',tablePlacement:'DQ0/DQ1 nominal length values DQLM0/DQLM1 are printed under MAX, not TYP; verified PDF text coordinates against column headers.',interpretation:'DQLM is placement-derived nominal data length; table places that nominal-length parameter under MAX. No absolute overlength tolerance is specified here. Do not treat 25mil skew as permission to exceed DQLM; do not equate a computed nominal value to an independently validated electrical propagation-delay limit.',sourceAuditSha256:createHash('sha256').update(readFileSync(auditPath)).digest('hex'),bytes}
writeFileSync(out,JSON.stringify(report,null,2)+'\n');console.log(JSON.stringify(bytes.map(b=>({byte:b.byte,dqlm:b.dqlmPairedDataMaximumMm,nominalUnreachableMembers:b.nominalUnreachableMembers,floors:b.frozenExitFloors.filter(f=>f.nominalUnreachableWithFrozenExits)})),null,2))