Generated reference › Propagation Delay — Control Systems/Discrete
kind: generated#block#control-systems-discrete

Propagation Delay — Control Systems/Discrete

Control_Systems/Discrete/Propagation_Delay · 2 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.

Propagation Delay

Control Systems / Discrete

Transports a signal across a delay that can change while the run is in progress – a cable, a pipeline, a transmission path, anything whose propagation time is itself a signal:

y(t) = u(t − d(t))

The delay arrives on its own port in seconds and is rounded to a whole number of samples, so a delay of zero is direct feedthrough. Values are held in a ring buffer whose depth is set by Buffer Capacity (samples); a delay longer than that is clamped to it, and a delay reaching back past the start of the run reads the configured Initial Output.

Ports

  • u – the transported signal, of any size [m,n]. This is what comes out delayed.
  • d – the propagation time in seconds, a scalar [1,1]. It applies to the whole signal, not per entry. Negative values are clamped to zero – nothing arrives before it was sent.
  • Output – the delayed signal, the SAME size [m,n] as u.

Parameters

  • Initial Output – scalar, what the block emits while the delay still reaches back past the start of the run. Defaults to 0. The whole buffer is pre-filled with it, so a run starts from a defined line rather than from samples that were never taken.
  • Buffer Capacity (samples) – the depth of the ring buffer, and so the longest delay the block can represent, in samples. Defaults to 64 – at a 0.01 s period that is 0.64 s. A requested delay beyond it is clamped rather than growing the buffer, because a line that reallocates mid-run is a latency spike on a real target. This one is an ICore-side implementation bound and has no Simulink counterpart – see the bridge section.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period. It is the resolution the delay is rounded to, and the unit the capacity is counted in, so it is part of the arithmetic and not merely a schedule.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. Each carries the ring buffer as its state, pre-filled with the initial output, and indexes it with the rounded delay exactly as the simulation does. Only 1/dt is baked in as a constant; the delay itself is read from its port every tick.

The three HDL targets carry the line as a slot × element register file and derive the read address in hardware, using the generated fixed-point package's fx_to_int – an arithmetic shift by the fraction width, which is floor() and so rounds the same way the software targets do. A zero delay is special-cased to the input rather than read back from the buffer: the write is registered, so on that tick the addressed slot still holds its previous contents.

Note that on the HDL targets the delay arrives already quantized to the Q-format, one quantum being about 1.5×10−5. A requested delay sitting within that of a half-sample boundary can round to a neighbouring sample count there but not on the software targets. If an exact count matters, Variable Integer Delay takes the delay in samples and avoids the conversion entirely.

Simulink bridge

Import and export, mapped to simulink/Discrete/Propagation Delay. "Initial Output" to InitialOutput, and "Sampling Time (s)" to SampleTime as on every block. RunAtFixedTimeIntervals is always emitted as on: ICore's block is fixed-rate by construction, and Simulink's refuses a fixed-step solver outright without it. "Buffer Capacity (samples)" does not cross – it bounds this implementation's ring buffer, and Simulink's block has no such parameter, so it is deliberately left behind rather than reported as unmapped.

A caveat worth knowing before exporting. Simulink's block is variable-step and event-driven: even with fixed intervals on, it requires each delayed output time t + d(t) to increase monotonically and to land on a whole multiple of the sample time, and it stops the run with an error when a moving delay violates that. ICore's block has no such restriction. So a model using a constant delay crosses and runs on both sides; one whose delay moves will run here and may be refused there.

Notes

  • Stateful: capacity + 1 past values of the signal port.
  • Discrete by nature – the line advances one slot per sample, so the block always runs at its own rate rather than being pushed through a continuous solver's stages.
  • The delay is rounded to whole samples, so sub-sample propagation is not interpolated. Shorten the sampling time for finer resolution, or use Variable Integer Delay if you are counting samples rather than seconds.
  • Deliberately carries no state space: a pure delay is the transcendental factor e^(−sd), which no finite A/B/C/D expresses, and a time-varying one is not even time-invariant.

Code facts#

FactValue
registered typeControl_Systems/Discrete/Propagation_Delay
familyControl_Systems/Discrete
solver environment classICoreBlock_0_Control_Systems_1_Discrete_2_Propagation_Delay
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Discrete/Propagation_Delay/ICoreBlock_0_Control_Systems_1_Discrete_2_Propagation_Delay.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Discrete/Propagation_Delay/ICoreBlock_0_Control_Systems_1_Discrete_2_Propagation_Delay.h
default size on canvas110 × 80 px
ports at insert2 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2inICoreDoubled
3outICoreDouble

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
Initial Output0InitialOutput
Buffer Capacity (samples)64not crossed

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/Discrete/Propagation Delay
port-count rulePortsParam::None
SampleTime parameteryes
deliberately not crossedBuffer Capacity (samples)
always setRunAtFixedTimeIntervals = on
ICore configSimulink parameterValue translation
Initial OutputInitialOutputpasses through

Caveat (shown to the user): Simulink's Propagation Delay is variable-step and event-driven: it requires every delayed output time t + d(t) to increase monotonically and to land on a multiple of the sample time, and errors out when a moving delay violates that. ICore's block is fixed-rate and has no such restriction, so a constant delay crosses and runs on both sides while a moving one may be refused by Simulink

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).

Propagation Delay — y(t) = u(t - d(t)), a transit line whose delay arrives on a port A ring buffer of capacity+1 past values, all pre-filled with the configured initial output so a delay reaching past the start of the run reads that rather than a sample never taken. buffer[head] = u (the current sample) k = clamp(round(d / dt), 0, capacity) y = buffer[(head - k + capacity + 1) mod (capacity + 1)] head = (head + 1) mod (capacity + 1) k = 0 is direct feedthrough, which is what a zero delay should be.

THE SIMULINK COUNTERPART IS A VARIABLE-STEP BLOCK, and that shapes how this one is tested. simulink/Discrete/Propagation Delay is event-driven: with RunAtFixedTimeIntervals off it refuses a fixed-step solver outright ("Block is not configured to work with a fixed step solver"), and with it on it additionally requires every delayed output time t + d(t) to increase monotonically and to land on a multiple of the sample time. A delay that MOVES therefore aborts the Simulink run rather than producing a trajectory to compare against:

d varying -> "delayed output times must increase monotonically ..." (hard error) d constant -> runs, and is exactly y[k] = u[k - d/Ts] (verified)

Measured against R2026a at Ts = 0.01 with u = 1..20: a constant 0.03 s gives -1 -1 -1 1 2 3 ... and a constant 0.07 s gives -1 x7 then 1 2 3 ..., i.e. a whole-sample shift with the initial output ahead of it. So the two suites deliberately part company, exactly as Variable Transport Delay's entry already documents:

  • EXPORT VERIFICATION drives the delay port from the harness and DOES move it, comparing

the ten backends against ICore's own solver. That is where the moving line is certified.

  • SIMULINK PARITY holds the delay CONSTANT, because a moving one has no reference to

compare against at all. Held constant the two agree exactly, which is what lets parity certify what it is chartered to certify: the catalog mapping and the MATLAB codegen.

ICore's block has no such restriction -- it is fixed-rate by construction, so a moving delay is ordinary here.

Discrete by nature: the line advances one slot per SAMPLE, so dt is the block's own rate.

Code export: all ten targets, the three HDL backends included (see generateBodyCode_VHDL).

Sample results#

Propagation Delay — Step: 0 -> 1 at t = 1 sPropagation Delay — Step: 0 -> 1 at t = 1 s00.51012345t (s)in ICoreDouble-Out-0in 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 … 0
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-1 … 0.9996
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-2 … 3

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__Discrete__Propagation_Delay.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).