Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 9 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
37 changes: 2 additions & 35 deletions Justfile
Original file line number Diff line number Diff line change
@@ -1,35 +1,2 @@

sim: sim_VoltageDivider sim_InvertingSum sim_LevelShifter sim_SallenKey sim_TwinTNotch sim_Sum sim_Inductor sim_NetTie sim_Diode sim_Led sim_Crystal

sim_VoltageDivider:
pcb sim simulation/test/test_VoltageDivider.zen --setup simulation/test/test_VoltageDivider.sp -o - | ngspice

sim_InvertingSum:
pcb sim simulation/test/test_InvertingSum.zen --setup simulation/test/test_InvertingSum.sp -o - | ngspice

sim_LevelShifter:
pcb sim simulation/test/test_LevelShifter.zen --setup simulation/test/test_LevelShifter.sp -o - | ngspice

sim_SallenKey:
pcb sim simulation/test/test_SallenKey.zen --setup simulation/test/test_SallenKey.sp -o - | ngspice

sim_TwinTNotch:
pcb sim simulation/test/test_TwinTNotch.zen --setup simulation/test/test_TwinTNotch.sp -o - | ngspice

sim_Sum:
pcb sim simulation/test/test_Sum.zen --setup simulation/test/test_Sum.sp -o - | ngspice

sim_Inductor:
pcb sim simulation/test/test_Inductor.zen --setup simulation/test/test_Inductor.sp -o - | ngspice

sim_NetTie:
pcb sim simulation/test/test_NetTie.zen --setup simulation/test/test_NetTie.sp -o - | ngspice

sim_Diode:
pcb sim simulation/test/test_Diode.zen --setup simulation/test/test_Diode.sp -o - | ngspice

sim_Led:
pcb sim simulation/test/test_Led.zen --setup simulation/test/test_Led.sp -o - | ngspice

sim_Crystal:
pcb sim simulation/test/test_Crystal.zen --setup simulation/test/test_Crystal.sp -o - | ngspice
_default:
@just --list
29 changes: 29 additions & 0 deletions generics/Bjt.zen
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,29 @@ def _symbol(bjt_type: BjtType):
return symbols[bjt_type]


def _spice_subcircuit_name(bjt_type: BjtType):
subcircuits = {
BjtType("NPN"): "NPN",
BjtType("PNP"): "PNP",
}
return subcircuits[bjt_type]


def _spice_args(bjt_type: BjtType, hfe):
args = {
"BF": str(hfe) if hfe else "100",
"IS": "1e-14",
"NF": "1.0",
"VAF": "100",
"RB": "10",
"RC": "1",
"RE": "0.5",
"CJE": "2p",
"CJC": "1p",
}
return args
Comment thread
cursor[bot] marked this conversation as resolved.


# -----------------------------------------------------------------------------
# Component definition
# -----------------------------------------------------------------------------
Expand All @@ -105,6 +128,12 @@ Component(
"C": C,
"E": E,
},
spice_model=SpiceModel(
"../simulation/Bjt.lib",
_spice_subcircuit_name(bjt_type),
nets=[B, C, E],
args=_spice_args(bjt_type, hfe),
),
properties=properties,
type="bjt",
)
27 changes: 27 additions & 0 deletions generics/FerriteBead.zen
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,27 @@ def _footprint(package: Package) -> str:
return resistor_footprints[package]


def _spice_args(impedance, frequency, dcr):
"""Derive RLC model parameters from rated impedance and frequency.

At resonance of L and Cp, only Rac remains, giving the rated impedance.
L is sized so its reactance equals Rac at the rated frequency (Q ≈ 1).
Cp is set to resonate with L at the rated frequency.
"""
rac = float(impedance.value)
freq = float(frequency.value)
rdc = float(dcr.value) if dcr else 0.3
pi = 3.14159265358979
l_val = rac / (2 * pi * freq)
cp_val = 1.0 / (2 * pi * freq * rac)
return {
"RDC": str(rdc),
"L": str(l_val),
"RAC": str(rac),
"CP": str(cp_val),
}


# -----------------------------------------------------------------------------
# Component definition
# -----------------------------------------------------------------------------
Expand All @@ -107,6 +128,12 @@ Component(
"P1": P1,
"P2": P2,
},
spice_model=SpiceModel(
"../simulation/FerriteBead.lib",
"FB",
nets=[P1, P2],
args=_spice_args(impedance, frequency, dcr),
),
properties=properties,
type="ferrite_bead",
)
36 changes: 36 additions & 0 deletions generics/Mosfet.zen
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,36 @@ def _symbol(channel: MosfetChannel):
return symbols[channel]


def _spice_subcircuit_name(channel: MosfetChannel):
subcircuits = {
MosfetChannel("N"): "NMOS",
MosfetChannel("P"): "PMOS",
}
return subcircuits[channel]


def _spice_args(channel: MosfetChannel):
if channel == MosfetChannel("P"):
return {
"VTH": "-1.5",
"KP": "60u",
"RD": "0.5",
"RS": "0.5",
"CBD": "5p",
"CBS": "5p",
"LAMBDA": "0.01",
}
return {
"VTH": "1.5",
"KP": "120u",
"RD": "0.5",
"RS": "0.5",
"CBD": "5p",
"CBS": "5p",
"LAMBDA": "0.01",
}


# -----------------------------------------------------------------------------
# Component definition
# -----------------------------------------------------------------------------
Expand All @@ -99,6 +129,12 @@ Component(
"D": D,
"S": S,
},
spice_model=SpiceModel(
"../simulation/Mosfet.lib",
_spice_subcircuit_name(channel),
nets=[G, D, S],
args=_spice_args(channel),
),
properties=properties,
type="mosfet",
)
6 changes: 6 additions & 0 deletions generics/Thermistor.zen
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ Component(
"P1": P1,
"P2": P2,
},
spice_model=SpiceModel(
"../simulation/Thermistor.lib",
"TH",
nets=[P1, P2],
args={"RVAL": str(value.value)},
),
Comment thread
cursor[bot] marked this conversation as resolved.
properties=properties,
type="thermistor",
)
26 changes: 26 additions & 0 deletions generics/Tvs.zen
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,39 @@ if reverse_clamping_voltage:
properties["reverse_clamping_voltage"] = reverse_clamping_voltage


def _spice_subcircuit_name(direction: Direction):
if direction == Direction("Bidirectional"):
return "TVS_Bi"
return "TVS_Uni"


def _spice_args(reverse_standoff_voltage, reverse_clamping_voltage):
bv = "6.4"
if reverse_clamping_voltage:
bv = str(reverse_clamping_voltage.value)
elif reverse_standoff_voltage:
bv = str(reverse_standoff_voltage.value * 1.1)
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
return {
"BV": bv,
"IBV": "10m",
"IS": "1e-14",
"RS": "5m",
}


Component(
name="D",
mpn=mpn,
manufacturer=manufacturer,
prefix="D",
symbol=Symbol(library="@kicad-symbols/Device.kicad_sym", name=_symbol_name(direction)),
footprint=File(_footprint(package)),
spice_model=SpiceModel(
"../simulation/Tvs.lib",
_spice_subcircuit_name(direction),
nets=[A, K],
args=_spice_args(reverse_standoff_voltage, reverse_clamping_voltage),
),
properties=properties,
type="tvs",
pins=_pins(direction),
Expand Down
8 changes: 5 additions & 3 deletions generics/test/test_Crystal.zen
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ for package in [package for package in Crystal.Package.values() if "2Pin" in str
for package in [package for package in Crystal.Package.values() if "4Pin" in str(package)]:
for mount in Crystal.Mount.values():
name = "Y_" + str(package) + "_" + str(mount)
name = name.upper()
Crystal(
name=name,
frequency="16MHz",
package=package,
mount=mount,
XIN=Net(name + "XIN"),
XOUT=Net(name + "XOUT"),
GND=Net(name + "GND"),
load_capacitance="18pF",
XIN=Net(f"{name}_XIN"),
XOUT=Net(f"{name}_XOUT"),
GND=Net(f"{name}_GND"),
)
2 changes: 1 addition & 1 deletion generics/test/test_TestPoint.zen
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ TestPoint = Module("../TestPoint.zen")
# Test all Variant values
for variant in TestPoint.Variant.values():
# Replace dots and special characters in variant name for valid component names
name = "TP_" + str(variant).replace(".", "_").replace("x", "x")
name = "TP_" + str(variant.value).replace(".", "_").replace("x", "x").upper()
TestPoint(
name=name,
variant=variant,
Expand Down
8 changes: 8 additions & 0 deletions properties.zen
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def Properties(user_properties: dict | None, base_properties: dict) -> dict:
Returns:
dict: The merged properties.
"""
warn("Properties is deprecated. Use properties dictionary directly.")
properties = {}

def to_string(value):
Expand Down Expand Up @@ -75,3 +76,10 @@ def Schematics(name: str, collapse: bool | None = None) -> None:

if collapse:
add_property("collapse", collapse)


def Simulation(name: str, setup: str | None = None) -> None:
"""Helper function to create a simulations object"""

builtin.set_sim_setup(content=setup)
Comment thread
cursor[bot] marked this conversation as resolved.

30 changes: 30 additions & 0 deletions simulation/Bjt.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
* BJT SPICE subcircuit
* Generic bipolar junction transistor model
*
* Parameters:
* BF - Forward current gain (hFE)
* IS - Saturation current
* NF - Forward emission coefficient
* VAF - Forward Early voltage
* RB - Base resistance
* RC - Collector resistance
* RE - Emitter resistance
* CJE - B-E junction capacitance
* CJC - B-C junction capacitance
*
* Nodes:
* 1 - Base
* 2 - Collector
* 3 - Emitter

* NPN transistor
.SUBCKT NPN 1 2 3 PARAMS: BF=100 IS=1e-14 NF=1.0 VAF=100 RB=10 RC=1 RE=0.5 CJE=2p CJC=1p
Q1 2 1 3 NPN_MODEL
.MODEL NPN_MODEL NPN(BF={BF} IS={IS} NF={NF} VAF={VAF} RB={RB} RC={RC} RE={RE} CJE={CJE} CJC={CJC})
.ENDS NPN

* PNP transistor
.SUBCKT PNP 1 2 3 PARAMS: BF=100 IS=1e-14 NF=1.0 VAF=100 RB=10 RC=1 RE=0.5 CJE=2p CJC=1p
Q1 2 1 3 PNP_MODEL
.MODEL PNP_MODEL PNP(BF={BF} IS={IS} NF={NF} VAF={VAF} RB={RB} RC={RC} RE={RE} CJE={CJE} CJC={CJC})
.ENDS PNP
27 changes: 27 additions & 0 deletions simulation/FerriteBead.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
* Ferrite Bead SPICE subcircuit
* RLC model capturing frequency-dependent impedance
*
* Equivalent circuit:
*
* ┌── L ──┐
* 1 ── Rdc ──┤ ├── 2
* ├─ Rac ─┤
* └─ Cp ──┘
*
* Behavior:
* DC: Z ≈ Rdc (nearly transparent)
* f_rated: Z peaks ≈ Rac (rated impedance)
* f >> f_r: Cp dominates, Z drops
*
* Parameters:
* RDC - DC resistance (ohms)
* L - Equivalent inductance (henries)
* RAC - AC resistance at resonance (ohms)
* CP - Parasitic parallel capacitance (farads)

.SUBCKT FB 1 2 PARAMS: RDC=0.3 L=350n RAC=220 CP=7.2p
R_dc 1 mid {RDC}
L1 mid 2 {L}
R_ac mid 2 {RAC}
C_p mid 2 {CP}
.ENDS FB
28 changes: 28 additions & 0 deletions simulation/Mosfet.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
* MOSFET SPICE subcircuit
* Generic MOSFET model
*
* Parameters:
* VTH - Threshold voltage
* KP - Transconductance parameter
* RD - Drain resistance
* RS - Source resistance
* CBD - Drain-bulk capacitance
* CBS - Source-bulk capacitance
* LAMBDA - Channel-length modulation
*
* Nodes:
* 1 - Gate
* 2 - Drain
* 3 - Source

* N-Channel MOSFET
.SUBCKT NMOS 1 2 3 PARAMS: VTH=1.5 KP=120u RD=0.5 RS=0.5 CBD=5p CBS=5p LAMBDA=0.01
M1 2 1 3 3 NMOS_MODEL
.MODEL NMOS_MODEL NMOS(VTO={VTH} KP={KP} RD={RD} RS={RS} CBD={CBD} CBS={CBS} LAMBDA={LAMBDA})
.ENDS NMOS

* P-Channel MOSFET
.SUBCKT PMOS 1 2 3 PARAMS: VTH=-1.5 KP=60u RD=0.5 RS=0.5 CBD=5p CBS=5p LAMBDA=0.01
M1 2 1 3 3 PMOS_MODEL
.MODEL PMOS_MODEL PMOS(VTO={VTH} KP={KP} RD={RD} RS={RS} CBD={CBD} CBS={CBS} LAMBDA={LAMBDA})
.ENDS PMOS
13 changes: 13 additions & 0 deletions simulation/Thermistor.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
* Thermistor SPICE subcircuit
* Simplified model as a fixed resistor at nominal resistance
*
* Parameters:
* RVAL - Resistance value in ohms (at 25°C)
*
* Nodes:
* 1 - Terminal 1
* 2 - Terminal 2

.SUBCKT TH 1 2 PARAMS: RVAL=10000
R1 1 2 {RVAL}
.ENDS TH
Loading
Loading