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-simple.mjs

import assert from "node:assert/strict"
import { readFileSync, writeFileSync } from "node:fs"
import {
  CONNECTOR_ROWS,
  CONNECTOR_SELECTION_SEED,
  pinMappings,
} from "../pin-mapping.ts"

const outputDir = "dist/rp2040-connector-routed"
const circuit = JSON.parse(readFileSync(`${outputDir}/circuit.json`, "utf8"))
const source = readFileSync("rp2040-connector-routed.circuit.tsx", "utf8")
const elements = (type) => circuit.filter((element) => element.type === type)
const traces = elements("pcb_trace")
const sourceTraces = elements("source_trace")
const representedSourceIds = new Set(traces.map((trace) => trace.source_trace_id))
const errors = circuit.filter((element) => element.type.endsWith("_error"))
const connectivityErrorTypes = new Set([
  "pcb_trace_missing_error",
  "pcb_port_not_connected_error",
  "pcb_trace_error",
  "pcb_autorouting_error",
  "source_trace_not_connected_error",
])
const connectivityErrors = errors.filter((error) =>
  connectivityErrorTypes.has(error.type),
)
const board = elements("pcb_board")[0]
const allowedLayers = new Set(["top", "inner1", "inner2", "bottom"])
const layersUsed = [...new Set(traces.flatMap((trace) =>
  trace.route.filter((point) => point.route_type === "wire").map((point) => point.layer),
))].sort()
const rowCounts = Object.fromEntries(CONNECTOR_ROWS.map((row) => [
  row,
  pinMappings.filter(({ contact }) => contact.startsWith(row)).length,
]))
const fanoutTraces = JSON.parse(
  readFileSync(
    "dist/rp2040-connector-fanout-1x-debug/phase-0.output.traces.json",
    "utf8",
  ),
)

assert.equal(elements("pcb_component").length, 2)
assert.equal(elements("pcb_smtpad").length, 297)
assert.equal(board.num_layers, 4)
assert(layersUsed.every((layer) => allowedLayers.has(layer)))
assert(elements("pcb_via").every((via) =>
  via.layers.every((layer) => allowedLayers.has(layer)),
))
assert.equal(sourceTraces.length, 30)
assert.equal(fanoutTraces.length, 30)
assert.equal(pinMappings.length, 30)
assert.equal(new Set(pinMappings.map(({ contact }) => contact)).size, 30)
assert(Object.values(rowCounts).every((count) => count > 0))
assert(sourceTraces.every((trace) => representedSourceIds.has(trace.source_trace_id)))
for (const { contact, gpio, name } of pinMappings) {
  const trace = sourceTraces.find((trace) => trace.name === name)
  assert(trace, `Missing source connection ${name}`)
  const ports = trace.connected_source_port_ids.map((id) =>
    elements("source_port").find((port) => port.source_port_id === id),
  )
  const endpointNames = ports.map((port) => {
    const component = elements("source_component").find((component) =>
      component.source_component_id === port.source_component_id,
    )
    return `${component.name}.${port.name}`
  }).sort()
  assert.deepEqual(endpointNames, [`J1.${contact}`, `U1.${gpio}`].sort())
  const routed = traces.filter((routed) =>
    routed.source_trace_id === trace.source_trace_id,
  )
  assert.equal(routed.length, 2, `Expected escape and board route for ${name}`)
  for (const port of ports) {
    const pcbPort = elements("pcb_port").find((pcbPort) =>
      pcbPort.source_port_id === port.source_port_id,
    )
    assert(routed.some((routed) => routed.route.some((point) =>
      point.start_pcb_port_id === pcbPort.pcb_port_id ||
      point.end_pcb_port_id === pcbPort.pcb_port_id,
    )), `No routed endpoint for ${name}: ${port.name}`)
  }
}
assert.equal((source.match(/<fanout\b/g) ?? []).length, 1)
assert(!/<breakoutpoint\b/i.test(source))
assert(
  [...source.matchAll(/autorouterEffortLevel="([^"]+)"/g)].every(
    (match) => match[1] === "1x",
  ),
)

const summary = {
  status: errors.length === 0 ? "pass" : "routing-errors",
  components: 2,
  boardLayers: board.num_layers,
  sourceConnections: sourceTraces.length,
  randomSelectionSeed: `0x${CONNECTOR_SELECTION_SEED.toString(16)}`,
  selectedContactsByRow: rowCounts,
  pinMappings,
  connectorFanoutTraces: fanoutTraces.length,
  pcbTraces: traces.length,
  vias: elements("pcb_via").length,
  layersUsed,
  viaHoleDiameter: board.min_via_hole_diameter,
  viaPadDiameter: board.min_via_pad_diameter,
  effort: "1x",
  explicitBreakoutPointElements: 0,
  connectivityErrors: connectivityErrors.length,
  drcErrors: errors.map((error) => ({ type: error.type, message: error.message })),
  warnings: circuit.filter((element) => element.type.endsWith("_warning"))
    .map((warning) => ({ type: warning.type, message: warning.message })),
}
writeFileSync(`${outputDir}/verification.json`, JSON.stringify(summary, null, 2))
console.log(JSON.stringify(summary, null, 2))

if (errors.length > 0) {
  console.error(`Routing validation failed: ${errors.length} DRC error reports.`)
  process.exitCode = 1
}