Skip to content
copperhead.sh
Get started

Examples

Divider

A voltage divider with a capacitor across its output: two resistors, one cap and nothing else. It is here to show the shape of a Fang program and no more than that.

the interconnect view
The interconnect view

divider.py declares three surfaces (supply, output, ground) and three parts, then joins them in architecture. The >> operator is the only way parts get connected, and every connection is an entity in the graph rather than a line in a file:

top = Resistor(resistance=10 * kOhm, package="R_0603_1608Metric")
def architecture(self):
self.top.p2 >> self.bottom.p1

The two constraints say what the divider is not allowed to be: a leg under 1 kOhm. They do not restate the values written above them. That is the distinction the whole language rests on. A value is a choice, and a constraint is the reason the choice is allowed.

4 parts, 2 nets, 28 entities, 2 checks, none failed and none undecided. This is the only example with nothing undecided in it, because it is the only one with no missing datasheet number anywhere.

The two resistors are drawn by the name they have in the program, top and bottom, because Resistor twice would not tell you which one is which.

Terminal window
fang check examples/divider/divider.py
fang netlist examples/divider/divider.py
fang export examples/divider/divider.py -o divider.net
fang view examples/divider/divider.py interconnect -o interconnect.svg
examples/divider/divider.py
"""A voltage divider with a decoupling capacitor: the smallest real board."""
from fang.lang import Electrical, System, V, kOhm, require, uF
from fang.parts import Capacitor, Resistor
class Divider(System):
"""Two resistors dividing a rail, with a capacitor across the output."""
supply = Electrical()
output = Electrical()
ground = Electrical()
top = Resistor(resistance=10 * kOhm, package="R_0603_1608Metric")
bottom = Resistor(resistance=4.7 * kOhm, package="R_0603_1608Metric")
filter_cap = Capacitor(
capacitance=100 * uF, voltage_rating=16 * V, package="C_0805_2012Metric"
)
def architecture(self):
self.top.p2 >> self.bottom.p1
self.bottom.p1 >> self.filter_cap.p1
self.bottom.p2 >> self.filter_cap.p2
def constraints(self):
# A divider that draws more than a milliamp from a sensing rail is
# usually a mistake; both legs stay well above that.
require(self.top.resistance >= 1 * kOhm)
require(self.bottom.resistance >= 1 * kOhm)

The parts, then the nets and the pads on them.

out/netlist.txt
C1 100 uF Package:C_0805_2012Metric
R1 4.7 kOhm Package:R_0603_1608Metric
R2 10 kOhm Package:R_0603_1608Metric
Net-(C1-Pad1) C1.1 R1.1 R2.2
Net-(C1-Pad2) C1.2 R1.2

Every check that ran, and every one left undecided.

out/checks.txt
2 checks, 0 failed, 0 undecided

What the elaborated graph contains, by entity kind.

out/graph.txt
1 block
3 component
6 connection
2 constraint
1 interface
6 pin
9 port
28 total
snapshot sha256:b9a24e84abebf4babb93d22dc622212f4474a266e4aece5ab4a8ff9395205c21

All of it, including the KiCad netlist, is in examples/divider/out/. Rebuild it with:

Terminal window
fang build examples/divider/divider.py