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/ddr-memory-board-profile.tsx

import{Fragment}from'react'
import{fanoutTracePath,type FanoutTracePath}from'@tscircuit/props'
import binding from'./generated/ddr-memory-board-profiles.json'
import pins from'./generated/ddr-board-pin-map.json'
import{MicronJlcFootprint}from'./generated/ddr-board-micron-footprint'
export type DdrMemoryBoardProfileName=keyof typeof binding.profiles
/** Read-only provenance; hashes describe generator inputs, never a DDR signoff. */
function freeze<T>(value:T):Readonly<T>{if(value&&typeof value==='object'){Object.freeze(value);for(const child of Object.values(value))freeze(child)}return value}
export const DDR_MEMORY_BOARD_PROFILE_BINDING=freeze(binding)
export const DDR_MEMORY_BOARD_PINS=freeze(pins)
export function getDdrMemoryBoardPaths(profile:DdrMemoryBoardProfileName):FanoutTracePath[]{
 const selected=binding.profiles[profile];if(!selected||selected.layerSpace!=='physical'||selected.layerCount!==10||selected.coordinateFrame!=='package-center')throw Error('Select an explicit ten-layer physical board profile')
 const paths=structuredClone(selected.paths) as FanoutTracePath[];for(const path of paths)fanoutTracePath.parse(path)
 const routed=pins.filter(p=>p.routed)
 if(paths.length!==72||routed.some(pin=>paths.filter(p=>p.connection===`U1.ball_${pin.ball}`).length!==1))throw Error('Every routed ball must have exactly one physical cache path')
 for(const path of paths){const pin=routed.find(p=>path.connection===`U1.ball_${p.ball}`)!,first=path.route[0];if(first?.route_type!=='wire'||first.layer!=='top'||Math.hypot(Number(first.x)-pin.x,Number(first.y)-pin.y)>1e-7)throw Error('Cache does not start at authoritative package pad')
  for(const p of path.route){if(typeof p.x!=='number'||typeof p.y!=='number'||!Number.isFinite(p.x)||!Number.isFinite(p.y))throw Error('Physical cache coordinates must be finite millimeters');if(p.route_type==='wire'&&!['top','inner2','inner4','inner6','bottom'].includes(String(p.layer)))throw Error('Cache requires a non-signal physical layer');if(p.route_type==='via'&&(p.via_diameter!==.4572||p.via_hole_diameter!==.254))throw Error('Expected preserved conventional via geometry')}
 }
 return paths
}
export interface DdrMemoryBoardProfileProps{
 name:string
 /** Explicit accepted board cache. Already uses physical layers; never adapt it again. */
 boardProfile:DdrMemoryBoardProfileName
 pcbX?:number
 pcbY?:number
 pcbRotation?:number
 schX?:number
 schY?:number
 connections?:Partial<Record<string,string>>
}
/** Portable package/cache replay only. Host must supply global reference planes,
 * support network and the binding's listed board-level power replacements. */
export function DdrMemoryBoardProfile(props:DdrMemoryBoardProfileProps){
 const allowed=new Set(['name','boardProfile','pcbX','pcbY','pcbRotation','schX','schY','connections'])
 if(Object.keys(props).some(k=>!allowed.has(k)))throw Error('Physical board wrapper does not accept host adaptation, cache overrides, or local plane options')
 const{name,boardProfile,connections={},...placement}=props
 if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)||Object.values(placement).some(v=>v!==undefined&&!Number.isFinite(v)))throw Error('Invalid module name or placement')
 if(placement.pcbRotation!==undefined&&![0,90,180,270].includes(placement.pcbRotation))throw Error('Only cardinal package rotations preserve the45-degree cache')
 const routed=pins.filter(p=>p.routed),terminals=new Set(routed.map(p=>p.terminal)),paths=getDdrMemoryBoardPaths(boardProfile),netName=(p:typeof pins[number])=>p.ball==='B2'?`${name}_GND`:`${name}_${p.terminal}`
 for(const terminal of Object.keys(connections))if(!terminals.has(terminal))throw Error(`Unknown RAM terminal ${terminal}`)
 const pinLabels=Object.fromEntries(pins.map(p=>[`pin${p.pin}`,[`ball_${p.ball}`,`SIG_${p.terminal}`]]))
 return <>
  <subcircuit name={name} {...placement} minTraceWidth={.12} minViaPadDiameter={.4572} minViaHoleDiameter={.254}>
   <fanout name="ESCAPE" pcbX={0} pcbY={0} pcbTracePaths={paths}>
    <chip name="U1" manufacturerPartNumber="MT41K512M8DA-107 IT:P" supplierPartNumbers={{jlcpcb:['C2888412']}} pinLabels={pinLabels} pcbX={0} pcbY={0} footprint={<MicronJlcFootprint/>}/>
   </fanout>
   {routed.map(p=><Fragment key={p.ball}><net name={netName(p)} isGroundNet={p.signal==='VSS'||p.signal==='VSSQ'}/><trace name={`${name}_${p.terminal}_escape`} from={`U1.ball_${p.ball}`} to={`net.${netName(p)}`}/></Fragment>)}
  </subcircuit>
  {Object.entries(connections).map(([terminal,target])=>target?<trace key={terminal} name={`${name}_${terminal}_external`} from={`.${name} .U1 > .SIG_${terminal}`} to={target}/>:null)}
 </>
}