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",
)