From e86efda4c31a9fb21c271e79c4ed5964a37a83e0 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 13:17:44 +0530 Subject: [PATCH 01/16] up --- .../stm32f405-differential-pair-pcb.snap.svg | 1 + .../stm32f405-differential-pair.test.tsx | 24 + .../imports/LSM6DS3TR_C.tsx | 217 +++++ .../imports/STM32F405RGT6.tsx | 756 ++++++++++++++++++ .../imports/TAXM8M4RFDCET2T.tsx | 108 +++ .../imports/TLV75533PDBVR.tsx | 154 ++++ .../imports/TS_1187A_B_A_B.tsx | 150 ++++ .../imports/USBLC6_2SC6.tsx | 125 +++ .../imports/UsbFootprint.tsx | 240 ++++++ .../index.circuit.tsx | 401 ++++++++++ 10 files changed, 2176 insertions(+) create mode 100644 tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg create mode 100644 tests/repros/stm32f405-differential-pair.test.tsx create mode 100644 tests/repros/stm32f405-differential-pair/imports/LSM6DS3TR_C.tsx create mode 100644 tests/repros/stm32f405-differential-pair/imports/STM32F405RGT6.tsx create mode 100644 tests/repros/stm32f405-differential-pair/imports/TAXM8M4RFDCET2T.tsx create mode 100644 tests/repros/stm32f405-differential-pair/imports/TLV75533PDBVR.tsx create mode 100644 tests/repros/stm32f405-differential-pair/imports/TS_1187A_B_A_B.tsx create mode 100644 tests/repros/stm32f405-differential-pair/imports/USBLC6_2SC6.tsx create mode 100644 tests/repros/stm32f405-differential-pair/imports/UsbFootprint.tsx create mode 100644 tests/repros/stm32f405-differential-pair/index.circuit.tsx diff --git a/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg b/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg new file mode 100644 index 000000000..2c73c8b86 --- /dev/null +++ b/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg @@ -0,0 +1 @@ +U1J1U4U3U2C1C2C3C4C5C6C7C8C9C10C11C12C13C14C15C16C17R1R2R3R4R5R6R7R8R9R10R11SW1SW2D1Y1pin1pin2pin3pin4pin5pin6J2pin1pin2pin3pin4J3pin1pin2pin3pin4pin5pin6J4F405 USB / IMURESETBOOTMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTED \ No newline at end of file diff --git a/tests/repros/stm32f405-differential-pair.test.tsx b/tests/repros/stm32f405-differential-pair.test.tsx new file mode 100644 index 000000000..468e291f9 --- /dev/null +++ b/tests/repros/stm32f405-differential-pair.test.tsx @@ -0,0 +1,24 @@ +import { expect, test } from "bun:test" +import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" +import { getTestFixture } from "tests/fixtures/get-test-fixture" +import Controller from "./stm32f405-differential-pair/index.circuit" + +test("repro: STM32F405 USB differential pair with named nets", async () => { + const { circuit } = getTestFixture() + circuit.add() + await circuit.renderUntilSettled() + const board = circuit.firstChild + if (!board) throw new Error("Expected the STM32F405 board") + + // Preserve the actual board placement before the routing-input conversion fails. + await expect(circuit).toMatchPcbSnapshot(import.meta.path) + expect(() => + getSimpleRouteJsonFromCircuitJson({ + db: circuit.db, + subcircuitComponent: board, + fanoutPourNetMap: { inner1: "GND", inner2: "V3_3" }, + }), + ).toThrow( + 'Could not find an SRJ connection for trace name or port selector ".U1 > .pin45" in differential pair "USB_FS"', + ) +}) diff --git a/tests/repros/stm32f405-differential-pair/imports/LSM6DS3TR_C.tsx b/tests/repros/stm32f405-differential-pair/imports/LSM6DS3TR_C.tsx new file mode 100644 index 000000000..cd8cd56ab --- /dev/null +++ b/tests/repros/stm32f405-differential-pair/imports/LSM6DS3TR_C.tsx @@ -0,0 +1,217 @@ +import type { ChipProps } from "@tscircuit/props" + +const pinLabels = { + pin1: ["pin1"], + pin2: ["SDx"], + pin3: ["SCx"], + pin4: ["INT1"], + pin5: ["VDDIO"], + pin6: ["GND1"], + pin7: ["GND2"], + pin8: ["VDD"], + pin9: ["INT2"], + pin10: ["NC1"], + pin11: ["NC2"], + pin12: ["CS"], + pin13: ["SCL"], + pin14: ["SDA"], +} as const + +const pinAttributes = { + pin6: { requiresGround: true }, + pin7: { requiresGround: true }, + pin8: { requiresPower: true }, + pin10: { doNotConnect: true }, + pin11: { doNotConnect: true }, +} as const + +export const LSM6DS3TR_C = (props: ChipProps) => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + } + cadModel={{ + objUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C967633.obj?uuid=f43373e142124ec98babb70d58d97864", + stepUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C967633.step?uuid=f43373e142124ec98babb70d58d97864", + pcbRotationOffset: 0, + modelOriginPosition: { + x: 0.00012700000002041634, + y: -0.000012700000070253736, + z: 0, + }, + }} + {...props} + /> + ) +} diff --git a/tests/repros/stm32f405-differential-pair/imports/STM32F405RGT6.tsx b/tests/repros/stm32f405-differential-pair/imports/STM32F405RGT6.tsx new file mode 100644 index 000000000..be61308d7 --- /dev/null +++ b/tests/repros/stm32f405-differential-pair/imports/STM32F405RGT6.tsx @@ -0,0 +1,756 @@ +import type { ChipProps } from "@tscircuit/props" + +const pinLabels = { + pin1: ["pin1"], + pin2: ["PC13"], + pin3: ["pin3"], + pin4: ["pin4"], + pin5: ["pin5"], + pin6: ["pin6"], + pin7: ["NRST"], + pin8: ["PC0"], + pin9: ["PC1"], + pin10: ["PC2"], + pin11: ["PC3"], + pin12: ["pin12"], + pin13: ["pin13"], + pin14: ["pin14"], + pin15: ["PA1"], + pin16: ["PA2"], + pin17: ["PA3"], + pin18: ["pin18"], + pin19: ["pin19"], + pin20: ["PA4"], + pin21: ["PA5"], + pin22: ["PA6"], + pin23: ["PA7"], + pin24: ["PC4"], + pin25: ["PC5"], + pin26: ["PB0"], + pin27: ["PB1"], + pin28: ["pin28"], + pin29: ["PB10"], + pin30: ["PB11"], + pin31: ["VCAP_1"], + pin32: ["pin32"], + pin33: ["PB12"], + pin34: ["PB13"], + pin35: ["PB14"], + pin36: ["PB15"], + pin37: ["PC6"], + pin38: ["PC7"], + pin39: ["PC8"], + pin40: ["PC9"], + pin41: ["PA8"], + pin42: ["PA9"], + pin43: ["PA10"], + pin44: ["PA11"], + pin45: ["PA12"], + pin46: ["pin46"], + pin47: ["VCAP_2"], + pin48: ["pin48"], + pin49: ["pin49"], + pin50: ["pin50"], + pin51: ["PC10"], + pin52: ["PC11"], + pin53: ["PC12"], + pin54: ["PD2"], + pin55: ["pin55"], + pin56: ["pin56"], + pin57: ["PB5"], + pin58: ["PB6"], + pin59: ["PB7"], + pin60: ["BOOT0"], + pin61: ["PB8"], + pin62: ["PB9"], + pin63: ["pin63"], + pin64: ["pin64"], +} as const + +export const STM32F405RGT6 = (props: ChipProps) => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + cadModel={{ + objUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C15742.obj?uuid=7e9b9111dcfd48d3add0eab11d882721", + stepUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C15742.step?uuid=7e9b9111dcfd48d3add0eab11d882721", + pcbRotationOffset: 0, + modelOriginPosition: { x: 0, y: -0.011810999999994465, z: 0.000795 }, + }} + {...props} + /> + ) +} diff --git a/tests/repros/stm32f405-differential-pair/imports/TAXM8M4RFDCET2T.tsx b/tests/repros/stm32f405-differential-pair/imports/TAXM8M4RFDCET2T.tsx new file mode 100644 index 000000000..dbd482a0a --- /dev/null +++ b/tests/repros/stm32f405-differential-pair/imports/TAXM8M4RFDCET2T.tsx @@ -0,0 +1,108 @@ +import type { CrystalProps } from "@tscircuit/props" + +type ImportedCrystalProps = Omit + +export const TAXM8M4RFDCET2T = (props: ImportedCrystalProps) => { + const { name = "X1", ...restProps } = props + + return ( + + + + + + + + + + + + } + cadModel={{ + objUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C403948.obj?uuid=6fb6f1ac4cf64e11ab7df9c0adbf8c3a", + stepUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C403948.step?uuid=6fb6f1ac4cf64e11ab7df9c0adbf8c3a", + pcbRotationOffset: 0, + modelOriginPosition: { + x: 0.000025400000140507473, + y: -0.00008889999992334197, + z: -0.001, + }, + }} + {...restProps} + /> + ) +} diff --git a/tests/repros/stm32f405-differential-pair/imports/TLV75533PDBVR.tsx b/tests/repros/stm32f405-differential-pair/imports/TLV75533PDBVR.tsx new file mode 100644 index 000000000..fd7eedf09 --- /dev/null +++ b/tests/repros/stm32f405-differential-pair/imports/TLV75533PDBVR.tsx @@ -0,0 +1,154 @@ +import type { ChipProps } from "@tscircuit/props" + +const pinLabels = { + pin1: ["IN"], + pin2: ["GND"], + pin3: ["EN"], + pin4: ["NC"], + pin5: ["OUT"], +} as const + +const pinAttributes = { + pin2: { requiresGround: true }, + pin4: { doNotConnect: true }, +} as const + +export const TLV75533PDBVR = (props: ChipProps) => { + return ( + + + + + + + + + + + + + + + + + + + } + cadModel={{ + objUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C404027.obj?uuid=460193f9bf2d42e58cf3c2f675b07dc6", + stepUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C404027.step?uuid=460193f9bf2d42e58cf3c2f675b07dc6", + pcbRotationOffset: 180, + modelOriginPosition: { + x: 0, + y: -0.000012700000070253736, + z: -0.049083, + }, + }} + {...props} + /> + ) +} diff --git a/tests/repros/stm32f405-differential-pair/imports/TS_1187A_B_A_B.tsx b/tests/repros/stm32f405-differential-pair/imports/TS_1187A_B_A_B.tsx new file mode 100644 index 000000000..b41fbc5a4 --- /dev/null +++ b/tests/repros/stm32f405-differential-pair/imports/TS_1187A_B_A_B.tsx @@ -0,0 +1,150 @@ +import type { PushButtonProps } from "@tscircuit/props" + +const pinLabels = { + pin1: ["A"], + pin2: ["B"], + pin3: ["C"], + pin4: ["D"], +} as const + +export const TS_1187A_B_A_B = (props: PushButtonProps) => { + const { name = "SW1", ...restProps } = props + + return ( + + + + + + + + + + + + + + + + + + + } + cadModel={{ + objUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C318884.obj?uuid=91b67c1735f643ffb2e7226c23dd3492", + stepUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C318884.step?uuid=91b67c1735f643ffb2e7226c23dd3492", + pcbRotationOffset: 0, + modelOriginPosition: { + x: 0.000012700000070253736, + y: 0.0004999999999999449, + z: -0.05, + }, + }} + {...restProps} + /> + ) +} diff --git a/tests/repros/stm32f405-differential-pair/imports/USBLC6_2SC6.tsx b/tests/repros/stm32f405-differential-pair/imports/USBLC6_2SC6.tsx new file mode 100644 index 000000000..58758e52f --- /dev/null +++ b/tests/repros/stm32f405-differential-pair/imports/USBLC6_2SC6.tsx @@ -0,0 +1,125 @@ +import type { ChipProps } from "@tscircuit/props" + +const pinLabels = { + pin1: ["pin1"], + pin2: ["GND"], + pin3: ["pin3"], + pin4: ["pin4"], + pin5: ["VBUS"], + pin6: ["pin6"], +} as const + +const pinAttributes = { + pin2: { requiresGround: true }, +} as const + +export const USBLC6_2SC6 = (props: ChipProps) => { + return ( + + + + + + + + + + + + + + } + cadModel={{ + objUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C2687116.obj?uuid=229b69761e2c45dba6a83d8866dec72d", + stepUrl: + "https://modelcdn.tscircuit.com/easyeda_models/assets/C2687116.step?uuid=229b69761e2c45dba6a83d8866dec72d", + pcbRotationOffset: 90, + modelOriginPosition: { + x: -0.000012700000070253736, + y: 0.000012700000070253736, + z: -0.048939, + }, + }} + {...props} + /> + ) +} diff --git a/tests/repros/stm32f405-differential-pair/imports/UsbFootprint.tsx b/tests/repros/stm32f405-differential-pair/imports/UsbFootprint.tsx new file mode 100644 index 000000000..9c7e217b0 --- /dev/null +++ b/tests/repros/stm32f405-differential-pair/imports/UsbFootprint.tsx @@ -0,0 +1,240 @@ +// Exact C165948 EasyEDA footprint, remapped to standard USB-C pin labels. +export const UsbFootprint = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/tests/repros/stm32f405-differential-pair/index.circuit.tsx b/tests/repros/stm32f405-differential-pair/index.circuit.tsx new file mode 100644 index 000000000..9c8a674c7 --- /dev/null +++ b/tests/repros/stm32f405-differential-pair/index.circuit.tsx @@ -0,0 +1,401 @@ +import { TAXM8M4RFDCET2T } from "./imports/TAXM8M4RFDCET2T" +import { UsbFootprint } from "./imports/UsbFootprint" +import { STM32F405RGT6 } from "./imports/STM32F405RGT6" +import { LSM6DS3TR_C } from "./imports/LSM6DS3TR_C" +import { TLV75533PDBVR } from "./imports/TLV75533PDBVR" +import { USBLC6_2SC6 } from "./imports/USBLC6_2SC6" +import { TS_1187A_B_A_B } from "./imports/TS_1187A_B_A_B" + +// Physical LQFP64 pin numbers, checked against ST DS8626. +const mcuNets: Record = { + 1: "V3_3", + 2: "STATUS", + 5: "OSC_IN", + 6: "OSC_OUT", + 7: "NRST", + 8: "GPIO_PC0", + 9: "GPIO_PC1", + 12: "GND", + 13: "VDDA", + 14: "GPIO_PA0", + 15: "GPIO_PA1", + 16: "UART2_TX", + 17: "UART2_RX", + 18: "GND", + 19: "V3_3", + 28: "BOOT1", + 31: "VCAP1", + 32: "V3_3", + 41: "IMU_INT", + 42: "VBUS", + 44: "USB_DM", + 45: "USB_DP", + 46: "SWDIO", + 47: "VCAP2", + 48: "V3_3", + 49: "SWCLK", + 55: "SWO", + 58: "I2C_SCL", + 59: "I2C_SDA", + 60: "BOOT0", + 63: "GND", + 64: "V3_3", +} +// name, value, PCB x/y, rotation, rail; capacitors are ceramic, >=10 V. +const caps: [string, string, number, number, number, string][] = [ + ["C1", "100nF", 8, -2.8, 90, "V3_3"], + ["C2", "100nF", 8, 4, 90, "V3_3"], + ["C3", "100nF", -4.2, 8, 0, "V3_3"], + ["C4", "100nF", -8, -3.3, 90, "V3_3"], + ["C5", "100nF", -4.5, -8, 0, "V3_3"], + ["C6", "4.7uF", -7, 7.8, 0, "V3_3"], + ["C7", "2.2uF", 8, 2, 90, "VCAP1"], + ["C8", "2.2uF", -2.2, 7.3, 0, "VCAP2"], + ["C9", "100nF", 2.3, -8, 0, "VDDA"], + ["C10", "1uF", 4.8, -8, 0, "VDDA"], + ["C11", "100nF", -10, -2.7, 0, "V3_3"], + ["C12", "100nF", -13, -2.7, 0, "V3_3"], + ["C13", "1uF", 15, 9, 90, "VBUS"], + ["C14", "4.7uF", 9, 9, 90, "V3_3"], + ["C15", "100nF", -0.5, -8, 0, "NRST"], + ["C16", "18pF", -5, -12, 90, "OSC_IN"], + ["C17", "18pF", 2, -11, 90, "OSC_OUT"], +] +const resistors: [string, string, number, number, number, string, string][] = [ + ["R1", "5.1k", -6, 11, 90, "CC1", "GND"], + ["R2", "5.1k", 6, 11, 90, "CC2", "GND"], + ["R3", "22", 0, 7.3, 0, "USB_DP", "USB_DP_CONN"], + ["R4", "22", 2.1, 7.3, 0, "USB_DM", "USB_DM_CONN"], + ["R5", "10k", -6, -10, 90, "V3_3", "NRST"], + ["R6", "10k", -9, 0, 90, "BOOT0", "GND"], + ["R7", "10k", 8, 0, 90, "BOOT1", "GND"], + ["R8", "4.7k", -10, 3, 90, "V3_3", "I2C_SCL"], + ["R9", "4.7k", -12, 3, 90, "V3_3", "I2C_SDA"], + ["R10", "1k", -10, -6, 0, "STATUS", "LED_A"], + ["R11", "10", 6.5, -8, 90, "V3_3", "VDDA"], +] +const wire = (pin: string, net: string) => ( + +) +export default function Controller({ + routingDisabled = false, +}: { routingDisabled?: boolean }) { + return ( + + {/* Drop supply connections to their planes before routing sensitive signals. */} + + + + + {["MCU", "Decoupling", "USB", "Power", "IMU", "Controls", "Headers"].map( + (name) => ( + + ), + )} + + {Object.entries(mcuNets).map(([pin, net]) => + wire(`.U1 > .pin${pin}`, net), + )} + } + manufacturerPartNumber="TYPE-C-31-M-12" + supplierPartNumbers={{ jlcpcb: ["C165948"] }} + pinLabels={{ + pin1: "GND1", + pin2: "VBUS1", + pin3: "CC1", + pin4: "DP1", + pin5: "DM1", + pin6: "SBU1", + pin7: "SBU2", + pin8: "DM2", + pin9: "DP2", + pin10: "CC2", + pin11: "VBUS2", + pin12: "GND2", + pin13: "SHELL1", + pin14: "SHELL2", + pin15: "SHELL3", + pin16: "SHELL4", + }} + pcbX={0} + pcbY={15} + pcbRotation={180} + schPortArrangement={{ + rightSide: { + pins: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + direction: "top-to-bottom", + }, + }} + schWidth={1.65} + schHeight={3.4} + schX={25} + schY={12} + schSectionName="USB" + /> + {Object.entries({ + GND1: "GND", + GND2: "GND", + VBUS1: "VBUS", + VBUS2: "VBUS", + CC1: "CC1", + CC2: "CC2", + DP1: "USB_DP_CONN", + DP2: "USB_DP_CONN", + DM1: "USB_DM_CONN", + DM2: "USB_DM_CONN", + SHELL1: "GND", + SHELL2: "GND", + SHELL3: "GND", + SHELL4: "GND", + }).map(([p, n]) => wire(`.J1 > .${p}`, n))} + + {Object.entries({ + 1: "USB_DP_CONN", + 2: "GND", + 3: "USB_DM_CONN", + 4: "USB_DM_CONN", + 5: "VBUS", + 6: "USB_DP_CONN", + }).map(([p, n]) => wire(`.U4 > .pin${p}`, n))} + + + {Object.entries({ 1: "VBUS", 2: "GND", 3: "VBUS", 5: "V3_3" }).map( + ([p, n]) => wire(`.U3 > .pin${p}`, n), + )} + + {Object.entries({ + 1: "GND", + 4: "IMU_INT", + 5: "V3_3", + 6: "GND", + 7: "GND", + 8: "V3_3", + 12: "V3_3", + 13: "I2C_SCL", + 14: "I2C_SDA", + }).map(([p, n]) => wire(`.U2 > .pin${p}`, n))} + {caps.map(([name, value, x, y, rot, net], i) => ( + + ))} + {resistors.map(([name, value, x, y, rot, n1, n2], i) => ( + + ))} + + + {[1, 3].map((p) => wire(`.SW1 > .pin${p}`, "NRST"))} + {[2, 4].map((p) => wire(`.SW1 > .pin${p}`, "GND"))} + {[1, 3].map((p) => wire(`.SW2 > .pin${p}`, "BOOT0"))} + {[2, 4].map((p) => wire(`.SW2 > .pin${p}`, "V3_3"))} + + + {( + [ + ["J2", -17, 8.7, ["V3_3", "SWDIO", "GND", "SWCLK", "NRST", "SWO"]], + ["J3", 17, -2, ["GND", "V3_3", "UART2_TX", "UART2_RX"]], + [ + "J4", + -17, + -8.7, + ["GND", "V3_3", "GPIO_PC0", "GPIO_PC1", "GPIO_PA0", "GPIO_PA1"], + ], + ] as [string, number, number, string[]][] + ).map(([name, x, y, nets], i) => ( + [`pin${i + 1}`, `net.${n}`]), + )} + /> + ))} + + + + + + + + ) +} From b9254fe2f9d0a6e1704a51e59c69fd11d9a6c2df Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 13:30:34 +0530 Subject: [PATCH 02/16] up --- .../Group/Group_getRoutingPhasePlans.ts | 28 +++++++--- .../Group/Group_phasedAutoroutingUtils.ts | 2 + lib/utils/autorouting/SimpleRouteJson.ts | 4 +- .../getDifferentialPairsForSimpleRouteJson.ts | 9 +++- .../getSimpleRouteJsonFromCircuitJson.ts | 3 ++ ...net-connections-routing-phase-pcb.snap.svg | 1 + .../net-connections-routing-phase.test.tsx | 51 +++++++++++++++++++ .../net-fragments-point-to-point.test.tsx | 17 +++++++ .../stm32f405-differential-pair.test.tsx | 36 +++++++++---- 9 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 tests/components/primitive-components/differential-pair/__snapshots__/net-connections-routing-phase-pcb.snap.svg create mode 100644 tests/components/primitive-components/differential-pair/net-connections-routing-phase.test.tsx diff --git a/lib/components/primitive-components/Group/Group_getRoutingPhasePlans.ts b/lib/components/primitive-components/Group/Group_getRoutingPhasePlans.ts index 780f2c5b2..bf0685af9 100644 --- a/lib/components/primitive-components/Group/Group_getRoutingPhasePlans.ts +++ b/lib/components/primitive-components/Group/Group_getRoutingPhasePlans.ts @@ -128,13 +128,14 @@ function traceHasEndpointMatchingConnectionSelector( trace: Trace, connectionSelectorEndpointKey: string, ): boolean { - return trace - .getTracePortPathSelectors() - .some( - (selector) => - convertPortSelectorToEndpointKey(selector) === - connectionSelectorEndpointKey, - ) + return [ + ...trace.getTracePortPathSelectors(), + ...trace.getTracePathNetSelectors(), + ].some( + (selector) => + convertPortSelectorToEndpointKey(selector) === + connectionSelectorEndpointKey, + ) } function getAutoroutersByPhaseIndex( @@ -307,6 +308,19 @@ export function Group_getRoutingPhasePlans( convertPortSelectorToEndpointKey, ) plan.connectionSelectors = connectionSelectors + for (const selector of connectionSelectors) { + const net = group.selectOne(selector, { type: "net" }) + if (!net || breakoutByNet.has(net)) continue + if (!phaseProps.reroute) { + for (const existingPlan of plansByPhaseIndex.values()) { + if (existingPlan === plan) continue + existingPlan.nets = existingPlan.nets.filter( + (existingNet) => existingNet !== net, + ) + } + } + if (!plan.nets.includes(net)) plan.nets.push(net) + } for (const trace of traces) { if (breakoutByTrace.has(trace)) continue diff --git a/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts b/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts index f41543fdf..c0d86e884 100644 --- a/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts +++ b/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts @@ -21,6 +21,8 @@ export function connectionIsInRoutingPhase( for (const trace of phasePlan.traces) { if (!trace.source_trace_id) continue if (connection.source_trace_id === trace.source_trace_id) return true + if (connection.source_trace_ids?.includes(trace.source_trace_id)) + return true if (connection.name === trace.source_trace_id) return true if (connection.rootConnectionName === trace.source_trace_id) return true if (connection.mergedConnectionNames?.includes(trace.source_trace_id)) { diff --git a/lib/utils/autorouting/SimpleRouteJson.ts b/lib/utils/autorouting/SimpleRouteJson.ts index cd872a580..7d574011b 100644 --- a/lib/utils/autorouting/SimpleRouteJson.ts +++ b/lib/utils/autorouting/SimpleRouteJson.ts @@ -2,7 +2,7 @@ import type { SimpleRouteJson as AutorouterSimpleRouteJson, SimplifiedPcbTrace as AutorouterSimplifiedPcbTrace, } from "@tscircuit/capacity-autorouter" -import type { PcbGroup } from "circuit-json" +import type { PcbGroup, SourceTrace } from "circuit-json" import type { CircuitJsonMetadata, Obstacle } from "../obstacles/types" export type { CircuitJsonMetadata, Obstacle } from "../obstacles/types" @@ -83,6 +83,8 @@ export type SimpleRouteConnection = { name: SrjConnectionName routingPcbGroupId?: PcbGroupId source_trace_id?: string + /** Source traces combined into a net-based routing connection. */ + source_trace_ids?: SourceTrace["source_trace_id"][] rootConnectionName?: string mergedConnectionNames?: string[] isOffBoard?: boolean diff --git a/lib/utils/autorouting/getDifferentialPairsForSimpleRouteJson.ts b/lib/utils/autorouting/getDifferentialPairsForSimpleRouteJson.ts index b24dcd341..ceadbcbca 100644 --- a/lib/utils/autorouting/getDifferentialPairsForSimpleRouteJson.ts +++ b/lib/utils/autorouting/getDifferentialPairsForSimpleRouteJson.ts @@ -137,8 +137,13 @@ const getDifferentialPairSrjConnectionNamesByCohortOrThrow = ({ const matchingSrjConnections: SimpleRouteConnection[] = [] for (const srjConnection of srjConnections) { if ( - srjConnection.source_trace_id && - differentialPairSourceTraceIds.includes(srjConnection.source_trace_id) + (srjConnection.source_trace_id && + differentialPairSourceTraceIds.includes( + srjConnection.source_trace_id, + )) || + srjConnection.source_trace_ids?.some((sourceTraceId) => + differentialPairSourceTraceIds.includes(sourceTraceId), + ) ) { matchingSrjConnections.push(srjConnection) } diff --git a/lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson.ts b/lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson.ts index 971b5e5bc..1018ab7f3 100644 --- a/lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson.ts +++ b/lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson.ts @@ -709,6 +709,9 @@ export const getSimpleRouteJsonFromCircuitJson = ({ name: net.source_net_id ?? sharedConnMap.getNetConnectedToId(net.source_net_id), + source_trace_ids: connectedSourceTraces.map( + (sourceTrace) => sourceTrace.source_trace_id, + ), nominalTraceWidth: nominalTraceWidthFromConnectedTraces, width: nominalTraceWidthFromConnectedTraces, pointsToConnect, diff --git a/tests/components/primitive-components/differential-pair/__snapshots__/net-connections-routing-phase-pcb.snap.svg b/tests/components/primitive-components/differential-pair/__snapshots__/net-connections-routing-phase-pcb.snap.svg new file mode 100644 index 000000000..127124adc --- /dev/null +++ b/tests/components/primitive-components/differential-pair/__snapshots__/net-connections-routing-phase-pcb.snap.svg @@ -0,0 +1 @@ +U1U2Pin-to-net differential pair: phase 0autorouting phase 0autorouting phase 1autorouting phase 2 \ No newline at end of file diff --git a/tests/components/primitive-components/differential-pair/net-connections-routing-phase.test.tsx b/tests/components/primitive-components/differential-pair/net-connections-routing-phase.test.tsx new file mode 100644 index 000000000..debe1bd61 --- /dev/null +++ b/tests/components/primitive-components/differential-pair/net-connections-routing-phase.test.tsx @@ -0,0 +1,51 @@ +import { expect, test } from "bun:test" +import { createAutoroutingPhaseIoStack } from "tests/fixtures/create-autorouting-phase-io-stack" +import { getTestFixture } from "tests/fixtures/get-test-fixture" + +test("routes a pin-to-net differential pair in its selected phase", async () => { + const { circuit } = getTestFixture() + const phases = createAutoroutingPhaseIoStack(circuit) + circuit.add( + + + + + + + + + + + + + + , + ) + await circuit.renderUntilSettled() + expect(phases).toHaveLength(2) + expect(phases[0]?.startSimpleRouteJson?.differentialPairs).toHaveLength(1) + expect(phases[0]?.startSimpleRouteJson?.connections).toHaveLength(2) + expect(phases[0]?.endSimpleRouteJson?.traces).toHaveLength(2) + expect(phases[1]?.startSimpleRouteJson?.connections).toHaveLength(1) + expect(phases[1]?.startSimpleRouteJson?.differentialPairs).toBeUndefined() + expect(circuit.db.pcb_trace.list()).toHaveLength(3) + expect(circuit.db.pcb_trace_error.list()).toEqual([]) + await expect(circuit).toMatchPcbSnapshot(import.meta.path) +}) diff --git a/tests/components/primitive-components/differential-pair/net-fragments-point-to-point.test.tsx b/tests/components/primitive-components/differential-pair/net-fragments-point-to-point.test.tsx index c35b9713b..0880abbca 100644 --- a/tests/components/primitive-components/differential-pair/net-fragments-point-to-point.test.tsx +++ b/tests/components/primitive-components/differential-pair/net-fragments-point-to-point.test.tsx @@ -1,4 +1,5 @@ import { expect, test } from "bun:test" +import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" import { getTestFixture } from "tests/fixtures/get-test-fixture" test("does not warn for a two-terminal conductor split through a source net", (): void => { @@ -30,4 +31,20 @@ test("does not warn for a two-terminal conductor split through a source net", () warning.property_name === "negativeConnection", ), ).toEqual([]) + + const board = circuit.firstChild + if (!board) throw new Error("Expected a board") + const { simpleRouteJson } = getSimpleRouteJsonFromCircuitJson({ + db: circuit.db, + subcircuitComponent: board, + }) + expect(simpleRouteJson.differentialPairs).toEqual([ + { + connectionNames: [ + circuit.db.source_net.getWhere({ name: "DP" })!.source_net_id, + circuit.db.source_trace.getWhere({ name: "DM" })!.source_trace_id, + ], + lengthTolerance: 0.1, + }, + ]) }) diff --git a/tests/repros/stm32f405-differential-pair.test.tsx b/tests/repros/stm32f405-differential-pair.test.tsx index 468e291f9..d98289058 100644 --- a/tests/repros/stm32f405-differential-pair.test.tsx +++ b/tests/repros/stm32f405-differential-pair.test.tsx @@ -3,22 +3,36 @@ import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimp import { getTestFixture } from "tests/fixtures/get-test-fixture" import Controller from "./stm32f405-differential-pair/index.circuit" -test("repro: STM32F405 USB differential pair with named nets", async () => { +test("STM32F405 USB differential pair resolves named nets", async () => { const { circuit } = getTestFixture() circuit.add() await circuit.renderUntilSettled() const board = circuit.firstChild if (!board) throw new Error("Expected the STM32F405 board") - // Preserve the actual board placement before the routing-input conversion fails. + // Routing-input conversion does not change the board placement. await expect(circuit).toMatchPcbSnapshot(import.meta.path) - expect(() => - getSimpleRouteJsonFromCircuitJson({ - db: circuit.db, - subcircuitComponent: board, - fanoutPourNetMap: { inner1: "GND", inner2: "V3_3" }, - }), - ).toThrow( - 'Could not find an SRJ connection for trace name or port selector ".U1 > .pin45" in differential pair "USB_FS"', - ) + const { simpleRouteJson } = getSimpleRouteJsonFromCircuitJson({ + db: circuit.db, + subcircuitComponent: board, + fanoutPourNetMap: { inner1: "GND", inner2: "V3_3" }, + }) + expect(simpleRouteJson.differentialPairs).toEqual([ + { + connectionNames: [ + circuit.db.source_net.getWhere({ name: "USB_DP" })!.source_net_id, + circuit.db.source_net.getWhere({ name: "USB_DM" })!.source_net_id, + ], + lengthTolerance: 0.5, + traceGap: 0.15, + }, + ]) + for (const connectionName of simpleRouteJson.differentialPairs![0]! + .connectionNames) { + const connection = simpleRouteJson.connections.find( + (connection) => connection.name === connectionName, + )! + expect(connection.pointsToConnect).toHaveLength(2) + expect(connection.source_trace_ids).toHaveLength(2) + } }) From 2f7ada8715ef84b1f3770408087d303cd19fdeb4 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 13:49:34 +0530 Subject: [PATCH 03/16] up --- .../stm32f405-differential-pair-pcb.snap.svg | 2 +- .../stm32f405-differential-pair.test.tsx | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg b/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg index 2c73c8b86..2bce021b3 100644 --- a/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg +++ b/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg @@ -1 +1 @@ -U1J1U4U3U2C1C2C3C4C5C6C7C8C9C10C11C12C13C14C15C16C17R1R2R3R4R5R6R7R8R9R10R11SW1SW2D1Y1pin1pin2pin3pin4pin5pin6J2pin1pin2pin3pin4J3pin1pin2pin3pin4pin5pin6J4F405 USB / IMURESETBOOTMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTED \ No newline at end of file +U1J1U4U3U2C1C2C3C4C5C6C7C8C9C10C11C12C13C14C15C16C17R1R2R3R4R5R6R7R8R9R10R11SW1SW2D1Y1pin1pin2pin3pin4pin5pin6J2pin1pin2pin3pin4J3pin1pin2pin3pin4pin5pin6J4F405 USB / IMURESETBOOTRouting stopped: power fanout escaped 69/70 connections; USB phase not reachedMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDautorouting phase 0 Power plane fanout \ No newline at end of file diff --git a/tests/repros/stm32f405-differential-pair.test.tsx b/tests/repros/stm32f405-differential-pair.test.tsx index d98289058..89b0778bb 100644 --- a/tests/repros/stm32f405-differential-pair.test.tsx +++ b/tests/repros/stm32f405-differential-pair.test.tsx @@ -1,4 +1,5 @@ import { expect, test } from "bun:test" +import { cloneElement } from "react" import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" import { getTestFixture } from "tests/fixtures/get-test-fixture" import Controller from "./stm32f405-differential-pair/index.circuit" @@ -10,8 +11,6 @@ test("STM32F405 USB differential pair resolves named nets", async () => { const board = circuit.firstChild if (!board) throw new Error("Expected the STM32F405 board") - // Routing-input conversion does not change the board placement. - await expect(circuit).toMatchPcbSnapshot(import.meta.path) const { simpleRouteJson } = getSimpleRouteJsonFromCircuitJson({ db: circuit.db, subcircuitComponent: board, @@ -35,4 +34,24 @@ test("STM32F405 USB differential pair resolves named nets", async () => { expect(connection.pointsToConnect).toHaveLength(2) expect(connection.source_trace_ids).toHaveLength(2) } -}) + + const { circuit: routedCircuit } = getTestFixture() + const routedBoard = Controller({}) + routedCircuit.add( + cloneElement( + routedBoard, + {}, + routedBoard.props.children, + , + ), + ) + await routedCircuit.renderUntilSettled() + expect(routedCircuit.db.pcb_autorouting_error.list()[0]?.message).toContain( + "only 69 of 70 connections could escape", + ) + await expect(routedCircuit).toMatchPcbSnapshot(import.meta.path) +}, 120_000) From 044749be97a3b15522f4acf7c5550a5830ff44b7 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 13:56:35 +0530 Subject: [PATCH 04/16] up --- .../usb-header-net-pair-pcb.snap.svg | 1 + tests/repros/usb-header-net-pair.test.tsx | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg create mode 100644 tests/repros/usb-header-net-pair.test.tsx diff --git a/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg b/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg new file mode 100644 index 000000000..6d77737e7 --- /dev/null +++ b/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg @@ -0,0 +1 @@ +pin1pin2pin3pin4J1pin1pin2pin3pin4J2USB header interconnect1: VBUS 2: D- 3: D+ 4: GND \ No newline at end of file diff --git a/tests/repros/usb-header-net-pair.test.tsx b/tests/repros/usb-header-net-pair.test.tsx new file mode 100644 index 000000000..4fe032178 --- /dev/null +++ b/tests/repros/usb-header-net-pair.test.tsx @@ -0,0 +1,73 @@ +import { expect, test } from "bun:test" +import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" +import { getTestFixture } from "tests/fixtures/get-test-fixture" + +// A passive USB signal interconnect with two standard 2.54 mm headers. +function UsbHeaderBoard() { + return ( + + + + + + + + + + + + + + + + + + ) +} + +test("USB header pair wired through named nets", async () => { + const { circuit } = getTestFixture() + circuit.add() + await circuit.renderUntilSettled() + const board = circuit.firstChild + if (!board) throw new Error("Expected USB header board") + expect(() => + getSimpleRouteJsonFromCircuitJson({ + db: circuit.db, + subcircuitComponent: board, + }), + ).toThrow( + 'Could not find an SRJ connection for trace name or port selector ".J1 > .pin3"', + ) + expect(circuit.db.pcb_trace.list()).toHaveLength(0) + await expect(circuit).toMatchPcbSnapshot(import.meta.path) +}, 30_000) From 4c59b21dc6b7097819784bf59b7ed47bc65efca4 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 13:57:47 +0530 Subject: [PATCH 05/16] up --- .../usb-header-net-pair-pcb.snap.svg | 2 +- tests/repros/usb-header-net-pair.test.tsx | 21 ++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg b/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg index 6d77737e7..9d7f0c7b3 100644 --- a/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg +++ b/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg @@ -1 +1 @@ -pin1pin2pin3pin4J1pin1pin2pin3pin4J2USB header interconnect1: VBUS 2: D- 3: D+ 4: GND \ No newline at end of file +pin1pin2pin3pin4J1pin1pin2pin3pin4J2USB header interconnect1: VBUS 2: D- 3: D+ 4: GNDautorouting phase 0autorouting phase 1autorouting phase 2 \ No newline at end of file diff --git a/tests/repros/usb-header-net-pair.test.tsx b/tests/repros/usb-header-net-pair.test.tsx index 4fe032178..6af3ee338 100644 --- a/tests/repros/usb-header-net-pair.test.tsx +++ b/tests/repros/usb-header-net-pair.test.tsx @@ -1,5 +1,5 @@ import { expect, test } from "bun:test" -import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" +import { createAutoroutingPhaseIoStack } from "tests/fixtures/create-autorouting-phase-io-stack" import { getTestFixture } from "tests/fixtures/get-test-fixture" // A passive USB signal interconnect with two standard 2.54 mm headers. @@ -56,18 +56,15 @@ function UsbHeaderBoard() { test("USB header pair wired through named nets", async () => { const { circuit } = getTestFixture() + const phases = createAutoroutingPhaseIoStack(circuit) circuit.add() await circuit.renderUntilSettled() - const board = circuit.firstChild - if (!board) throw new Error("Expected USB header board") - expect(() => - getSimpleRouteJsonFromCircuitJson({ - db: circuit.db, - subcircuitComponent: board, - }), - ).toThrow( - 'Could not find an SRJ connection for trace name or port selector ".J1 > .pin3"', - ) - expect(circuit.db.pcb_trace.list()).toHaveLength(0) + expect(phases).toHaveLength(2) + expect(phases[0]?.startSimpleRouteJson?.differentialPairs).toHaveLength(1) + expect(phases[0]?.endSimpleRouteJson?.traces).toHaveLength(2) + expect(circuit.db.pcb_trace.list()).toHaveLength(4) + expect(circuit.db.pcb_autorouting_error.list()).toEqual([]) + expect(circuit.db.pcb_trace_error.list()).toEqual([]) + expect(circuit.db.pcb_port_not_connected_error.list()).toEqual([]) await expect(circuit).toMatchPcbSnapshot(import.meta.path) }, 30_000) From 59881cb128cbfcf6e8146359f30185fb02d34cd9 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 13:58:45 +0530 Subject: [PATCH 06/16] up --- .../stm32f405-differential-pair-pcb.snap.svg | 1 - .../stm32f405-differential-pair.test.tsx | 24 - .../imports/LSM6DS3TR_C.tsx | 217 ----- .../imports/STM32F405RGT6.tsx | 756 ------------------ .../imports/TAXM8M4RFDCET2T.tsx | 108 --- .../imports/TLV75533PDBVR.tsx | 154 ---- .../imports/TS_1187A_B_A_B.tsx | 150 ---- .../imports/USBLC6_2SC6.tsx | 125 --- .../imports/UsbFootprint.tsx | 240 ------ .../index.circuit.tsx | 401 ---------- 10 files changed, 2176 deletions(-) delete mode 100644 tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg delete mode 100644 tests/repros/stm32f405-differential-pair.test.tsx delete mode 100644 tests/repros/stm32f405-differential-pair/imports/LSM6DS3TR_C.tsx delete mode 100644 tests/repros/stm32f405-differential-pair/imports/STM32F405RGT6.tsx delete mode 100644 tests/repros/stm32f405-differential-pair/imports/TAXM8M4RFDCET2T.tsx delete mode 100644 tests/repros/stm32f405-differential-pair/imports/TLV75533PDBVR.tsx delete mode 100644 tests/repros/stm32f405-differential-pair/imports/TS_1187A_B_A_B.tsx delete mode 100644 tests/repros/stm32f405-differential-pair/imports/USBLC6_2SC6.tsx delete mode 100644 tests/repros/stm32f405-differential-pair/imports/UsbFootprint.tsx delete mode 100644 tests/repros/stm32f405-differential-pair/index.circuit.tsx diff --git a/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg b/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg deleted file mode 100644 index 2c73c8b86..000000000 --- a/tests/repros/__snapshots__/stm32f405-differential-pair-pcb.snap.svg +++ /dev/null @@ -1 +0,0 @@ -U1J1U4U3U2C1C2C3C4C5C6C7C8C9C10C11C12C13C14C15C16C17R1R2R3R4R5R6R7R8R9R10R11SW1SW2D1Y1pin1pin2pin3pin4pin5pin6J2pin1pin2pin3pin4J3pin1pin2pin3pin4pin5pin6J4F405 USB / IMURESETBOOTMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTEDMARKED INTERNALLY CONNECTED \ No newline at end of file diff --git a/tests/repros/stm32f405-differential-pair.test.tsx b/tests/repros/stm32f405-differential-pair.test.tsx deleted file mode 100644 index 468e291f9..000000000 --- a/tests/repros/stm32f405-differential-pair.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { expect, test } from "bun:test" -import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" -import { getTestFixture } from "tests/fixtures/get-test-fixture" -import Controller from "./stm32f405-differential-pair/index.circuit" - -test("repro: STM32F405 USB differential pair with named nets", async () => { - const { circuit } = getTestFixture() - circuit.add() - await circuit.renderUntilSettled() - const board = circuit.firstChild - if (!board) throw new Error("Expected the STM32F405 board") - - // Preserve the actual board placement before the routing-input conversion fails. - await expect(circuit).toMatchPcbSnapshot(import.meta.path) - expect(() => - getSimpleRouteJsonFromCircuitJson({ - db: circuit.db, - subcircuitComponent: board, - fanoutPourNetMap: { inner1: "GND", inner2: "V3_3" }, - }), - ).toThrow( - 'Could not find an SRJ connection for trace name or port selector ".U1 > .pin45" in differential pair "USB_FS"', - ) -}) diff --git a/tests/repros/stm32f405-differential-pair/imports/LSM6DS3TR_C.tsx b/tests/repros/stm32f405-differential-pair/imports/LSM6DS3TR_C.tsx deleted file mode 100644 index cd8cd56ab..000000000 --- a/tests/repros/stm32f405-differential-pair/imports/LSM6DS3TR_C.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import type { ChipProps } from "@tscircuit/props" - -const pinLabels = { - pin1: ["pin1"], - pin2: ["SDx"], - pin3: ["SCx"], - pin4: ["INT1"], - pin5: ["VDDIO"], - pin6: ["GND1"], - pin7: ["GND2"], - pin8: ["VDD"], - pin9: ["INT2"], - pin10: ["NC1"], - pin11: ["NC2"], - pin12: ["CS"], - pin13: ["SCL"], - pin14: ["SDA"], -} as const - -const pinAttributes = { - pin6: { requiresGround: true }, - pin7: { requiresGround: true }, - pin8: { requiresPower: true }, - pin10: { doNotConnect: true }, - pin11: { doNotConnect: true }, -} as const - -export const LSM6DS3TR_C = (props: ChipProps) => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - } - cadModel={{ - objUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C967633.obj?uuid=f43373e142124ec98babb70d58d97864", - stepUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C967633.step?uuid=f43373e142124ec98babb70d58d97864", - pcbRotationOffset: 0, - modelOriginPosition: { - x: 0.00012700000002041634, - y: -0.000012700000070253736, - z: 0, - }, - }} - {...props} - /> - ) -} diff --git a/tests/repros/stm32f405-differential-pair/imports/STM32F405RGT6.tsx b/tests/repros/stm32f405-differential-pair/imports/STM32F405RGT6.tsx deleted file mode 100644 index be61308d7..000000000 --- a/tests/repros/stm32f405-differential-pair/imports/STM32F405RGT6.tsx +++ /dev/null @@ -1,756 +0,0 @@ -import type { ChipProps } from "@tscircuit/props" - -const pinLabels = { - pin1: ["pin1"], - pin2: ["PC13"], - pin3: ["pin3"], - pin4: ["pin4"], - pin5: ["pin5"], - pin6: ["pin6"], - pin7: ["NRST"], - pin8: ["PC0"], - pin9: ["PC1"], - pin10: ["PC2"], - pin11: ["PC3"], - pin12: ["pin12"], - pin13: ["pin13"], - pin14: ["pin14"], - pin15: ["PA1"], - pin16: ["PA2"], - pin17: ["PA3"], - pin18: ["pin18"], - pin19: ["pin19"], - pin20: ["PA4"], - pin21: ["PA5"], - pin22: ["PA6"], - pin23: ["PA7"], - pin24: ["PC4"], - pin25: ["PC5"], - pin26: ["PB0"], - pin27: ["PB1"], - pin28: ["pin28"], - pin29: ["PB10"], - pin30: ["PB11"], - pin31: ["VCAP_1"], - pin32: ["pin32"], - pin33: ["PB12"], - pin34: ["PB13"], - pin35: ["PB14"], - pin36: ["PB15"], - pin37: ["PC6"], - pin38: ["PC7"], - pin39: ["PC8"], - pin40: ["PC9"], - pin41: ["PA8"], - pin42: ["PA9"], - pin43: ["PA10"], - pin44: ["PA11"], - pin45: ["PA12"], - pin46: ["pin46"], - pin47: ["VCAP_2"], - pin48: ["pin48"], - pin49: ["pin49"], - pin50: ["pin50"], - pin51: ["PC10"], - pin52: ["PC11"], - pin53: ["PC12"], - pin54: ["PD2"], - pin55: ["pin55"], - pin56: ["pin56"], - pin57: ["PB5"], - pin58: ["PB6"], - pin59: ["PB7"], - pin60: ["BOOT0"], - pin61: ["PB8"], - pin62: ["PB9"], - pin63: ["pin63"], - pin64: ["pin64"], -} as const - -export const STM32F405RGT6 = (props: ChipProps) => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } - cadModel={{ - objUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C15742.obj?uuid=7e9b9111dcfd48d3add0eab11d882721", - stepUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C15742.step?uuid=7e9b9111dcfd48d3add0eab11d882721", - pcbRotationOffset: 0, - modelOriginPosition: { x: 0, y: -0.011810999999994465, z: 0.000795 }, - }} - {...props} - /> - ) -} diff --git a/tests/repros/stm32f405-differential-pair/imports/TAXM8M4RFDCET2T.tsx b/tests/repros/stm32f405-differential-pair/imports/TAXM8M4RFDCET2T.tsx deleted file mode 100644 index dbd482a0a..000000000 --- a/tests/repros/stm32f405-differential-pair/imports/TAXM8M4RFDCET2T.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import type { CrystalProps } from "@tscircuit/props" - -type ImportedCrystalProps = Omit - -export const TAXM8M4RFDCET2T = (props: ImportedCrystalProps) => { - const { name = "X1", ...restProps } = props - - return ( - - - - - - - - - - - - } - cadModel={{ - objUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C403948.obj?uuid=6fb6f1ac4cf64e11ab7df9c0adbf8c3a", - stepUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C403948.step?uuid=6fb6f1ac4cf64e11ab7df9c0adbf8c3a", - pcbRotationOffset: 0, - modelOriginPosition: { - x: 0.000025400000140507473, - y: -0.00008889999992334197, - z: -0.001, - }, - }} - {...restProps} - /> - ) -} diff --git a/tests/repros/stm32f405-differential-pair/imports/TLV75533PDBVR.tsx b/tests/repros/stm32f405-differential-pair/imports/TLV75533PDBVR.tsx deleted file mode 100644 index fd7eedf09..000000000 --- a/tests/repros/stm32f405-differential-pair/imports/TLV75533PDBVR.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import type { ChipProps } from "@tscircuit/props" - -const pinLabels = { - pin1: ["IN"], - pin2: ["GND"], - pin3: ["EN"], - pin4: ["NC"], - pin5: ["OUT"], -} as const - -const pinAttributes = { - pin2: { requiresGround: true }, - pin4: { doNotConnect: true }, -} as const - -export const TLV75533PDBVR = (props: ChipProps) => { - return ( - - - - - - - - - - - - - - - - - - - } - cadModel={{ - objUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C404027.obj?uuid=460193f9bf2d42e58cf3c2f675b07dc6", - stepUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C404027.step?uuid=460193f9bf2d42e58cf3c2f675b07dc6", - pcbRotationOffset: 180, - modelOriginPosition: { - x: 0, - y: -0.000012700000070253736, - z: -0.049083, - }, - }} - {...props} - /> - ) -} diff --git a/tests/repros/stm32f405-differential-pair/imports/TS_1187A_B_A_B.tsx b/tests/repros/stm32f405-differential-pair/imports/TS_1187A_B_A_B.tsx deleted file mode 100644 index b41fbc5a4..000000000 --- a/tests/repros/stm32f405-differential-pair/imports/TS_1187A_B_A_B.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import type { PushButtonProps } from "@tscircuit/props" - -const pinLabels = { - pin1: ["A"], - pin2: ["B"], - pin3: ["C"], - pin4: ["D"], -} as const - -export const TS_1187A_B_A_B = (props: PushButtonProps) => { - const { name = "SW1", ...restProps } = props - - return ( - - - - - - - - - - - - - - - - - - - } - cadModel={{ - objUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C318884.obj?uuid=91b67c1735f643ffb2e7226c23dd3492", - stepUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C318884.step?uuid=91b67c1735f643ffb2e7226c23dd3492", - pcbRotationOffset: 0, - modelOriginPosition: { - x: 0.000012700000070253736, - y: 0.0004999999999999449, - z: -0.05, - }, - }} - {...restProps} - /> - ) -} diff --git a/tests/repros/stm32f405-differential-pair/imports/USBLC6_2SC6.tsx b/tests/repros/stm32f405-differential-pair/imports/USBLC6_2SC6.tsx deleted file mode 100644 index 58758e52f..000000000 --- a/tests/repros/stm32f405-differential-pair/imports/USBLC6_2SC6.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import type { ChipProps } from "@tscircuit/props" - -const pinLabels = { - pin1: ["pin1"], - pin2: ["GND"], - pin3: ["pin3"], - pin4: ["pin4"], - pin5: ["VBUS"], - pin6: ["pin6"], -} as const - -const pinAttributes = { - pin2: { requiresGround: true }, -} as const - -export const USBLC6_2SC6 = (props: ChipProps) => { - return ( - - - - - - - - - - - - - - } - cadModel={{ - objUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C2687116.obj?uuid=229b69761e2c45dba6a83d8866dec72d", - stepUrl: - "https://modelcdn.tscircuit.com/easyeda_models/assets/C2687116.step?uuid=229b69761e2c45dba6a83d8866dec72d", - pcbRotationOffset: 90, - modelOriginPosition: { - x: -0.000012700000070253736, - y: 0.000012700000070253736, - z: -0.048939, - }, - }} - {...props} - /> - ) -} diff --git a/tests/repros/stm32f405-differential-pair/imports/UsbFootprint.tsx b/tests/repros/stm32f405-differential-pair/imports/UsbFootprint.tsx deleted file mode 100644 index 9c7e217b0..000000000 --- a/tests/repros/stm32f405-differential-pair/imports/UsbFootprint.tsx +++ /dev/null @@ -1,240 +0,0 @@ -// Exact C165948 EasyEDA footprint, remapped to standard USB-C pin labels. -export const UsbFootprint = () => ( - - - - - - - - - - - - - - - - - - - - - - - - - - -) diff --git a/tests/repros/stm32f405-differential-pair/index.circuit.tsx b/tests/repros/stm32f405-differential-pair/index.circuit.tsx deleted file mode 100644 index 9c8a674c7..000000000 --- a/tests/repros/stm32f405-differential-pair/index.circuit.tsx +++ /dev/null @@ -1,401 +0,0 @@ -import { TAXM8M4RFDCET2T } from "./imports/TAXM8M4RFDCET2T" -import { UsbFootprint } from "./imports/UsbFootprint" -import { STM32F405RGT6 } from "./imports/STM32F405RGT6" -import { LSM6DS3TR_C } from "./imports/LSM6DS3TR_C" -import { TLV75533PDBVR } from "./imports/TLV75533PDBVR" -import { USBLC6_2SC6 } from "./imports/USBLC6_2SC6" -import { TS_1187A_B_A_B } from "./imports/TS_1187A_B_A_B" - -// Physical LQFP64 pin numbers, checked against ST DS8626. -const mcuNets: Record = { - 1: "V3_3", - 2: "STATUS", - 5: "OSC_IN", - 6: "OSC_OUT", - 7: "NRST", - 8: "GPIO_PC0", - 9: "GPIO_PC1", - 12: "GND", - 13: "VDDA", - 14: "GPIO_PA0", - 15: "GPIO_PA1", - 16: "UART2_TX", - 17: "UART2_RX", - 18: "GND", - 19: "V3_3", - 28: "BOOT1", - 31: "VCAP1", - 32: "V3_3", - 41: "IMU_INT", - 42: "VBUS", - 44: "USB_DM", - 45: "USB_DP", - 46: "SWDIO", - 47: "VCAP2", - 48: "V3_3", - 49: "SWCLK", - 55: "SWO", - 58: "I2C_SCL", - 59: "I2C_SDA", - 60: "BOOT0", - 63: "GND", - 64: "V3_3", -} -// name, value, PCB x/y, rotation, rail; capacitors are ceramic, >=10 V. -const caps: [string, string, number, number, number, string][] = [ - ["C1", "100nF", 8, -2.8, 90, "V3_3"], - ["C2", "100nF", 8, 4, 90, "V3_3"], - ["C3", "100nF", -4.2, 8, 0, "V3_3"], - ["C4", "100nF", -8, -3.3, 90, "V3_3"], - ["C5", "100nF", -4.5, -8, 0, "V3_3"], - ["C6", "4.7uF", -7, 7.8, 0, "V3_3"], - ["C7", "2.2uF", 8, 2, 90, "VCAP1"], - ["C8", "2.2uF", -2.2, 7.3, 0, "VCAP2"], - ["C9", "100nF", 2.3, -8, 0, "VDDA"], - ["C10", "1uF", 4.8, -8, 0, "VDDA"], - ["C11", "100nF", -10, -2.7, 0, "V3_3"], - ["C12", "100nF", -13, -2.7, 0, "V3_3"], - ["C13", "1uF", 15, 9, 90, "VBUS"], - ["C14", "4.7uF", 9, 9, 90, "V3_3"], - ["C15", "100nF", -0.5, -8, 0, "NRST"], - ["C16", "18pF", -5, -12, 90, "OSC_IN"], - ["C17", "18pF", 2, -11, 90, "OSC_OUT"], -] -const resistors: [string, string, number, number, number, string, string][] = [ - ["R1", "5.1k", -6, 11, 90, "CC1", "GND"], - ["R2", "5.1k", 6, 11, 90, "CC2", "GND"], - ["R3", "22", 0, 7.3, 0, "USB_DP", "USB_DP_CONN"], - ["R4", "22", 2.1, 7.3, 0, "USB_DM", "USB_DM_CONN"], - ["R5", "10k", -6, -10, 90, "V3_3", "NRST"], - ["R6", "10k", -9, 0, 90, "BOOT0", "GND"], - ["R7", "10k", 8, 0, 90, "BOOT1", "GND"], - ["R8", "4.7k", -10, 3, 90, "V3_3", "I2C_SCL"], - ["R9", "4.7k", -12, 3, 90, "V3_3", "I2C_SDA"], - ["R10", "1k", -10, -6, 0, "STATUS", "LED_A"], - ["R11", "10", 6.5, -8, 90, "V3_3", "VDDA"], -] -const wire = (pin: string, net: string) => ( - -) -export default function Controller({ - routingDisabled = false, -}: { routingDisabled?: boolean }) { - return ( - - {/* Drop supply connections to their planes before routing sensitive signals. */} - - - - - {["MCU", "Decoupling", "USB", "Power", "IMU", "Controls", "Headers"].map( - (name) => ( - - ), - )} - - {Object.entries(mcuNets).map(([pin, net]) => - wire(`.U1 > .pin${pin}`, net), - )} - } - manufacturerPartNumber="TYPE-C-31-M-12" - supplierPartNumbers={{ jlcpcb: ["C165948"] }} - pinLabels={{ - pin1: "GND1", - pin2: "VBUS1", - pin3: "CC1", - pin4: "DP1", - pin5: "DM1", - pin6: "SBU1", - pin7: "SBU2", - pin8: "DM2", - pin9: "DP2", - pin10: "CC2", - pin11: "VBUS2", - pin12: "GND2", - pin13: "SHELL1", - pin14: "SHELL2", - pin15: "SHELL3", - pin16: "SHELL4", - }} - pcbX={0} - pcbY={15} - pcbRotation={180} - schPortArrangement={{ - rightSide: { - pins: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - direction: "top-to-bottom", - }, - }} - schWidth={1.65} - schHeight={3.4} - schX={25} - schY={12} - schSectionName="USB" - /> - {Object.entries({ - GND1: "GND", - GND2: "GND", - VBUS1: "VBUS", - VBUS2: "VBUS", - CC1: "CC1", - CC2: "CC2", - DP1: "USB_DP_CONN", - DP2: "USB_DP_CONN", - DM1: "USB_DM_CONN", - DM2: "USB_DM_CONN", - SHELL1: "GND", - SHELL2: "GND", - SHELL3: "GND", - SHELL4: "GND", - }).map(([p, n]) => wire(`.J1 > .${p}`, n))} - - {Object.entries({ - 1: "USB_DP_CONN", - 2: "GND", - 3: "USB_DM_CONN", - 4: "USB_DM_CONN", - 5: "VBUS", - 6: "USB_DP_CONN", - }).map(([p, n]) => wire(`.U4 > .pin${p}`, n))} - - - {Object.entries({ 1: "VBUS", 2: "GND", 3: "VBUS", 5: "V3_3" }).map( - ([p, n]) => wire(`.U3 > .pin${p}`, n), - )} - - {Object.entries({ - 1: "GND", - 4: "IMU_INT", - 5: "V3_3", - 6: "GND", - 7: "GND", - 8: "V3_3", - 12: "V3_3", - 13: "I2C_SCL", - 14: "I2C_SDA", - }).map(([p, n]) => wire(`.U2 > .pin${p}`, n))} - {caps.map(([name, value, x, y, rot, net], i) => ( - - ))} - {resistors.map(([name, value, x, y, rot, n1, n2], i) => ( - - ))} - - - {[1, 3].map((p) => wire(`.SW1 > .pin${p}`, "NRST"))} - {[2, 4].map((p) => wire(`.SW1 > .pin${p}`, "GND"))} - {[1, 3].map((p) => wire(`.SW2 > .pin${p}`, "BOOT0"))} - {[2, 4].map((p) => wire(`.SW2 > .pin${p}`, "V3_3"))} - - - {( - [ - ["J2", -17, 8.7, ["V3_3", "SWDIO", "GND", "SWCLK", "NRST", "SWO"]], - ["J3", 17, -2, ["GND", "V3_3", "UART2_TX", "UART2_RX"]], - [ - "J4", - -17, - -8.7, - ["GND", "V3_3", "GPIO_PC0", "GPIO_PC1", "GPIO_PA0", "GPIO_PA1"], - ], - ] as [string, number, number, string[]][] - ).map(([name, x, y, nets], i) => ( - [`pin${i + 1}`, `net.${n}`]), - )} - /> - ))} - - - - - - - - ) -} From fb3ae5339001c9714fefc436d29c5faae4708a3e Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 14:02:17 +0530 Subject: [PATCH 07/16] up --- .../usb-c-breakout-net-pair-pcb.snap.svg | 1 + .../usb-header-net-pair-pcb.snap.svg | 1 - tests/repros/usb-c-breakout-net-pair.test.tsx | 23 ++ tests/repros/usb-c-breakout/board.tsx | 129 ++++++++++ tests/repros/usb-c-breakout/usb-footprint.tsx | 240 ++++++++++++++++++ tests/repros/usb-header-net-pair.test.tsx | 73 ------ 6 files changed, 393 insertions(+), 74 deletions(-) create mode 100644 tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg delete mode 100644 tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg create mode 100644 tests/repros/usb-c-breakout-net-pair.test.tsx create mode 100644 tests/repros/usb-c-breakout/board.tsx create mode 100644 tests/repros/usb-c-breakout/usb-footprint.tsx delete mode 100644 tests/repros/usb-header-net-pair.test.tsx diff --git a/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg b/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg new file mode 100644 index 000000000..342c8220f --- /dev/null +++ b/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg @@ -0,0 +1 @@ +J1R1R2R3R4C1USB-C DEVICE BREAKOUTCC pull-downs: 5.1k \ No newline at end of file diff --git a/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg b/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg deleted file mode 100644 index 6d77737e7..000000000 --- a/tests/repros/__snapshots__/usb-header-net-pair-pcb.snap.svg +++ /dev/null @@ -1 +0,0 @@ -pin1pin2pin3pin4J1pin1pin2pin3pin4J2USB header interconnect1: VBUS 2: D- 3: D+ 4: GND \ No newline at end of file diff --git a/tests/repros/usb-c-breakout-net-pair.test.tsx b/tests/repros/usb-c-breakout-net-pair.test.tsx new file mode 100644 index 000000000..80051fdb3 --- /dev/null +++ b/tests/repros/usb-c-breakout-net-pair.test.tsx @@ -0,0 +1,23 @@ +import { expect, test } from "bun:test" +import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" +import { getTestFixture } from "tests/fixtures/get-test-fixture" + +import UsbBreakoutBoard from "./usb-c-breakout/board" + +test("USB-C breakout pair wired through named nets", async () => { + const { circuit } = getTestFixture() + circuit.add() + await circuit.renderUntilSettled() + const board = circuit.firstChild + if (!board) throw new Error("Expected USB-C breakout board") + expect(() => + getSimpleRouteJsonFromCircuitJson({ + db: circuit.db, + subcircuitComponent: board, + }), + ).toThrow( + 'Could not find an SRJ connection for trace name or port selector ".R3 > .pin1"', + ) + expect(circuit.db.pcb_trace.list()).toHaveLength(0) + await expect(circuit).toMatchPcbSnapshot(import.meta.path) +}, 30_000) diff --git a/tests/repros/usb-c-breakout/board.tsx b/tests/repros/usb-c-breakout/board.tsx new file mode 100644 index 000000000..e12b2c6d8 --- /dev/null +++ b/tests/repros/usb-c-breakout/board.tsx @@ -0,0 +1,129 @@ +import { UsbFootprint } from "./usb-footprint" + +export default function UsbBreakoutBoard() { + return ( + + } + pcbY={8} + pcbRotation={180} + pinLabels={{ + pin1: "GND1", + pin2: "VBUS1", + pin3: "CC1", + pin4: "DP1", + pin5: "DM1", + pin6: "SBU1", + pin7: "SBU2", + pin8: "DM2", + pin9: "DP2", + pin10: "CC2", + pin11: "VBUS2", + pin12: "GND2", + pin13: "SHELL1", + pin14: "SHELL2", + pin15: "SHELL3", + pin16: "SHELL4", + }} + /> + + + + + + + {Object.entries({ + GND1: "GND", + GND2: "GND", + SHELL1: "GND", + SHELL2: "GND", + SHELL3: "GND", + SHELL4: "GND", + VBUS1: "VBUS", + VBUS2: "VBUS", + DP1: "DP_USB", + DP2: "DP_USB", + DM1: "DM_USB", + DM2: "DM_USB", + CC1: "CC1", + CC2: "CC2", + }).map(([pin, net]) => ( + .${pin}`} to={`net.${net}`} /> + ))} + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/tests/repros/usb-c-breakout/usb-footprint.tsx b/tests/repros/usb-c-breakout/usb-footprint.tsx new file mode 100644 index 000000000..9c7e217b0 --- /dev/null +++ b/tests/repros/usb-c-breakout/usb-footprint.tsx @@ -0,0 +1,240 @@ +// Exact C165948 EasyEDA footprint, remapped to standard USB-C pin labels. +export const UsbFootprint = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/tests/repros/usb-header-net-pair.test.tsx b/tests/repros/usb-header-net-pair.test.tsx deleted file mode 100644 index 4fe032178..000000000 --- a/tests/repros/usb-header-net-pair.test.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { expect, test } from "bun:test" -import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" -import { getTestFixture } from "tests/fixtures/get-test-fixture" - -// A passive USB signal interconnect with two standard 2.54 mm headers. -function UsbHeaderBoard() { - return ( - - - - - - - - - - - - - - - - - - ) -} - -test("USB header pair wired through named nets", async () => { - const { circuit } = getTestFixture() - circuit.add() - await circuit.renderUntilSettled() - const board = circuit.firstChild - if (!board) throw new Error("Expected USB header board") - expect(() => - getSimpleRouteJsonFromCircuitJson({ - db: circuit.db, - subcircuitComponent: board, - }), - ).toThrow( - 'Could not find an SRJ connection for trace name or port selector ".J1 > .pin3"', - ) - expect(circuit.db.pcb_trace.list()).toHaveLength(0) - await expect(circuit).toMatchPcbSnapshot(import.meta.path) -}, 30_000) From 3743601d18d1970bf72e310b8c99ff5e5c1209dc Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 14:05:15 +0530 Subject: [PATCH 08/16] up --- tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg | 2 +- tests/repros/usb-c-breakout-net-pair.test.tsx | 2 ++ tests/repros/usb-c-breakout/board.tsx | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg b/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg index 342c8220f..c3f43b019 100644 --- a/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg +++ b/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg @@ -1 +1 @@ -J1R1R2R3R4C1USB-C DEVICE BREAKOUTCC pull-downs: 5.1k \ No newline at end of file +J1VBUSDMDPGNDJ2R1R2R3R4C1USB-C DEVICE BREAKOUTCC pull-downs: 5.1k \ No newline at end of file diff --git a/tests/repros/usb-c-breakout-net-pair.test.tsx b/tests/repros/usb-c-breakout-net-pair.test.tsx index 80051fdb3..d6b225c22 100644 --- a/tests/repros/usb-c-breakout-net-pair.test.tsx +++ b/tests/repros/usb-c-breakout-net-pair.test.tsx @@ -8,6 +8,8 @@ test("USB-C breakout pair wired through named nets", async () => { const { circuit } = getTestFixture() circuit.add() await circuit.renderUntilSettled() + expect(circuit.db.source_failed_to_create_component_error.list()).toEqual([]) + expect(circuit.db.source_trace_not_connected_error.list()).toEqual([]) const board = circuit.firstChild if (!board) throw new Error("Expected USB-C breakout board") expect(() => diff --git a/tests/repros/usb-c-breakout/board.tsx b/tests/repros/usb-c-breakout/board.tsx index e12b2c6d8..3d0bf343d 100644 --- a/tests/repros/usb-c-breakout/board.tsx +++ b/tests/repros/usb-c-breakout/board.tsx @@ -34,7 +34,7 @@ export default function UsbBreakoutBoard() { pinCount={4} footprint="pinrow4" pcbY={-8} - pinLabels={["VBUS", "D-", "D+", "GND"]} + pinLabels={["VBUS", "DM", "DP", "GND"]} /> Date: Sat, 5 Sep 2026 14:06:01 +0530 Subject: [PATCH 09/16] up --- .../usb-c-breakout-net-pair-pcb.snap.svg | 2 +- tests/repros/usb-c-breakout-net-pair.test.tsx | 20 ++++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg b/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg index c3f43b019..a940c24bf 100644 --- a/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg +++ b/tests/repros/__snapshots__/usb-c-breakout-net-pair-pcb.snap.svg @@ -1 +1 @@ -J1VBUSDMDPGNDJ2R1R2R3R4C1USB-C DEVICE BREAKOUTCC pull-downs: 5.1k \ No newline at end of file +J1VBUSDMDPGNDJ2R1R2R3R4C1USB-C DEVICE BREAKOUTCC pull-downs: 5.1kautorouting phase 0autorouting phase 1 \ No newline at end of file diff --git a/tests/repros/usb-c-breakout-net-pair.test.tsx b/tests/repros/usb-c-breakout-net-pair.test.tsx index d6b225c22..ad867e694 100644 --- a/tests/repros/usb-c-breakout-net-pair.test.tsx +++ b/tests/repros/usb-c-breakout-net-pair.test.tsx @@ -1,25 +1,21 @@ import { expect, test } from "bun:test" -import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" +import { createAutoroutingPhaseIoStack } from "tests/fixtures/create-autorouting-phase-io-stack" import { getTestFixture } from "tests/fixtures/get-test-fixture" import UsbBreakoutBoard from "./usb-c-breakout/board" test("USB-C breakout pair wired through named nets", async () => { const { circuit } = getTestFixture() + const phases = createAutoroutingPhaseIoStack(circuit) circuit.add() await circuit.renderUntilSettled() expect(circuit.db.source_failed_to_create_component_error.list()).toEqual([]) expect(circuit.db.source_trace_not_connected_error.list()).toEqual([]) - const board = circuit.firstChild - if (!board) throw new Error("Expected USB-C breakout board") - expect(() => - getSimpleRouteJsonFromCircuitJson({ - db: circuit.db, - subcircuitComponent: board, - }), - ).toThrow( - 'Could not find an SRJ connection for trace name or port selector ".R3 > .pin1"', - ) - expect(circuit.db.pcb_trace.list()).toHaveLength(0) + expect(phases).toHaveLength(2) + expect(phases[0]?.startSimpleRouteJson?.differentialPairs).toHaveLength(1) + expect(phases[0]?.endSimpleRouteJson?.traces).toHaveLength(2) + expect(circuit.db.pcb_autorouting_error.list()).toEqual([]) + expect(circuit.db.pcb_trace_error.list()).toEqual([]) + expect(circuit.db.pcb_port_not_connected_error.list()).toEqual([]) await expect(circuit).toMatchPcbSnapshot(import.meta.path) }, 30_000) From 033ef9992e5f8bcd4acf473ad716f067c0b24d3a Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 14:08:09 +0530 Subject: [PATCH 10/16] up --- ...net-connections-routing-phase-pcb.snap.svg | 1 - .../net-connections-routing-phase.test.tsx | 51 ------------------- 2 files changed, 52 deletions(-) delete mode 100644 tests/components/primitive-components/differential-pair/__snapshots__/net-connections-routing-phase-pcb.snap.svg delete mode 100644 tests/components/primitive-components/differential-pair/net-connections-routing-phase.test.tsx diff --git a/tests/components/primitive-components/differential-pair/__snapshots__/net-connections-routing-phase-pcb.snap.svg b/tests/components/primitive-components/differential-pair/__snapshots__/net-connections-routing-phase-pcb.snap.svg deleted file mode 100644 index 127124adc..000000000 --- a/tests/components/primitive-components/differential-pair/__snapshots__/net-connections-routing-phase-pcb.snap.svg +++ /dev/null @@ -1 +0,0 @@ -U1U2Pin-to-net differential pair: phase 0autorouting phase 0autorouting phase 1autorouting phase 2 \ No newline at end of file diff --git a/tests/components/primitive-components/differential-pair/net-connections-routing-phase.test.tsx b/tests/components/primitive-components/differential-pair/net-connections-routing-phase.test.tsx deleted file mode 100644 index debe1bd61..000000000 --- a/tests/components/primitive-components/differential-pair/net-connections-routing-phase.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { expect, test } from "bun:test" -import { createAutoroutingPhaseIoStack } from "tests/fixtures/create-autorouting-phase-io-stack" -import { getTestFixture } from "tests/fixtures/get-test-fixture" - -test("routes a pin-to-net differential pair in its selected phase", async () => { - const { circuit } = getTestFixture() - const phases = createAutoroutingPhaseIoStack(circuit) - circuit.add( - - - - - - - - - - - - - - , - ) - await circuit.renderUntilSettled() - expect(phases).toHaveLength(2) - expect(phases[0]?.startSimpleRouteJson?.differentialPairs).toHaveLength(1) - expect(phases[0]?.startSimpleRouteJson?.connections).toHaveLength(2) - expect(phases[0]?.endSimpleRouteJson?.traces).toHaveLength(2) - expect(phases[1]?.startSimpleRouteJson?.connections).toHaveLength(1) - expect(phases[1]?.startSimpleRouteJson?.differentialPairs).toBeUndefined() - expect(circuit.db.pcb_trace.list()).toHaveLength(3) - expect(circuit.db.pcb_trace_error.list()).toEqual([]) - await expect(circuit).toMatchPcbSnapshot(import.meta.path) -}) From 9a1712f889e2e2ec6c0b3620a7e473e1f59df9c2 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sat, 5 Sep 2026 14:16:01 +0530 Subject: [PATCH 11/16] up --- .../Group/Group_phasedAutoroutingUtils.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts b/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts index c0d86e884..a8141a41a 100644 --- a/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts +++ b/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts @@ -21,7 +21,11 @@ export function connectionIsInRoutingPhase( for (const trace of phasePlan.traces) { if (!trace.source_trace_id) continue if (connection.source_trace_id === trace.source_trace_id) return true - if (connection.source_trace_ids?.includes(trace.source_trace_id)) + // Breakouts use their split, group-owned connections, not the whole net. + if ( + !phasePlan.routingPcbGroupId && + connection.source_trace_ids?.includes(trace.source_trace_id) + ) return true if (connection.name === trace.source_trace_id) return true if (connection.rootConnectionName === trace.source_trace_id) return true From 447b555d5e7d3d77172b19cb7f55ebec97e94c99 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sun, 6 Sep 2026 00:09:53 +0530 Subject: [PATCH 12/16] up --- .../usb-c-breakout-routing-phases.snap.svg | 591 ++++++++++++++++++ tests/repros/usb-c-breakout-net-pair.test.tsx | 5 + 2 files changed, 596 insertions(+) create mode 100644 tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg diff --git a/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg b/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg new file mode 100644 index 000000000..ba3deac3e --- /dev/null +++ b/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg @@ -0,0 +1,591 @@ + + + + + + AUTOROUTING PHASE 1 START: 2 CONNECTIONS, 0 TRACES + + + + + + + + + AUTOROUTING PHASE 1 END: 2 CONNECTIONS, 2 TRACES + + + + + + + + + + + AUTOROUTING PHASE 2 START: 6 CONNECTIONS, 2 TRACES + + + + + + + + + AUTOROUTING PHASE 2 END: 6 CONNECTIONS, 20 TRACES + + + + + + + + + + + FULL ROUTED CIRCUIT: 4 CONNECTIONS, 20 TRACES + + + + + + + + + + diff --git a/tests/repros/usb-c-breakout-net-pair.test.tsx b/tests/repros/usb-c-breakout-net-pair.test.tsx index ad867e694..04f31ea40 100644 --- a/tests/repros/usb-c-breakout-net-pair.test.tsx +++ b/tests/repros/usb-c-breakout-net-pair.test.tsx @@ -17,5 +17,10 @@ test("USB-C breakout pair wired through named nets", async () => { expect(circuit.db.pcb_autorouting_error.list()).toEqual([]) expect(circuit.db.pcb_trace_error.list()).toEqual([]) expect(circuit.db.pcb_port_not_connected_error.list()).toEqual([]) + await expect(phases).toMatchAutoroutingPhaseIoStackSnapshot( + import.meta.path, + "usb-c-breakout-routing-phases", + circuit, + ) await expect(circuit).toMatchPcbSnapshot(import.meta.path) }, 30_000) From 233961215d835f8700482b8b2e8d472af89e5e8d Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sun, 6 Sep 2026 00:14:13 +0530 Subject: [PATCH 13/16] up --- .../usb-c-breakout-routing-phases.snap.svg | 10 ++++++++++ tests/repros/usb-c-breakout-net-pair.test.tsx | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg diff --git a/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg b/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg new file mode 100644 index 000000000..80891c5d8 --- /dev/null +++ b/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg @@ -0,0 +1,10 @@ + + + + AUTOROUTING BLOCKED: NO PHASE STARTED + USB differential-pair connection lookup failed + + + J1VBUSDMDPGNDJ2R1R2R3R4C1USB-C DEVICE BREAKOUTCC pull-downs: 5.1k + + diff --git a/tests/repros/usb-c-breakout-net-pair.test.tsx b/tests/repros/usb-c-breakout-net-pair.test.tsx index d6b225c22..48bd144af 100644 --- a/tests/repros/usb-c-breakout-net-pair.test.tsx +++ b/tests/repros/usb-c-breakout-net-pair.test.tsx @@ -1,4 +1,7 @@ import { expect, test } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { stackSvgsVertically } from "stack-svgs" +import { createAutoroutingPhaseIoStack } from "tests/fixtures/create-autorouting-phase-io-stack" import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" import { getTestFixture } from "tests/fixtures/get-test-fixture" @@ -6,6 +9,7 @@ import UsbBreakoutBoard from "./usb-c-breakout/board" test("USB-C breakout pair wired through named nets", async () => { const { circuit } = getTestFixture() + const phases = createAutoroutingPhaseIoStack(circuit) circuit.add() await circuit.renderUntilSettled() expect(circuit.db.source_failed_to_create_component_error.list()).toEqual([]) @@ -21,5 +25,19 @@ test("USB-C breakout pair wired through named nets", async () => { 'Could not find an SRJ connection for trace name or port selector ".R3 > .pin1"', ) expect(circuit.db.pcb_trace.list()).toHaveLength(0) + expect(phases).toHaveLength(0) + // No phase input exists yet: differential-pair conversion fails first. + const blockedRoutingSvg = stackSvgsVertically([ + ` + + AUTOROUTING BLOCKED: NO PHASE STARTED + USB differential-pair connection lookup failed + `, + convertCircuitJsonToPcbSvg(circuit.getCircuitJson()), + ]) + await expect(blockedRoutingSvg).toMatchSvgSnapshot( + import.meta.path, + "usb-c-breakout-routing-phases", + ) await expect(circuit).toMatchPcbSnapshot(import.meta.path) }, 30_000) From f8bddf0744f948b82f30f3ef2d3827558acdd594 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sun, 6 Sep 2026 00:16:52 +0530 Subject: [PATCH 14/16] up --- .../usb-c-breakout-routing-phases.snap.svg | 10 ---------- tests/repros/usb-c-breakout-net-pair.test.tsx | 18 ------------------ 2 files changed, 28 deletions(-) delete mode 100644 tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg diff --git a/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg b/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg deleted file mode 100644 index 80891c5d8..000000000 --- a/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - AUTOROUTING BLOCKED: NO PHASE STARTED - USB differential-pair connection lookup failed - - - J1VBUSDMDPGNDJ2R1R2R3R4C1USB-C DEVICE BREAKOUTCC pull-downs: 5.1k - - diff --git a/tests/repros/usb-c-breakout-net-pair.test.tsx b/tests/repros/usb-c-breakout-net-pair.test.tsx index 48bd144af..d6b225c22 100644 --- a/tests/repros/usb-c-breakout-net-pair.test.tsx +++ b/tests/repros/usb-c-breakout-net-pair.test.tsx @@ -1,7 +1,4 @@ import { expect, test } from "bun:test" -import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" -import { stackSvgsVertically } from "stack-svgs" -import { createAutoroutingPhaseIoStack } from "tests/fixtures/create-autorouting-phase-io-stack" import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/autorouting/getSimpleRouteJsonFromCircuitJson" import { getTestFixture } from "tests/fixtures/get-test-fixture" @@ -9,7 +6,6 @@ import UsbBreakoutBoard from "./usb-c-breakout/board" test("USB-C breakout pair wired through named nets", async () => { const { circuit } = getTestFixture() - const phases = createAutoroutingPhaseIoStack(circuit) circuit.add() await circuit.renderUntilSettled() expect(circuit.db.source_failed_to_create_component_error.list()).toEqual([]) @@ -25,19 +21,5 @@ test("USB-C breakout pair wired through named nets", async () => { 'Could not find an SRJ connection for trace name or port selector ".R3 > .pin1"', ) expect(circuit.db.pcb_trace.list()).toHaveLength(0) - expect(phases).toHaveLength(0) - // No phase input exists yet: differential-pair conversion fails first. - const blockedRoutingSvg = stackSvgsVertically([ - ` - - AUTOROUTING BLOCKED: NO PHASE STARTED - USB differential-pair connection lookup failed - `, - convertCircuitJsonToPcbSvg(circuit.getCircuitJson()), - ]) - await expect(blockedRoutingSvg).toMatchSvgSnapshot( - import.meta.path, - "usb-c-breakout-routing-phases", - ) await expect(circuit).toMatchPcbSnapshot(import.meta.path) }, 30_000) From ae1e143f930d6a9978270d3ae252253ea3b56e2e Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Sun, 6 Sep 2026 01:51:14 +0530 Subject: [PATCH 15/16] up --- .../Group/Group_getRoutingPhasePlans.ts | 28 +- .../Group/Group_phasedAutoroutingUtils.ts | 6 - .../usb-c-breakout-routing-phases.snap.svg | 406 +++++++++--------- tests/repros/usb-c-breakout-net-pair.test.tsx | 131 +++++- tests/repros/usb-c-breakout/board.tsx | 129 ------ 5 files changed, 340 insertions(+), 360 deletions(-) delete mode 100644 tests/repros/usb-c-breakout/board.tsx diff --git a/lib/components/primitive-components/Group/Group_getRoutingPhasePlans.ts b/lib/components/primitive-components/Group/Group_getRoutingPhasePlans.ts index bf0685af9..780f2c5b2 100644 --- a/lib/components/primitive-components/Group/Group_getRoutingPhasePlans.ts +++ b/lib/components/primitive-components/Group/Group_getRoutingPhasePlans.ts @@ -128,14 +128,13 @@ function traceHasEndpointMatchingConnectionSelector( trace: Trace, connectionSelectorEndpointKey: string, ): boolean { - return [ - ...trace.getTracePortPathSelectors(), - ...trace.getTracePathNetSelectors(), - ].some( - (selector) => - convertPortSelectorToEndpointKey(selector) === - connectionSelectorEndpointKey, - ) + return trace + .getTracePortPathSelectors() + .some( + (selector) => + convertPortSelectorToEndpointKey(selector) === + connectionSelectorEndpointKey, + ) } function getAutoroutersByPhaseIndex( @@ -308,19 +307,6 @@ export function Group_getRoutingPhasePlans( convertPortSelectorToEndpointKey, ) plan.connectionSelectors = connectionSelectors - for (const selector of connectionSelectors) { - const net = group.selectOne(selector, { type: "net" }) - if (!net || breakoutByNet.has(net)) continue - if (!phaseProps.reroute) { - for (const existingPlan of plansByPhaseIndex.values()) { - if (existingPlan === plan) continue - existingPlan.nets = existingPlan.nets.filter( - (existingNet) => existingNet !== net, - ) - } - } - if (!plan.nets.includes(net)) plan.nets.push(net) - } for (const trace of traces) { if (breakoutByTrace.has(trace)) continue diff --git a/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts b/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts index a8141a41a..f41543fdf 100644 --- a/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts +++ b/lib/components/primitive-components/Group/Group_phasedAutoroutingUtils.ts @@ -21,12 +21,6 @@ export function connectionIsInRoutingPhase( for (const trace of phasePlan.traces) { if (!trace.source_trace_id) continue if (connection.source_trace_id === trace.source_trace_id) return true - // Breakouts use their split, group-owned connections, not the whole net. - if ( - !phasePlan.routingPcbGroupId && - connection.source_trace_ids?.includes(trace.source_trace_id) - ) - return true if (connection.name === trace.source_trace_id) return true if (connection.rootConnectionName === trace.source_trace_id) return true if (connection.mergedConnectionNames?.includes(trace.source_trace_id)) { diff --git a/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg b/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg index ba3deac3e..9f32f2d94 100644 --- a/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg +++ b/tests/repros/__snapshots__/usb-c-breakout-routing-phases.snap.svg @@ -15,29 +15,29 @@