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
autorouting/am62l-middle-channel.ts
import type {
AutorouterProgressEvent,
GenericLocalAutorouter,
SimpleRouteJson,
SimplifiedPcbTrace,
} from "@tscircuit/core"
import {
InProcessAutorouter,
type InProcessAutorouterResult,
} from "./create-in-process-autorouter"
type Point = { x: number; y: number; layer: string; pointId?: string }
type Segment = { a: Point; b: Point; connectionName: string; width: number }
const EPS = 1e-6
const pointSegmentDistance = (
point: { x: number; y: number },
start: { x: number; y: number },
end: { x: number; y: number },
) => {
const dx = end.x - start.x
const dy = end.y - start.y
const lengthSquared = dx * dx + dy * dy
const progress =
lengthSquared <= EPS
? 0
: Math.max(
0,
Math.min(
1,
((point.x - start.x) * dx + (point.y - start.y) * dy) /
lengthSquared,
),
)
return Math.hypot(
point.x - start.x - progress * dx,
point.y - start.y - progress * dy,
)
}
const segmentDistance = (first: Segment, second: Segment) =>
Math.min(
pointSegmentDistance(first.a, second.a, second.b),
pointSegmentDistance(first.b, second.a, second.b),
pointSegmentDistance(second.a, first.a, first.b),
pointSegmentDistance(second.b, first.a, first.b),
)
const fail = (connectionName: string, message: string): never => {
throw new Error(`[route_middle_channel] ${connectionName}: ${message}`)
}
/**
* Route only the open channel between the two already-completed breakouts.
* The coordinated breakout solver gives both ends of every signal the same
* layer and ordered y coordinate, so this stage must not redo fanout work or
* introduce vias: each connection is the exact horizontal channel segment.
*/
export const solveAm62lMiddleChannel = (
input: SimpleRouteJson,
reportProgress: (event: AutorouterProgressEvent) => void,
): InProcessAutorouterResult => {
reportProgress({
type: "progress",
phase: "route_ordered_middle_channel",
steps: 1,
progress: 0.25,
})
const segments: Segment[] = input.connections.map((connection) => {
if (connection.pointsToConnect.length !== 2) {
fail(
connection.name,
`expected exactly two synchronized breakout endpoints, received ${connection.pointsToConnect.length}`,
)
}
const [first, second] = connection.pointsToConnect as [Point, Point]
if (first.layer !== second.layer) {
fail(
connection.name,
`endpoint layers differ (${first.layer} -> ${second.layer})`,
)
}
if (Math.abs(first.y - second.y) > EPS) {
fail(
connection.name,
`coordinated endpoints are not horizontally aligned (${first.y} -> ${second.y})`,
)
}
if (Math.abs(first.x - second.x) <= EPS) {
fail(connection.name, "coordinated endpoints have no channel span")
}
return {
a: first.x < second.x ? first : second,
b: first.x < second.x ? second : first,
connectionName: connection.name,
width:
connection.nominalTraceWidth ??
connection.width ??
input.nominalTraceWidth ??
input.minTraceWidth,
}
})
reportProgress({
type: "progress",
phase: "validate_ordered_middle_channel",
steps: 2,
progress: 0.75,
})
const clearance =
input.minTraceToPadEdgeClearance ??
fail("all", "SRJ is missing the minimum copper-edge clearance rule")
for (let firstIndex = 0; firstIndex < segments.length; firstIndex++) {
const first = segments[firstIndex]!
for (
let secondIndex = firstIndex + 1;
secondIndex < segments.length;
secondIndex++
) {
const second = segments[secondIndex]!
if (first.a.layer !== second.a.layer) continue
const requiredCenterDistance =
first.width / 2 + second.width / 2 + clearance
if (segmentDistance(first, second) + EPS < requiredCenterDistance) {
fail(
`${first.connectionName}/${second.connectionName}`,
`${first.a.layer} channel traces violate the SRJ-derived trace clearance`,
)
}
}
}
const traces: SimplifiedPcbTrace[] = input.connections.map(
(connection, connectionIndex) => {
const segment = segments[connectionIndex]!
const wire = (point: Point) => ({
route_type: "wire" as const,
x: point.x,
y: point.y,
width: segment.width,
layer: point.layer,
})
return {
type: "pcb_trace",
pcb_trace_id: `am62l-middle:${connection.name}`,
connection_name: connection.name,
connectsTo: connection.pointsToConnect.flatMap((point) =>
point.pointId ? [point.pointId] : [],
),
route: [wire(segment.a), wire(segment.b)],
}
},
)
reportProgress({
type: "progress",
phase: "validate_ordered_middle_channel",
steps: 3,
progress: 1,
})
return {
traces,
outputSimpleRouteJson: {
...input,
connections: input.connections.map((connection) => ({
...connection,
pointsToConnect: connection.pointsToConnect.map((point) => ({
...point,
})),
})),
buses: input.buses?.map((bus) => ({
...bus,
connectionNames: [...bus.connectionNames],
})),
traces: [...(input.traces ?? []), ...traces],
},
}
}
export type Am62lMiddleChannelAlgorithmFn = (
input: SimpleRouteJson,
) => Promise<GenericLocalAutorouter>
export const createAm62lMiddleChannelAlgorithm =
(): Am62lMiddleChannelAlgorithmFn => async (input) =>
new InProcessAutorouter(input, solveAm62lMiddleChannel)