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/ddr-support-routing-context.ts

import {readFileSync} from 'node:fs'
import {resolve,dirname} from 'node:path'
import {createHash} from 'node:crypto'
import {projectHostObstacles} from './prepare-ddr-host-signal-projection'
const hash=(x:any)=>createHash('sha256').update(JSON.stringify(x)).digest('hex')
const fileHash=(p:string)=>createHash('sha256').update(readFileSync(p)).digest('hex')
export function supportCopperObstacles(elements:any[]){
 const layers=['top','inner1','inner2','inner3','inner4','inner5','inner6','inner7','inner8','bottom'],obstacles:any[]=[]
 const rect=(x:number,y:number,width:number,height:number,ls:string[],id:string,rotation=0)=>{if(![x,y,width,height,rotation].every(Number.isFinite)||width<=0||height<=0||ls.some(l=>!layers.includes(l)))throw Error('Invalid support copper');obstacles.push({type:'rect',center:{x,y},width,height,layers:ls,ccwRotationDegrees:rotation,connectedTo:['support'],obstacleId:id})}
 for(const e of elements){
  if(e.type==='pcb_via')rect(e.x,e.y,e.outer_diameter,e.outer_diameter,layers,`fixed_via_${e.pcb_via_id}`)
  else if(e.type==='pcb_smtpad'){
   if(e.shape==='circle')rect(e.x,e.y,e.radius*2,e.radius*2,[e.layer],`support_pad_${e.pcb_smtpad_id}`)
   else if(e.shape==='rect')rect(e.x,e.y,e.width,e.height,[e.layer],`support_pad_${e.pcb_smtpad_id}`,e.ccw_rotation??0)
   else throw Error(`Unsupported support pad shape ${e.shape}`)
  }else if(e.type==='pcb_trace')for(const [i,p]of e.route.entries()){
   if(p.route_type==='via')rect(p.x,p.y,p.via_diameter??.4572,p.via_diameter??.4572,layers,`fixed_via_${e.pcb_trace_id}_${i}`)
   else if(p.route_type==='wire'){
    rect(p.x,p.y,p.width,p.width,[p.layer],`fixed_trace_${e.pcb_trace_id}_${i}_cap`)
    const q=e.route[i+1];if(q?.route_type==='wire'&&q.layer===p.layer&&Math.hypot(q.x-p.x,q.y-p.y)>1e-10)rect((p.x+q.x)/2,(p.y+q.y)/2,Math.hypot(q.x-p.x,q.y-p.y),Math.max(p.width,q.width),[p.layer],`fixed_trace_${e.pcb_trace_id}_${i}_segment`,Math.atan2(q.y-p.y,q.x-p.x)*180/Math.PI)
   }else throw Error('Unknown support trace primitive')
  }else throw Error(`Unsupported support copper ${e.type}`)
 }
 return obstacles
}
export async function loadDdrSupportRoutingContext(captureDirectory:string,path:string,original:any[],seed:any[],captureHash:string){
 const read=(p:string)=>JSON.parse(readFileSync(p,'utf8')),folder=dirname(path),combined=read(path),reportPath=resolve(folder,'report.json'),report=read(reportPath),dropPath=resolve(captureDirectory,'power-via-reuse/candidate.json'),drop=read(dropPath)
 if(report.captureHash!==captureHash||drop.captureHash!==captureHash||report.hostTracesSha256!==hash(seed)||report.dropReplacementsSha256!==hash(drop.replacements))throw Error('Support report/host/drop provenance mismatch')
 if(report.physical.errors.length||report.physical.violations.length||!report.physical.angles.valid||report.physical.joinedBends.length)throw Error('Support report is not physically clean')
 const id=(e:any)=>e[`${e.type}_id`],byId=new Map(combined.filter((e:any)=>id(e)).map((e:any)=>[id(e),e])),replacements=new Map(drop.replacements.map((e:any)=>[id(e),e])),changed=new Set<string>()
 for(const e of original.filter(e=>['pcb_trace','pcb_via','pcb_smtpad','pcb_board','pcb_port'].includes(e.type)||e.type.startsWith('source_'))){
  const expected=replacements.get(id(e))??e
  if(hash(byId.get(id(e)))!==hash(expected))throw Error(`Support changed undeclared original copper ${id(e)}`)
 }
 for(const e of drop.replacements)if(hash(byId.get(id(e)))!==hash(e))throw Error(`Missing approved power replacement ${id(e)}`)
 const originalById=new Map(original.filter(e=>id(e)).map(e=>[id(e),e]))
 for(const e of combined.filter((e:any)=>['pcb_trace','pcb_via','pcb_smtpad'].includes(e.type)))if(hash(originalById.get(id(e))??null)!==hash(e))changed.add(id(e))
 const seedIds=new Set(seed.map(t=>t.pcb_trace_id))
 for(const t of seed)if(hash(byId.get(id(t)))!==hash(t))throw Error(`Immutable host seed mismatch ${id(t)}`)
 for(const key of seedIds)changed.delete(key)
 const fixed=combined.filter((e:any)=>!seedIds.has(id(e)))
 const added=supportCopperObstacles(combined.filter((e:any)=>changed.has(id(e))))
 const projected=projectHostObstacles(added).projected,inverse:Record<string,string>={top:'top',inner1:'inner2',inner2:'inner4',bottom:'inner6'}
 const obstacles=projected.map((o:any,i:number)=>({...o,layers:o.layers.map((l:string)=>inverse[l]),connectedTo:[`reserved-support:${i}`]}))
 const pours=combined.filter((e:any)=>e.type==='pcb_copper_pour')
 if(pours.length!==3||pours.some((p:any)=>!['inner1','inner3','inner5'].includes(p.layer)))throw Error('Unexpected support planes; signal-layer polygon projection unsupported')
 // No new vias are permitted by this same-inner2 winding adapter. Off-signal
 // reference/supply planes stay in fixed for the complete physical predicate.
 return {fixed,obstacles,aliases:report.aliases,evidence:{captureHash,path:resolve(path),candidateSha256:fileHash(path),reportSha256:fileHash(reportPath),dropSha256:fileHash(dropPath),seedTraceHash:hash(seed),changedCopperCount:changed.size,supportObstacleCount:obstacles.length,planeLayers:pours.map((p:any)=>p.layer),removedOriginalPowerTraceIds:[...replacements.keys()],scope:'Retained original geometry checked byte-for-byte except explicit power replacements and global plane replacement. Old power-stub obstacles remain conservatively reserved.'}}
}