Upsample — Control Systems/Resampling
Control_Systems/Resampling/Upsample · 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.
Upsample
Control Systems / Resampling
Passes one sample in every K through and emits an exact zero on the rest – the zero-stuffing half of a rate change, written for a single-rate wire:
y[k] = u[k] when k mod K = offset, and y[k] = 0 on the K−1 samples in between. The sample index k counts from 0 at the start of the run.
Its usual job is to sit in front of an interpolation filter: zero-stuffing puts the spectrum of a slow stream onto a fast grid, and the filter that follows removes the images it creates. On its own the block adds no information – it only changes the grid the samples sit on.
Ports
- Input – the signal u to stuff, of any size [m,n]. Every entry is passed or zeroed on the same samples; the block does not stagger them and the entries do not interact.
- Output – the stuffed signal y, the SAME size [m,n] as the input.
Parameters
- Upsampling Factor – K, the number of samples in one period, of which exactly one carries the input. A whole number of 1 or more; at K = 1 every sample is passed and the block is a wire. Defaults to 3.
- Sample Offset – which sample of each period carries the input, as a whole number from 0 to K−1. At 0 the very first sample of the run is a pass-through; at a larger value the run opens with that many zeros. Defaults to 0.
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period. On this block it is the fast rate – the one the stuffed stream comes out at – so a source feeding it should run at K times this period for the block to carry every one of that source's samples.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The factor and the offset are baked into the core at export time rather than exposed as tunable parameters: they are structural, and a stuffer whose period could be retuned on a built core would be a different block.
The three HDL targets are not simulation-only: the block does no arithmetic at all, so a core is a small counter and a multiplexer choosing between the input word and a literal zero, and the Q-format word is passed through rather than computed on – not one bit of it is disturbed. What those columns do still carry is the port's own quantization, which is a property of an HDL signal and not of this block: measured over a 1001-sample export-verification run, the three of them come back at 0.00076–0.00094 % residual, the same figures a block that only passes its input through scores in the same run, while all seven software targets are exact at residual 0.
Simulink bridge
Neither direction. dspsigops/Upsample is the block this one is
modelled on and it does the same arithmetic, but it produces K output samples
for every input sample: its output port runs at K times its input port's rate,
and it does so even under RateOptions =
Enforce single-rate processing, which for that block pulls the whole
model up to the fast rate rather than making the block single-rate. An ICore wire
carries one rate and no parameter on either side carries that multiplication, so a
mapping would export a connection into a model whose rates no longer agree. The
entry names the block rather than claiming there is no counterpart, and gives the
arrangement that reproduces it: run the SOURCE feeding this block at K times this
block's "Sampling Time (s)", and the two agree sample for sample, because a value
is held between the upstream block's own ticks.
That block's InitialConditions parameter has no counterpart here
because it does nothing there: measured at offset 1 with
InitialConditions = −9, it still emits 0 on the leading sample
under single-rate processing.
Notes
- Stateful, barely: the phase counter is the whole state, and it starts at 0 at the beginning of every run, so a re-run reproduces the stream exactly. No sample is ever held.
- Discrete by nature – the phase advances once per sample, so the block always takes its period from its own "Sampling Time (s)" and is never pushed through a continuous solver's intermediate stages.
- The zeros are exact zeros, not a held or faded value. If what you want between samples is the previous value rather than nothing, that is a hold, and Downsample beside it or Zero-Order Hold is the block for it.
- It is not MATLAB's
upsamplefunction, which returns a LONGER vector. A wire cannot get longer, so the stuffing happens on the rate the block already runs at – which is what the Simulink block does once its output rate is taken into account, and what the bridge section above describes.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Resampling/Upsample |
| family | Control_Systems/Resampling |
| solver environment class | ICoreBlock_0_Control_Systems_1_Resampling_2_Upsample |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Resampling/Upsample/ICoreBlock_0_Control_Systems_1_Resampling_2_Upsample.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Resampling/Upsample/ICoreBlock_0_Control_Systems_1_Resampling_2_Upsample.h |
| default size on canvas | 90 × 70 px |
| ports at insert | 1 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 | in | ICoreDouble | — |
| 2 | 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 |
|---|---|---|
Upsampling Factor | 3 | not crossed |
Sample Offset | 0 | not 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.
Simulink bridge#
| support | Support::None |
| Simulink path | — |
| port-count rule | PortsParam::None |
SampleTime parameter | yes |
| deliberately not crossed | Upsampling Factor, Sample Offset |
Caveat (shown to the user): dspsigops/Upsample computes the same thing and RAISES THE RATE while doing it - it emits K output samples per input sample, even under 'Enforce single-rate processing', which for that block pulls the whole model up to the fast rate. An ICore wire carries one rate and no parameter on either side carries the multiplication, so a mapping would export a connection into a model whose rates no longer agree. To reproduce it, run the SOURCE feeding this block at K times this block's "Sampling Time (s)". Measured on R2026a, including that the Simulink block's InitialConditions has no effect under single-rate processing
Catalog contract: src/ICoreBlocks/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).
Upsample block — pass one sample in every K and emit zero on the rest The zero-stuffing half of a rate change, written for a single-rate wire. It passes its input through on one sample in every K and emits an exact zero on the K-1 samples in between:
y[k] = u[k] when k mod K = offset y[k] = 0 otherwise
k counts SAMPLES from the start of the run, beginning at 0. The block is stateless apart from that counter: it holds nothing, and there is no initial condition to configure because every sample before the first pass-through is one of the zeros.
⚠ WHAT THIS IS COMPARED AGAINST, AND WHY THE BRIDGE IS
None. dspsigops/Upsample is a real Simulink block and this block is NOT registered against it, which is a measurement rather than an assumption. Driven with u = 1..6 and its output read on the FAST grid, that block answers, at K = 3:offset 0 : 1 0 0 2 0 0 3 0 0 4 0 0 5 0 0 6 0 0 offset 1 : 0 1 0 0 2 0 0 3 0 0 4 0 0 5 0 0 6 0 offset 2 : 0 0 1 0 0 2 0 0 3 0 0 4 0 0 5 0 0 6
- the recursion above, term for term. But it produces K output samples for every input
sample: its output port runs at K times its input port's rate even under RateOptions = 'Enforce single-rate processing', which for THIS block means the whole model is dragged up to the fast rate rather than the block being made single-rate. An ICore wire carries one rate, and no config on either side carries that multiplication, so a mapping would export an add_line into a model whose rates no longer agree. The entry NAMES the block and gives the arrangement that reproduces it instead of claiming no counterpart exists: put the source at K times this block's "Sampling Time (s)" and the two agree sample for sample, because ICore holds an upstream value between the upstream block's own ticks.
⚠
InitialConditionsDOES NOT CROSS BECAUSE IT DOES NOTHING THERE. Measured: with offset 1 and InitialConditions = -9 the Simulink block still answers 0 on the leading sample under single-rate processing. There is nothing for an ICore config to carry, so the block has none.Code export: all ten targets, none of them simulation-only and none of them approximate. The block does no arithmetic - every output is a copy of an input sample or a literal zero - so the three HDL cores are a counter and a multiplexer, and the Q-format word is passed through without a single bit being disturbed.
Sample results#
The same rig also ran:
| Stimulus | What it is | Output range |
|---|---|---|
impulse | Impulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair) | 0 … 0 |
ramp | Ramp: slope 1 from t = 0 | 0 … 5.7 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -0.9962 … 0.9985 |
table | Repeating 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 513f52f58 · produced by docsSample --out <folder> --blocks Downsample Upsample --steps 60 · data docs/generated/samples/Control_Systems__Resampling__Upsample.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).