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/check-reference-planes.ts

import assert from 'node:assert/strict'
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'
import { assertLayoutProfile, LAYOUT_PROFILES, getAM3352Bounds } from '../src/profiles'
const profile=process.argv[2]??'ddr_left'
assertLayoutProfile(profile)
assert.equal(LAYOUT_PROFILES[profile].layerCount,10,'Reference-plane contract applies to ten-layer DDR profiles')
const json=JSON.parse(readFileSync(`src/generated/${profile}.circuit.json`,'utf8'))
const bounds=getAM3352Bounds(profile)
const corners=[{x:bounds.minX,y:bounds.minY},{x:bounds.maxX,y:bounds.minY},{x:bounds.maxX,y:bounds.maxY},{x:bounds.minX,y:bounds.maxY}]
const cross=(a:any,b:any,p:any)=>(b.x-a.x)*(p.y-a.y)-(b.y-a.y)*(p.x-a.x)
function insideOuter(p:any,vertices:any[]){
 let inside=false
 for(let i=0,j=vertices.length-1;i<vertices.length;j=i++){
  const a=vertices[j],b=vertices[i]
  if(Math.abs(cross(a,b,p))<1e-8&&p.x>=Math.min(a.x,b.x)-1e-8&&p.x<=Math.max(a.x,b.x)+1e-8&&p.y>=Math.min(a.y,b.y)-1e-8&&p.y<=Math.max(a.y,b.y)+1e-8)return true
  if((a.y>p.y)!==(b.y>p.y)&&p.x<(b.x-a.x)*(p.y-a.y)/(b.y-a.y)+a.x)inside=!inside
 }
 return inside
}
const report=[]
for(const [layer,netName] of [['inner1','GND'],['inner3','VCC_DDR_1V5'],['inner5','GND']]){
 const net=json.find((e:any)=>e.type==='source_net'&&e.name===netName)
 assert.ok(net,`Missing reference net ${netName}`)
 const pours=json.filter((e:any)=>e.type==='pcb_copper_pour'&&e.layer===layer)
 assert.equal(pours.length,1,`${layer} must have one connected rendered reference-plane polygon`)
 assert.equal(pours[0].source_net_id,net.source_net_id,`${layer} has foreign-net copper pour`)
 const vertices=pours[0].brep_shape.outer_ring.vertices
 const outerArea=Math.abs(vertices.reduce((sum:number,p:any,i:number)=>{const next=vertices[(i+1)%vertices.length];return sum+p.x*next.y-next.x*p.y},0)/2)
 assert.ok(outerArea>=(bounds.maxX-bounds.minX)*(bounds.maxY-bounds.minY)-1e-8,`${layer} reference area is smaller than the rectangular fanout`)
 for(const corner of corners)assert(insideOuter(corner,vertices),`${layer} reference excludes fanout corner (${corner.x},${corner.y})`)
 for(let i=0;i<vertices.length;i++){
  const a=vertices[i],b=vertices[(i+1)%vertices.length]
  assert(!(a.x>bounds.minX+1e-8&&a.x<bounds.maxX-1e-8&&a.y>bounds.minY+1e-8&&a.y<bounds.maxY-1e-8),`${layer} outer reference boundary intrudes into the fanout rectangle`)
  for(let k=0;k<4;k++){
   const c=corners[k],d=corners[(k+1)%4]
   assert(!(cross(a,b,c)*cross(a,b,d)<-1e-12&&cross(c,d,a)*cross(c,d,b)<-1e-12),`${layer} reference boundary crosses the fanout rectangle`)
  }
 }
 let lateralTraceSegments=0
 for(const trace of json.filter((e:any)=>e.type==='pcb_trace'))for(let i=1;i<trace.route.length;i++){
  const a=trace.route[i-1],b=trace.route[i]
  if(a.route_type==='wire'&&b.route_type==='wire'&&a.layer===layer&&b.layer===layer&&Math.hypot(a.x-b.x,a.y-b.y)>1e-7)lateralTraceSegments++
 }
 assert.equal(lateralTraceSegments,0,`${layer} carries lateral traces in a reserved reference plane`)
 report.push({layer,netName,connectedOuterPolygons:pours.length,outerAreaMm2:outerArea,antipadOrCutoutCount:pours[0].brep_shape.inner_rings.length,lateralTraceSegments})
}
mkdirSync('dist/ddr',{recursive:true})
writeFileSync(`dist/ddr/${profile}.reference-planes.json`,JSON.stringify({profile,bounds,checks:report,limitation:'Rendered polygon continuity and absence of lateral tracks only; no impedance, return-current field, or fabrication stackup validation.'},null,2)+'\n')
console.log(`${profile}: inner1/inner3/inner5 each have one connected reference polygon and no lateral trace segments`)