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-board-rules.ts
import { AM3352_BALL_MAP } from './pin-map'
import { ddrNetClass } from './ddr'
/** Board-level rules. Measurements include CPU escape + host + RAM escape.
* This evaluator checks supplied evidence; it does not extract geometry or run SI. */
export const DDR_BOARD_RULES = {
source: 'https://www.ti.com/lit/ds/sprs717l/sprs717l.pdf',
memorySource: 'https://www.micron.com/products/memory/dram-components/ddr3-sdram/part-catalog/part-detail/mt41k512m8da-107-it-p',
memoryPart: 'MT41K512M8DA-107 IT:P', memoryCount: 2, capacityBytes: 1073741824,
designClockMHz: 400, transferRateMTps: 800, supplyV: 1.5,
maximumTurnDegrees: 45, // User routing requirement, not a TI electrical guarantee.
byteSkewMm: .635, pairSkewMm: .127,
a1a2MaxMm: 63.5, a1a2SkewMm: .635, a3MaxMm: 16.764,
nonmirroredA3SkewMm: 3.175, asMaxMm: 2.54, asSkewMm: .635,
ckAsMaxMm: 1.778, ckAsSkewMm: .127, atPreferredMaxMm: 12.7,
ckAtSkewMm: .127, caclmToleranceMm: 1.27,
reducedSpacingMaxMm: 31.75, minWidthMm: .1016,
} as const
export interface BoardRouteMeasurement {
signal: string
/** CPU and RAM source-pad selectors, proven connected by copper extraction. */
connectedMemoryBytes: number[]
fullLengthMm: number
minimumWidthMm: number
maxTurnDegrees: number
/** Union length below applicable nominal 3w/4w spacing; do not double-count overlaps. */
reducedSpacingLengthMm: number
/** CK/ADDR_CTRL segments named exactly as TI Figures 7-54…7-61. */
addressSegments?: { a1a2Mm: number; a3Mm: number; asMm: number[]; atMm: number }
}
export const DDR_EVIDENCE_GATES = [
'completeCopperConnectivity', 'allLayerClearance', 'minimumSpacingAndPairCoupling',
'ddrKeepoutAndPlacement', 'continuousReferencePlanes', 'referenceTransitionReturns',
'fabricatorImpedance', 'viaDelayAndStubs', 'terminationAndOdt',
'vrefVttZqReset', 'powerAndDecoupling', 'controllerConfiguration', 'electricalTimingAndSi',
] as const
export type DdrEvidenceGate = typeof DDR_EVIDENCE_GATES[number]
export interface DdrEvidence {
status: 'pass' | 'fail' | 'unverified'
/** Specific extracted report / simulation / review artifact, not a declaration of intent. */
artifact: string
}
export interface DdrBoardMeasurements {
routes: readonly BoardRouteMeasurement[]
clockMHz?: number
/** Chosen clock requires exact CPU ordering code and RAM timing register validation. */
cpuOrderingCode?: string
memoryPart?: string
memoryCount?: number
/** Same-side two-x8 topology in this acceptance model. */
topology?: 'two-x8-same-side'
/** Longest CK/ADDR_CTRL pad-to-pad Manhattan path + 7.62 mm (TI Figure 7-66). */
caclmMm?: number
/** Longest connected data/DM ball-pair Manhattan distance per byte (TI Fig7-67/Table7-69). */
dqlmMm?: readonly [number, number]
evidence?: Partial<Record<DdrEvidenceGate, DdrEvidence>>
}
export function auditDdrBoard(input: DdrBoardMeasurements) {
const failures: { code: string; signal?: string; message: string }[] = []
const fail = (code: string, message: string, signal?: string) => failures.push({ code, message, signal })
const valid = (n: unknown): n is number => typeof n === 'number' && Number.isFinite(n) && n >= 0
const limit = (n: unknown, max: number, code: string, signal?: string) => {
if (!valid(n) || n > max + 1e-6) fail(code, `Expected a finite measurement in [0, ${max}] mm/degrees`, signal)
}
const spread = (values: number[], max: number, code: string) => {
if (!values.length || values.some(v => !valid(v))) fail(code, 'Missing or invalid length measurements')
else limit(Math.max(...values) - Math.min(...values), max, code)
}
if (!input.cpuOrderingCode?.trim()) fail('cpu-ordering-code', 'Exact CPU speed grade is required')
if (!valid(input.clockMHz) || input.clockMHz <= 0 || input.clockMHz > 400) fail('clock', 'Select a supported CPU clock no greater than 400 MHz; validate RAM timing configuration separately')
if (input.memoryPart !== DDR_BOARD_RULES.memoryPart || input.memoryCount !== 2) fail('memory', 'This acceptance model requires two MT41K512M8DA-107 IT:P x8 parts, one rank / 1 GiB')
if (input.topology !== 'two-x8-same-side') fail('topology', 'This model covers TI same-side two-x8 branched/fly-by CK/address topology')
const expected = Object.values(AM3352_BALL_MAP).filter(n => ddrNetClass(n))
for (const name of expected) {
const matches = input.routes.filter(r => r.signal === name)
if (matches.length !== 1) { fail('coverage', `Expected exactly one complete route measurement; found ${matches.length}`, name); continue }
const r = matches[0]!, cls = ddrNetClass(name)!
const destinations = cls === 'DQ0' || cls === 'DQS0' ? [0] : cls === 'DQ1' || cls === 'DQS1' ? [1] : [0, 1]
if (JSON.stringify([...r.connectedMemoryBytes].sort()) !== JSON.stringify(destinations)) fail('destinations', `Expected memory byte destinations ${destinations.join(',')}`, name)
if (!valid(r.fullLengthMm) || r.fullLengthMm <= 0) fail('full-length', 'Missing complete pad-to-pad copper length', name)
if (!valid(r.minimumWidthMm) || r.minimumWidthMm < .1016 - 1e-6) fail('width', 'DDR minimum width is 0.1016 mm', name)
limit(r.maxTurnDegrees, 45, 'bend', name)
limit(r.reducedSpacingLengthMm, 31.75, 'reduced-spacing-budget', name)
if (cls === 'CK' || cls === 'ADDR_CTRL') {
const s = r.addressSegments
if (!s) { fail('address-segments', 'Missing A1+A2 / A3 / AS / AT topology measurements', name); continue }
limit(s.a1a2Mm, 63.5, 'a1a2-length', name)
limit(s.a3Mm, 16.764, 'a3-length', name)
// AT=500 mil is a recommended maximum explicitly permitted to increase by TI note 6.
if (!valid(s.atMm)) fail('at-length', 'Missing terminator stub length', name)
if (s.asMm.length !== 2) fail('as-coverage', 'Measure each of the two device stubs', name)
for (const length of s.asMm) limit(length, cls === 'CK' ? 1.778 : 2.54, 'as-length', name)
if (!valid(input.caclmMm) || Math.abs(s.a1a2Mm + s.a3Mm - input.caclmMm) > 1.27 + 1e-6) fail('caclm', 'Main path must follow CACLM ±1.27 mm; exclude device and terminator stubs', name)
}
}
for (const r of input.routes) if (!expected.includes(r.signal)) fail('unexpected-timed-net', 'Supply, RESET and VREF require separate electrical checks, not timed-bus membership', r.signal)
for (const byte of [0, 1]) {
const dq = input.routes.filter(r => ddrNetClass(r.signal) === `DQ${byte}`)
const dqs = input.routes.filter(r => ddrNetClass(r.signal) === `DQS${byte}`)
const nominal = input.dqlmMm?.[byte]
if (!valid(nominal) || nominal <= 0) fail(`byte-${byte}-dqlm`, 'Missing placement-derived nominal byte length')
else for (const route of dq) limit(route.fullLengthMm, nominal, `byte-${byte}-nominal-length`, route.signal)
spread(dq.map(r => r.fullLengthMm), .635, `byte-${byte}-dq-skew`)
spread(dqs.map(r => r.fullLengthMm), .127, `byte-${byte}-dqs-skew`)
// Conservative: both strobe legs, DQ and DM fit one 25-mil envelope.
spread([...dq, ...dqs].map(r => r.fullLengthMm), .635, `byte-${byte}-dq-dqs-skew`)
}
const ca = input.routes.filter(r => ['CK', 'ADDR_CTRL'].includes(ddrNetClass(r.signal) ?? '') && r.addressSegments)
spread(ca.map(r => r.addressSegments!.a1a2Mm), .635, 'a1a2-skew')
spread(ca.map(r => r.addressSegments!.a3Mm), 3.175, 'a3-skew')
spread(ca.flatMap(r => r.addressSegments!.asMm), .635, 'as-skew')
const ck = ca.filter(r => ddrNetClass(r.signal) === 'CK')
for (const stub of [0, 1]) spread(ck.map(r => r.addressSegments!.asMm[stub]!), .127, `ck-as-${stub}-skew`)
spread(ck.map(r => r.addressSegments!.atMm), .127, 'ck-at-skew')
const gates = DDR_EVIDENCE_GATES.map(name => {
const evidence = input.evidence?.[name]
return { name, status: evidence?.artifact?.trim() ? evidence.status : 'unverified' as const, artifact: evidence?.artifact ?? '' }
})
return {
scope: 'Evaluation of supplied complete-board measurements and evidence; not a simulator or copper extractor',
measuredRulesPass: failures.length === 0,
acceptanceEvidenceComplete: failures.length === 0 && gates.every(g => g.status === 'pass'),
failures, gates,
recommendations: ca.filter(r => r.addressSegments!.atMm > 12.7).map(r => `${r.signal}: minimize AT (${r.addressSegments!.atMm} mm); TI permits extension beyond 12.7 mm`),
}
}