0hmX/am3352-ddr-track-a-local-dfm-wip-20261007

Experimental RJ45-corrected board with isolated PG indication. 1005/1005 source joins pass; 15 expanded-guard physical errors, sourcing and qualification gates remain. Not fabrication-ready.

Version
0.0.14
License
unset
Stars
0

work/vip-routing-repro.circuit.tsx

import { Circuit } from "@tscircuit/core"
import { Children, cloneElement, isValidElement, Fragment, type ReactElement } from "react"
import { AM3352 } from "../components/AM3352"
import { W631GG6MB_12, pinLabels as ramLabels } from "../imports/W631GG6MB_12"
import { checkTracesAreContiguous, checkEachPcbPortConnectedToPcbTraces, checkViaPadClearance, checkEachPcbTraceNonOverlapping, checkPadTraceClearance } from "@tscircuit/checks"
import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map"

type ViaBinding = "inherited" | "explicit-net"

/** Native TSX reproduction: two connected pads with coincident blind vias. */
export default function ViaInPadRoutingRepro({ binding = "inherited", phased = false, actualBga = true, sparse = false, rounded = false, viaOffset = 0, noVias = false, savedFanout = false }: { binding?: ViaBinding; phased?: boolean; actualBga?: boolean; sparse?: boolean; rounded?: boolean; viaOffset?: number; noVias?: boolean; savedFanout?: boolean }) {
  const explicitNet = (chip: ReactElement<any>) => cloneElement(chip, {
    footprint: cloneElement(chip.props.footprint, {}, ...Children.toArray(chip.props.footprint.props.children).flatMap(child => {
      if (!isValidElement<any>(child)) return [child]
      if (sparse && child.type === "smtpad" && !child.props.portHints.some((pin: string) => chip.props.connections[pin])) return []
      if (noVias && child.type === "via") return []
      return [cloneElement(child, {
        ...(child.type === "via" && binding === "explicit-net" ? { connectsTo: "net.SIGNAL" } : {}),
        ...(rounded && (child.type === "via" || child.type === "smtpad") ? {
          pcbX: Number.parseFloat(child.props.pcbX).toFixed(6), pcbY: Number.parseFloat(child.props.pcbY).toFixed(6),
        } : {}),
        ...(viaOffset && child.type === "via" ? { pcbX: Number.parseFloat(child.props.pcbX) + viaOffset } : {}),
      })]
    })),
  })
  const maybeFanout = (chip: ReactElement<any>) => {
    if (!savedFanout) return explicitNet(chip)
    const pads = Children.toArray(chip.props.footprint.props.children).filter(child => isValidElement<any>(child) && child.type === "smtpad") as ReactElement<any>[]
    const pin = Object.keys(chip.props.connections)[0]
    const pad = pads.find(pad => pad.props.portHints.includes(pin))!
    const x = Number.parseFloat(pad.props.pcbX), y = Number.parseFloat(pad.props.pcbY)
    return <fanout name={`${chip.props.name}_VIP`} pcbX={chip.props.pcbX} pcbY={chip.props.pcbY}
      autorouter={{ allowViaInPad: true }} pcbTracePaths={[{ connection: `.${chip.props.name} > .${pin}`, route: [
        { route_type: "via", x, y, from_layer: "top", to_layer: "inner2", via_diameter: 0.3, via_hole_diameter: 0.15 },
        { route_type: "wire", x, y, width: 0.1, layer: "inner2" },
        { route_type: "wire", x: x + 0.2, y, width: 0.1, layer: "inner2" },
      ] }]}>
      {cloneElement(chip, { pcbX: 0, pcbY: 0, footprint: cloneElement(chip.props.footprint, {}, ...Children.toArray(chip.props.footprint.props.children).filter(child => !isValidElement(child) || child.type !== "via")) })}
    </fanout>
  }
  const ramPin = Object.entries(ramLabels).find(([, [, signal]]) => signal === "A5")![0]
  return <board width={98} height={65.5} layers={8} isViaInPadAllowed allowBlindAndBuriedVias
    outline={[{ x: -28, y: 26.5 }, { x: 70, y: 26.5 }, { x: 70, y: -39 }, { x: -28, y: -39 }]}
    autorouter={{ preset: "auto_local", allowViaInPad: true }} autorouterVersion="latest"
    minTraceWidth={0.1} minTraceToPadEdgeClearance={0.1} minPadEdgeToPadEdgeClearance={0.1}
    minViaHoleDiameter={0.15} minViaPadDiameter={0.3} minBoardEdgeClearance={0.2} routeRemaining={!phased}>
    <net name="SIGNAL" routingPhaseIndex={phased ? 1 : undefined} />
    {!actualBga && <net name="OTHER" routingPhaseIndex={phased ? 2 : undefined} />}
    {phased && <><autoroutingphase phaseIndex={1} /><autoroutingphase phaseIndex={2} /></>}
    {actualBga ? <>
      {maybeFanout(AM3352({ name: "U1", pcbX: 0, pcbY: 0, connections: { pin2: "net.SIGNAL" }, noSchematicRepresentation: true, viaInPad: true, viaInPadToLayer: "inner2" }))}
      {maybeFanout(W631GG6MB_12({ name: "U3", pcbX: 0, pcbY: -27, connections: { [ramPin]: "net.SIGNAL" }, noSchematicRepresentation: true, viaInPad: true, viaInPadToLayer: "inner2" }))}
    </> : [-3, 3].map((x, index) => <chip key={index} name={`U${index + 1}`} pcbX={x} pcbY={0}
      pinLabels={{ pin1: ["B1"], pin2: ["A1"] }} connections={{ pin1: "net.SIGNAL", pin2: "net.OTHER" }}
      footprint={<footprint>
        {["SIGNAL", "OTHER"].map((net, pin) => <Fragment key={pin}>
          <smtpad key={`pad${pin}`} portHints={[`pin${pin + 1}`]} pcbX={0} pcbY={pin * 0.8} shape="circle" radius={0.2} />
          <via key={`via${pin}`} name={`padVia_pin${pin + 1}`} pcbX={0} pcbY={pin * 0.8} holeDiameter={0.15} outerDiameter={0.3}
            fromLayer="top" toLayer="inner2" connectsTo={binding === "explicit-net" ? `net.${net}` : undefined} />
        </Fragment>)}
      </footprint>} />)}
  </board>
}

if (import.meta.main) {
  // Default succeeds only when the known dense-BGA topology failure reproduces.
  // --saved-fanout succeeds only when the workaround routes and passes all checks.
  // --generic, --sparse and --no-vias are passing isolation controls.
  // An unexpected result in any variant exits nonzero; nothing is serialized.
  let failures = 0
  function assert(condition: unknown, message: string): asserts condition {
    if (!condition) throw Error(message)
  }
  const actualBga = !process.argv.includes("--generic")
  const sparse = process.argv.includes("--sparse")
  const rounded = process.argv.includes("--rounded")
  const viaOffset = Number(process.argv.find(arg => arg.startsWith("--via-offset="))?.split("=")[1] ?? 0)
  const noVias = process.argv.includes("--no-vias")
  const savedFanout = process.argv.includes("--saved-fanout")
  const bindings: ViaBinding[] = savedFanout ? ["inherited"] : ["inherited", "explicit-net"]
  for (const phased of actualBga ? [false] : [false, true]) for (const binding of bindings) {
    const circuit = new Circuit()
    circuit.schematicDisabled = true
    const started = Date.now()
    const variant = `${binding}, phased=${phased}, actualBga=${actualBga}, sparse=${sparse}, rounded=${rounded}, viaOffset=${viaOffset}, noVias=${noVias}, savedFanout=${savedFanout}`
    console.log(`START ${variant}`)
    circuit.on("solver:started", (event: any) => {
      const input = event.solverParams?.input
      console.log(`  ${event.solverName}: ${input?.connections?.length} connections, ${input?.obstacles?.length} obstacles`)
      for (const obstacle of input?.obstacles ?? []) {
        if (Math.abs(Math.abs(obstacle.center?.x) - 3) < 1e-6) {
          console.log(`  coincident obstacle ${obstacle.obstacleId ?? obstacle.componentId}: net=${obstacle.connectedTo?.filter((id: string) => id.startsWith("connectivity_net")).join(",")} layers=${obstacle.layers?.join(",")}`)
        }
      }
    })
    const timeout = setTimeout(() => {
      console.error(`TIMEOUT ${variant} after 120s`)
      process.exit(124)
    }, 120_000)
    try {
      circuit.add(<ViaInPadRoutingRepro binding={binding} phased={phased} actualBga={actualBga} sparse={sparse} rounded={rounded} viaOffset={viaOffset} noVias={noVias} savedFanout={savedFanout} />)
      await circuit.renderUntilSettled()
      const records = circuit.getCircuitJson()
      const errors = records.filter(record => record.type.endsWith("_error"))
      const vias = records.filter(record => record.type === "pcb_via")
      const traces = records.filter(record => record.type === "pcb_trace")
      console.log(`RESULT ${variant}: ${traces.length} traces, ${vias.length} vias, ${errors.length} errors in ${((Date.now() - started) / 1000).toFixed(1)}s`)
      for (const error of errors) console.log(`  ${error.type}: ${String((error as any).message).slice(0, 1500)}`)
      console.log(`  via references: ${vias.map(via => `${via.source_trace_id ?? via.source_net_id ?? via.pcb_trace_id ?? "none"} [${via.layers.join(",")}]`).join("; ")}`)
      const expectedFailure = actualBga && !sparse && !noVias && !savedFanout
      if (expectedFailure) {
        const topologyErrors = errors.filter(error => error.type === "pcb_autorouting_error" &&
          /TopologyMergingSolver:.*unresolved inter-group overlap/.test(String(error.message)))
        assert(topologyErrors.length === 1 && errors.length === 3 &&
          errors.filter(error => error.type === "pcb_port_not_connected_error").length === 2,
        "Expected one topology overlap and two disconnected-port errors; the failure changed or no longer reproduces")
        assert(traces.length === 0 && vias.length === 2, "Expected no routed traces and two native blind vias")
        console.log("PASS: expected dense-BGA blind-VIP topology failure reproduced")
      } else {
        assert(errors.length === 0, `Expected successful routing; found ${errors.length} render errors`)
        assert(vias.length === (noVias ? 0 : actualBga ? 2 : 4), "Unexpected via count")
        assert(traces.length === (savedFanout ? 3 : actualBga ? 1 : 2), "Unexpected trace count")
      }
      if (savedFanout) {
        assert(vias.every(via => via.layers.join() === "top,inner1,inner2"), "Incorrect blind-via layer span")
        assert(vias.every(via => via.hole_diameter === 0.15 && via.outer_diameter === 0.3), "Incorrect blind-via dimensions")
        const connectivity = getFullConnectivityMapFromCircuitJson(records)
        const net = records.find(record => record.type === "source_net" && record.name === "SIGNAL")!
        assert(net, "SIGNAL net is missing")
        assert(vias.every(via => connectivity.areIdsConnected(via.pcb_via_id, (net as any).source_net_id)), "A fanout via is not logically connected to SIGNAL")
        for (const [name, check] of Object.entries({ checkTracesAreContiguous, checkEachPcbPortConnectedToPcbTraces, checkViaPadClearance, checkEachPcbTraceNonOverlapping, checkPadTraceClearance })) {
          const findings = check(records)
          console.log(`  ${name}: ${findings.length} findings`)
          for (const finding of findings.slice(0, 3)) console.log(`    ${finding.message}`)
          assert(findings.length === 0, `${name} reported ${findings.length} findings`)
        }
        console.log("PASS: saved fanout routes with correct blind vias and zero connectivity/clearance findings")
      }
    } catch (error) {
      failures++
      console.error(`UNEXPECTED ${variant}: ${error instanceof Error ? error.message : String(error)}`)
    } finally {
      clearTimeout(timeout)
    }
  }
  process.exitCode = failures ? 1 : 0
}
// Rendering and solver input stay in memory; no Circuit JSON is saved or edited.