Circular Convolution — Control Systems/Correlation And Convolution
Control_Systems/Correlation_And_Convolution/Circular_Convolution · 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.
Circular Convolution
Control Systems / Correlation And Convolution
Convolves the last NA samples of one stream with the last NB
samples of another modulo L, every sample:
y[n] = Σk+j≡n (mod L) a[k]·b[j], a vector of
L entries. It is the streaming counterpart of MATLAB's
cconv(a, b, L) applied to the two windows.
The output length is a parameter, not a consequence. Linear convolution answers with NA + NB − 1 entries and has no say in the matter; the circular one folds every term that runs past the end back onto the beginning. Set L to NA + NB − 1 or more and nothing wraps – the block is then the plain Convolution with zero padding. Set it smaller and the tail aliases onto the head, which is what the operation is for.
Ports
- a – the first stream. Scalar; the block keeps its own window of the last NA samples – see Notes.
- b – the second stream. Scalar, with its own window of the last NB samples.
- y – the circular convolution, a column of L entries. Its size is the Output Length parameter alone and does not follow from the windows or from the inputs’ values.
Parameters
- Window Length A – NA, how many samples of a take part. A whole number from 1 to 32. The bound is a code-size bound: both windows are unrolled in every target and the emitted work is their product.
- Window Length B – NB, the same for b.
- Output Length – L, the modulus and the output size. A whole number from 1 to 64. It is independent of the two window lengths.
- Sampling Time (s) – zero or less inherits the solver’s rate; a positive value runs the block at that period.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text.
All three lengths are structural: they decide how many multiplies the exported core contains and how wide its output is, which no runtime parameter can change, so they are baked in at export time rather than offered as tunable parameters. Re-export after changing any of them.
The three HDL targets are genuine synthesizable Q16.16: two shift registers and a fixed multiply-accumulate tree, with every product accumulated at full width and shifted back once per output entry rather than per term.
Simulink bridge
No equivalent (Support::None). The Signal Processing
Toolbox ships no Simulink library at all, and nothing in the Simulink
standard library or in DSP System Toolbox convolves two signals modulo a chosen
length – DSP System Toolbox’s Convolution block is linear, and
takes its lengths from its inputs’ dimensions rather than from a
parameter. The bridge reports this block rather than dropping it silently, and
it therefore has no parity testbench. Code export verification still
covers it across all ten languages.
Notes
- Stateful, and discrete by nature
(
setDiscreteOnlyBlock(true)): both windows advance once per sample. - Computed directly, where MATLAB transforms.
cconvtakes an FFT of each operand, multiplies and transforms back; this block sums the products. On a = [0.7 −1.3 0.45 2.1 −0.6] against b = [−0.9 0.25 1.7 −0.35 0.8] the two agree to 4.4e−16 at L = 5, 7 and 3 – a difference in rounding between two algorithms, not in the definition. A direct sum is also the only form the hardware targets can carry. - Oldest-first is the published order. a[0] is the oldest sample still in the window and a[NA−1] the newest.
- The windows are zero-prefilled, and the zeros count. The first max(NA, NB)−1 outputs of a run are a startup transient rather than a convolution of real data.
- Scalar inputs. Each input is one channel with its own window.
- No state space. The block multiplies two signals together, so it is bilinear rather than linear, carries no A/B/C/D pair, and model reduction correctly declines to merge it.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Correlation_And_Convolution/Circular_Convolution |
| family | Control_Systems/Correlation_And_Convolution |
| solver environment class | ICoreBlock_0_Control_Systems_1_Correlation_And_Convolution_2_Circular_Convolution |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Correlation_And_Convolution/Circular_Convolution/ICoreBlock_0_Control_Systems_1_Correlation_And_Convolution_2_Circular_Convolution.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Correlation_And_Convolution/Circular_Convolution/ICoreBlock_0_Control_Systems_1_Correlation_And_Convolution_2_Circular_Convolution.h |
| default size on canvas | 132 × 80 px |
| ports at insert | 2 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 | a |
| 2 | in | ICoreDouble | b |
| 3 | out | ICoreDouble | y |
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 |
|---|---|---|
Window Length A | 8 | — |
Window Length B | 8 | — |
Output Length | 8 | — |
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 |
Caveat (shown to the user): no Simulink equivalent: cconv() is a MATLAB function and the Signal Processing Toolbox ships no Simulink library at all. Nothing in the Simulink standard library or in DSP System Toolbox convolves two signals modulo a chosen length -- DSP System Toolbox's Convolution block is LINEAR and takes its lengths from its inputs' dimensions rather than from a parameter. Reported rather than dropped, and it carries no parity testbench
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).
Circular Convolution -- the modulo-L convolution of two windowed streams, y = cconv(a, b, L) THE OUTPUT LENGTH IS A PARAMETER, NOT A CONSEQUENCE. Linear convolution of an NA-sample and an NB-sample window has NA + NB - 1 entries and no say in the matter; the circular one folds every term back modulo L and answers with L. Choose L >= NA + NB - 1 and nothing wraps, so the block becomes the plain Convolution with zero padding; choose it smaller and the tail aliases onto the head, which is the whole point of the operation and the default here.
⚠ COMPUTED DIRECTLY, WHERE MATLAB TRANSFORMS. R2026a's cconv() takes an FFT of each operand, multiplies and transforms back, so its answer and a direct sum of products differ by the rounding of two different algorithms rather than by their definitions. MEASURED on a = [0.7 -1.3 0.45 2.1 -0.6] and b = [-0.9 0.25 1.7 -0.35 0.8]: worst disagreement 4.4e-16 at L = 5, 7 and 3. A direct sum is also the only form the three hardware targets can carry, so this is a deliberate choice and not an approximation of one.
⚠ THE WINDOWS ARE ZERO-PREFILLED AND THE ZEROS COUNT -- the convention Detrend and Moving Median carry, the latter measured against Simulink. The first max(NA, NB) - 1 outputs of a run are a startup transient.
⚠ OLDEST-FIRST IS THE PUBLISHED ORDER; NEWEST-FIRST IS THE STORAGE ORDER, exactly as in the sibling Convolution block. The two orders meet in one expression per backend and nowhere else.
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 … 1 |
ramp | Ramp: slope 1 from t = 0 | 0 … 246.3 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -0.5398 … 6.499 |
table | Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample | -10.5 … 8.25 |
Plotted: step — Step: 0 -> 1 at t = 1 s
Category dynamic · sample time 0.1 · 60 steps · commit 3c100aff6f27235305db4ad4d572f32e342718ad · produced by docsSample --out <folder> --blocks Convolution Circular_Convolution Convolution_Matrix Deconvolution Cross_Correlation Cross_Covariance --steps 60 · data docs/generated/samples/Control_Systems__Correlation_And_Convolution__Circular_Convolution.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).