Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and output an array of arrays for any issues found.
| Function | Description |
| --- | --- |
| [`checkConnectorAccessibleOrientation`](./lib/check-connector-accessible-orientation.ts) | Returns `pcb_accessibility_error` for connectors whose orientation makes them inaccessible. |
| [`checkAllPinsInComponentAreUnderspecified`](./lib/check-all-pins-in-component-are-underspecified.ts) | Returns `source_component_pins_underspecified_warning` when every pin on a chip lacks pin attributes. |
| [`checkDifferentNetViaSpacing`](./lib/check-different-net-via-spacing.ts) | Returns `pcb_via_clearance_error` if vias on different nets are too close together. |
| [`checkEachPcbPortConnectedToPcbTraces`](./lib/check-each-pcb-port-connected-to-pcb-trace.ts) | Returns `pcb_trace_error` if any `source_port` is not connected to its corresponding PCB traces. |
| [`checkEachPcbTraceNonOverlapping`](./lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts) | Returns `pcb_trace_error` when `pcb_trace` segments overlap incompatible geometry on the same layer. |
Expand All @@ -27,7 +28,7 @@ and output an array of arrays for any issues found.
| Function | Description |
| --- | --- |
| [`runAllPlacementChecks`](./lib/run-all-checks.ts) | Runs all placement checks (`checkViasOffBoard`, `checkPcbComponentsOutOfBoard`, `checkPcbComponentOverlap`, and `checkConnectorAccessibleOrientation`). |
| [`runAllNetlistChecks`](./lib/run-all-checks.ts) | Runs all netlist checks (`checkPinMustBeConnected`). |
| [`runAllNetlistChecks`](./lib/run-all-checks.ts) | Runs all netlist checks (e.g. `checkPinMustBeConnected`, `checkAllPinsInComponentAreUnderspecified`). |
| [`runAllRoutingChecks`](./lib/run-all-checks.ts) | Runs all routing checks currently enabled (`checkEachPcbPortConnectedToPcbTraces`, `checkSourceTracesHavePcbTraces`, `checkEachPcbTraceNonOverlapping`, same/different net via spacing, and `checkPcbTracesOutOfBoard`). |
| [`runAllChecks`](./lib/run-all-checks.ts) | Runs all placement, netlist, and routing checks and returns a combined list of errors. |

Expand Down
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { checkSourceTracesHavePcbTraces } from "./lib/check-source-traces-have-p
export { checkPcbTracesOutOfBoard } from "./lib/check-trace-out-of-board/checkTraceOutOfBoard"
export { checkPcbComponentOverlap } from "./lib/check-pcb-components-overlap/checkPcbComponentOverlap"
export { checkPinMustBeConnected } from "./lib/check-pin-must-be-connected"
export { checkAllPinsInComponentAreUnderspecified } from "./lib/check-all-pins-in-component-are-underspecified"
export {
runAllChecks,
runAllNetlistChecks,
Expand Down
75 changes: 75 additions & 0 deletions lib/check-all-pins-in-component-are-underspecified.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { cju } from "@tscircuit/circuit-json-util"
import type {
AnyCircuitElement,
SourcePinAttributes,
SourcePort,
SourceComponentBase,
} from "circuit-json"
import { source_pin_attributes } from "circuit-json"

type SourceComponentPinsUnderspecifiedWarning = {
type: "source_component_pins_underspecified_warning"
source_component_pins_underspecified_warning_id: string
warning_type: "source_component_pins_underspecified_warning"
message: string
source_component_id: string
source_port_ids: string[]
subcircuit_id?: string
}

const PIN_ATTRIBUTE_KEYS = Object.keys(
source_pin_attributes.shape,
) as (keyof SourcePinAttributes)[]

function hasAnyPinAttribute(port: SourcePort): boolean {
return PIN_ATTRIBUTE_KEYS.some((key) => port[key] !== undefined)
}

/**
* Check that each component with ports has at least one pin attribute
* specified across its ports. Returns a warning when all pins are
* underspecified (no SourcePinAttributes fields set on any port).
*/
export function checkAllPinsInComponentAreUnderspecified(
circuitJson: AnyCircuitElement[],
): SourceComponentPinsUnderspecifiedWarning[] {
const warnings: SourceComponentPinsUnderspecifiedWarning[] = []
const db = cju(circuitJson)

const sourceComponents = db.source_component.list() as SourceComponentBase[]
const sourcePorts = db.source_port.list() as SourcePort[]

const portsByComponent = new Map<string, SourcePort[]>()
for (const port of sourcePorts) {
if (!port.source_component_id) continue
const existing = portsByComponent.get(port.source_component_id) ?? []
existing.push(port)
portsByComponent.set(port.source_component_id, existing)
}

for (const component of sourceComponents) {
if (component.ftype !== "simple_chip") continue

const componentPorts =
portsByComponent.get(component.source_component_id) ?? []
if (componentPorts.length === 0) continue

const hasAnySpecifiedAttributes = componentPorts.some((port) =>
hasAnyPinAttribute(port),
)

if (hasAnySpecifiedAttributes) continue

warnings.push({
type: "source_component_pins_underspecified_warning",
source_component_pins_underspecified_warning_id: `source_component_pins_underspecified_warning_${component.source_component_id}`,
warning_type: "source_component_pins_underspecified_warning",
message: `All pins on ${component.name} are underspecified (no pinAttributes set)`,
source_component_id: component.source_component_id,
source_port_ids: componentPorts.map((port) => port.source_port_id),
subcircuit_id: componentPorts[0]?.subcircuit_id,
})
}

return warnings
}
6 changes: 5 additions & 1 deletion lib/run-all-checks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { AnyCircuitElement } from "circuit-json"
import { checkAllPinsInComponentAreUnderspecified } from "./check-all-pins-in-component-are-underspecified"
import { checkDifferentNetViaSpacing } from "./check-different-net-via-spacing"
import { checkEachPcbPortConnectedToPcbTraces } from "./check-each-pcb-port-connected-to-pcb-trace"
import { checkEachPcbTraceNonOverlapping } from "./check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping"
Expand All @@ -22,7 +23,10 @@ export async function runAllPlacementChecks(circuitJson: AnyCircuitElement[]) {
}

export async function runAllNetlistChecks(circuitJson: AnyCircuitElement[]) {
return [...checkPinMustBeConnected(circuitJson)]
return [
...checkPinMustBeConnected(circuitJson),
...checkAllPinsInComponentAreUnderspecified(circuitJson),
]
}

export async function runAllRoutingChecks(circuitJson: AnyCircuitElement[]) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@types/debug": "^4.1.12",
"bun-match-svg": "^0.0.11",
"circuit-to-svg": "^0.0.333",
"circuit-json": "^0.0.391",
"circuit-json": "^0.0.392",
"debug": "^4.3.5",
"tscircuit": "^0.0.1439",
"zod": "^3.23.8",
Expand Down
128 changes: 128 additions & 0 deletions tests/lib/check-all-pins-in-component-are-underspecified.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { describe, expect, test } from "bun:test"
import type { AnyCircuitElement } from "circuit-json"
import { checkAllPinsInComponentAreUnderspecified } from "lib/check-all-pins-in-component-are-underspecified"

describe("checkAllPinsInComponentAreUnderspecified", () => {
test("returns a warning when all ports on a component are missing pinAttributes", () => {
const circuitJson: AnyCircuitElement[] = [
{
type: "source_component",
source_component_id: "component_1",
name: "U1",
ftype: "simple_chip",
},
{
type: "source_port",
source_port_id: "port_1",
source_component_id: "component_1",
name: "IO1",
},
{
type: "source_port",
source_port_id: "port_2",
source_component_id: "component_1",
name: "IO2",
},
]

const warnings = checkAllPinsInComponentAreUnderspecified(circuitJson)
expect(warnings).toHaveLength(1)
expect(warnings[0].type).toBe(
"source_component_pins_underspecified_warning",
)
expect(warnings[0].warning_type).toBe(
"source_component_pins_underspecified_warning",
)
expect(warnings[0].source_component_id).toBe("component_1")
expect(warnings[0].source_port_ids).toEqual(["port_1", "port_2"])
expect(warnings[0].message).toContain("All pins on U1 are underspecified")
})

test("returns no warning when at least one port on a component has pinAttributes", () => {
const circuitJson: AnyCircuitElement[] = [
{
type: "source_component",
source_component_id: "component_1",
name: "U1",
ftype: "simple_chip",
},
{
type: "source_port",
source_port_id: "port_1",
source_component_id: "component_1",
name: "IO1",
},
{
type: "source_port",
source_port_id: "port_2",
source_component_id: "component_1",
name: "IO2",
must_be_connected: false,
},
]

const warnings = checkAllPinsInComponentAreUnderspecified(circuitJson)
expect(warnings).toHaveLength(0)
})

test("only returns warnings for components whose all ports are underspecified", () => {
const circuitJson: AnyCircuitElement[] = [
{
type: "source_component",
source_component_id: "component_1",
name: "U1",
ftype: "simple_chip",
},
{
type: "source_component",
source_component_id: "component_2",
name: "U2",
ftype: "simple_chip",
},
{
type: "source_port",
source_port_id: "u1_port_1",
source_component_id: "component_1",
name: "IO1",
},
{
type: "source_port",
source_port_id: "u2_port_1",
source_component_id: "component_2",
name: "IO1",
provides_power: true,
},
]

const warnings = checkAllPinsInComponentAreUnderspecified(circuitJson)
expect(warnings).toHaveLength(1)
expect(warnings[0].source_component_id).toBe("component_1")
})

test("ignores non-chip components", () => {
const circuitJson: AnyCircuitElement[] = [
{
type: "source_component",
source_component_id: "component_1",
name: "R1",
ftype: "simple_resistor",
resistance: 1000,
},
{
type: "source_port",
source_port_id: "port_1",
source_component_id: "component_1",
name: "pos",
},
{
type: "source_port",
source_port_id: "port_2",
source_component_id: "component_1",
name: "neg",
},
]

const warnings = checkAllPinsInComponentAreUnderspecified(circuitJson)
expect(warnings).toHaveLength(0)
})
})
3 changes: 3 additions & 0 deletions tests/lib/user-circuit-netlist.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ test("test.tsx builds and has no netlist errors", async () => {
<board width="12mm" height="30mm">
<SmdUsbC
name="USBC"
pinAttributes={{
GND1: { mustBeConnected: false },
}}
connections={{
GND1: "net.GND",
GND2: "net.GND",
Expand Down