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

src/profiles.ts

import { AM3352_POWER_PLANES } from './layout'
import boundsJson from './generated/profile-bounds.json'

export interface AM3352Bounds { minX:number; maxX:number; minY:number; maxY:number }
function withBounds<T extends {fanoutSizeMm:number;packageSizeMm:number;paddingMm:number}>(profile:string,settings:T) {
 const half=settings.fanoutSizeMm/2
 const bounds:AM3352Bounds=(boundsJson as Record<string,AM3352Bounds>)[profile]??{minX:-half,maxX:half,minY:-half,maxY:half}
 if(!Object.values(bounds).every(Number.isFinite)||bounds.minX>=bounds.maxX||bounds.minY>=bounds.maxY)throw Error(`Invalid ${profile} fanout bounds`)
 const mm=(value:number)=>Number(value.toFixed(9))
 const fanoutWidthMm=mm(bounds.maxX-bounds.minX),fanoutHeightMm=mm(bounds.maxY-bounds.minY),bodyHalf=settings.packageSizeMm/2
 const padding={left:mm(-bounds.minX-bodyHalf),right:mm(bounds.maxX-bodyHalf),top:mm(bounds.maxY-bodyHalf),bottom:mm(-bounds.minY-bodyHalf)}
 return {...settings,fanoutBounds:{...bounds},fanoutWidthMm,fanoutHeightMm,padding,fanoutSizeMm:Math.max(fanoutWidthMm,fanoutHeightMm),paddingMm:Math.max(...Object.values(padding))}
}

type CopperLayer = 'top' | 'bottom' | 'inner1' | 'inner2' | 'inner3' | 'inner4' | 'inner5' | 'inner6' | 'inner7' | 'inner8'
const native = {
  description: 'AM3352 ZCZ324, six layers, through-hole vias, bottom decoupling',
  layerCount: 6 as const,
  packageSizeMm: 15,
  fanoutSizeMm: 17,
  paddingMm: 1,
  viaPadDiameterMm: 0.4,
  viaHoleDiameterMm: 0.2,
  allowBlindAndBuriedVias: false,
  traceWidthMm: 0.08128,
  clearanceMm: 0.0762,
  savedPathCount: 313,
  signalExitCount: 201,
  capacitorCount: 82,
  differentialPairSkewMm: 0.1,
  powerPlaneLayers: Object.fromEntries(AM3352_POWER_PLANES.map(p => [p.netName, p.layer])) as Record<string, CopperLayer>,
  additionalGroundLayers: [] as readonly CopperLayer[],
}
const ddr = {
  ...native,
  layerCount: 10 as const,
  fanoutSizeMm: 25,
  paddingMm: 5,
  viaPadDiameterMm: 0.4572,
  viaHoleDiameterMm: 0.254,
  traceWidthMm: 0.1016,
  clearanceMm: 0.1016,
  powerPlaneLayers: {
    VCC_IO_3V3: 'top', GND: 'inner1', VCC_DDR_1V5: 'inner3',
    VDD_CORE_1V1: 'inner8', VDD_MPU_1V26: 'inner7', VCC_1V8: 'bottom',
  } as Record<string, CopperLayer>,
  additionalGroundLayers: ['inner5'] as readonly CopperLayer[],
}
/** Geometry contracts for the independently checked DDR caches. */
export const DDR_PROFILE_DEFINITIONS = {
  ddr_left: { ...ddr, description: 'AM3352 ten-layer DDR routing profile, left exits' },
  ddr_top: { ...ddr, fanoutSizeMm: 40, paddingMm: 12.5, description: 'AM3352 ten-layer DDR routing profile, top exits' },
  ddr_bottom: { ...ddr, fanoutSizeMm: 40, paddingMm: 12.5, description: 'AM3352 ten-layer DDR routing profile, bottom exits' },
} as const

/** Registered profiles have portable saved trace caches. */
export const LAYOUT_PROFILES = { native:withBounds('native',native), ddr_left:withBounds('ddr_left',DDR_PROFILE_DEFINITIONS.ddr_left), ddr_top:withBounds('ddr_top',DDR_PROFILE_DEFINITIONS.ddr_top), ddr_bottom:withBounds('ddr_bottom',DDR_PROFILE_DEFINITIONS.ddr_bottom) } as const
/** Bounds use CPU-center coordinates. Legacy fanoutSizeMm/paddingMm are maximum extents, not symmetric dimensions. */
export function getAM3352Bounds(profile:LayoutProfile='native'):AM3352Bounds { assertLayoutProfile(profile);return {...LAYOUT_PROFILES[profile].fanoutBounds} }
export type LayoutProfile = keyof typeof LAYOUT_PROFILES
export function assertLayoutProfile(value: string): asserts value is LayoutProfile {
  if (!Object.hasOwn(LAYOUT_PROFILES, value)) throw new Error(`Unknown AM3352 layoutProfile '${value}'; only ${Object.keys(LAYOUT_PROFILES).map(name => `'${name}'`).join(', ')} is available`)
}
export function getAM3352Layers(profile: LayoutProfile = 'native'): string[] {
  assertLayoutProfile(profile)
  return ['top', ...Array.from({ length: LAYOUT_PROFILES[profile].layerCount - 2 }, (_, i) => `inner${i + 1}`), 'bottom']
}
export function getAM3352PowerPlanes(profile: LayoutProfile = 'native') {
  assertLayoutProfile(profile)
  const settings = LAYOUT_PROFILES[profile]
  const planes = AM3352_POWER_PLANES.map(plane => {
    const layer = settings.powerPlaneLayers[plane.netName]
    if (!layer) throw Error(`Missing ${plane.netName} power layer in ${profile}`)
    return { ...plane, layer }
  })
  const ground = planes.find(plane => plane.netName === 'GND')!
  return [...planes, ...settings.additionalGroundLayers.map(layer => ({ ...ground, layer, pins: [] as number[] }))]
}