From Workspace — Control Systems/Sources
Control_Systems/Sources/From_Workspace · 0 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.
From Workspace
Control Systems / Sources
A source that plays a table of recorded samples back against the simulation clock:
y(t) = the table's value at t, interpolated between samples, extrapolated or held outside them.
Ports
- Output – the played-back signal. It has no inputs, and its size is M×1, where M is the number of channel columns in Data – one output row per channel, in column order. A single-channel table gives a scalar signal.
Parameters
- Data – the table, as an N×(1+M) matrix whose first column
is time and whose remaining columns are one channel each. Times must be strictly
increasing. Three ways to give it:
- written out in place,
[0 0.5; 0.05 -1.2; 0.12 2.4]; - the name of a matrix in the variables space, which must have that same [time, channels] layout;
- the name of a recorded signal in the variables space – what a Signal Recorder leaves behind. Its samples and channels are used directly, so recording one run and driving another from it needs nothing but the name.
- written out in place,
- Interpolation – how the value between two samples is found.
- Linear – the straight line between them. Also the rule used before the first sample and, under Extrapolation, after the last.
- Zero Order Hold – the most recent sample's value, held until the next. Before the first sample this gives zero, not the first value.
- Output After Final Value – what happens once the table runs out.
- Extrapolation – continue the last segment's straight line. It requires Interpolation to be Linear: a zero-order hold has no line to continue, and the pair is refused rather than quietly held, because Simulink refuses it too.
- Setting to zero – zero from the sample after the last one onwards. The final sample itself still reports its own value.
- Holding final value – hold the last sample's value.
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period. It is the rate the table is SAMPLED at, not the table's own spacing – the two are independent.
Code export
All ten targets: Python, MATLAB, Java, Rust,
C, C++, VHDL, Verilog, SystemVerilog and
PLC Structured Text. The table is captured at export time and unrolled
into an if/else-if chain, one per channel: nothing in the generated core reads the
variables space, and nothing is exposed as a tunable parameter, because a retuned
sample and its pre-computed segment slope would have to move together. Segment slopes
are computed here, once, and baked in – so no backend redoes the division in its
own rounding. A large table therefore produces a large core, and notably a long
comparison chain on the three HDL targets, which are simulation-only here: they
read the testbench's sim_time and compare in real arithmetic, quantizing
only on the way out to the Q16.16 port.
Simulink bridge
Import and export, mapped to simulink/Sources/From Workspace.
"Data" to VariableName – the text crosses verbatim, so a table
written out in place is evaluated by Simulink as the same literal, and a name is
resolved by Simulink in its base workspace (where the user must have put it).
"Interpolation" to Interpolate, translated Linear →
on and Zero Order Hold → off, 1:1 and so
lossless both ways. "Output After Final Value" to
OutputAfterFinalValue, whose three values are spelled identically on both
sides and pass through unchanged. "Sampling Time (s)" to SampleTime, as on
every block. Simulink's remaining parameters are left at their defaults:
OutDataTypeStr is its fixed-point tooling, which ICore has no counterpart
for because every ICore signal is a double, and ZeroCross selects
zero-crossing detection, which belongs to Simulink's variable-step solver.
Simulink's fourth OutputAfterFinalValue value, Cyclic repetition,
is deliberately not offered: Simulink itself rejects it for data carrying an
explicit time vector, which this block's data always does, so the value would only ever
have aborted the generated script.
Notes
- Time-driven and stateless: the output depends only on the current time, so the block replays identically however it is stepped.
- The before the first sample rule is deliberately Simulink's and differs from the sibling Signal Editor block, which holds its first value there. Under Linear this block continues the line through the first two samples backwards; under Zero Order Hold it reports zero.
- The table is read when the run starts. A recorded signal named here is whatever the variables space held at that moment – recording into the same name during the run does not change what is already playing.
- Zero Order Hold and Extrapolation cannot be combined; the run stops and names the block. Simulink reports the same combination as an error rather than degrading it, so accepting it here would have produced models that cannot be exported.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Sources/From_Workspace |
| family | Control_Systems/Sources |
| solver environment class | ICoreBlock_0_Control_Systems_1_Sources_2_From_Workspace |
| source | src/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Sources/From_Workspace/ICoreBlock_0_Control_Systems_1_Sources_2_From_Workspace.cpp |
| header | src/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Sources/From_Workspace/ICoreBlock_0_Control_Systems_1_Sources_2_From_Workspace.h |
| default size on canvas | 90 × 70 px |
| ports at insert | 0 in, 1 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | out | ICoreDouble | — |
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 variable | Default | Simulink parameter |
|---|---|---|
Data | [0 0; 1 1] | VariableName |
Interpolation | Linear%~%Zero Order Hold~~Linear | Interpolate |
Output After Final Value | Extrapolation%~%Setting to zero%~%Holding final value~~Ex… | OutputAfterFinalValue |
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.
Simulink bridge#
| support | Support::Both |
| Simulink path | simulink/Sources/From Workspace |
| port-count rule | PortsParam::None |
SampleTime parameter | yes |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
Data | VariableName | passes through |
Interpolation | Interpolate | Linear → on, Zero Order Hold → off |
Output After Final Value | OutputAfterFinalValue | passes through |
Caveat (shown to the user): 'Data' crosses as VariableName verbatim: a table written out in place is evaluated by Simulink as the same literal, and a NAME is resolved by Simulink in its base workspace rather than in ICore's variables space -- so a model that names a recorded signal here needs that data put into MATLAB's workspace on the far side. Simulink's OutDataTypeStr (fixed-point tooling; every ICore signal is a double) and ZeroCross (variable-step zero-crossing detection) have no ICore counterpart and are left at their defaults. Simulink's fourth OutputAfterFinalValue value, 'Cyclic repetition', is deliberately not offered: Simulink rejects it whenever the data carries an explicit time vector, which this block's data always does, so it could only ever have aborted the generated script. For the same reason the block refuses Zero Order Hold together with Extrapolation, which Simulink reports as an invalid configuration rather than degrading to a hold.
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).
From Workspace block — playback of a recorded or authored signal from the variables space A table of samples played back against the simulation clock. Column 0 is time and every further column is one channel, which is the same [time, ch0, ch1, ...] layout the Signal Recorder writes and the charts read -- so "record a run, feed it back into another model" is one config away.
WHERE THE TABLE COMES FROM. "Data" is either a matrix written out in place, or the NAME of something in the variables space: an ordinary matrix variable, or a recorded Time Series (which is what a Signal Recorder leaves behind). Those three are the whole of it, and the name form is why this config does its own resolution -- see resolveDataTable().
THE PLAYBACK RULE, matching Simulink's From Workspace exactly, because this block has a parity testbench and every one of these was measured against the real block rather than assumed:
t < T[0] Linear: V[0] + m[0](t - T[0]) (extrapolated BACKWARDS) ZOH: 0 (ground, not the first value) T[i] <= t < T[i+1] Linear: V[i] + m[i](t - T[i]), m[i] = (V[i+1]-V[i])/(T[i+1]-T[i]) ZOH: V[i] t == T[n-1] V[n-1] t > T[n-1] the "Output After Final Value" policy
The before-the-table rule is the one that differs from the sibling Signal Editor block, which holds V[0] there. Neither is wrong: Signal Editor has no Simulink counterpart and chose the reading that suits an authored envelope, while this block has one and has to match it. Measured: a table starting at t = 0.03 with values 1 and 2 at 0.03 / 0.06 reports 0, 0.3333, 0.6667 at t = 0, 0.01, 0.02 -- which is the line through the first two points, continued backwards, and NOT the first value held.
ONE RULE, FORMATTED TEN TIMES, per Signal Editor: the rule is built once, in buildBranches(), as a language-neutral ordered list of (condition, value) pairs, and each backend only chooses punctuation. This block builds one chain PER CHANNEL, since its output is an M x 1 column rather than a scalar. The table is unrolled into the chain rather than emitted as an array, so no backend needs array-literal syntax or a loop -- which also means a LARGE table produces a large core, and on the three HDL targets a long comparison chain.
Code export: all ten targets. The three HDL ones are SIMULATION-ONLY -- they read the testbench's
sim_timeand compare in real arithmetic, quantizing only on the way out.
Sample results#
Plotted: free — No input: the block run alone
Category source · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Control_Systems__Sources__From_Workspace.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).