seveibar/adm6-breakout

This code defines PCB designs featuring a 60-pin fanout or direct routing layout with multiple signal and power layers, test pads, and component footprints for a high-density electronic breakout board.

Version
1.0.2
License
unset
Stars
0

scripts/verify-fanout.mjs

import { readFileSync } from "node:fs"

const debugDir = "dist/autorouter-debug"
const traces = JSON.parse(
  readFileSync(`${debugDir}/phase-1.previous-output.traces.json`, "utf8"),
)
const timeout = JSON.parse(
  readFileSync(`${debugDir}/phase-1.timeout.json`, "utf8"),
)
const circuit = JSON.parse(
  readFileSync(`${debugDir}/board.source-and-pcb.circuit.json`, "utf8"),
)

const j1Source = circuit.find(
  (element) => element.type === "source_component" && element.name === "J1",
)
const j1Pcb = circuit.find(
  (element) =>
    element.type === "pcb_component" &&
    element.source_component_id === j1Source?.source_component_id,
)
const connectorPads = circuit.filter(
  (element) =>
    element.type === "pcb_smtpad" &&
    element.pcb_component_id === j1Pcb?.pcb_component_id,
)
const alignmentHoles = circuit.filter(
  (element) =>
    element.type === "pcb_hole" &&
    element.pcb_component_id === j1Pcb?.pcb_component_id,
)
const firstRowX = connectorPads
  .filter((pad) => Math.abs(pad.y - connectorPads[0].y) < 0.001)
  .map((pad) => pad.x)
  .sort((a, b) => a - b)
const measuredPitch = firstRowX[1] - firstRowX[0]
const measuredPadDiameter = connectorPads[0]?.radius * 2

const sourcePortIds = new Set()
const layers = new Set()
let viaCount = 0

for (const trace of traces) {
  if (trace.type !== "pcb_trace") {
    throw new Error(`Unexpected fanout element type: ${trace.type}`)
  }
  for (const point of trace.route ?? []) {
    if (point.start_pcb_port_id) sourcePortIds.add(point.start_pcb_port_id)
    if (point.layer) layers.add(point.layer)
    if (point.route_type === "via") viaCount += 1
  }
}

const summary = {
  exactFootprintPads: connectorPads.length,
  alignmentHoles: alignmentHoles.length,
  measuredPadDiameterMm: measuredPadDiameter,
  measuredPitchMm: measuredPitch,
  denseFanoutTraceCount: traces.length,
  uniqueSourcePadsEscaped: sourcePortIds.size,
  layersUsed: [...layers].sort(),
  viaCount,
  followUpPhase: timeout.phaseOrdinal,
  followUpConnectionCount: timeout.connectionCount,
  followUpStatus: "intentionally timeout-bounded",
}

console.log(JSON.stringify(summary, null, 2))

if (traces.length !== 240) {
  throw new Error(`Expected 240 dense fanout traces, found ${traces.length}`)
}
if (connectorPads.length !== 240 || alignmentHoles.length !== 2) {
  throw new Error(
    `Exact ADM6 footprint mismatch: ${connectorPads.length} pads, ${alignmentHoles.length} alignment holes`,
  )
}
if (Math.abs(measuredPitch - 0.635) > 0.001) {
  throw new Error(`Expected 0.635 mm connector pitch, found ${measuredPitch}`)
}
if (Math.abs(measuredPadDiameter - 0.356) > 0.001) {
  throw new Error(
    `Expected 0.356 mm connector pads, found ${measuredPadDiameter}`,
  )
}
if (sourcePortIds.size !== 240) {
  throw new Error(
    `Expected 240 unique connector pads to escape, found ${sourcePortIds.size}`,
  )
}
if (timeout.previousTraceCount !== 240) {
  throw new Error(
    `Follow-up phase did not receive all 240 fanout traces (${timeout.previousTraceCount})`,
  )
}