0hmX/am62l-lpddr4-ti-pth-router

This code defines a hardware-oriented React/TypeScript setup for a PCB breakout circuit featuring an AM62L LPDDR4 interface, including detailed PCB component placement, routing algorithms, and validation scripts for DDR connections, via placement, and manufacturing constraints.

Version
1.0.1
License
unset
Stars
0

single-ddr-bus-layout.tsx

import { MT53E1G16D1ZW, ballMap } from "@tsci/0hmX.mt53e1g16d1zw-footprint"
import { AM62L32, AM62L32BOGHAANBR } from "@tsci/tscircuit.ti"
import { Children, cloneElement, Fragment, isValidElement } from "react"
import { DDR_CONNECTIONS, type DdrBusName } from "./ddr-connections"

const AM62L_BALL_PITCH_MM = 0.5
const REQUIRED_ESCAPE_CHANNEL_MM = 0.24384
const AM62L_PAD_DIAMETER_MM =
  AM62L_BALL_PITCH_MM - REQUIRED_ESCAPE_CHANNEL_MM
const AM62L_PAD_RADIUS_MM = AM62L_PAD_DIAMETER_MM / 2

const createAm62lEscapeFootprint = () => {
  const defaultChip = AM62L32BOGHAANBR({} as any)
  const defaultFootprint = defaultChip.props.footprint

  return cloneElement(
    defaultFootprint,
    {},
    Children.map(defaultFootprint.props.children, (child) =>
      isValidElement(child) && child.type === "smtpad"
        ? cloneElement(child, { radius: `${AM62L_PAD_RADIUS_MM}mm` } as any)
        : child,
    ),
  )
}

const AM62L_ESCAPE_FOOTPRINT = createAm62lEscapeFootprint()

const SIGNAL_LAYERS = [
  "top",
  "inner1",
  "inner2",
  "inner3",
  "inner4",
  "inner5",
  "inner6",
  "bottom",
] as const

const BUS_CONFIGS = {
  DDR_BYTE0: {
    socPins: [76, 91, 92, 93, 94, 103, 104, 105, 121, 122, 123],
    socDirection: "bottom_right",
    ramDirection: "bottom_left",
    fanoutGroups: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]],
    fanoutLayers: [undefined],
  },
  DDR_BYTE1: {
    socPins: [236, 238, 255, 256, 257, 272, 273, 275, 276, 284, 285],
    socDirection: "top_right",
    ramDirection: "top_left",
    // Group by physical AM62L ball row so each group can share one layer.
    fanoutGroups: [
      [1, 2],
      [3, 4, 6],
      [0, 5, 9, 10],
      [7, 8],
    ],
    fanoutLayers: ["inner2", "bottom", "inner3", "inner2"],
  },
  DDR_ADDR_CTRL: {
    socPins: [124, 125, 139, 140, 149, 150, 162, 164, 165, 215, 216],
    socDirection: "center_right",
    ramDirection: "center_left",
    fanoutGroups: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]],
    fanoutLayers: [undefined],
  },
} as const

export const SingleDdrBusCircuit = ({ busName }: { busName: DdrBusName }) => {
  const config = BUS_CONFIGS[busName]
  const breakoutPadding = busName === "DDR_BYTE1" ? "2mm" : "1mm"
  const connections = DDR_CONNECTIONS.filter(
    (connection) => connection.busName === busName,
  )
  const fanoutBuses = config.fanoutGroups.map((connectionIndices, index) => ({
    name: `${busName}_FANOUT_${index}`,
    connections: connectionIndices.map(
      (connectionIndex) => connections[connectionIndex]!.traceName,
    ),
    preferredLayer: config.fanoutLayers[index],
    routingPhaseIndex: index,
  }))
  const socFanoutDirections = Object.fromEntries(
    fanoutBuses.map((fanoutBus) => [
      fanoutBus.name,
      config.socDirection,
    ]),
  )
  const ramFanoutDirections = Object.fromEntries(
    fanoutBuses.map((fanoutBus) => [
      fanoutBus.name,
      config.ramDirection,
    ]),
  )

  const usedSocPins = new Set<number>(config.socPins)
  const socNoConnect = Array.from({ length: 373 }, (_, index) => index + 1)
    .filter((pin) => !usedSocPins.has(pin))
    .map((pin) => `pin${pin}`)

  const usedMemorySignals = new Set(
    connections.map(({ memorySignal }) => memorySignal),
  )
  const memoryNoConnect = ballMap
    .map(({ signal }, index) => ({ signal, selector: `pin${index + 1}` }))
    .filter(({ signal }) => !usedMemorySignals.has(signal))
    .map(({ selector }) => selector)

  return (
    <board
      name={`AM62L_LPDDR4_${busName}_ONLY`}
      width="45.2mm"
      height="25mm"
      layers={8}
      defaultTraceWidth="0.08128mm"
      minTraceWidth="0.08128mm"
      minTraceToPadEdgeClearance="0.05mm"
      minViaEdgeToPadEdgeClearance="0.08128mm"
      minViaHoleEdgeToViaHoleEdgeClearance="0.1016mm"
      minViaHoleDiameter="0.1mm"
      minViaPadDiameter="0.24mm"
      pcbStyle={{ viaHoleDiameter: "0.1mm", viaPadDiameter: "0.24mm" }}
      autorouter="beta_pipeline9"
      autorouterEffortLevel="1x"
    >
      <breakout
        name="SOC_BREAKOUT"
        pcbX={-10}
        pcbY={0}
        padding={breakoutPadding}
        fanoutRoutingLayers={[...SIGNAL_LAYERS]}
        busFanoutDirections={socFanoutDirections}
      >
        <AM62L32
          name="U1"
          footprintVariant="fccsp_373_anb"
          footprint={AM62L_ESCAPE_FOOTPRINT}
          pcbX={0}
          pcbY={0}
          pcbRotation={180}
          noSchematicRepresentation
          noConnect={socNoConnect as any}
        />
      </breakout>

      <breakout
        name="RAM_BREAKOUT"
        pcbX={10.116917}
        pcbY={-0.050917}
        padding={breakoutPadding}
        fanoutRoutingLayers={[...SIGNAL_LAYERS]}
        busFanoutDirections={ramFanoutDirections}
      >
        <MT53E1G16D1ZW
          name="U2"
          pcbX={0}
          pcbY={0}
          pcbRotation={90}
          noSchematicRepresentation
          noConnect={memoryNoConnect as any}
        />
      </breakout>

      {/* Each shared bus is consumed by both breakout phases, so this single
          preference assigns matching SoC and RAM escape layers. */}
      {fanoutBuses.map((fanoutBus) => (
        <Fragment key={fanoutBus.name}>
          <bus
            name={fanoutBus.name}
            connections={fanoutBus.connections}
            preferredLayer={fanoutBus.preferredLayer}
            routingPhaseIndex={fanoutBus.routingPhaseIndex}
          />
        </Fragment>
      ))}

      {connections.map(({ socSignal, memorySignal, traceName }) => (
        <Fragment key={traceName}>
          <trace
            name={traceName}
            from={`U1.${socSignal}`}
            to={`U2.${memorySignal}`}
          />
        </Fragment>
      ))}
    </board>
  )
}