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

tests/am62l-free-space-fanout.test.ts

import { expect, test } from "bun:test"
import type { SimpleRouteJson } from "@tscircuit/core"
import { AM62L_FREE_SPACE_FANOUT_PHASES } from "../autorouting/am62l-free-space-fanout"
import { solveAm62lFreeSpaceFanout } from "../autorouting/solve-am62l-free-space-fanout"

const loadFixture = async (name: string) =>
  (await Bun.file(
    `${import.meta.dir}/fixtures/${name}`,
  ).json()) as SimpleRouteJson

const withManufacturableThroughVias = (input: SimpleRouteJson) => ({
  ...input,
  minTraceToPadEdgeClearance: 0.08128,
  minViaEdgeToPadEdgeClearance: 0.08128,
  minViaPadDiameter: 0.4572,
  min_via_pad_diameter: 0.4572,
  minViaHoleDiameter: 0.2032,
  min_via_hole_diameter: 0.2032,
})

const verifyFullFanout = async (
  fixtureName: string,
  expectedDirection: "left" | "right",
) => {
  const input = withManufacturableThroughVias(await loadFixture(fixtureName))
  const phases: string[] = []
  const result = solveAm62lFreeSpaceFanout(input, (event) => {
    phases.push(event.phase)
  })

  expect(input.connections).toHaveLength(33)
  expect(input.differentialPairs ?? []).toEqual([])
  expect(result.traces).toHaveLength(33)
  expect(phases).toEqual([...AM62L_FREE_SPACE_FANOUT_PHASES])

  const outputByName = new Map(
    result.outputSimpleRouteJson.connections.map((connection) => [
      connection.name,
      connection,
    ]),
  )
  const traceByName = new Map(
    result.traces.map((trace) => [trace.connection_name, trace]),
  )
  for (const inputConnection of input.connections) {
    const source = inputConnection.pointsToConnect.find(
      (point) => point.pcb_port_id !== undefined,
    )!
    const target = inputConnection.pointsToConnect.find((point) =>
      point.pointId?.startsWith("pcb_breakout_point"),
    )!
    expect(
      expectedDirection === "right" ? target.x > source.x : target.x < source.x,
    ).toBe(true)

    const outputConnection = outputByName.get(inputConnection.name)!
    expect(outputConnection.source_trace_id).toBe(
      inputConnection.source_trace_id,
    )
    const outputTarget = outputConnection.pointsToConnect.find(
      (point) => point.pointId === target.pointId,
    )!
    expect(outputTarget.pointId).toBe(target.pointId)
    expect(outputTarget.layer).toBe(target.layer)
    expect(outputTarget.x).toBeCloseTo(target.x, 6)
    expect(outputTarget.y).toBeCloseTo(target.y, 6)

    const trace = traceByName.get(inputConnection.name)!
    expect(trace.connectsTo).toContain(target.pointId)
    const vias = trace.route.filter((point) => point.route_type === "via")
    expect(vias).toHaveLength(1)
    expect(vias[0]).toMatchObject({
      via_diameter: 0.4572,
      via_hole_diameter: 0.2032,
      from_layer: "top",
      to_layer: "bottom",
    })
    for (const pad of input.obstacles.filter(
      (obstacle) =>
        Boolean(obstacle.componentId) && obstacle.layers.includes("top"),
    )) {
      const centerDistance = Math.hypot(
        vias[0]!.x - pad.center.x,
        vias[0]!.y - pad.center.y,
      )
      expect(centerDistance).toBeGreaterThanOrEqual(
        Math.max(pad.width, pad.height) / 2 +
          0.4572 / 2 +
          (input.minViaEdgeToPadEdgeClearance ?? 0) -
          1e-6,
      )
    }
    const finalWire = [...trace.route]
      .reverse()
      .find((point) => point.route_type === "wire")!
    expect(finalWire.layer).toBe(target.layer)
    expect(finalWire.x).toBeCloseTo(target.x, 6)
    expect(finalWire.y).toBeCloseTo(target.y, 6)

    const wires = trace.route.filter((point) => point.route_type === "wire")
    for (let index = 1; index < wires.length; index++) {
      if (wires[index - 1]!.layer !== wires[index]!.layer) continue
      const dx = Math.abs(wires[index]!.x - wires[index - 1]!.x)
      const dy = Math.abs(wires[index]!.y - wires[index - 1]!.y)
      expect(dx <= 1e-6 || dy <= 1e-6 || Math.abs(dx - dy) <= 1e-6).toBe(true)
    }
    for (let index = 1; index < wires.length - 1; index++) {
      if (
        wires[index - 1]!.layer !== wires[index]!.layer ||
        wires[index]!.layer !== wires[index + 1]!.layer
      ) {
        continue
      }
      const incoming = {
        x: wires[index]!.x - wires[index - 1]!.x,
        y: wires[index]!.y - wires[index - 1]!.y,
      }
      const outgoing = {
        x: wires[index + 1]!.x - wires[index]!.x,
        y: wires[index + 1]!.y - wires[index]!.y,
      }
      const dot = incoming.x * outgoing.x + incoming.y * outgoing.y
      expect(Math.abs(dot)).toBeGreaterThan(1e-9)
    }
  }

  expect(
    result.outputSimpleRouteJson.buses?.map((bus) => ({
      busId: bus.busId,
      connectionNames: bus.connectionNames,
    })),
  ).toEqual(
    input.buses?.map((bus) => ({
      busId: bus.busId,
      connectionNames: bus.connectionNames,
    })),
  )
}

test(
  "routes all 33 right-escape AM62L connections to exact ordered Core targets",
  () => verifyFullFanout("am62l-soc-fanout.srj.json", "right"),
  30_000,
)

test(
  "routes all 33 left-escape LPDDR4 connections to exact ordered Core targets",
  () => verifyFullFanout("lpddr4-ram-fanout.srj.json", "left"),
  30_000,
)