circuitRF Reference Guide

Expressions

One expression language runs everywhere a value can be computed: global variables, component parameters, SDD device equations, and measurements. Anywhere you can type a number, you can type an expression — `2*pi*freq0`, `polar(0.1, 35)`, `if(Vg > Vth, gm*Vg, 0)`.

Where expressions are used

ConsumerWhat it evaluatesResult
Global variables (VAR)a testbench variableReal or Complex
Cell parametersa parameter default or an instance overrideReal or Complex
SDD device equationsi = f(v), q = f(v) over port voltagesReal (and its derivatives)
Measurementsa figure of merit over result cubesReal or Complex
Same grammar, operators, and functions everywhere; only the available operands differ.

circuitRF parses an expression once into a tree and evaluates it against a scope — it never does text substitution. That makes it fast (an SDD equation is parsed once, evaluated thousands of times) and correct (no fragile longest-name-first variable replacement).

Values: Real, Complex, Bool, String

Every value carries a kind:

Ordering needs real operands

< <= > >= require Real operands (complex numbers are unordered); == / != work on Real and Complex. Real ∘ Complex promotes to Complex.

Operators & precedence

Lowest-binding (evaluated last) to highest-binding (tightest):

OperatorsNotes
?: (ternary)cond ? a : b — same as if(cond, a, b)
||logical OR
&&logical AND
== !=equality (Real or Complex)
< <= > >=ordering (Real only)
+ - (binary)
* /
+ - ! (unary prefix)
^ (power)binds tighter than unary minus: -2^2 == -4; right-assoc: 2^3^2 == 2^(3^2)
function call, ( ), atomshighest

Constants & functions

Constants: j = (0,1), pi, e. These (and all function names) are reserved — a variable may not shadow them.

GroupFunctions
Trigsin cos tan asin acos atan atan2(y,x)
Hyperbolicsinh cosh tanh
Exp / log / powerexp log (natural) log10 sqrt pow(x,y) abs
Miscmin(a,b) max(a,b) sign(x), if(cond,then,else)
Complex → Realreal(z) imag(z) abs(z) mag(z) (= abs) phase(z) (degrees) phase_rad(z) (radians)
Real,Real → Complexpolar(mag, phase_deg) — e.g. polar(0.1, 10) is 0.1∠10°
phase and the .phase cube transform both use degrees. dB/dBm are measurement functions, not general built-ins (and so are never unit suffixes).

Units

A unit attaches at the assignment level (after the expression), and scales the value by a linear factor — L = L1 nH, Z = 50 Ohm, M = 0.5 pH. Units are not part of the expression grammar.

DomainUnits
SI prefixesT G M k m u n p f (1e12 … 1e-15)
FrequencyHz kHz MHz GHz THz
InductanceH mH uH nH pH fH
CapacitanceF mF uF nF pF fF
ResistanceOhm kOhm MOhm
Lengthm mm um mil
Angledeg rad
dB and dBm are not units

They are logarithmic, not linear scale factors, so they are functions — dB(...), dBm(...) — never a trailing unit on a value.

Variables, scope & cell parameters

Global variables (authored in a VAR block) are visible everywhere. Cell parameters pass top-down: an instance binds overrides in the parent scope; the cell evaluates its own defaults and component values in its own scope, then passes its scope down to its sub-cells. A name resolves to the local cell variable/parameter first, then the global — a cell never sees a parent's locals or a sibling's, which is what keeps a cell's meaning independent of where it's placed.

freq is reserved

Lowercase freq is the simulator's current stamping frequency (Hz), injected when the engine evaluates a frequency-dependent value (a Z_Port impedance, an SDD H[w]). You can't name a variable freq. It is distinct from a source's capital-Freq tone parameter.

User-defined functions

Define a function whose body is an expression in the same language, with any number of parameters:

gm(vgs, vth) = beta * tanh(vgs - vth)

A call binds arguments positionally into a fresh scope whose parent is the definition's scope (so a user function sees globals, not the caller's locals). User functions compose with built-ins and with each other, and are subject to the same cycle detection.

Cycle detection & errors

circuitRF detects and rejects dependency cycles across the whole graph — global variables, cell-parameter defaults, and overrides — and reports the chain (e.g. a → b → a) rather than hanging. Every error names the offending text, never a silent zero or NaN:

SDD equations are real-only

SDD device equations operate on real time-domain voltages and produce real current/charge, so j is disallowed there and sqrt/log of a negative is a domain error rather than a promotion to Complex. circuitRF differentiates SDD equations automatically (forward-mode AD) to get the solver's conductance/capacitance — including through an if, where it differentiates the branch that is taken. For tough convergence prefer soft switching (tanh) over a hard if. See The SDD.


See also: Measurements · The SDD · VAR component. Full design: docs/design/expressions.md.