Status: authoritative — every virtual control block dispatched
inside RuntimeCircuit::execute_mixed_domain_step. Source:
core/include/pulsim/v1/runtime_circuit.hpp. Aliases / arity from
core/src/v1/yaml_parser.cpp. Updated through Phase 28.
Control blocks are the non-stamping side of the runtime. They produce
named channels that drive switches (pwm_generator), feed back into
control loops (pi_controller), shape signals (gain, lookup_table),
synchronize to grids (pll), or build full vector-control pipelines
(clarke_transform → park_transform → inverse_park_transform →
svm). They live alongside electrical components in the same
components: list but contribute zero rows to the MNA system.
Every block publishes its outputs as channels indexed by <name>.<key>
in result.channel_values and virtual_signal_state_. Downstream blocks
read those channels through metadata pointers:
-type:pwm_generatorname:PWMnodes:[trigger]duty_from_channel:PI.output# ← reads PI controller's channeltarget_component:SW1
Every block also writes its primary scalar output to
virtual_signal_state_[<name>], so simpler blocks (gain, sum) can be
referenced by name only (no suffix needed).
Channels are emitted in the trace CSV under the column prefix chan::
chan:PI.output, chan:CLK.alpha, chan:PLL.theta, etc.
The output clamp protocol is shared across most arithmetic and
controller blocks: optional keys output_min/output_max, falling back
to min/max, then to rail_low/rail_high (defaults ±1e12). The
op_amp block forces its rails to ±15 V by default. Whenever a block
honors this protocol it's labelled clamp keys below.
Modes:
- toggle — flips state on each rising edge above threshold.
- level — state = (signal > threshold) every step.
- set_reset — nodes[0] is SET, nodes[1] is RESET. Reset dominates.
Today this acts as a pass-through of in0 — the multi-output fan-out
semantics are not yet implemented. Use multiple gain taps if you need
real fan-out.
1 to 3 (signal_in optional, ignored unless duty_from_input: 1)
-type:pwm_generatorname:PWMnodes:[trigger]frequency:100e3duty_from_channel:PI.output# channel-driven dutyduty_min:0.05duty_max:0.95target_component:SW1# forces SW1 from the PWM signal
Key
Default
Notes
frequency
1e3
Carrier frequency (Hz), ≥ 1.
duty
0.5
Static duty cycle.
duty_min
0.0
Lower clamp.
duty_max
1.0
Upper clamp.
duty_from_input
0.0
Boolean (> 0.5). When true, V(signal_in) drives duty after the affine map below.
duty_offset
0.0
Affine offset on input-driven duty.
duty_gain
1.0
Affine gain on input-driven duty.
duty_from_channel (metadata)
—
Name of a channel whose value drives duty. Overrides duty_from_input.
target_component (metadata)
—
Name of a switch (switch, mosfet, igbt, vcswitch) to drive.
Symmetric triangular carrier. Output <name> is binary 1/0, plus extra
channels <name>.duty (commanded duty, post-clamp) and <name>.carrier
(the carrier signal in [0, 1]).
Channels: <name>.theta, <name>.omega, <name>.lock_error (the
instantaneous q-axis error). Locks at a quadrature offset from a pure
sine input — see Three-Phase Grid Library
for the convention.
components:# 3-phase sources Va, Vb, Vc at 120° apart-type:pllname:PLLnodes:[a]f_nominal_hz:60.0-type:clarke_transformname:CLKnodes:[a,b,c]-type:park_transformname:PARKnodes:[a,b]alpha_from_channel:CLK.alphabeta_from_channel:CLK.betatheta_from_channel:PLL.theta-type:inverse_park_transformname:IPARKnodes:[a,b]d_from_channel:PARK.dq_from_channel:PARK.qtheta_from_channel:PLL.theta-type:svmname:SVMnodes:[a]v_dc:200.0alpha_from_channel:IPARK.alphabeta_from_channel:IPARK.beta
The trace CSV will contain chan:CLK.alpha, chan:CLK.beta,
chan:PLL.theta, chan:PARK.d, chan:PARK.q, chan:IPARK.alpha,
chan:IPARK.beta, chan:SVM.d_a, chan:SVM.d_b, chan:SVM.d_c as
first-class observable columns.
-type:hysteresisname:SCHMITTnodes:[sense,0]threshold:5.0hysteresis:0.5high:1.0low:0.0-type:pwm_generator# repurpose PWM block as level-drivername:GATEnodes:[trigger]frequency:1e6# high enough that level "wins"duty_from_channel:SCHMITTtarget_component:SW1
Some virtual blocks live outside execute_mixed_domain_step but still
emit channels:
Event-driven components (thyristor, triac, fuse,
circuit_breaker, relay) — see
Components Reference
for full parameter lists. They write
<name>.state, <name>.trigger, <name>.i_est, <name>.i2t,
<name>.trip_timer, <name>.no_state, <name>.nc_state etc.
Magnetic annotations (saturable_inductor, coupled_inductor) —
emit <name>.l_eff, <name>.i_est, <name>.k, <name>.mutual.
Probes (voltage_probe, current_probe, power_probe,
electrical_scope, thermal_scope) — emit their main scalar to
<name>.