ShiboSoftwareDev/am625sip-linux-board

This code defines the physical footprint and pin layout for an AM625 System-in-Package (SIP) component, detailing pad positions, pin labels, and package outline for hardware integration.

Version
1.0.24
License
unset
Stars
0

subcircuits/cached-soc-fanout-autorouter.ts

import { EventEmitter } from "node:events"
import cachedFanout from "./cache/soc-fanout-routes.json"

type SimpleRouteJson = any
type SimplifiedPcbTrace = any

const createCachedOutput = (input: SimpleRouteJson): SimpleRouteJson => {
  const traces = cachedFanout.fanoutTraces as SimplifiedPcbTrace[]
  const inputConnectionNames = new Set(
    input.connections.map((connection: any) => connection.name),
  )

  if (
    traces.length !== input.connections.length ||
    traces.some((trace) => !inputConnectionNames.has(trace.connection_name))
  ) {
    throw new Error(
      `SOC fanout cache does not match input: ${traces.length} cached traces for ${input.connections.length} connections`,
    )
  }

  const sourceComponentIds = new Set<string>()
  const connections = input.connections.map((connection: any) => {
    const trace = traces.find(
      (candidate) => candidate.connection_name === connection.name,
    )
    if (!trace) throw new Error(`Missing cached SOC fanout ${connection.name}`)
    const startPoint = trace.route.find(
      (point: any) => point.route_type === "wire" && point.start_pcb_port_id,
    )
    const exitPoint = [...trace.route]
      .reverse()
      .find((point: any) => point.route_type === "wire")
    const exitPointId = trace.connectsTo.find((id: string) =>
      id.startsWith("fanout-exit:"),
    )
    const sourcePointIndex = connection.pointsToConnect.findIndex(
      (point: any) => point.pcb_port_id === startPoint?.start_pcb_port_id,
    )
    if (!startPoint || !exitPoint || !exitPointId || sourcePointIndex < 0) {
      throw new Error(`Invalid cached SOC fanout ${connection.name}`)
    }
    for (const obstacle of input.obstacles) {
      if (
        obstacle.componentId &&
        obstacle.connectedTo?.includes(startPoint.start_pcb_port_id)
      ) {
        sourceComponentIds.add(obstacle.componentId)
      }
    }
    const pointsToConnect = connection.pointsToConnect.map((point: any) => ({
      ...point,
    }))
    pointsToConnect[sourcePointIndex] = {
      x: exitPoint.x,
      y: exitPoint.y,
      layer: exitPoint.layer,
      pointId: exitPointId,
    }
    return { ...connection, pointsToConnect }
  })

  const sourceKeepouts = [...sourceComponentIds].map((componentId) => {
    const componentObstacles = input.obstacles.filter(
      (obstacle: any) => obstacle.componentId === componentId,
    )
    const minX = Math.min(
      ...componentObstacles.map((obstacle: any) =>
        obstacle.center.x - obstacle.width / 2,
      ),
    )
    const maxX = Math.max(
      ...componentObstacles.map((obstacle: any) =>
        obstacle.center.x + obstacle.width / 2,
      ),
    )
    const minY = Math.min(
      ...componentObstacles.map((obstacle: any) =>
        obstacle.center.y - obstacle.height / 2,
      ),
    )
    const maxY = Math.max(
      ...componentObstacles.map((obstacle: any) =>
        obstacle.center.y + obstacle.height / 2,
      ),
    )
    return {
      obstacleId: `fanout-source-keepout:${componentId}`,
      componentId,
      isFanoutSourceKeepout: true,
      type: "rect",
      layers: ["top", "inner1", "inner2", "bottom"],
      center: { x: (minX + maxX) / 2, y: (minY + maxY) / 2 },
      width: maxX - minX,
      height: maxY - minY,
      connectedTo: [],
    }
  })

  const viaObstacles = traces.flatMap((trace) =>
    trace.route.flatMap((point: any, viaIndex: number) =>
      point.route_type === "via"
        ? [{
            obstacleId: `fanout-via:${trace.connection_name}:${viaIndex}`,
            type: "rect",
            center: { x: point.x, y: point.y },
            width: point.via_diameter,
            height: point.via_diameter,
            layers: point.layers ?? [point.from_layer, point.to_layer],
            connectedTo: [trace.connection_name, trace.pcb_trace_id],
          }]
        : [],
    ),
  )

  return {
    ...input,
    connections: connections.filter(
      (connection: any) => connection.pointsToConnect.length > 1,
    ),
    traces,
    obstacles: [
      ...input.obstacles.filter(
        (obstacle: any) => !sourceComponentIds.has(obstacle.componentId),
      ),
      ...sourceKeepouts,
      ...viaObstacles,
    ],
  }
}

class CachedSocFanoutAutorouter extends EventEmitter {
  isRouting = false
  private readonly output: SimpleRouteJson

  constructor(input: SimpleRouteJson) {
    super()
    this.output = createCachedOutput(input)
  }

  start() {
    this.isRouting = true
    queueMicrotask(() => {
      if (!this.isRouting) return
      this.isRouting = false
      this.emit("complete", {
        type: "complete",
        traces: cachedFanout.fanoutTraces,
      })
    })
  }

  stop() {
    this.isRouting = false
  }

  getOutputSimpleRouteJson() {
    return this.output
  }
}

export const createCachedSocFanoutAutorouter = async (
  input: SimpleRouteJson,
) => {
  console.log(
    `[SOC_FANOUT cache] loading ${cachedFanout.fanoutTraces.length} cached traces for ${input.connections.length} connections`,
  )
  return new CachedSocFanoutAutorouter(input)
}