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.tsx
import { Fragment, type ReactElement } from 'react'
import { fanoutTracePath, type FanoutTracePath } from '@tscircuit/props'
/** Physical layers of the experimental ten-layer CPU/RAM integration. */
export const DDR_MEMORY_LAYER_MAP = { top: 'top', inner1: 'inner2', inner2: 'inner4', bottom: 'bottom' } as const
export const DDR_MEMORY_SIX_LAYER_MAP = { top: 'top', inner1: 'inner1', inner2: 'inner2', inner3: 'inner4', inner4: 'inner5', bottom: 'bottom' } as const
export type DdrMemoryLayerMap = Readonly<Record<string, string>>
export interface DdrMemoryBoundary { minX: number; maxX: number; minY: number; maxY: number }
export const DDR_MEMORY_VIA = { diameterMm: 0.4572, holeDiameterMm: 0.254 } as const
export interface DdrMemoryPin { ball: string; pin: number; terminal: string; routed: boolean }
/** Accept external memory caches explicitly: no machine-local import or hidden solver. */
export function adaptDdrMemoryPaths(paths: readonly FanoutTracePath[], layerMap: DdrMemoryLayerMap = DDR_MEMORY_LAYER_MAP, traceWidthMm?: number): FanoutTracePath[] {
if (traceWidthMm !== undefined && (!Number.isFinite(traceWidthMm) || traceWidthMm < .1016)) throw new Error('RAM trace width must be at least 0.1016 mm')
const allowed = new Set(['top', 'inner1', 'inner2', 'inner3', 'inner4', 'inner5', 'inner6', 'inner7', 'inner8', 'bottom'])
const entries = Object.entries(layerMap)
if (entries.some(([source, target]) => !allowed.has(source) || !allowed.has(target))) throw new Error('Invalid ten-layer RAM layer mapping')
if (new Set(entries.map(([,target]) => target)).size !== entries.length) throw new Error('RAM layer mapping must preserve distinct copper layers')
if (layerMap.top !== 'top' || layerMap.bottom !== 'bottom') throw new Error('RAM outer copper layers must stay top and bottom')
const mapLayer = (layer: string) => {
const mapped = layerMap[layer]
if (!mapped) throw new Error(`Unsupported original RAM layer '${layer}'`)
return mapped
}
return paths.map(input => {
const path = fanoutTracePath.parse(structuredClone(input))
return fanoutTracePath.parse({ ...path, route: path.route.map(point => {
if (point.route_type === 'wire') return { ...point, layer: mapLayer(point.layer), ...(traceWidthMm === undefined ? {} : { width: traceWidthMm }) }
if (point.route_type === 'via') return {
...point, from_layer: mapLayer(point.from_layer), to_layer: mapLayer(point.to_layer),
via_diameter: DDR_MEMORY_VIA.diameterMm, via_hole_diameter: DDR_MEMORY_VIA.holeDiameterMm,
}
throw new Error('Unsupported RAM route primitive')
}) })
})
}
/** RAM0/RAM1 are byte lanes, not separately selected ranks. */
export function getDdrMemoryConnections(cpuName: string, byte: 0 | 1): Record<string, string> {
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(cpuName)) throw new Error('Use a selector-safe CPU module name')
if (byte !== 0 && byte !== 1) throw new Error('RAM byte must be 0 or 1')
const names: Record<string, string> = {
DM: `DDR_DQM${byte}`, DQS_P: `DDR_DQS${byte}`, DQS_N: `DDR_DQSn${byte}`,
CK_P: 'DDR_CK', CK_N: 'DDR_CKn', CS_N: 'DDR_CSn0', RAS_N: 'DDR_RASn',
CAS_N: 'DDR_CASn', WE_N: 'DDR_WEn', CKE: 'DDR_CKE', ODT: 'DDR_ODT', RESET_N: 'DDR_RESETn',
}
for (let n = 0; n < 8; n++) names[`DQ${n}`] = `DDR_D${byte * 8 + n}`
for (let n = 0; n < 16; n++) names[`A${n}`] = `DDR_A${n}`
for (let n = 0; n < 3; n++) names[`BA${n}`] = `DDR_BA${n}`
return Object.fromEntries(Object.entries(names).map(([terminal, signal]) => [terminal, `.${cpuName} .U1 > .${signal}`]))
}
export interface DdrMemoryAdapterProps {
name: string
/** Original paths supplied by the memory package. */
paths: readonly FanoutTracePath[]
/** Explicit source-to-host mapping; defaults to the legacy four-layer source. */
layerMap?: DdrMemoryLayerMap
/** Explicit physical width override; requires host impedance recalculation. */
traceWidthMm?: number
/** Bounds in RAM package-center coordinates, independent of host board edge. */
boundary?: DdrMemoryBoundary
pins: readonly DdrMemoryPin[]
footprint: ReactElement
pcbX?: number
pcbY?: number
pcbRotation?: number
connections?: Partial<Record<string, string>>
}
/** Experimental ten-layer escape adapter. Requires enlarged-via copper validation;
* it provides neither complete board routing nor impedance/DDR compliance. */
export function DdrMemoryAdapter({ name, paths, pins, footprint, connections = {}, layerMap = DDR_MEMORY_LAYER_MAP, boundary, traceWidthMm, ...placement }: DdrMemoryAdapterProps) {
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) throw new Error('Use a selector-safe RAM module name')
const routed = pins.filter(p => p.routed)
const terminals = new Set(routed.map(p => p.terminal))
for (const terminal of Object.keys(connections)) if (!terminals.has(terminal)) throw new Error(`Unknown RAM terminal '${terminal}'`)
const pinLabels = Object.fromEntries(pins.map(p => [`pin${p.pin}`, [`ball_${p.ball}`, `SIG_${p.terminal}`]]))
const transformed = adaptDdrMemoryPaths(paths, layerMap, traceWidthMm)
if (transformed.length !== routed.length || routed.some(p => transformed.filter(t => t.connection === `U1.ball_${p.ball}`).length !== 1)) {
throw new Error('Every routed RAM ball must have exactly one saved path')
}
if (boundary && (!Object.values(boundary).every(Number.isFinite) || boundary.minX >= boundary.maxX || boundary.minY >= boundary.maxY)) throw new Error('Invalid RAM boundary')
const cx = boundary ? (boundary.minX + boundary.maxX) / 2 : 0
const cy = boundary ? (boundary.minY + boundary.maxY) / 2 : 0
const localPaths = transformed.map(path => ({ ...path, route: path.route.map(point => ({ ...point, x: Number(point.x) - cx, y: Number(point.y) - cy })) }))
return <>
<subcircuit name={name} {...placement} minTraceWidth={traceWidthMm ?? 0.12} minViaPadDiameter={DDR_MEMORY_VIA.diameterMm} minViaHoleDiameter={DDR_MEMORY_VIA.holeDiameterMm}>
<fanout name="ESCAPE" pcbX={cx} pcbY={cy} width={boundary ? boundary.maxX-boundary.minX : undefined} height={boundary ? boundary.maxY-boundary.minY : undefined} pcbTracePaths={localPaths}>
<group pcbX={-cx} pcbY={-cy}>
<chip name="U1" manufacturerPartNumber="MT41K512M8DA-107 IT:P" pinLabels={pinLabels} pcbX={0} pcbY={0} footprint={footprint}/></group>
</fanout>
{routed.map(p => <Fragment key={`net_${p.ball}`}><net name={`${name}_${p.terminal}`}/></Fragment>)}
{routed.map(p => <trace key={p.ball} name={`${name}_${p.terminal}_escape`} from={`U1.ball_${p.ball}`} to={`net.${name}_${p.terminal}`}/>)}
</subcircuit>
{Object.entries(connections).map(([terminal, target]) => target ? <trace key={terminal} name={`${name}_${terminal}_external`} from={`.${name} .U1 > .SIG_${terminal}`} to={target}/> : null)}
</>
}