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
}