Generated reference › PWM — Control Systems/Discontinuities
kind: generated#block#control-systems-discontinuities

PWM — Control Systems/Discontinuities

Control_Systems/Discontinuities/PWM · 1 input / 1 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

PWM

Control Systems / Discontinuities

A pulse-width modulator: a square wave of fixed period whose high fraction is the duty cycle arriving on the input port. Everything is counted in whole samples at the block's own rate, with nPeriod = Period / Ts samples per period:

  • the duty is sampled once at the start of each period and held – a duty that changes mid-period has no effect until the next boundary;
  • the output is 1 for the first floor(duty · nPeriod) samples of the period and 0 for the rest.

So a duty of 0.25 over a 50-sample period gives 12 high samples, not 12.5 and not 13. A duty outside [0,1] is clamped. Applied entry by entry: a matrix duty gives a matrix of independently modulated pulse trains sharing one clock.

Ports

  • Input – the duty cycle dc, a fraction in [0,1], of any size [m,n]. Values outside the range are clamped rather than rejected.
  • Output – the pulse train, of the SAME size [m,n], carrying 1 or 0 per entry. The block never reshapes a signal.

Parameters

  • Period (s) – scalar, the length of one full cycle. Defaults to 1, as in Simulink. Must be positive and a whole number of sampling periods; anything else is reported rather than silently rounded.
  • Initial Delay (s) – scalar, how long the output is held low before the first pulse. Defaults to 0. Must not be negative, must likewise be a whole number of sampling periods, and must not exceed one Period. It delays the output rather than postponing the generator: the cycle is still aligned to t = 0 and the first cycle's duty is read at t = 0, not at the end of the delay. With a constant duty the two are indistinguishable; with a changing one they are not.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period. It is also the resolution of the pulse: the high time can only ever be a whole number of samples, so a coarse rate quantizes the duty heavily.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The period and the delay are baked in as whole sample counts at export time rather than exposed as tunable parameters, so the generated core carries no division and no clock arithmetic – just a counter. The exported state is that counter plus the latched duty, one value per entry. Every target evaluates the same phase + 1 ≤ duty · nPeriod test, so none of them can drift from the others on a rounding convention. The three HDL targets carry the duty comparison in simulation-only real arithmetic, quantizing only at the port boundary, as the Sine Wave block does.

Simulink bridge

Import and export, mapped to simulink/Discontinuities/PWM. "Period (s)" to Period and "Initial Delay (s)" to InitialDelay, both as plain pass-through values, so the round trip is lossless. "Sampling Time (s)" goes to SampleTime, as on every block. Two Simulink parameters are always written and have no ICore counterpart: RunAtFixedTimeIntervals is fixed to on, without which the Simulink block refuses to run under a fixed-step solver at all, and DisallowZeroDutyCycle is fixed to off, because ICore has nothing to reject – a zero duty simply holds the output low for the period.

Notes

  • Stateful: a sample counter, plus one latched duty per entry.
  • Discrete by nature – the output is a function of the sample index, so the block always runs at its own rate rather than being pushed through a continuous solver's stages.
  • The duty is read only on the sample that opens a period. Feeding it a signal that changes faster than the period is not an error, but everything between boundaries is discarded.
  • An Initial Delay longer than one Period is reported rather than approximated: the output would then be drawing on duty values more than one cycle old, which this block does not keep.
  • Not linear, and so deliberately carries no state space – a switching output is not a linear map.

Code facts#

FactValue
registered typeControl_Systems/Discontinuities/PWM
familyControl_Systems/Discontinuities
solver environment classICoreBlock_0_Control_Systems_1_Discontinuities_2_PWM
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Discontinuities/PWM/ICoreBlock_0_Control_Systems_1_Discontinuities_2_PWM.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Discontinuities/PWM/ICoreBlock_0_Control_Systems_1_Discontinuities_2_PWM.h
default size on canvas70 × 70 px
ports at insert1 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubledc
2outICoreDouble

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
Period (s)1Period
Initial Delay (s)0InitialDelay

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::Both
Simulink pathsimulink/Discontinuities/PWM
port-count rulePortsParam::None
SampleTime parameteryes
always setRunAtFixedTimeIntervals = on, DisallowZeroDutyCycle = off
ICore configSimulink parameterValue translation
Period (s)Periodpasses through
Initial Delay (s)InitialDelaypasses through

Catalog contract: src/ICoreSDK/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

PWM block — pulse-width modulator driven by a duty-cycle signal Everything is counted in SAMPLES, which is what the R2026a block does:

nPeriod = round(Period / Ts) nDelay = round(Initial Delay / Ts) the generator runs from tick 0, latching the duty (clamped to [0,1]) whenever tick mod nPeriod == 0, and emitting 1 while phase + 1 <= duty * nPeriod output(k) = generator(k - nDelay) -- the delay shifts the OUTPUT, not the cycle

Carried as a two-deep duty history: during the first nDelay ticks of a cycle the output still belongs to the previous cycle, so dutyPrev is all a sub-period delay ever needs.

That comparison is exactly "phase < floor(duty * nPeriod)" -- for an INTEGER phase, phase < floor(z) iff phase + 1 <= z -- but it needs no floor() in any of the ten targets, so all ten evaluate the identical expression and cannot drift apart on a rounding convention. Verified against Simulink: a duty of 0.25 over a 50-sample period gives 12 high samples, not 13.

Stateful: the sample counter and the latched duty. Applied entry by entry, all entries sharing one clock. Same per-language state contract as Backlash.

Sample results#

PWM — Step: 0 -> 1 at t = 1 sPWM — Step: 0 -> 1 at t = 1 s00.51012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 0
rampRamp: slope 1 from t = 00 … 1
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias0 … 1
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample0 … 1

Plotted: step — Step: 0 -> 1 at t = 1 s

Category dynamic · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Control_Systems__Discontinuities__PWM.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).