0hmX/am62l-lpddr4-breakout-repro
This code defines the physical layout, pin assignments, and power connections for the TI AM62L system-on-chip with DDR4/LPDDR4 memory, including detailed component placement, decoupling capacitors, and signal traces for a 6-layer PCB design.
- Version
- 1.0.14
- License
- unset
- Stars
- 0
am62l-connectivity.ts
import { AM62L_BALLS } from "./am62l-ball-data"
import { DDR_CONNECTIONS } from "./ddr-connections"
export type Am62lBall = (typeof AM62L_BALLS)[number]
function assertModel(condition: boolean, message: string): asserts condition {
if (!condition) throw new Error(`AM62L connectivity assertion failed: ${message}`)
}
const isPowerOrLocalCap = ({ signal }: Am62lBall) =>
/^(VDD|VDDA|VDDS|VDDSHV|CAP_|VPP$)/.test(signal)
export const SOC_GROUND_BALLS = AM62L_BALLS.filter(({ signal }) =>
signal.startsWith("VSS"),
)
export const SOC_POWER_BALLS = AM62L_BALLS.filter(isPowerOrLocalCap)
export const SOC_DDR_BALLS = AM62L_BALLS.filter(({ signal }) =>
signal.startsWith("DDR0_"),
)
const directDdrSignals = new Set(
DDR_CONNECTIONS.map(({ socSignal }) => socSignal),
)
export const SOC_DDR_DIRECT_BALLS = SOC_DDR_BALLS.filter(({ signal }) =>
directDdrSignals.has(signal),
)
export const SOC_DDR_CAL_BALL = SOC_DDR_BALLS.find(
({ signal }) => signal === "DDR0_CAL0",
)!
// LPDDR4 uses CA0..CA5 rather than the unused DDR4-style address/control balls.
export const SOC_LPDDR4_UNUSED_DDR_BALLS = SOC_DDR_BALLS.filter(
({ signal }) => !directDdrSignals.has(signal) && signal !== "DDR0_CAL0",
)
export const SOC_RSVD_BALL = AM62L_BALLS.find(
({ ball, signal }) => ball === "AB17" && signal === "RSVD0",
)!
export const SOC_NC_BALLS = [
...SOC_LPDDR4_UNUSED_DDR_BALLS,
SOC_RSVD_BALL,
] as const
export const SOC_FUNCTIONAL_IO_BALLS = AM62L_BALLS.filter(
(ball) =>
!ball.signal.startsWith("VSS") &&
!ball.signal.startsWith("DDR0_") &&
!isPowerOrLocalCap(ball) &&
ball.signal !== "RSVD0",
)
export type EndpointGroupId =
| "MMC0_EMMC"
| "MMC12"
| "OSPI_GPMC"
| "RGMII1"
| "RGMII2_MDIO"
| "USB0"
| "USB1"
| "DSI"
| "MCASP_SPI"
| "SERIAL_ADC"
| "JTAG_RESET_WAKE"
export const ENDPOINT_SPECS: Record<
EndpointGroupId,
{
ref: string
capacity: number
sortAxis: "pcbX" | "pcbY"
sortDirection: 1 | -1
}
> = {
MMC0_EMMC: { ref: "U10", capacity: 64, sortAxis: "pcbX", sortDirection: 1 },
MMC12: { ref: "J1", capacity: 20, sortAxis: "pcbX", sortDirection: 1 },
OSPI_GPMC: { ref: "U11", capacity: 48, sortAxis: "pcbX", sortDirection: 1 },
RGMII1: { ref: "U15", capacity: 32, sortAxis: "pcbY", sortDirection: -1 },
RGMII2_MDIO: { ref: "U16", capacity: 32, sortAxis: "pcbY", sortDirection: -1 },
USB0: { ref: "U13", capacity: 20, sortAxis: "pcbY", sortDirection: 1 },
USB1: { ref: "U14", capacity: 20, sortAxis: "pcbY", sortDirection: 1 },
DSI: { ref: "U12", capacity: 32, sortAxis: "pcbX", sortDirection: -1 },
MCASP_SPI: { ref: "U17", capacity: 20, sortAxis: "pcbX", sortDirection: -1 },
SERIAL_ADC: { ref: "U18", capacity: 48, sortAxis: "pcbX", sortDirection: -1 },
JTAG_RESET_WAKE: { ref: "J2", capacity: 26, sortAxis: "pcbX", sortDirection: -1 },
}
const endpointGroupForSignal = (signal: string): EndpointGroupId | null => {
if (/^MMC0_/.test(signal)) return "MMC0_EMMC"
if (/^MMC[12]_/.test(signal)) return "MMC12"
if (/^(OSPI0_|GPMC0_)/.test(signal)) return "OSPI_GPMC"
if (/^RGMII1_/.test(signal)) return "RGMII1"
if (/^(RGMII2_|MDIO0_)/.test(signal)) return "RGMII2_MDIO"
if (/^USB0_/.test(signal)) return "USB0"
if (/^USB1_/.test(signal)) return "USB1"
if (/^DSI0_/.test(signal)) return "DSI"
if (/^(MCASP0_|SPI0_)/.test(signal)) return "MCASP_SPI"
if (/^(I2C[0-2]_|UART0_|MCAN0_|ADC0_)/.test(signal)) return "SERIAL_ADC"
if (/^(WKUP_OSC0_|LFOSC0_|PMIC_)/.test(signal)) return null
return "JTAG_RESET_WAKE"
}
export const ENDPOINT_GROUP_BALLS = Object.fromEntries(
(Object.keys(ENDPOINT_SPECS) as EndpointGroupId[]).map((groupId) => {
const spec = ENDPOINT_SPECS[groupId]
const balls = SOC_FUNCTIONAL_IO_BALLS.filter(
({ signal }) => endpointGroupForSignal(signal) === groupId,
).sort(
(a, b) =>
(a[spec.sortAxis] - b[spec.sortAxis]) * spec.sortDirection ||
a.pin - b.pin,
)
return [groupId, balls]
}),
) as Record<EndpointGroupId, Am62lBall[]>
export type FunctionalIoAssignment = {
sourceSignal: string
sourceBall: string
sourcePin: number
endpointGroup: EndpointGroupId | "CLOCKS" | "PMIC"
targetRef: string
targetSignal: string
targetPin: number
}
export const PACKAGE_ENDPOINT_ASSIGNMENTS: FunctionalIoAssignment[] = (
Object.keys(ENDPOINT_SPECS) as EndpointGroupId[]
).flatMap((groupId) => {
const spec = ENDPOINT_SPECS[groupId]
return ENDPOINT_GROUP_BALLS[groupId].map((ball, index) => ({
sourceSignal: ball.signal,
sourceBall: ball.ball,
sourcePin: ball.pin,
endpointGroup: groupId,
targetRef: spec.ref,
targetSignal: ball.signal,
targetPin: index + 1,
}))
})
export const SPECIAL_ENDPOINT_ASSIGNMENTS: FunctionalIoAssignment[] = [
{
sourceSignal: "WKUP_OSC0_XI",
sourceBall: "AC18",
sourcePin: 368,
endpointGroup: "CLOCKS",
targetRef: "Y1",
targetSignal: "pin1",
targetPin: 1,
},
{
sourceSignal: "WKUP_OSC0_XO",
sourceBall: "AC17",
sourcePin: 367,
endpointGroup: "CLOCKS",
targetRef: "Y1",
targetSignal: "pin2",
targetPin: 2,
},
{
sourceSignal: "LFOSC0_XI",
sourceBall: "AC21",
sourcePin: 371,
endpointGroup: "CLOCKS",
targetRef: "Y2",
targetSignal: "pin1",
targetPin: 1,
},
{
sourceSignal: "LFOSC0_XO",
sourceBall: "AC20",
sourcePin: 370,
endpointGroup: "CLOCKS",
targetRef: "Y2",
targetSignal: "pin2",
targetPin: 2,
},
{
sourceSignal: "PMIC_LPM_EN0",
sourceBall: "AA18",
sourcePin: 324,
endpointGroup: "PMIC",
targetRef: "U3",
targetSignal: "EN",
targetPin: 2,
},
]
export const FUNCTIONAL_IO_ASSIGNMENTS = [
...PACKAGE_ENDPOINT_ASSIGNMENTS,
...SPECIAL_ENDPOINT_ASSIGNMENTS,
]
export const SOC_CONNECTED_BALLS = AM62L_BALLS.filter(
({ pin }) => !new Set(SOC_NC_BALLS.map((ball) => ball.pin)).has(pin),
)
assertModel(AM62L_BALLS.length === 373, `expected 373 balls, got ${AM62L_BALLS.length}`)
assertModel(SOC_GROUND_BALLS.length === 97, `expected 97 VSS balls, got ${SOC_GROUND_BALLS.length}`)
assertModel(SOC_POWER_BALLS.length === 50, `expected 50 supply/local-CAP balls, got ${SOC_POWER_BALLS.length}`)
assertModel(SOC_DDR_BALLS.length === 51, `expected 51 DDR-labelled balls, got ${SOC_DDR_BALLS.length}`)
assertModel(SOC_DDR_DIRECT_BALLS.length === 33, `expected 33 direct DDR balls, got ${SOC_DDR_DIRECT_BALLS.length}`)
assertModel(SOC_LPDDR4_UNUSED_DDR_BALLS.length === 17, `expected 17 LPDDR4-unused DDR balls, got ${SOC_LPDDR4_UNUSED_DDR_BALLS.length}`)
assertModel(SOC_FUNCTIONAL_IO_BALLS.length === 174, `expected 174 functional I/O balls, got ${SOC_FUNCTIONAL_IO_BALLS.length}`)
assertModel(SOC_NC_BALLS.length === 18, `expected 18 NC balls, got ${SOC_NC_BALLS.length}`)
assertModel(SOC_CONNECTED_BALLS.length === 355, `expected 355 connected balls, got ${SOC_CONNECTED_BALLS.length}`)
for (const groupId of Object.keys(ENDPOINT_SPECS) as EndpointGroupId[]) {
const used = ENDPOINT_GROUP_BALLS[groupId].length
const capacity = ENDPOINT_SPECS[groupId].capacity
assertModel(used <= capacity, `${groupId} uses ${used}/${capacity} endpoint pins`)
}
const assignedSourceSignals = FUNCTIONAL_IO_ASSIGNMENTS.map(
({ sourceSignal }) => sourceSignal,
)
const consumedEndpointPins = FUNCTIONAL_IO_ASSIGNMENTS.map(
({ targetRef, targetPin }) => `${targetRef}.pin${targetPin}`,
)
assertModel(FUNCTIONAL_IO_ASSIGNMENTS.length === 174, `expected 174 functional assignments, got ${FUNCTIONAL_IO_ASSIGNMENTS.length}`)
assertModel(new Set(assignedSourceSignals).size === 174, "functional source assignment is duplicated")
assertModel(new Set(consumedEndpointPins).size === 174, "endpoint pin consumption is duplicated")
assertModel(
SOC_FUNCTIONAL_IO_BALLS.every(({ signal }) =>
assignedSourceSignals.includes(signal),
),
"one or more functional I/O balls are unassigned",
)