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/render-exit-directions.ts

import { mkdirSync, writeFileSync } from 'node:fs'
import { Resvg } from '@resvg/resvg-js'
import { getAM3352Exits } from '../src/exits'
import { getAM3352TracePaths } from '../src/saved-paths'
import { AM3352_PINS } from '../src/pin-map'
import { LAYOUT_PROFILES, getAM3352Bounds, type LayoutProfile } from '../src/profiles'
const colors:Record<string,string>={top:'#ff7777',inner2:'#ffe56b',inner3:'#84db7e',inner4:'#57dddb',inner6:'#f5a0dc'}
const profiles=Object.keys(LAYOUT_PROFILES) as LayoutProfile[]
const panel=800
function renderProfile(profile:LayoutProfile){
 const settings=LAYOUT_PROFILES[profile],bounds=getAM3352Bounds(profile),scale=650/(Math.max(settings.fanoutWidthMm,settings.fanoutHeightMm)+3)
 const cx=(bounds.minX+bounds.maxX)/2,cy=(bounds.minY+bounds.maxY)/2
 const px=(x:number)=>400+(x-cx)*scale,py=(y:number)=>420-(y-cy)*scale
 const exits=getAM3352Exits(profile).filter(e=>e.signal.startsWith('DDR_'))
 const paths=getAM3352TracePaths(profile).filter(path=>exits.some(exit=>path.connection===`U1.${exit.ball}`))
 const body=[`<rect width="800" height="800" fill="#10151c"/><text x="25" y="35" fill="white" font-family="sans-serif" font-size="26">${profile}</text><text x="25" y="65" fill="#afc3d2" font-family="sans-serif" font-size="17">${settings.fanoutWidthMm} × ${settings.fanoutHeightMm} mm boundary · ${settings.layerCount} layers · ${exits.length} DDR terminals</text>`,
 `<rect x="${px(bounds.minX)}" y="${py(bounds.maxY)}" width="${settings.fanoutWidthMm*scale}" height="${settings.fanoutHeightMm*scale}" fill="none" stroke="#738698" stroke-dasharray="5 5"/>`,
 `<rect x="${px(-7.5)}" y="${py(7.5)}" width="${15*scale}" height="${15*scale}" fill="#19232c" stroke="#5d7384"/>`,
 ...AM3352_PINS.map(pin=>`<circle cx="${px(pin.x)}" cy="${py(pin.y)}" r="${.12*scale}" fill="#3b4e60"/>`)]
 for(const path of paths){
  for(let i=1;i<path.route.length;i++){
   const a=path.route[i-1]!,b=path.route[i]!
   if(a.route_type==='wire'&&b.route_type==='wire'&&a.layer===b.layer)body.push(`<path d="M ${px(a.x)} ${py(a.y)} L ${px(b.x)} ${py(b.y)}" fill="none" stroke="${colors[a.layer]??'#bbbbbb'}" stroke-width="${Math.max(.9,a.width*scale)}"/>`)
   if(b.route_type==='via')body.push(`<circle cx="${px(b.x)}" cy="${py(b.y)}" r="${(b.via_diameter??settings.viaPadDiameterMm)*scale/2}" fill="#16212b" stroke="#d9e6ef" stroke-width=".65"/>`)
  }
 }
 for(const exit of exits)body.push(`<circle cx="${px(exit.x)}" cy="${py(exit.y)}" r="2" fill="${colors[exit.layer]??'#bbbbbb'}"/>`)
 const sides=[...new Set(exits.map(exit=>exit.side))]
 for(const side of sides){const v={left:{x:-1,y:0},right:{x:1,y:0},top:{x:0,y:1},bottom:{x:0,y:-1}}[side];const p={x:side==='left'?bounds.minX-.7:side==='right'?bounds.maxX+.7:cx,y:side==='top'?bounds.maxY+.7:side==='bottom'?bounds.minY-.7:cy};body.push(`<text x="${px(p.x)}" y="${py(p.y)}" fill="#ffffff" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="22">${{left:'←',right:'→',top:'↑',bottom:'↓'}[side]}</text>`)}
 body.push('<text x="25" y="775" fill="#afc3d2" font-family="sans-serif" font-size="16">Actual saved DDR copper · +X right / +Y up · package outline shown</text>')
 return body.join('')
}
mkdirSync('previews',{recursive:true})
for(const profile of profiles){const svg=`<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800">${renderProfile(profile)}</svg>`;writeFileSync(`previews/${profile}-directions.svg`,svg);writeFileSync(`previews/${profile}-directions.png`,new Resvg(svg).render().asPng())}
const svg=`<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="${Math.ceil(profiles.length/2)*panel+70}"><rect width="1600" height="100%" fill="#10151c"/>${profiles.map((profile,i)=>`<g transform="translate(${i%2*panel},${Math.floor(i/2)*panel})">${renderProfile(profile)}</g>`).join('')}<text x="25" y="${Math.ceil(profiles.length/2)*panel+30}" font-family="sans-serif" font-size="18" fill="#afc3d2">Layer colors: ${Object.entries(colors).map(([layer,color])=>`<tspan fill="${color}">${layer} </tspan>`).join('')} · Each panel fits its own boundary; profiles are not shown at equal scale.</text></svg>`
writeFileSync('previews/exit-directions.svg',svg)
writeFileSync('previews/exit-directions.png',new Resvg(svg).render().asPng())
console.log(`Rendered ${profiles.length} DDR direction diagrams and comparison`)