Skip to content
copperhead.sh
Get started

Examples

Blinky

An MCU pin, a series resistor and an LED: the board everybody builds first. Read it after divider/, because it is the first example where the parts have interfaces rather than bare pads.

the interconnect view
The interconnect view

blinky.py declares its own parts. MCU is a Part with a PowerIn, a Signal, three pins and a PinMap joining the two:

power = PowerIn(voltage=3.3 * V, current_demand=30 * mA)
blink = Signal()
pinmap = PinMap({"power.vcc": "VDD", "power.gnd": "VSS", "blink.line": "PA5"})

The pin map is what makes self.header.dc >> self.mcu.power mean something. One connection between two typed surfaces lowers to the pads that carry it. The netlist below is that lowering, not a second description of it.

The constraint is the point of the example:

require(self.series.resistance >= 150 * Ohm)
require(self.indicator.forward_current <= 20 * mA)

The 330 Ohm is not defended by the comment beside it. The bound it has to satisfy is written down, so replacing the LED re-decides the question instead of inheriting an old answer.

6 parts, 4 nets, 47 entities, 6 checks. None failed, one undecided.

That undecided one is worth reading in out/checks.txt: the board’s supply surface has no current_demand on it, so “does the source carry the load?” has no answer. The kernel says so rather than passing the check on an assumed number.

the power view
The power view
Terminal window
fang check examples/blinky/blinky.py
fang netlist examples/blinky/blinky.py
fang view examples/blinky/blinky.py power -o power.svg
examples/blinky/blinky.py
"""The smallest program that drives something: an MCU pin, a resistor, an LED.
Show 5 more lines
The board everybody builds first. What it shows is the shape of a Fang program:
declared surfaces, connections between them and a constraint that states the
engineering intent rather than the answer.
"""
from fang.interfaces import Pin, PinMap, PowerIn, PowerOut
from fang.lang import Ohm, Part, Signal, System, V, mA, mW, nF, require
from fang.parts import LED, Capacitor, Resistor
class MCU(Part):
"""A microcontroller, reduced to the one output this board uses."""
designator_prefix = "U"
power = PowerIn(voltage=3.3 * V, current_demand=30 * mA)
blink = Signal()
VDD = Pin("VDD", role="power", number="1")
VSS = Pin("VSS", role="ground", number="8")
PA5 = Pin("PA5", role="data", number="5")
pinmap = PinMap({"power.vcc": "VDD", "power.gnd": "VSS", "blink.line": "PA5"})
class PowerHeader(Part):
"""Two pins and a rail: the board's power has to arrive on a pad."""
designator_prefix = "J"
dc = PowerOut(voltage=3.3 * V, current_capability=200 * mA)
VCC = Pin("VCC", role="power", number="1")
GND = Pin("GND", role="ground", number="2")
pinmap = PinMap({"dc.vcc": "VCC", "dc.gnd": "GND"})
class Blinky(System):
supply = PowerIn(voltage=3.3 * V, current_capability=200 * mA)
header = PowerHeader(package="PinHeader_1x02_P2.54mm")
mcu = MCU(package="SOIC-8")
series = Resistor(resistance=330 * Ohm, power_rating=125 * mW, package="R_0603")
indicator = LED(
forward_voltage=2 * V, forward_current=10 * mA, package="LED_0603"
)
bypass = Capacitor(capacitance=100 * nF, voltage_rating=16 * V, package="C_0402")
def architecture(self):
self.supply >> self.header.dc
self.header.dc >> self.mcu.power
# The pin drives the anode through the series resistor; the cathode
# returns to the rail's ground rather than to a second ground of its own.
self.mcu.blink >> self.series.p1
self.series.p2 >> self.indicator.p1
self.indicator.p2 >> self.header.dc.gnd
# Decoupling belongs to the pin it decouples, so a checker can find it.
self.mcu.power.vcc >> self.bypass.p1
self.mcu.power.gnd >> self.bypass.p2
def constraints(self):
# (3.3 V - 2.0 V) / 330 Ohm is about 4 mA: visible, and inside what a
# GPIO will source. Both bounds are stated; neither is computed here.
require(self.series.resistance >= 150 * Ohm)
require(self.indicator.forward_current <= 20 * mA)

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

out/netlist.txt
C1 100 nF Package:C_0402
DS1 2 V Package:LED_0603
J1 PowerHeader Package:PinHeader_1x02_P2.54mm
R1 330 Ohm Package:R_0603
U1 MCU Package:SOIC-8
Net-(C1-Pad1) C1.1 J1.VCC U1.VDD
Net-(C1-Pad2) C1.2 DS1.K J1.GND U1.VSS
Net-(DS1-PadA) DS1.A R1.2
Net-(R1-Pad1) R1.1 U1.PA5

Every check that ran, and every one left undecided.

out/checks.txt
UNDECIDED interface_compatibility: current capability is undecided on power_input: current_demand unknown
6 checks, 0 failed, 1 undecided

What the elaborated graph contains, by entity kind.

out/graph.txt
1 block
5 component
14 connection
2 constraint
4 interface
11 pin
10 port
47 total
snapshot sha256:dab7fbedff03ec019e23be5984d535093cf576f8c12c787f2b41cd448315f8a3

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

Terminal window
fang build examples/blinky/blinky.py