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
184 changes: 184 additions & 0 deletions docs/elements/drccheck.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
title: <drccheck />
sidebar_position: 5.5
description: >-
The `<drccheck />` element lets you add a custom design rule check that runs
against a circuit and emits circuit-json errors.
---

## Overview

The `<drccheck />` element lets you add a custom design rule check close to the
components it applies to. Use it for project-specific rules that tscircuit does
not check automatically, such as "these two I2C devices must not share the same
address."

A custom DRC uses a `checkFn`. The function can return:

- a single circuit-json error object
- an array of circuit-json error objects
- `undefined`, `null`, or nothing when the rule passes

## TMP117MAIDRVR I2C Address Conflict

This example detects two TMP117MAIDRVR temperature sensors on the same I2C bus
with the same address configuration. Both sensors have `ADD0` pulled down
through a 10k resistor, and both share `SDA` and `SCL`, so they would respond at
the same I2C address.

```tsx
import React from "react"

export default () => (
<board width="35mm" height="22mm" routingDisabled>
<chip
name="U1"
footprint="sot23_6"
manufacturerPartNumber="TMP117MAIDRVR"
pinLabels={{
pin1: "SCL",
pin2: "GND",
pin3: "ALERT",
pin4: "ADD0",
pin5: "VCC",
pin6: "SDA",
}}
pcbX={-8}
pcbY={0}
/>
<chip
name="U2"
footprint="sot23_6"
manufacturerPartNumber="TMP117MAIDRVR"
pinLabels={{
pin1: "SCL",
pin2: "GND",
pin3: "ALERT",
pin4: "ADD0",
pin5: "VCC",
pin6: "SDA",
}}
pcbX={8}
pcbY={0}
/>

<resistor
name="R_ADD0_U1"
resistance="10k"
footprint="0402"
pcbX={-8}
pcbY={-8}
/>
<resistor
name="R_ADD0_U2"
resistance="10k"
footprint="0402"
pcbX={8}
pcbY={-8}
/>

<trace from=".U1 > .VCC" to="net.VCC" />
<trace from=".U2 > .VCC" to="net.VCC" />
<trace from=".U1 > .GND" to="net.GND" />
<trace from=".U2 > .GND" to="net.GND" />

<trace from=".U1 > .SDA" to="net.SDA" />
<trace from=".U2 > .SDA" to="net.SDA" />
<trace from=".U1 > .SCL" to="net.SCL" />
<trace from=".U2 > .SCL" to="net.SCL" />

<trace from=".U1 > .ADD0" to=".R_ADD0_U1 > .pin1" />
<trace from=".R_ADD0_U1 > .pin2" to="net.GND" />
<trace from=".U2 > .ADD0" to=".R_ADD0_U2 > .pin1" />
<trace from=".R_ADD0_U2 > .pin2" to="net.GND" />

<drccheck
name="tmp117maidrvr-i2c-address-conflict"
checkFn={({ selectAll, isConnected, isPulledDown }) => {
const chips = selectAll("chip[manufacturerPartNumber='TMP117MAIDRVR']")
const [chipA, chipB] = chips
if (!chipA || !chipB) return

const aSda = chipA.getPort("SDA")
const bSda = chipB.getPort("SDA")
const aScl = chipA.getPort("SCL")
const bScl = chipB.getPort("SCL")
const aAdd0 = chipA.getPort("ADD0")
const bAdd0 = chipB.getPort("ADD0")
if (!aSda || !bSda || !aScl || !bScl || !aAdd0 || !bAdd0) return

if (
isConnected(aSda, bSda) &&
isConnected(aScl, bScl) &&
isPulledDown(aAdd0) &&
isPulledDown(bAdd0)
) {
return {
error_type: "source_component_misconfigured_error",
message: "Two TMP117MAIDRVR chips share the same I2C address",
source_component_ids: [
chipA.getSourceComponent()!.source_component_id,
chipB.getSourceComponent()!.source_component_id,
],
source_port_ids: [
aSda.getSourcePort()!.source_port_id,
bSda.getSourcePort()!.source_port_id,
aScl.getSourcePort()!.source_port_id,
bScl.getSourcePort()!.source_port_id,
aAdd0.getSourcePort()!.source_port_id,
bAdd0.getSourcePort()!.source_port_id,
],
}
}
}}
/>
</board>
)
```

![Custom DRC error shown in the PCB viewer](/img/drc-check-error.png)

*The emitted `source_component_misconfigured_error` appears in the viewer with
the message returned from `checkFn`.*

## Props

| Prop | Type | Description |
| ---- | ---- | ----------- |
| `name` | `string` | Optional name for the DRC rule. Use a stable, descriptive name such as `"tmp117maidrvr-i2c-address-conflict"`. |
| `checkFn` | `(ctx) => error \| error[] \| void \| Promise<error \| error[] \| void>` | Function that runs after the circuit renders. Return circuit-json errors when the rule fails. |

## Check Function Context

The `checkFn` receives helpers for selecting elements and checking connectivity.

| Helper | Description |
| ------ | ----------- |
| `select(selector)` | Select one component, port, or net using familiar tscircuit selectors such as `"U1.ADD0"` or `"net.GND"`. |
| `selectAll(selector)` | Select multiple components, ports, or nets. |
| `isConnected(a, b)` | Returns `true` when two selected items share connectivity. |
| `isPulledUp(a)` | Returns `true` when the item is connected or resistor-connected to `net.VCC` or `net.VDD`. |
| `isPulledDown(a)` | Returns `true` when the item is connected or resistor-connected to `net.GND`. |
| `getResistanceBetween(a, b)` | Returns `0` for a direct connection, a resistance value for a resistor path, or `null` when no resistor-only path is found. |

## Selection Results

`select` and `selectAll` return small wrapper objects instead of raw
circuit-json. This lets the check function stay close to normal tscircuit
concepts.

Component selections support:

- `getPort(name)`
- `getPorts()`
- `getSourceComponent()`
- `getPcbComponent()`

Port selections support:

- `getSourcePort()`
- `getPcbPort()`

Net selections support:

- `getSourceNet()`
Binary file added static/img/drc-check-error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading