shibosoftwaredev/f1c100s-linux-nema17-stepper-controller

A four-layer NEMA 17 motor-controller PCB combining an Allwinner F1C100S Linux computer, USB‑PD/buck power system, W5500 Ethernet, STM32 real-time motion control, DRV8825 stepper driver, protection/temperature/current telemetry, USB interfaces, and local LED/buzzer diagnostics.

Version
0.4.56
License
unset
Stars
0

scripts/verify.mjs

import assert from "node:assert/strict"
import crypto from "node:crypto"
import fs from "node:fs"

const circuitPath = "dist/index/circuit.json"
const circuitBytes = fs.readFileSync(circuitPath)
const circuit = JSON.parse(circuitBytes)
const of = (type) => circuit.filter((item) => item.type === type)

const parent = new Map()
const root = (id) => {
  if (!parent.has(id)) parent.set(id, id)
  if (parent.get(id) !== id) parent.set(id, root(parent.get(id)))
  return parent.get(id)
}
const join = (a, b) => parent.set(root(a), root(b))
for (const trace of of("source_trace")) {
  const ids = [...(trace.connected_source_port_ids ?? []), ...(trace.connected_source_net_ids ?? [])]
  ids.slice(1).forEach((id) => join(ids[0], id))
}

const component = (name) => {
  const matches = of("source_component").filter((item) => item.name === name)
  assert.equal(matches.length, 1, `unique component ${name}`)
  return matches[0]
}
const port = (name, pin) => {
  const source = component(name)
  const result = of("source_port").find((item) =>
    item.source_component_id === source.source_component_id &&
    (item.pin_number === pin || item.name === pin || item.port_hints?.includes(pin)))
  assert.ok(result, `${name}.${pin}`)
  return result.source_port_id
}
const net = (name) => {
  const result = of("source_net").find((item) => item.name === name)
  assert.ok(result, `net ${name}`)
  return result.source_net_id
}
const expectNet = (name, pin, netName) => {
  assert.equal(root(port(name, pin)), root(net(netName)), `${name}.${pin} -> ${netName}`)
}
const expectPair = (aName, aPin, bName, bPin) => {
  assert.equal(root(port(aName, aPin)), root(port(bName, bPin)), `${aName}.${aPin} -> ${bName}.${bPin}`)
}
const expectPart = (name, mpn, lcsc) => {
  assert.equal(component(name).manufacturer_part_number, mpn, `${name} MPN`)
  assert.deepEqual(component(name).supplier_part_numbers.jlcpcb, [lcsc], `${name} JLCPCB part`)
}
const placedComponent = (name) => of("pcb_component").find((item) =>
  item.source_component_id === component(name).source_component_id)
const schematicComponent = (name) => {
  const result = of("schematic_component").find((item) =>
    item.source_component_id === component(name).source_component_id)
  assert.ok(result, `schematic component ${name}`)
  return result
}

// Keep each LDO's input bypass on its input (left) side and output bypass on
// its output (right) side. The cloud style analyzer checks this relationship,
// while older local CLI versions do not yet report it.
for (const regulator of ["U_2V5", "U_1V2", "U_2V8"]) {
  const regulatorX = schematicComponent(regulator).center.x
  assert.ok(schematicComponent(`C_${regulator}_IN`).center.x < regulatorX,
    `${regulator} input capacitor is left of the regulator`)
  assert.ok(schematicComponent(`C_${regulator}_OUT`).center.x > regulatorX,
    `${regulator} output capacitor is right of the regulator`)
}

// Fabrication geometry and DRC invariants.
const errors = circuit.filter((item) => item.type.endsWith("_error"))
assert.deepEqual(errors, [], "circuit JSON contains no DRC errors")
const board = of("pcb_board")[0]
const BOARD_LEFT = -21.15
const BOARD_RIGHT = 21.15
const BOARD_BOTTOM = -21.15
const BOARD_TOP = 21.15
const FAB_EDGE_CLEARANCE = 0.2
const CONNECTOR_HOLE_EDGE_CLEARANCE = 0.25
const MOUNT_HOLE_DIAMETER = 3.5
const MOUNT_HARDWARE_RADIUS = 2.75
const MOUNT_TOP_COPPER_CLEARANCE = 0.1
const MOTOR_SIDE_STANDOFF_RADIUS = 2.5
const MOUNT_CENTERS = [
  { x: -15.5, y: -15.5 },
  { x: -15.5, y: 15.5 },
  { x: 15.5, y: -15.5 },
  { x: 15.5, y: 15.5 },
]
const EXPECTED_OUTLINE = [
  { x: BOARD_LEFT, y: BOARD_TOP }, { x: BOARD_RIGHT, y: BOARD_TOP },
  { x: BOARD_RIGHT, y: BOARD_BOTTOM }, { x: BOARD_LEFT, y: BOARD_BOTTOM },
]
assert.equal(board.num_layers, 4)
assert.equal(board.thickness,1.6,"explicit standard 1.6 mm four-layer fabrication thickness")
assert.deepEqual(board.outline, EXPECTED_OUTLINE,
  "strict 42.3 x 42.3 mm NEMA17 square with no connector-support tabs")
assert.equal(board.min_via_hole_diameter, 0.3)
assert.equal(board.min_via_pad_diameter, 0.45)
const vias = of("pcb_via")
assert.ok(vias.length > 0)
assert.ok(vias.every((via) => via.hole_diameter === 0.3 && via.outer_diameter === 0.45),
  "every via is exactly 0.30 mm drill / 0.45 mm pad")
assert.ok(vias.every((via) =>
  via.layers?.length === 4 &&
  ["top", "inner1", "inner2", "bottom"].every((layer) => via.layers.includes(layer))),
"every via is a full-stack L1-L4 through-via")

const highCurrentSourceTraceIds = new Set([
  "USB_VBUS_A", "USB_VBUS_B", "BUCK_IN_CAP", "BUCK_SW", "VM_INPUT", "VM_FUSED",
  "VM_PROTECTED", "BUCK_INPUT_BYPASS", "MOTOR_A_SENSED_PLUS", "MOTOR_B_SENSED_PLUS",
].map(name => {
  const source = of("source_trace").find(trace => trace.name === name)
  assert.ok(source, `required high-current source ${name}`)
  return source.source_trace_id
}))
const highCurrentPcbTraces = of("pcb_trace")
  .filter((trace) => highCurrentSourceTraceIds.has(trace.source_trace_id))
assert.equal(new Set(highCurrentPcbTraces.map(trace => trace.source_trace_id)).size,
  highCurrentSourceTraceIds.size, "every high-current trunk has routed copper")
assert.ok(highCurrentPcbTraces.every((trace) => trace.route
  .filter((point) => point.route_type === "wire")
  .every((point) => point.layer === "top" || point.layer === "bottom")),
"USB-PD, buck and motor-supply high-current trunks use outer-layer copper only")
for(const name of ["DRV_ISEN_A","DRV_ISEN_B","MOTOR_A_PLUS","MOTOR_A_MINUS","MOTOR_B_PLUS","MOTOR_B_MINUS"]){
  const source=of("source_trace").find(t=>t.name===name)
  assert.ok(source,`${name} source exists`)
  const routes=of("pcb_trace").filter(t=>t.source_trace_id===source.source_trace_id)
  assert.ok(routes.length>0,`${name} has actual current-carrying copper`)
  assert.ok(routes.every(t=>t.route.filter(p=>p.route_type==="wire").every(p=>["top","bottom"].includes(p.layer)&&p.width>=.3-1e-9)),`${name} including regulation-shunt return stays >=0.30 mm on outer layers`)
}
assert.equal(new Set(vias.map((via) => `${via.x.toFixed(6)},${via.y.toFixed(6)}`)).size, vias.length,
  "no coincident vias")

for (const placed of of("pcb_component")) {
  if (placed.is_allowed_to_be_off_board) continue
  const x0 = placed.center.x - placed.width / 2
  const x1 = placed.center.x + placed.width / 2
  const y0 = placed.center.y - placed.height / 2
  const y1 = placed.center.y + placed.height / 2
  assert.ok(x0 >= -21.15 - 1e-6 && x1 <= 21.15 + 1e-6 && y0 >= -21.15 - 1e-6 && y1 <= 21.15 + 1e-6,
    `${placed.pcb_component_id} is within the board outline`)
}

// `allowOffBoard` is appropriate for an edge connector's plastic housing,
// never for its drilled features or copper. Check primitives independently so
// a component-level exemption cannot hide clipped holes or annular rings.
const geometrySourceById = new Map(of("source_component")
  .map((item) => [item.source_component_id, item]))
const geometryPcbById = new Map(of("pcb_component")
  .map((item) => [item.pcb_component_id, item]))
for (const cad of of("cad_component")) {
  const pcb = geometryPcbById.get(cad.pcb_component_id)
  if (!pcb) continue
  assert.ok(pcb.layer === "bottom" ? cad.position.z < 0 : cad.position.z > 0,
    `${geometrySourceById.get(pcb.source_component_id)?.name} CAD model matches its assembly face`)
}
const pdCad = of("cad_component").find((entry) =>
  entry.source_component_id === component("U_PD").source_component_id)
assert.ok(pdCad?.model_obj_url?.includes("C24833806"), "U_PD retains its supplier 3D model")
assert.equal(placedComponent("U_PD").layer, "top", "U_PD occupies the top-side layout pocket")
const primitiveComponentName = (primitive) => {
  const placed = geometryPcbById.get(primitive.pcb_component_id)
  return geometrySourceById.get(placed?.source_component_id)?.name
}
const primitiveBounds = (primitive) => {
  if (primitive.points?.length) {
    const xs = primitive.points.map((point) => point.x)
    const ys = primitive.points.map((point) => point.y)
    return {
      left: Math.min(...xs), right: Math.max(...xs),
      bottom: Math.min(...ys), top: Math.max(...ys),
    }
  }
  const width = primitive.outer_width ?? primitive.outer_diameter ??
    primitive.width ?? primitive.hole_diameter ?? primitive.radius * 2
  const height = primitive.outer_height ?? primitive.outer_diameter ??
    primitive.height ?? primitive.hole_diameter ?? primitive.radius * 2
  assert.ok(Number.isFinite(width) && Number.isFinite(height),
    `${primitive.type} ${primitive.pcb_component_id ?? primitive.pcb_via_id} has measurable bounds`)
  return {
    left: primitive.x - width / 2,
    right: primitive.x + width / 2,
    bottom: primitive.y - height / 2,
    top: primitive.y + height / 2,
  }
}
const edgeClearance = (primitive) => {
  const bounds = primitiveBounds(primitive)
  return Math.min(
    bounds.left - BOARD_LEFT,
    BOARD_RIGHT - bounds.right,
    bounds.bottom - BOARD_BOTTOM,
    BOARD_TOP - bounds.top,
  )
}
const distancePointToSegment = (point, a, b) => {
  const dx = b.x - a.x
  const dy = b.y - a.y
  const lengthSquared = dx * dx + dy * dy
  const t = lengthSquared === 0 ? 0 : Math.max(0, Math.min(1,
    ((point.x - a.x) * dx + (point.y - a.y) * dy) / lengthSquared))
  return Math.hypot(point.x - (a.x + t * dx), point.y - (a.y + t * dy))
}
const pointInOutline = (point) => {
  let inside = false
  for (let i = 0, j = EXPECTED_OUTLINE.length - 1; i < EXPECTED_OUTLINE.length; j = i++) {
    const a = EXPECTED_OUTLINE[i]
    const b = EXPECTED_OUTLINE[j]
    if ((a.y > point.y) !== (b.y > point.y) &&
      point.x < ((b.x - a.x) * (point.y - a.y)) / (b.y - a.y) + a.x)
      inside = !inside
  }
  return inside
}
const connectorHoleEdgeClearance = (primitive) => {
  const bounds = primitiveBounds(primitive)
  const radius = Math.max(bounds.right - bounds.left, bounds.top - bounds.bottom) / 2
  const center = { x: primitive.x, y: primitive.y }
  if (!pointInOutline(center)) return -Infinity
  return Math.min(...EXPECTED_OUTLINE.map((point, index) =>
    distancePointToSegment(center, point, EXPECTED_OUTLINE[(index + 1) % EXPECTED_OUTLINE.length]))) - radius
}
const fabricationPrimitives = [
  ...of("pcb_plated_hole"),
  ...of("pcb_hole"),
  ...of("pcb_smtpad"),
  ...of("pcb_via"),
]
for (const primitive of fabricationPrimitives) {
  const name = primitiveComponentName(primitive)
  const clearance = (name === "J_ETH" || name === "J_MOTOR") &&
      (primitive.type === "pcb_plated_hole" || primitive.type === "pcb_hole")
    ? connectorHoleEdgeClearance(primitive)
    : edgeClearance(primitive)
  assert.ok(clearance >= FAB_EDGE_CLEARANCE - 1e-6,
    `${primitiveComponentName(primitive) ?? primitive.type} ${primitive.type} has at least ${FAB_EDGE_CLEARANCE} mm edge clearance`)
}

for (const primitive of [...of("pcb_plated_hole"), ...of("pcb_hole")]) {
  const name = primitiveComponentName(primitive)
  if (name !== "J_ETH" && name !== "J_MOTOR") continue
  assert.ok(connectorHoleEdgeClearance(primitive) >= CONNECTOR_HOLE_EDGE_CLEARANCE - 1e-6,
    `${name} ${primitive.type} has at least ${CONNECTOR_HOLE_EDGE_CLEARANCE} mm edge clearance`)
}
const distance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y)
const assertNear = (actual, expected, tolerance, message) =>
  assert.ok(Math.abs(actual - expected) <= tolerance,
    `${message}: expected ${expected} +/- ${tolerance}, got ${actual}`)
const mountHoles = MOUNT_CENTERS.map((center, index) => {
  const placed = placedComponent(`MH${index + 1}`)
  assert.ok(placed, `MH${index + 1} placed component`)
  assertNear(placed.center.x, center.x, 1e-6, `MH${index + 1} x`)
  assertNear(placed.center.y, center.y, 1e-6, `MH${index + 1} y`)
  const holes = of("pcb_hole").filter((hole) =>
    hole.pcb_component_id === placed.pcb_component_id &&
    hole.hole_diameter === MOUNT_HOLE_DIAMETER)
  assert.equal(holes.length, 1, `MH${index + 1} has one 3.5 mm NPTH`)
  assertNear(holes[0].x, center.x, 1e-6, `MH${index + 1} drill x`)
  assertNear(holes[0].y, center.y, 1e-6, `MH${index + 1} drill y`)
  return center
})
const mountKeepouts = of("pcb_keepout")
assert.equal(mountKeepouts.length, 4,
  "four standard-head mounting keepouts reach fabrication output")
for (const center of MOUNT_CENTERS) {
  const keepout = mountKeepouts.find((entry) =>
    distance(entry.center, center) <= 1e-6)
  assert.ok(keepout, `mounting keepout exists at ${center.x},${center.y}`)
  assert.equal(keepout.shape, "circle")
  assert.equal(keepout.radius, MOUNT_HARDWARE_RADIUS,
    "mounting keepout reserves the 5.5 mm ISO 4762 M3 head envelope")
  assert.deepEqual(keepout.layers, ["top"],
    "mounting keepout marks the screw-head side")
  assert.equal(keepout.allow_placements, true,
    "courtyard-only overlaps are allowed; physical bodies are checked below")
  assert.equal(keepout.allow_traces, true,
    "masked signal copper may pass below the mechanical head envelope")
  assert.equal(keepout.warning_only, true,
    "through-via overlap is advisory because every affected via is tented")
}
const mountSilkscreenCircles = of("pcb_silkscreen_circle").filter((entry) =>
  entry.layer === "top" &&
  entry.radius === MOUNT_HARDWARE_RADIUS &&
  MOUNT_CENTERS.some((center) => distance(entry.center, center) <= 1e-6))
assert.equal(mountSilkscreenCircles.length, 4,
  "four visible top-silkscreen rings mark the M3 head keepouts")

// The circular records are mechanical screw-head envelopes. Copper pours are
// clipped by them; masked signal traces are permitted below the head. Keep all
// exposed pads and plated-hole copper out, and require every through-via whose
// annulus falls under a head to be tented on the assembled board.
for (const center of MOUNT_CENTERS) {
  for (const via of vias) {
    const overlapsHead = distance(center, via) < MOUNT_HARDWARE_RADIUS +
      via.outer_diameter / 2 + MOUNT_TOP_COPPER_CLEARANCE - 1e-6
    if (overlapsHead)
      assert.equal(via.tented_on_top, true,
        `${via.pcb_via_id} below the M3 head is solder-mask tented`)
  }
  for (const pad of of("pcb_smtpad").filter((entry) => entry.layer === "top")) {
    const bounds = primitiveBounds(pad)
    const dx = Math.max(bounds.left - center.x, center.x - bounds.right, 0)
    const dy = Math.max(bounds.bottom - center.y, center.y - bounds.top, 0)
    assert.ok(Math.hypot(dx, dy) >= MOUNT_HARDWARE_RADIUS - 1e-6,
    `${primitiveComponentName(pad)} top pad clears the standard M3 head keepout`)
  }
  for (const hole of of("pcb_plated_hole")) {
    const copperRadius = Math.max(hole.outer_diameter ?? 0,
      hole.outer_width ?? 0, hole.outer_height ?? 0) / 2
    assert.ok(distance(center, hole) >= MOUNT_HARDWARE_RADIUS + copperRadius - 1e-6,
    `${primitiveComponentName(hole)} plated copper clears the standard M3 head keepout`)
  }
}
for (const [a, b] of [[0, 1], [0, 2], [1, 3], [2, 3]])
  assertNear(distance(mountHoles[a], mountHoles[b]), 31, 2e-6,
    `MH${a + 1}-MH${b + 1} NEMA17 pitch`)
for (const [a, b] of [[0, 3], [1, 2]])
  assertNear(distance(mountHoles[a], mountHoles[b]), 31 * Math.SQRT2, 2e-6,
    `MH${a + 1}-MH${b + 1} NEMA17 diagonal`)
const mountPatternRotationDeg = Math.atan2(
  mountHoles[2].y - mountHoles[0].y,
  mountHoles[2].x - mountHoles[0].x,
) * 180 / Math.PI
assertNear(mountPatternRotationDeg, 0, 2e-6,
  "carrier is square to the motor")
const projectedCarrierOverhangMm =
  (42.3 * (Math.abs(Math.cos(mountPatternRotationDeg * Math.PI / 180)) +
    Math.abs(Math.sin(mountPatternRotationDeg * Math.PI / 180))) - 42) / 2
assertNear(projectedCarrierOverhangMm, 0.15, 2e-6,
  "aligned 42.3 mm carrier projects 0.15 mm beyond each side of a 42 mm motor")

// BOM and assembly invariants.
const dnpSourceIds = new Set(of("pcb_component")
  .filter((item) => item.do_not_place)
  .map((item) => item.source_component_id))
const placedSourceIds = new Set(of("pcb_component")
  .map((item) => item.source_component_id)
  .filter(Boolean))
const populated = of("source_component").filter((item) =>
  placedSourceIds.has(item.source_component_id) &&
  item.ftype !== "simple_test_point" &&
  !dnpSourceIds.has(item.source_component_id))
const addedCurrentParts = ["R_INLINE_A","R_INLINE_B","U_CURRENT_A","U_CURRENT_B",
  "C_CURRENT_A_SUPPLY","C_CURRENT_B_SUPPLY","R_CURRENT_A_FILTER","R_CURRENT_B_FILTER",
  "C_CURRENT_A_FILTER","C_CURRENT_B_FILTER"]
const addedUsbParts = ["U_USB_GATE","Q_USB_ATTACH","R_USB_BASE","R_USB_BASE_PD","R_USB_OE_PU","C_USB_GATE"]
const addedSoftwareCurrentParts = ["U_CURRENT_DAC","C_CURRENT_DAC"]
for (const name of [...addedCurrentParts,...addedUsbParts,...addedSoftwareCurrentParts])
  assert.ok(populated.some(part=>part.name===name),`${name} is populated`)
assert.equal(populated.length, 183 + addedCurrentParts.length + addedUsbParts.length + addedSoftwareCurrentParts.length)
for (const item of populated) {
  assert.ok(item.supplier_part_numbers?.jlcpcb?.some((code) => /^C\d+$/.test(code)),
    `${item.name} has a JLCPCB part number`)
}
const sourceById = new Map(of("source_component").map((item) => [item.source_component_id, item]))
const assembled = of("pcb_component").filter((placed) => {
  const source = sourceById.get(placed.source_component_id)
  return source && source.ftype !== "simple_test_point" && !placed.do_not_place
})
assert.equal(assembled.length, 183 + addedCurrentParts.length + addedUsbParts.length + addedSoftwareCurrentParts.length)
assert.ok(assembled.some((placed) => placed.layer === "top"), "top-side assembly exists")
assert.ok(assembled.some((placed) => placed.layer === "bottom"), "bottom-side assembly exists")
assert.ok(of("pcb_smtpad").some((pad) => pad.layer === "bottom"), "bottom-side SMT pads exist")

const motorSource = of("source_component").find((item) => item.name === "MOTOR")
assert.ok(motorSource, "NEMA 17 motor exists in the generated assembly")
const motorCad = of("cad_component").find((item) =>
  item.source_component_id === motorSource.source_component_id)
assert.ok(motorCad, "NEMA 17 motor has a generated CAD model")
assert.match(motorCad.model_glb_url ?? "",
  /nema17_bodylength34mm_shaftlength20mm_round_jstph4\.glb$/)
assert.doesNotMatch(motorCad.model_glb_url ?? "", /backfaceholes|plainbackface/,
  "motor retains the modelprinter default rear M3 screws")
assertNear(motorCad.position.z, -34-6-board.thickness/2, 1e-9, "motor assembly Z follows board thickness, 6 mm gap and 34 mm body")
assertNear(motorCad.rotation.x, 180, 1e-9, "motor assembly shaft-out rotation")
assertNear(motorCad.rotation.y, 0, 1e-9, "motor assembly has no Y-axis tilt")
assertNear(motorCad.rotation.z, 90, 1e-9,
  "motor body stays square with the PCB while its wire side faces J_MOTOR")
const motorConnector = component("J_MOTOR")
assert.equal(motorConnector.ftype, "simple_connector",
  "J_MOTOR is the board-side harness connector")
assert.equal(motorConnector.standard, "jst_ph")
assert.equal(motorConnector.pin_count, 4)
const motorCables = of("cad_cable").filter((item) => item.name === "MOTOR_CABLE")
assert.equal(motorCables.length, 1, "one documented motor cable is rendered")
const motorCable = motorCables[0]
assert.equal(motorCable.from_source_component_id, motorSource.source_component_id,
  "motor cable starts at MOTOR.wireside")
assert.equal(motorCable.to_source_component_id, motorConnector.source_component_id,
  "motor cable ends at the real board-side J_MOTOR connector")
assert.equal(motorCable.cableprinter_string, "jst_ph_pins4")
assert.ok(motorCable.path.length >= 2, "motor cable has a rendered route")
const cableExit = {
  x: motorCable.path[1].x - motorCable.path[0].x,
  y: motorCable.path[1].y - motorCable.path[0].y,
}
const connectorPlacement = placedComponent("J_MOTOR")
const bottomEdgeDot = -cableExit.y / Math.hypot(cableExit.x, cableExit.y)
assert.ok(bottomEdgeDot > 0.999,
  "MOTOR.wireside exits squarely toward the PCB bottom edge")
assert.ok(connectorPlacement.center.y < board.center.y,
  "J_MOTOR is on the same bottom edge as MOTOR.wireside")
assertNear(
  Math.abs(motorCable.path.at(-1).x - motorCable.path[0].x),
  3,
  1e-6,
  "motor cable bends across J_MOTOR's small along-edge offset",
)

expectPart("J_USB_DATA", "TYPE-C-31-M-12", "C165948")
expectPart("J_USB_PWR", "TYPE-C-31-M-12", "C165948")
const sameEdgeData = placedComponent("J_USB_DATA")
const sameEdgePower = placedComponent("J_USB_PWR")
assert.ok(sameEdgeData.center.y > 15 && sameEdgePower.center.y > 15,
  "both unchanged USB-C connectors are recessed at the north edge")
assert.ok(Math.abs(sameEdgeData.center.y-sameEdgePower.center.y) < 1e-6)
assert.equal(sameEdgeData.rotation, 180)
assert.equal(sameEdgePower.rotation, 180)
assert.equal(sameEdgeData.layer, "top")
assert.equal(sameEdgePower.layer, "top", "power and data USB-C both occupy the top assembly face")
// C165948 OBJ bounds: x [-4.47,4.47], y [-5.3,2.6], referenced to
// model_origin_position.y=-2.7500289. Checking the PAD bounds alone misses
// the shell's 5.3500289 mm forward extension from the placement anchor.
for(const usb of [sameEdgeData,sameEdgePower]) {
  assert.ok(usb.center.y+5.3500289<=BOARD_TOP-.1,
    "complete USB-C metal housing remains inside the board with 0.1 mm margin")
  assert.ok(usb.center.x-4.47>=BOARD_LEFT && usb.center.x+4.47<=BOARD_RIGHT)
}
expectPart("R_USB_DATA_CC1", "0402WGF5101TCE", "C25905")
expectPart("R_USB_DATA_CC2", "0402WGF5101TCE", "C25905")
expectPart("U_PD", "HUSB238A-BB001-QN16R", "C24833806")
expectPart("Q_VBUS", "AO4407A", "C3011194")
expectPart("Q_PD_ENABLE", "BSS84_R1_00001", "C391434")
expectPart("U_BUCK", "TPS54302DDCR", "C311983")
// Check actual emitted metal as well as noConnect. A schematic-only fix must
// never leave the former 15 V branch touching EN's 7 V maximum-rated pin.
const buckEnablePort = port("U_BUCK", "EN")
assert.equal(of("source_port").find(p=>p.source_port_id===buckEnablePort).do_not_connect,true)
assert.ok(!of("source_trace").some(t=>t.connected_source_port_ids?.includes(buckEnablePort)),"EN remains externally open")
const buckEnableAudit = JSON.parse(fs.readFileSync("reports/buck-enable-audit.json","utf8"))
assert.equal(buckEnableAudit.circuitSha256,crypto.createHash("sha256").update(circuitBytes).digest("hex"),"fresh emitted EN isolation audit")
assert.equal(buckEnableAudit.logicalOpen,true)
assert.equal(buckEnableAudit.explicitNoConnect,true)
assert.equal(buckEnableAudit.emittedCopperIncludingPours,true)
assert.ok(buckEnableAudit.minimumEmittedCopperClearanceMm>=.1-2e-4,"EN is clear of actual tracks, drills and pours")
expectPair("Q_VBUS", "pin8", "U_BUCK", "VIN")
expectPart("U_3V3", "AP2112K-3.3TRG1", "C51118")
expectPart("U_ETH", "W5500", "C32843")
expectPart("J_ETH", "HR911105A", "C12074")
expectPart("U_MOTION", "STM32G030F6P6", "C724040")
expectPart("U_MOTOR", "DRV8825PWPR", "C81582")
expectPart("U_TEMP", "TMP102AIDRLR", "C99269")
expectPart("D_STATUS", "WS2812B-2020-V6", "C52917434")
expectPart("U_STATUS_BUFFER", "SN74AHCT1G125DBVR", "C7484")
expectPart("BZ1", "KLJ-4020", "C556936")
expectPart("Q_BUZZER", "AO3400A", "C20917")
expectPart("D_BUZZER", "1N5819WT", "C5185918")
expectPart("C_VM_BULK", "CA55-D050M226TE100", "C41417029")
assert.equal(component("C_VM_BULK").max_voltage_rating, 50)
expectPart("SW_RESET", "B3U-1000P", "C231329")
expectPart("R_ISET_210", "RC0402FR-071K8L", "C163481")
expectPart("R_PD_12V", "0402WGF1332TCE", "C122547")

// Service option resistors are DNP in the safe production default: their
// documented values remain JLCPCB-orderable but produce no paste or 3D body.
for (const name of ["R_ISET_210", "R_PD_12V"]) {
  const source = component(name)
  const placed = placedComponent(name)
  assert.equal(placed.do_not_place, true, `${name} is DNP`)
  assert.ok(source.manufacturer_part_number, `${name} has an MPN for assembly variants`)
  assert.ok(source.supplier_part_numbers?.jlcpcb?.some((code) => /^C\d+$/.test(code)),
    `${name} has a JLCPCB part for assembly variants`)
  assert.equal(of("pcb_solder_paste").filter((paste) =>
    paste.pcb_component_id === placed.pcb_component_id).length, 0,
  `${name} is excluded from the assembly stencil`)
  assert.equal(of("cad_component").filter((cad) =>
    cad.source_component_id === source.source_component_id).length, 0,
  `${name} has no assembled 3D body`)
}

// JP_BOOT is fabricated as a normally closed cuttable copper link. It must
// never reappear as a fitted header, paste aperture or phantom 3D body.
const bootSource = component("JP_BOOT")
const bootPlaced = placedComponent("JP_BOOT")
assert.equal(bootPlaced.do_not_place, true)
assert.equal(bootSource.manufacturer_part_number, undefined)
assert.ok(!bootSource.supplier_part_numbers?.jlcpcb?.length)
expectNet("JP_BOOT", "SOC_CS", "SOC_FLASH_CS")
expectNet("JP_BOOT", "FLASH_CS", "FLASH_CS")
const bootPads = of("pcb_smtpad")
  .filter((pad) => pad.pcb_component_id === bootPlaced.pcb_component_id)
  .sort((a, b) => a.x - b.x)
assert.deepEqual(bootPads.map((pad) => ({
  x: Number((pad.x - bootPlaced.center.x).toFixed(6)),
  y: Number((pad.y - bootPlaced.center.y).toFixed(6)),
  width: pad.width,
  height: pad.height,
})), [
  { x: -0.5, y: 0, width: 0.7, height: 0.8 },
  { x: 0.5, y: 0, width: 0.7, height: 0.8 },
], "JP_BOOT uses the compact 1.0 mm-pitch cuttable land pattern")
assert.equal(of("pcb_plated_hole").filter((hole) =>
  hole.pcb_component_id === bootPlaced.pcb_component_id).length, 0,
"JP_BOOT has no through-hole features")
assert.equal(of("pcb_solder_paste").filter((paste) =>
  paste.pcb_component_id === bootPlaced.pcb_component_id).length, 0,
"JP_BOOT is excluded from the assembly stencil")
assert.equal(of("cad_component").filter((cad) =>
  cad.source_component_id === bootSource.source_component_id).length, 0,
"JP_BOOT has no assembled 3D body")
const bootBridge = of("pcb_trace").filter((trace) =>
  trace.pcb_component_id === bootPlaced.pcb_component_id)
assert.equal(bootBridge.length, 1, "JP_BOOT retains one fabricated copper bridge")
assert.ok(bootBridge[0].route.every((point) =>
  point.route_type !== "wire" || (point.layer === "top" && point.width === 0.2)),
"JP_BOOT bridge is a 0.20 mm cuttable top-copper neck")

// SW_RESET previously paired a small two-pad footprint with a larger four-pad
// TL3342 assembly model. Lock the electrical land pattern, BOM identity, CAD
// model, and mechanical clearances together so that mismatch cannot recur.
const resetPlaced = placedComponent("SW_RESET")
const resetPads = of("pcb_smtpad")
  .filter((pad) => pad.pcb_component_id === resetPlaced.pcb_component_id)
  .sort((a, b) => a.x - b.x)
assert.deepEqual(resetPads.map((pad) => ({
  x: Number((pad.x - resetPlaced.center.x).toFixed(6)),
  y: Number((pad.y - resetPlaced.center.y).toFixed(6)),
  width: pad.width,
  height: pad.height,
})), [
  { x: -1.7, y: 0, width: 0.8, height: 1.7 },
  { x: 1.7, y: 0, width: 0.8, height: 1.7 },
], "SW_RESET uses the Omron B3U-1000P recommended 4.2 x 1.7 mm land pattern")
const resetCad = of("cad_component").find((item) =>
  item.source_component_id === component("SW_RESET").source_component_id)
assert.equal(resetCad.model_obj_url,
  "https://modelcdn.tscircuit.com/easyeda_models/assets/C231329.obj")
assert.equal(resetCad.model_step_url,
  "https://modelcdn.tscircuit.com/easyeda_models/assets/C231329.step")
const boundsAround = (placed, width = placed.width, height = placed.height) => ({
  left: placed.center.x - width / 2,
  right: placed.center.x + width / 2,
  bottom: placed.center.y - height / 2,
  top: placed.center.y + height / 2,
})
const rectangularClearance = (a, b) => Math.hypot(
  Math.max(a.left - b.right, b.left - a.right, 0),
  Math.max(a.bottom - b.top, b.bottom - a.top, 0),
)
const pointToRectClearance = (point, rect) => Math.hypot(
  Math.max(rect.left - point.x, point.x - rect.right, 0),
  Math.max(rect.bottom - point.y, point.y - rect.top, 0),
)
const assemblyBodyBounds = (placed, name) => {
  if (name !== "J_ETH") return boundsAround(placed)
  // HR911105A is rotated 270 degrees. The imported courtyard is 18.585 mm
  // wide because it encloses the two shield tabs; it is not the plastic body.
  // Use the more conservative 16.1 x 21.6 mm imported body/silkscreen outline
  // (HanRun specifies a 16.0 mm body) for the physical screw-head check.
  return {
    left: placed.center.x - 12.8650111,
    right: placed.center.x + 8.7349711,
    bottom: placed.center.y - 8.0503522,
    top: placed.center.y + 8.0503522,
  }
}

// Standard ISO 4762 socket heads are 5.5 mm diameter. Reserve that exact
// top-side assembly envelope for the normal NEMA 17 screw without a washer.
// The motor-facing side uses a small nonconductive spacer.
const mountPlaced = MOUNT_CENTERS.map((center, index) => {
  const placed = placedComponent(`MH${index + 1}`)
  assert.ok(placed, `MH${index + 1} placed component`)
  const courtyard = of("pcb_courtyard_circle")
    .find((entry) => entry.pcb_component_id === placed.pcb_component_id)
  assert.ok(courtyard, `MH${index + 1} hardware courtyard exists`)
  assert.equal(courtyard.radius, MOUNT_HARDWARE_RADIUS,
    `MH${index + 1} courtyard reserves the standard M3 screw-head envelope`)
  assert.deepEqual(courtyard.center, center,
    `MH${index + 1} hardware courtyard is concentric with its drill`)
  return { center, placed }
})
for (const placed of assembled) {
  const name = sourceById.get(placed.source_component_id)?.name
  const body = assemblyBodyBounds(placed, name)
  const requiredRadius = placed.layer === "top"
    ? MOUNT_HARDWARE_RADIUS
    : MOTOR_SIDE_STANDOFF_RADIUS
  for (const { center } of mountPlaced)
    assert.ok(pointToRectClearance(center, body) >= requiredRadius - 1e-6,
      placed.layer === "top"
        ? `${name} body clears the 5.5 mm ISO 4762 M3 no-washer head envelope`
        : `${name} body clears the 5.0 mm motor-side spacer envelope`)
}
const ethernetPlaced = placedComponent("J_ETH")
const ethernetBodyClearance = pointToRectClearance(
  MOUNT_CENTERS[0], assemblyBodyBounds(ethernetPlaced, "J_ETH"),
) - MOUNT_HARDWARE_RADIUS
assert.ok(ethernetBodyClearance >= 0.5,
  "HR911105A body has at least 0.5 mm lower-left ISO 4762 head clearance")
const ethernetFeatures = [...of("pcb_plated_hole"), ...of("pcb_hole")]
  .filter((item) => item.pcb_component_id === ethernetPlaced.pcb_component_id)
const ethernetFeatureClearance = Math.min(...ethernetFeatures.map((item) =>
  Math.hypot(item.x - MOUNT_CENTERS[0].x, item.y - MOUNT_CENTERS[0].y) -
    Math.max(item.outer_diameter ?? 0, item.hole_diameter ?? 0) / 2 -
    MOUNT_HARDWARE_RADIUS))
assert.ok(ethernetFeatureClearance >= 0.5,
  "HR911105A drilled/copper features have at least 0.5 mm lower-left screw-head clearance")
const bootCopper = boundsAround(bootPlaced, 1.7, 0.8)
const bootMountCenter = MOUNT_CENTERS[1]
const nearestBootPoint = {
  x: Math.max(bootCopper.left, Math.min(bootMountCenter.x, bootCopper.right)),
  y: Math.max(bootCopper.bottom, Math.min(bootMountCenter.y, bootCopper.top)),
}
assert.ok(Math.hypot(nearestBootPoint.x - bootMountCenter.x,
  nearestBootPoint.y - bootMountCenter.y) - MOUNT_HOLE_DIAMETER / 2 >= 0.75,
"JP_BOOT copper has at least 0.75 mm clearance from the NEMA17 mounting hole")
const usbDataShell = boundsAround(placedComponent("J_USB_DATA"), 8.94, 7.9)
assert.ok(rectangularClearance(bootCopper, usbDataShell) >= 1.5,
"JP_BOOT copper has at least 1.5 mm clearance from the DATA USB-C shell")
const resetBody = boundsAround(resetPlaced, 4.0, 2.5)
for (const name of ["U_USB_ESD", "C_ESD"])
  assert.ok(rectangularClearance(resetBody, boundsAround(placedComponent(name))) >= 0.5,
    `SW_RESET body has at least 0.5 mm clearance from ${name}`)
const resetMountCenter = MOUNT_CENTERS[1]
const nearestResetPoint = {
  x: Math.max(resetBody.left, Math.min(resetMountCenter.x, resetBody.right)),
  y: Math.max(resetBody.bottom, Math.min(resetMountCenter.y, resetBody.top)),
}
assert.ok(Math.hypot(nearestResetPoint.x - resetMountCenter.x,
  nearestResetPoint.y - resetMountCenter.y) - MOUNT_HOLE_DIAMETER / 2 >= 0.5,
"SW_RESET body has at least 0.5 mm clearance from the NEMA17 mounting hole")

// Linux talks to the real-time MCU; it does not generate step pulses.
expectNet("U1", "UART0_TX", "MOTION_RX")
expectNet("U1", "UART0_RX", "MOTION_TX")
expectNet("U1", "PE2", "MOTION_BOOT")
expectNet("U1", "PE3", "MOTION_NRST")
expectNet("U_MOTION", "PA3", "MOTION_RX")
expectNet("U_MOTION", "PA2", "MOTION_TX")
expectNet("U_MOTION", "PA6", "MOTOR_DIR")
expectNet("U_MOTION", "PA7", "MOTOR_STEP")
expectNet("U_MOTION", "PA4", "MOTOR_ENABLE_RAW")
expectNet("U_MOTION", "PB7_PB8", "MOTOR_FAULT")
expectNet("U_MOTION", "PA0", "MOTION_TEMP_ALERT")
expectNet("U_MOTOR", "STEP", "MOTOR_STEP")
expectNet("U_MOTOR", "DIR", "MOTOR_DIR")
expectNet("U_MOTOR", "nSLEEP", "MOTOR_ENABLE")
expectNet("U_MOTOR", "nRESET", "MOTOR_ENABLE")
expectNet("U_MOTOR", "nFAULT", "MOTOR_FAULT")
expectNet("Q_PD_ENABLE", "S", "MOTOR_ENABLE_RAW")
expectNet("Q_PD_ENABLE", "D", "MOTOR_ENABLE")
expectPair("Q_PD_ENABLE", "G", "U_PD", "GATE")
expectPair("R_PD_GOOD_PU", 1, "U_PD", "GATE")
expectPair("R_PD_GOOD_PU", 2, "Q_VBUS", "S")
expectNet("R_ENABLE_PD", 1, "MOTOR_ENABLE")
expectNet("R_ENABLE_PD", 2, "BOARD_GND")

// Raw PD voltage and gate-valid are independently observed by the MCU.
// No raw-VBUS or GATE net may be directly connected to an MCU input.
expectPart("U_1V2", "TLV75512PDBVR", "C2877864")
expectPair("R_PD_ADC_TOP", 1, "U_PD", "VBUS")
expectPair("R_PD_ADC_TOP", 2, "R_PD_ADC_BOTTOM", 1)
expectNet("R_PD_ADC_BOTTOM", 1, "PD_ADC")
expectNet("R_PD_ADC_BOTTOM", 2, "BOARD_GND")
expectNet("C_PD_ADC", 1, "PD_ADC")
expectNet("C_PD_ADC", 2, "BOARD_GND")
expectNet("U_MOTION", "PA1", "PD_ADC")
expectPair("R_PD_STATUS_TOP", 1, "U_PD", "GATE")
expectPair("R_PD_STATUS_TOP", 2, "Q_PD_STATUS", "G")
expectPair("R_PD_STATUS_BOTTOM_A", 1, "Q_PD_STATUS", "G")
expectPair("R_PD_STATUS_BOTTOM_A", 2, "R_PD_STATUS_BOTTOM_B", 1)
expectNet("R_PD_STATUS_BOTTOM_B", 2, "BOARD_GND")
expectNet("Q_PD_STATUS", "S", "BOARD_GND")
expectNet("Q_PD_STATUS", "D", "PD_POWER_GOOD")
expectNet("U_MOTION", 20, "PD_POWER_GOOD")
expectNet("R_PD_STATUS_PULLUP", 1, "PD_POWER_GOOD")
expectPair("R_PD_STATUS_PULLUP", 2, "U_MOTION", 4)
expectPart("Q_PD_STATUS", "BSS138", "C52895")
for(const name of ["R_PD_ADC_TOP","R_PD_STATUS_TOP","R_PD_STATUS_BOTTOM_A","R_PD_STATUS_BOTTOM_B"]) {
  expectPart(name,"0402WGF1004TCE","C26083")
  assert.equal(component(name).resistance,1_000_000)
}
for(const name of ["R_PD_ADC_BOTTOM","R_PD_STATUS_PULLUP"]) {
  expectPart(name,"0402WGF1003TCE","C25741")
  assert.equal(component(name).resistance,100_000)
}
assert.equal(component("C_PD_ADC").capacitance,100e-9)
for(const [name,pin] of [["U_PD","VBUS"],["U_PD","GATE"]])
  for(const adcPin of [8,20]) assert.notEqual(root(port(name,pin)),root(port("U_MOTION",adcPin)),
    "high-voltage PD signals remain isolated from MCU inputs")

// Fixed 1/16 microstepping, software-set nominal 0–396 mA peak per phase.
expectPart("U_CURRENT_DAC", "MCP4725A0T-E/CH", "C144198")
expectPart("C_CURRENT_DAC", "CL03A104KP3NNNC", "C49062")
expectPair("U_CURRENT_DAC", "VOUT", "R_REF_TOP", 1)
expectPair("U_CURRENT_DAC", "VDD", "C_U_2V5_IN", 1)
expectPair("U_CURRENT_DAC", "SCL", "U_MOTION", 16)
expectPair("U_CURRENT_DAC", "SDA", "U_MOTION", 17)
expectNet("U_CURRENT_DAC", "A0", "BOARD_GND")
expectNet("U_CURRENT_DAC", "VSS", "BOARD_GND")
expectNet("U_MOTOR", "NENBL", "BOARD_GND")
expectNet("U_MOTOR", "MODE0", "BOARD_GND")
expectNet("U_MOTOR", "MODE1", "BOARD_GND")
expectPair("U_MOTOR", "MODE2", "R_V3V3_MOTOR", 2)
assert.equal(component("R_REF_TOP").resistance, 9100)
assert.equal(component("R_REF_BOTTOM").resistance, 2000)
assert.equal(component("R_ISEN_A").resistance, 0.3)
assert.equal(component("R_ISEN_B").resistance, 0.3)
const traceWidths = (name) => {
  const source = of("source_trace").find((item) => item.name === name)
  assert.ok(source, `source trace ${name}`)
  return of("pcb_trace").filter((item) => item.source_trace_id === source.source_trace_id)
    .flatMap((item) => item.route.filter((point) => point.route_type === "wire").map((point) => point.width))
}
const traceLayers = (name) => {
  const source = of("source_trace").find((item) => item.name === name)
  assert.ok(source, `source trace ${name}`)
  return of("pcb_trace").filter((item) => item.source_trace_id === source.source_trace_id)
    .flatMap((item) => item.route.filter((point) => point.route_type === "wire").map((point) => point.layer))
}
for (const name of ["MOTOR_A_PLUS", "MOTOR_A_MINUS", "MOTOR_B_PLUS", "MOTOR_B_MINUS",
  "MOTOR_A_SENSED_PLUS", "MOTOR_B_SENSED_PLUS"]) {
  const widths = traceWidths(name)
  assert.ok(widths.length > 0 && Math.min(...widths) >= 0.275 && Math.max(...widths) === 0.3,
    `${name} is routed at approximately 0.30 mm`)
  assert.ok(traceLayers(name).every((layer) => layer === "top" || layer === "bottom"),
    `${name} stays on outer copper`)
}
for (const name of ["VM_INPUT", "VM_FUSED"]) {
  const widths = traceWidths(name)
  assert.ok(widths.length > 0 && Math.min(...widths) >= 0.15,
    `${name} maintains at least 0.15 mm copper for the fixed 0.40 A/phase target`)
  assert.ok(traceLayers(name).every((layer) => layer === "top" || layer === "bottom"),
    `${name} stays on outer copper`)
}

// The DNP parallel option can only reduce the default DRV8825 current limit.
expectPair("R_ISET_210", 1, "C_REF", 1)
expectNet("R_ISET_210", 2, "BOARD_GND")
assert.equal(component("R_ISET_210").resistance, 1800)
const parallel = (...values) => 1 / values.reduce((sum, value) => sum + 1 / value, 0)
const motorCurrent = (bottomResistance) =>
  (3.3 * bottomResistance / (9100 + bottomResistance)) / (5 * 0.3)
assert.ok(Math.abs(motorCurrent(2000) - 0.3964) < 0.0001)
assert.ok(Math.abs(motorCurrent(parallel(2000, 1800)) - 0.2074) < 0.0001)
assert.ok(motorCurrent(parallel(2000, 1800)) < motorCurrent(2000),
  "closing the current selector remains fail-safe")

// The power-only USB-C input negotiates PD; the self-powered DATA connector
// carries only USB2/FEL and has independent Type-C device pull-downs.
expectPair("J_USB_PWR", 15, "J_USB_PWR", 16)
expectPair("J_USB_PWR", 15, "Q_VBUS", "S")
expectPair("J_USB_PWR", 15, "U_PD", "VBUS")
assert.notEqual(root(port("J_USB_PWR", 15)), root(port("Q_VBUS", "D")),
  "raw receptacle VBUS remains isolated from switched VBUS_PD")
// CC1/CC2 are symmetric HUSB238A orientation-detector inputs. They are
// intentionally crossed here to avoid a board-level copper crossing; the
// unused ORIENT indication would simply have inverted polarity.
expectPair("J_USB_PWR", 6, "U_PD", "CC2")
expectPair("J_USB_PWR", 12, "U_PD", "CC1")
expectNet("J_USB_PWR", 13, "BOARD_GND")
expectNet("J_USB_PWR", 14, "BOARD_GND")
for (const pin of [7, 9]) assert.notEqual(root(port("J_USB_PWR", pin)), root(net("USB_N")))
for (const pin of [8, 10]) assert.notEqual(root(port("J_USB_PWR", pin)), root(net("USB_P")))

expectNet("J_USB_DATA", 7, "USB_N_PORT")
expectNet("J_USB_DATA", 9, "USB_N_PORT")
expectNet("J_USB_DATA", 8, "USB_P_PORT")
expectNet("J_USB_DATA", 10, "USB_P_PORT")
expectNet("U_USB_ESD", 1, "USB_N_PORT")
expectNet("U_USB_ESD", 3, "USB_P_PORT")
// USBLC6-2SC6 is SOT23-6L, not the substantially smaller SOT-666 P6.
expectPart("U_USB_ESD", "USBLC6-2SC6", "C7519")
const esdLands = of("pcb_smtpad").filter(p => p.pcb_component_id === placedComponent("U_USB_ESD").pcb_component_id)
assert.equal(esdLands.length, 6)
assert.ok(esdLands.every(p => Math.abs(p.width - 1.2) < 1e-6 && Math.abs(p.height - .6) < 1e-6))
assert.ok(Math.abs(Math.max(...esdLands.map(p => p.x)) - Math.min(...esdLands.map(p => p.x)) - 2.3) < 1e-6)
assert.ok(Math.abs(Math.max(...esdLands.map(p => p.y)) - Math.min(...esdLands.map(p => p.y)) - 1.9) < 1e-6)
expectPart("U_USB_GATE", "TS3USB31ERSER", "C967436")
expectPart("Q_USB_ATTACH", "MMBT3904(RANGE:100-300)", "C20526")
expectPair("U_USB_ESD", 4, "U_USB_GATE", "D_N")
expectPair("U_USB_ESD", 6, "U_USB_GATE", "D_P")
expectNet("U_USB_GATE", "HSD_N", "USB_P")
expectNet("U_USB_GATE", "HSD_P", "USB_N")
for (const [input, output] of [["D_P", "HSD_P"], ["D_N", "HSD_N"]])
  assert.notEqual(root(port("U_USB_GATE", input)), root(port("U_USB_GATE", output)),
    "no PCB copper bypasses either hardware disconnect channel")
expectPair("J_USB_DATA", 15, "J_USB_DATA", 16)
expectPair("J_USB_DATA", 16, "U_USB_ESD", 5)
expectPair("U_USB_ESD", 5, "C_ESD", 1)
expectPair("J_USB_DATA", 16, "R_USB_BASE", 1)
expectPair("R_USB_BASE", 2, "Q_USB_ATTACH", "B")
expectPair("R_USB_BASE_PD", 1, "Q_USB_ATTACH", "B")
expectNet("R_USB_BASE_PD", 2, "BOARD_GND")
expectNet("Q_USB_ATTACH", "E", "BOARD_GND")
expectPair("Q_USB_ATTACH", "C", "U_USB_GATE", "N_OE")
expectPair("R_USB_OE_PU", 1, "U_USB_GATE", "N_OE")
expectPair("R_USB_OE_PU", 2, "U_USB_GATE", "VCC")
expectPair("C_USB_GATE", 1, "U_USB_GATE", "VCC")
expectPair("U_USB_GATE", "VCC", "VCC_TV", 1)
expectNet("U_USB_GATE", "GND", "BOARD_GND")
expectNet("C_USB_GATE", 2, "BOARD_GND")
assert.equal(component("R_USB_BASE").resistance, 47000)
assert.equal(component("R_USB_BASE_PD").resistance, 100000)
assert.equal(component("R_USB_OE_PU").resistance, 10000)
assert.equal(component("C_USB_GATE").capacitance, 100e-9)
assert.equal(of("source_trace").find(trace=>trace.name==="USB_GATE_VCC")?.max_length,5,
  "the local USB-switch bypass keeps its 5 mm decoupling constraint")
assert.notEqual(root(port("J_USB_DATA", 16)), root(port("U_USB_GATE", "VCC")),
  "host VBUS only biases Q through 47k and never supplies the system rail")
expectPair("J_USB_DATA", 6, "R_USB_DATA_CC1", 1)
expectNet("R_USB_DATA_CC1", 2, "BOARD_GND")
expectPair("J_USB_DATA", 12, "R_USB_DATA_CC2", 2)
expectNet("R_USB_DATA_CC2", 1, "BOARD_GND")
assert.equal(component("R_USB_DATA_CC1").resistance, 5100)
assert.equal(component("R_USB_DATA_CC2").resistance, 5100)
for (const pin of [15, 16]) {
    assert.notEqual(root(port("J_USB_DATA", pin)), root(port("Q_VBUS", "D")))
    assert.notEqual(root(port("J_USB_DATA", pin)), root(port("L_BUCK", 2)))
}

// USB-PD power path and protected 15 V motor rail. HUSB238A can cold-start
// from its raw VBUS input while VDD is unavailable, then drives the P-FET.
expectPair("U_PD", "VDD", "R_V3V3_RIGHT", 2)
expectNet("U_PD", "EN_N", "BOARD_GND")
expectPair("U_PD", "SNK_VSET", "R_PD_VSET", 1)
expectPair("U_PD", "SNK_ISET", "R_PD_ISET", 1)
expectPair("U_PD", "DBG_N", "R_PD_DEBUG", 1)
assert.equal(component("R_PD_VSET").resistance, 11000)
assert.equal(component("R_PD_ISET").resistance, 21000)
assert.equal(component("R_PD_DEBUG").resistance, 910000)
expectPair("R_PD_12V", 1, "R_PD_VSET", 1)
expectNet("R_PD_12V", 2, "BOARD_GND")
assert.equal(component("R_PD_12V").resistance, 13300)
assert.ok(Math.abs(parallel(11000, 13300) - 6020.576) < 0.01,
  "PD voltage selector produces the nominal 12 V VSET resistance")
expectPair("Q_VBUS", "G", "U_PD", "GATE")
expectPair("Q_VBUS", "D", "F_VM", 1)
for (const pin of [2, 3]) expectPair("Q_VBUS", "S", "Q_VBUS", pin)
for (const pin of [6, 7, 8]) expectPair("Q_VBUS", "D", "Q_VBUS", pin)
for (const name of ["VBUS_SOURCE_1", "VBUS_SOURCE_2",
  "VBUS_DRAIN_5", "VBUS_DRAIN_7"]) {
  const widths = traceWidths(name)
  assert.ok(widths.length > 0 && Math.min(...widths) >= 0.6,
    `${name} maintains at least 0.60 mm copper outside package pads`)
}
// The Type-C VBUS contacts require short 0.25 mm pad escapes. Both routes widen
// to 0.80 mm immediately after their dogbone vias and remain wide through the
// internal-layer trunks.
const usbVbusAWidths = traceWidths("USB_VBUS_A")
assert.ok(usbVbusAWidths.length > 0 && Math.min(...usbVbusAWidths) >= 0.25 &&
  Math.max(...usbVbusAWidths) >= 0.8,
  "USB_VBUS_A uses 0.25 mm connector escapes and an 0.80 mm trunk")
const usbVbusBWidths = traceWidths("USB_VBUS_B")
assert.ok(usbVbusBWidths.length > 0 && Math.min(...usbVbusBWidths) >= 0.25 &&
  Math.max(...usbVbusBWidths) >= 0.8,
  "USB_VBUS_B uses a 0.25 mm connector escape and widens to a 0.80 mm trunk")
expectPair("F_VM", 1, "Q_VBUS", "D")
expectPair("F_VM", 2, "D_VM_BLOCK", "anode")
expectNet("D_VM_BLOCK", "cathode", "VMOTOR")
expectNet("U_MOTOR", "VMA", "VMOTOR")
expectNet("U_MOTOR", "VMB", "VMOTOR")
assert.notEqual(root(net("VMOTOR")), root(port("L_BUCK", 2)))
assert.notEqual(root(net("VMOTOR")), root(port("R_V3V3_RIGHT", 2)))

// WIZnet reference MDI network: 49.9 ohm 1% termination, RX AC coupling,
// 10 ohm TX center-tap feed and the prescribed center-tap decoupling values.
for (const name of ["R_ETH_TXN", "R_ETH_TXP", "R_ETH_RXN", "R_ETH_RXP"])
  assert.equal(component(name).resistance, 49.9)
assert.equal(component("C_ETH_RXN_AC").capacitance, 6.8e-9)
assert.equal(component("C_ETH_RXP_AC").capacitance, 6.8e-9)
assert.equal(component("R_ETH_TCT").resistance, 10)
assert.equal(component("C_ETH_TCT").capacitance, 22e-9)
assert.equal(component("C_ETH_RCT").capacitance, 10e-9)
expectPair("U_ETH", "TXP", "J_ETH", 1)
expectPair("U_ETH", "TXN", "J_ETH", 2)
expectPair("U_ETH", "RXP", "C_ETH_RXP_AC", 1)
expectPair("C_ETH_RXP_AC", 2, "J_ETH", 3)
expectPair("U_ETH", "RXN", "C_ETH_RXN_AC", 1)
expectPair("C_ETH_RXN_AC", 2, "J_ETH", 6)

// W5500 is the dedicated internal SPI1 peripheral; there are no expansion headers.
for (const [socPin, ethNet] of [["PE7", "ETH_CS"], ["PE8", "ETH_MOSI"],
  ["PE10", "ETH_MISO"], ["PE5", "ETH_RST"], ["PE6", "ETH_INT"]]) {
  expectNet("U1", socPin, ethNet)
}
expectNet("U1", "PE9", "ETH_SCLK_SOC")
expectNet("U_ETH", "SCSn", "ETH_CS")
expectNet("U_ETH", "MOSI", "ETH_MOSI")
expectPair("U_ETH", "SCLK", "R_ETH_SCLK", 1)
expectPart("R_ETH_SCLK","RC0201JR-070RL","C106228")
expectNet("R_ETH_SCLK", 2, "ETH_SCLK_SOC")
expectNet("U_ETH", "MISO", "ETH_MISO")
expectNet("U_ETH", "RSTn", "ETH_RST")
expectNet("U_ETH", "INTn", "ETH_INT")
assert.equal(of("source_component").filter((item) => ["J_SPI", "J_I2C"].includes(item.name)).length, 0)

// Connector mapping and motion-MCU-owned temperature telemetry.
for (const [driverPin, connectorPin] of [["AOUT2", 3], ["BOUT2", 2]]) {
  expectPair("U_MOTOR", driverPin, "J_MOTOR", connectorPin)
}
for (const [phase, driverPin, connectorPin, adcPin] of [["A","AOUT1",4,12],["B","BOUT1",1,15]]) {
  const shunt=`R_INLINE_${phase}`, amplifier=`U_CURRENT_${phase}`
  const filter=`R_CURRENT_${phase}_FILTER`, capacitor=`C_CURRENT_${phase}_FILTER`
  expectPair("U_MOTOR",driverPin,shunt,1)
  expectPair(shunt,2,"J_MOTOR",connectorPin)
  assert.notEqual(root(port(shunt,1)),root(port(shunt,2)),`${phase} shunt must not be bypassed`)
  assert.equal(component(shunt).resistance,.3,`${phase} telemetry shunt is 0.30 ohm`)
  expectPart(`C_CURRENT_${phase}_SUPPLY`,"TCC0201X7R104K100ZT","C7393837")
  assert.equal(component(`C_CURRENT_${phase}_SUPPLY`).max_voltage_rating,10,
    "current-sense 3.3 V bypass declares its actual 10 V catalog rating")
  expectPair(amplifier,"IN_POS",shunt,1)
  expectPair(amplifier,"IN_NEG",shunt,2)
  expectPair(amplifier,"VS","U_MOTION",4)
  expectPair(amplifier,"REF1","U_MOTION",4)
  for (const pin of ["GND","REF2","NC"]) expectNet(amplifier,pin,"BOARD_GND")
  expectPair(amplifier,"OUT",filter,1)
  expectPair(filter,2,capacitor,1)
  expectPair(filter,2,"U_MOTION",adcPin)
  expectNet(capacitor,2,"BOARD_GND")
  assert.equal(component(filter).resistance,1000,`${phase} ADC isolation resistor is 1k`)
  assert.equal(component(capacitor).capacitance,1e-7,`${phase} ADC filter is 100nF`)
}
expectNet("U_TEMP", "SDA", "MOTION_I2C_SDA")
expectNet("U_TEMP", "SCL", "MOTION_I2C_SCL")
expectNet("U_TEMP", "ADD0", "BOARD_GND")
expectNet("U_TEMP", "ALERT", "MOTION_TEMP_ALERT")

// The motion MCU owns all local diagnostics so fault indication survives a
// stalled Linux scheduler. PA13 feeds the 5 V logic buffer/LED, while PB9's
// timer PWM drives a protected low-side magnetic-buzzer stage.
expectPair("U_MOTION", "PA13", "R_STATUS_PD", 1)
expectPair("R_STATUS_PD", 1, "U_STATUS_BUFFER", "A")
expectNet("R_STATUS_PD", 2, "BOARD_GND")
expectNet("U_STATUS_BUFFER", "N_OE", "BOARD_GND")
expectNet("U_STATUS_BUFFER", "GND", "BOARD_GND")
expectPair("U_STATUS_BUFFER", "Y", "R_STATUS_DATA", 1)
expectPair("R_STATUS_DATA", 2, "D_STATUS", "DI")
expectPair("D_STATUS", "VDD", "U_STATUS_BUFFER", "VCC")
expectNet("D_STATUS", "GND", "BOARD_GND")
expectPair("U_MOTION", "PB9_PC14_OSC32_IN", "R_BUZZER_GATE", 1)
expectPair("R_BUZZER_GATE", 2, "Q_BUZZER", "G")
expectPair("R_BUZZER_PD", 1, "Q_BUZZER", "G")
expectNet("R_BUZZER_PD", 2, "BOARD_GND")
expectNet("Q_BUZZER", "S", "BOARD_GND")
expectPair("BZ1", "NEG", "Q_BUZZER", "D")
expectPair("D_BUZZER", "anode", "BZ1", "NEG")
expectPair("D_BUZZER", "cathode", "BZ1", "POS")
expectPair("BZ1", "POS", "C_MOTION_BULK", 1)

const motorPlaced = of("pcb_component").find((item) =>
  item.source_component_id === component("U_MOTOR").source_component_id)
const motorEpGround = of("source_trace").find((item) => item.name === "DRV_EP_GND")
const thermalVias = vias.filter((via) => via.pcb_via_id.startsWith("driver_thermal_"))
assert.equal(thermalVias.length, 12)
assert.ok(thermalVias.some(via=>via.x===-.85 && via.y===-12.9),
  "the DAC ground stitch reaches the existing driver ground pad")
assert.ok(thermalVias.every((via) => via.source_trace_id === motorEpGround.source_trace_id),
  "all native thermal drills belong to the motor PowerPAD ground")
assert.ok(thermalVias.every((via) =>
  Math.abs(via.x - motorPlaced.center.x) <= 2.8 &&
  Math.abs(via.y - motorPlaced.center.y) <= 1.55),
"all twelve thermal vias drill through the DRV8825 exposed pad")

// Stock report must correspond to this exact generated circuit and contain no failures.
const stock = JSON.parse(fs.readFileSync("reports/jlcpcb-stock-audit.json", "utf8"))
const circuitSha256 = crypto.createHash("sha256").update(circuitBytes).digest("hex")
const continuity=JSON.parse(fs.readFileSync("reports/connectivity-audit.json","utf8"))
assert.equal(continuity.circuitSha256,circuitSha256,"continuity audit matches built copper")
assert.equal(continuity.sourceFailures,0)
assert.equal(continuity.disconnectedTerminalIslands,0)
assert.equal(continuity.emittedCopperOnly,true)
const preflightPath="reports/coil-all-copper-preflight.json"
const preflight=JSON.parse(fs.readFileSync(preflightPath,"utf8"))
const preflightMeta=JSON.parse(fs.readFileSync("reports/coil-all-copper-preflight-meta.json","utf8"))
const fileSha256=path=>crypto.createHash("sha256").update(fs.readFileSync(path)).digest("hex")
assert.equal(preflightMeta.circuitSha256,circuitSha256,"independent copper preflight matches this emitted circuit")
assert.equal(preflightMeta.physicalCopperSha256,fileSha256("routing/physical-copper.json"))
assert.equal(preflightMeta.findingsSha256,fileSha256(preflightPath))
assert.equal(preflightMeta.failureCount,0,"independent copper clearance must pass, not only native DRC")
assert.deepEqual(preflight,[],"no independent copper-clearance regressions")
assert.equal(stock.circuitSha256, circuitSha256, "stock report matches current circuit JSON")
assert.equal(stock.populatedComponentCount, populated.length)
assert.equal(stock.uniquePartCount, new Set(populated.map(part=>part.supplier_part_numbers.jlcpcb[0])).size)
assert.deepEqual(stock.missingCodes, [])
assert.ok(stock.boardQuantity>=5,"stock audit covers at least five assembled boards")
assert.ok(stock.parts.every((part) => part.status === "in-stock" && part.orderableStock >= part.requiredForBatch),
  "all JLCPCB parts cover board quantity, assembly minimums and attrition")
const drillRegression = JSON.parse(fs.readFileSync("reports/drill-clearance-regression.json", "utf8"))
assert.equal(drillRegression.circuitSha256, circuitSha256, "drill comparison matches emitted copper")
assert.equal(drillRegression.regressionCount, 0, "no added drill/foreign-copper clearance violations")
const moduleRegression = JSON.parse(fs.readFileSync("reports/module-test-regression.json", "utf8"))
assert.equal(moduleRegression.addedFailures, 0, "no added stored-module test failures")
assert.ok(moduleRegression.currentFailed <= moduleRegression.baselineFailed,"module failures may be fixed, not introduced")
assert.equal(moduleRegression.currentPassed+moduleRegression.currentFailed,32,"all original module tests still execute")
const bomValidation=JSON.parse(fs.readFileSync("reports/bom-validation.json","utf8"))
assert.equal(bomValidation.circuitSha256,circuitSha256,"catalog electrical checks match emitted circuit")
assert.deepEqual(bomValidation.findings,[],"no wrong-value/rating/declared-MPN selections")
const additionalDfm=JSON.parse(fs.readFileSync("reports/additional-dfm-review.json","utf8"))
assert.equal(additionalDfm.circuitSha256,circuitSha256)
assert.deepEqual(additionalDfm.findings,[],"no additional pad/PTH DFM findings in the stated scope")
const solderProcess=JSON.parse(fs.readFileSync("reports/solder-process-audit.json","utf8"))
assert.equal(solderProcess.circuitSha256,circuitSha256,"solder-process review matches emitted circuit")
assert.equal(solderProcess.ordinaryPadDrillIntersections.length,0,"no via drill intersects any populated ordinary SMT solder land")
const stencilArtwork=JSON.parse(fs.readFileSync("reports/stencil-artwork-audit.json","utf8"))
assert.equal(stencilArtwork.circuitSha256,circuitSha256,"stencil artwork audit matches emitted circuit")
assert.deepEqual(stencilArtwork.failures,[],"every populated SMT land needs same-face paste artwork, no bare test-pad paste")
assert.equal(stencilArtwork.missingLandCount,0)

const report = {
  circuitSha256,
  fabricationReady: false,
  fabricationBlockers: {
    independentDrillClearanceFindings: drillRegression.currentFindings,
    silkscreenBelowPublished1mmHeight: additionalDfm.silkscreenBelowPublished1mmHeight,
    ordinaryPadDrillIntersections: solderProcess.ordinaryPadDrillIntersections.length,
    thermalSolderProcessQualified: solderProcess.thermalStencilProcessQualified,
    filledCappedViaProcessSpecified: solderProcess.specifiedFilledCappedProcess,
    stencilArtworkCoveredLands: stencilArtwork.populatedExposedSmtLands,
    stencilArtworkIsNotProcessApproval: true,
    usbDataVbusAttachDetection: "hardware TS3USB31E isolation implemented, including ROM FEL; powered/unpowered host attach and USB signal integrity still require bench qualification",
    inheritedModuleTestFailures: moduleRegression.currentFailed,
    ethernetAndAnalogSignalIntegrity: "requires further layout/stackup review",
    currentFabricationHandoff: "not exported; unresolved solder-process gate blocks assembly handoff",
    hardwareBringupAndCurrentCalibration: "not performed",
    actualRearMotorFastening: "requires physical confirmation",
  },
  board: {
    motorFacingBodyMm: { width: 42.3, height: 42.3 },
    pcbOutlineMm: { width: board.width, height: board.height },
    layers: board.num_layers,
    connectorHoleMinEdgeClearanceMm: { ethernet: 0.355, motor: 0.35 },
    mounting: {
      patternMm: 31,
      carrierRotationDeg: Number(mountPatternRotationDeg.toFixed(3)),
      projectedCarrierOverhangMm: Number(projectedCarrierOverhangMm.toFixed(3)),
      holeDiameterMm: MOUNT_HOLE_DIAMETER,
      fittedBodyEnvelopeDiameterMm: 2 * MOUNT_HARDWARE_RADIUS,
      motorSideSpacerEnvelopeDiameterMm: 2 * MOTOR_SIDE_STANDOFF_RADIUS,
      hardware: "Standard ISO 4762 M3 socket-head screw, 5.5 mm nominal head; no washer",
      motorSideInterface: "5.0 mm OD nonconductive spacer; verify the rear endbell interface and measure replacement M3 through-bolt length on the actual motor",
      ethernetBodyClearanceMm: Number(ethernetBodyClearance.toFixed(3)),
      ethernetFeatureClearanceMm: Number(ethernetFeatureClearance.toFixed(3)),
    },
  },
  drcErrors: errors.length,
  nativeWarnings: circuit.filter(item => item.type.endsWith("_warning")).length,
  addedDrillClearanceFindings: drillRegression.regressionCount,
  storedModuleTests: { status: moduleRegression.status, passed: moduleRegression.currentPassed,
    failed: moduleRegression.currentFailed, addedFailures: moduleRegression.addedFailures },
  pcbTraces: of("pcb_trace").length,
  pcbVias: vias.length,
  viaHoleDiameterMm: 0.3,
  viaPadDiameterMm: 0.45,
  populatedComponents: populated.length,
  bottomAssemblyComponents: assembled.filter((placed) => placed.layer === "bottom").length,
  jlcpcbUniqueParts: stock.uniquePartCount,
  jlcpcbStockIssues: stock.parts.filter((part) => part.status !== "in-stock").length,
  assemblyOrderCaveats: stock.parts.filter(part =>
    part.orderableStock < (part.minimumAssemblyQuantity ?? part.quantityPerBoard))
    .map(({code,designators,orderableStock,minimumAssemblyQuantity,assemblyLossQuantity}) =>
      ({code,designators,orderableStock,minimumAssemblyQuantity,assemblyLossQuantity})),
  targetMotor: "StepperOnline 17HS13-0404S1 NEMA 17, 0.40 A/phase, 30 ohm, 37 mH +/-20%",
  motorStage: {
    driver: "DRV8825PWPR",
    supply: "15 V USB-PD default; 12 V selectable",
    nominalSoftwarePeakCurrentRangeMa: [0, 396],
    currentSetting: "MCP4725; disarmed-only; verified readback; legacy R_ISET_210 remains DNP",
    motorPhaseCopper: "outer layers",
    fusedSupplyCopper: "0.50 mm fuse feed; 0.80 mm VM trunk and restored motor-area bridge, with retained local pad breakouts; outer layers",
    microstep: "1/16",
    coilTelemetry: "two inline 0.300 ohm shunts, INA241B1 gain 10, 1k/100nF filters; nominal signed mA, not RMS or protection",
  },
  control: { linux: "F1C100S", realtimeMotion: "STM32G030F6P6", link: "UART0" },
  ethernet: "W5500 + HR911105A",
  thermalVias: thermalVias.length,
}
fs.writeFileSync("verification.json", `${JSON.stringify(report, null, 2)}\n`)
console.log(JSON.stringify(report, null, 2))