Skip to content
Open
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 @@ -11,6 +11,7 @@ and output an array of arrays for any issues found.
| --- | --- |
| [`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. |
| [`checkChipPowerPinsHaveDecouplingCapacitors`](./lib/check-chip-power-pins-have-decoupling-capacitors.ts) | Returns `source_pin_missing_trace_warning` when a connected chip power pin has no decoupling capacitor to ground. |
| [`checkNoPowerPinDefined`](./lib/check-no-power-pin-defined.ts) | Returns `source_no_power_pin_defined_warning` when a chip has no pin with `requires_power=true`. |
| [`checkNoGroundPinDefined`](./lib/check-no-ground-pin-defined.ts) | Returns `source_no_ground_pin_defined_warning` when a chip has no pin with `requires_ground=true`. |
| [`checkDifferentNetViaSpacing`](./lib/check-different-net-via-spacing.ts) | Returns `pcb_via_clearance_error` if vias on different nets are too close together. |
Expand All @@ -32,7 +33,7 @@ and output an array of arrays for any issues found.
| Function | Description |
| --- | --- |
| [`runAllPlacementChecks`](./lib/run-all-checks.ts) | Runs placement checks (`checkViasOffBoard`, `checkPcbComponentsOutOfBoard`, `checkPcbComponentOverlap`, `checkPadPadClearance`, and `checkConnectorAccessibleOrientation`). |
| [`runAllNetlistChecks`](./lib/run-all-checks.ts) | Runs netlist connectivity checks (currently `checkPinMustBeConnected`). |
| [`runAllNetlistChecks`](./lib/run-all-checks.ts) | Runs netlist connectivity checks (`checkPinMustBeConnected` and `checkChipPowerPinsHaveDecouplingCapacitors`). |
| [`runAllPinSpecificationChecks`](./lib/run-all-checks.ts) | Runs pin specification checks (e.g. `checkAllPinsInComponentAreUnderspecified`, `checkNoPowerPinDefined`, and `checkNoGroundPinDefined`). |
| [`runAllRoutingChecks`](./lib/run-all-checks.ts) | Runs all routing checks currently enabled (`checkEachPcbPortConnectedToPcbTraces`, `checkSourceTracesHavePcbTraces`, `checkEachPcbTraceNonOverlapping`, `checkPadTraceClearance`, `checkViaTraceClearance`, same/different net via spacing, and `checkPcbTracesOutOfBoard`). Trace-obstacle pairs are classified before aggregation, so each pair produces one overlap or clearance diagnostic, never both. |
| [`runAllChecks`](./lib/run-all-checks.ts) | Runs placement, netlist, pin specification, and routing checks and returns a combined list of issues. |
Expand Down
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export { checkViaTraceClearance } from "./lib/check-via-trace-clearance"
export { dedupePcbDrcErrors } from "./lib/dedupe-pcb-drc-errors"
export { checkPinMustBeConnected } from "./lib/check-pin-must-be-connected"
export { checkAllPinsInComponentAreUnderspecified } from "./lib/check-all-pins-in-component-are-underspecified"
export { checkChipPowerPinsHaveDecouplingCapacitors } from "./lib/check-chip-power-pins-have-decoupling-capacitors"
export { checkNoPowerPinDefined } from "./lib/check-no-power-pin-defined"
export { checkNoGroundPinDefined } from "./lib/check-no-ground-pin-defined"
export {
Expand Down
10 changes: 10 additions & 0 deletions lib/check-chip-power-pins-have-decoupling-capacitors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type {
AnyCircuitElement,
SourcePinMissingTraceWarning,
} from "circuit-json"
import { DecouplingCapacitorChecker } from "./check-chip-power-pins-have-decoupling-capacitors/decoupling-capacitor-checker"

export const checkChipPowerPinsHaveDecouplingCapacitors = (
Comment thread
0hmX marked this conversation as resolved.
circuitJson: AnyCircuitElement[],
): SourcePinMissingTraceWarning[] =>
new DecouplingCapacitorChecker(circuitJson).getWarnings()
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { SourcePort, SourceSimpleCapacitor } from "circuit-json"
import type { DecouplingCapacitorChecker } from "./decoupling-capacitor-checker"

export const capacitorConnectsPowerSourcePortToGround = (
checker: DecouplingCapacitorChecker,
capacitorSourceComponent: SourceSimpleCapacitor,
chipPowerSourcePort: SourcePort,
): boolean => {
const capacitorSourcePorts = checker.getSourcePorts(
capacitorSourceComponent.source_component_id,
)
if (capacitorSourcePorts.length !== 2) return false

const [firstCapacitorSourcePort, secondCapacitorSourcePort] =
capacitorSourcePorts
return (
(checker.sourceConnectivityMap.areIdsConnected(
chipPowerSourcePort.source_port_id,
firstCapacitorSourcePort.source_port_id,
) &&
checker.sourcePortIsConnectedToGround(secondCapacitorSourcePort)) ||
(checker.sourceConnectivityMap.areIdsConnected(
chipPowerSourcePort.source_port_id,
secondCapacitorSourcePort.source_port_id,
) &&
checker.sourcePortIsConnectedToGround(firstCapacitorSourcePort))
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { SourcePinMissingTraceWarning } from "circuit-json"
import type { DecouplingCapacitorChecker } from "./decoupling-capacitor-checker"
import { getSourcePortDisplayLabel } from "./get-source-port-display-label"

export const getDecouplingCapacitorWarnings = (
checker: DecouplingCapacitorChecker,
): SourcePinMissingTraceWarning[] => {
const warnings: SourcePinMissingTraceWarning[] = []

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

for (const chipSourcePort of checker.getSourcePorts(
chipSourceComponent.source_component_id,
)) {
if (!checker.sourcePortShouldHaveDecouplingCapacitor(chipSourcePort)) {
continue
}
if (!checker.sourcePortHasConnection(chipSourcePort)) continue

const hasDecouplingCapacitor = checker.capacitorSourceComponents.some(
(capacitorSourceComponent) =>
checker.capacitorConnectsPowerSourcePortToGround(
capacitorSourceComponent,
chipSourcePort,
),
)
if (hasDecouplingCapacitor) continue

const recommendedCapacitance =
chipSourcePort.recommended_decoupling_capacitor_capacitance
const capacitanceDescription =
recommendedCapacitance === undefined ? "" : ` ${recommendedCapacitance}`

warnings.push({
type: "source_pin_missing_trace_warning",
source_pin_missing_trace_warning_id: `source_pin_missing_trace_warning_decoupling_${chipSourcePort.source_port_id}`,
warning_type: "source_pin_missing_trace_warning",
message: `Power pin ${getSourcePortDisplayLabel(chipSourcePort)} on ${chipSourceComponent.name} should have a${capacitanceDescription} decoupling capacitor connected to ground`,
source_component_id: chipSourceComponent.source_component_id,
source_port_id: chipSourcePort.source_port_id,
subcircuit_id: chipSourcePort.subcircuit_id,
})
}
}

return warnings
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { SourcePort } from "circuit-json"
import type { DecouplingCapacitorChecker } from "./decoupling-capacitor-checker"

export const sourcePortHasConnection = (
checker: DecouplingCapacitorChecker,
sourcePort: SourcePort,
): boolean => {
const connectedNetId = checker.sourceConnectivityMap.getNetConnectedToId(
sourcePort.source_port_id,
)
if (!connectedNetId) return false
return checker.sourceConnectivityMap
.getIdsConnectedToNet(connectedNetId)
.some((connectedId) => connectedId !== sourcePort.source_port_id)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { SourcePort } from "circuit-json"
import type { DecouplingCapacitorChecker } from "./decoupling-capacitor-checker"

export const sourcePortIsConnectedToGround = (
checker: DecouplingCapacitorChecker,
sourcePort: SourcePort,
): boolean =>
checker.groundSourcePorts.some((groundSourcePort) =>
checker.sourceConnectivityMap.areIdsConnected(
sourcePort.source_port_id,
groundSourcePort.source_port_id,
),
) ||
checker.groundSourceNets.some((groundSourceNet) =>
checker.sourceConnectivityMap.areIdsConnected(
sourcePort.source_port_id,
groundSourceNet.source_net_id,
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { SourcePort } from "circuit-json"

export const sourcePortShouldHaveDecouplingCapacitor = (
sourcePort: SourcePort,
): boolean => {
if (sourcePort.should_have_decoupling_capacitor !== undefined) {
return sourcePort.should_have_decoupling_capacitor
}
return (
sourcePort.requires_power === true && sourcePort.provides_power !== true
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { cju } from "@tscircuit/circuit-json-util"
import type {
AnyCircuitElement,
SourceComponentBase,
SourceNet,
SourcePinMissingTraceWarning,
SourcePort,
SourceSimpleCapacitor,
} from "circuit-json"
import {
type ConnectivityMap,
getSourcePortConnectivityMapFromCircuitJson,
} from "circuit-json-to-connectivity-map"
import { capacitorConnectsPowerSourcePortToGround } from "./decoupling-capacitor-checker-capacitor-connects-power-source-port-to-ground"
import { getDecouplingCapacitorWarnings } from "./decoupling-capacitor-checker-get-warnings"
import { sourcePortHasConnection } from "./decoupling-capacitor-checker-source-port-has-connection"
import { sourcePortIsConnectedToGround } from "./decoupling-capacitor-checker-source-port-is-connected-to-ground"
import { sourcePortShouldHaveDecouplingCapacitor } from "./decoupling-capacitor-checker-source-port-should-have-decoupling-capacitor"
import type { SourceComponentId } from "./types"

export class DecouplingCapacitorChecker {
readonly sourceComponents: SourceComponentBase[]
readonly sourcePorts: SourcePort[]
readonly groundSourcePorts: SourcePort[]
readonly groundSourceNets: SourceNet[]
readonly capacitorSourceComponents: SourceSimpleCapacitor[]
readonly sourcePortsBySourceComponentId = new Map<
SourceComponentId,
SourcePort[]
>()
readonly sourceConnectivityMap: ConnectivityMap

constructor(circuitJson: AnyCircuitElement[]) {
const db = cju(circuitJson)
this.sourceComponents = db.source_component.list() as SourceComponentBase[]
this.sourcePorts = db.source_port.list()
this.groundSourcePorts = this.sourcePorts.filter(
(sourcePort) =>
sourcePort.requires_ground === true ||
sourcePort.provides_ground === true,
)
this.groundSourceNets = db.source_net
.list()
.filter((sourceNet) => sourceNet.is_ground)
this.capacitorSourceComponents = this.sourceComponents.filter(
(sourceComponent): sourceComponent is SourceSimpleCapacitor =>
sourceComponent.ftype === "simple_capacitor",
)
this.sourceConnectivityMap =
getSourcePortConnectivityMapFromCircuitJson(circuitJson)

for (const sourcePort of this.sourcePorts) {
if (!sourcePort.source_component_id) continue
const sourceComponentPorts =
this.sourcePortsBySourceComponentId.get(
sourcePort.source_component_id,
) ?? []
sourceComponentPorts.push(sourcePort)
this.sourcePortsBySourceComponentId.set(
sourcePort.source_component_id,
sourceComponentPorts,
)
}
}

getWarnings(): SourcePinMissingTraceWarning[] {
return getDecouplingCapacitorWarnings(this)
}

getSourcePorts(sourceComponentId: SourceComponentId): SourcePort[] {
return this.sourcePortsBySourceComponentId.get(sourceComponentId) ?? []
}

sourcePortShouldHaveDecouplingCapacitor(sourcePort: SourcePort): boolean {
return sourcePortShouldHaveDecouplingCapacitor(sourcePort)
}

sourcePortHasConnection(sourcePort: SourcePort): boolean {
return sourcePortHasConnection(this, sourcePort)
}

sourcePortIsConnectedToGround(sourcePort: SourcePort): boolean {
return sourcePortIsConnectedToGround(this, sourcePort)
}

capacitorConnectsPowerSourcePortToGround(
capacitorSourceComponent: SourceSimpleCapacitor,
chipPowerSourcePort: SourcePort,
): boolean {
return capacitorConnectsPowerSourcePortToGround(
this,
capacitorSourceComponent,
chipPowerSourcePort,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { SourcePort } from "circuit-json"

const sourcePortLabelIsGenericPinName = (sourcePortLabel: string): boolean => {
const normalizedSourcePortLabel = sourcePortLabel.trim().toLowerCase()
const possiblePinNumber = normalizedSourcePortLabel.startsWith("pin")
? normalizedSourcePortLabel.slice(3)
: normalizedSourcePortLabel
return (
possiblePinNumber.length > 0 &&
[...possiblePinNumber].every(
(character) => character >= "0" && character <= "9",
)
)
}

export const getSourcePortDisplayLabel = (sourcePort: SourcePort): string =>
sourcePort.port_hints?.find(
(sourcePortHint) => !sourcePortLabelIsGenericPinName(sourcePortHint),
) ?? sourcePort.name
4 changes: 4 additions & 0 deletions lib/check-chip-power-pins-have-decoupling-capacitors/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { SourceComponentBase } from "circuit-json"

// circuit-json does not currently export a standalone source-component ID type.
export type SourceComponentId = SourceComponentBase["source_component_id"]
6 changes: 5 additions & 1 deletion lib/run-all-checks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AnyCircuitElement } from "circuit-json"
import { checkAllPinsInComponentAreUnderspecified } from "./check-all-pins-in-component-are-underspecified"
import { checkChipPowerPinsHaveDecouplingCapacitors } from "./check-chip-power-pins-have-decoupling-capacitors"
import { checkConnectorAccessibleOrientation } from "./check-connector-accessible-orientation"
import { checkCourtyardOverlap } from "./check-courtyard-overlap/checkCourtyardOverlap"
import { checkDifferentNetViaSpacing } from "./check-different-net-via-spacing"
Expand Down Expand Up @@ -32,7 +33,10 @@ export async function runAllPlacementChecks(circuitJson: AnyCircuitElement[]) {
}

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

export async function runAllPinSpecificationChecks(
Expand Down
Loading
Loading