Matrix Concatenate — Control Systems/Base Blocks
Control_Systems/Base_Blocks/Matrix_Concatenate · 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.
Matrix Concatenate
Control Systems / Base Blocks
Joins its input signals into one larger signal, along the dimension chosen by Concatenate Dimension: stacking them on top of each other, or side by side.
Ports
- Inputs – two by default, and the count is user-editable. At least two are required. What they must agree on depends on the dimension: when stacking rows they must share a column count, and when stacking columns they must share a row count.
- Output – the join, in port order. Stacking rows, it has as many rows as all the inputs together and their shared column count; stacking columns, their shared row count and as many columns as all of them together.
Parameters
- Concatenate Dimension – which way the signals are joined. This
selects the arithmetic rather than retuning it, so each option is a separate code
path.
- 1 – Vertical (stack rows) – a [2,3] and a [4,3] become a [6,3].
- 2 – Horizontal (stack columns) – a [3,2] and a [3,4] become a [3,6]. This is the default, as in Simulink.
- 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. The join is unrolled at export time, so the generated code is plain assignment with no loop over ports and no dimension branch left in it.
The three HDL targets are fully synthesizable. Concatenation is data movement, so the values stay in the Q16.16 datapath and nothing is converted at the port boundary or approximated.
Simulink bridge
Import and export, mapped to simulink/Math Operations/Matrix
Concatenate. "Concatenate Dimension" to ConcatenateDimension,
one option for one option (1, 2), so that round trip is
lossless; the input port count becomes Simulink's NumInputs number
and an imported count becomes this port list.
Mode is always written as Multidimensional array,
because this block has no choice to offer there. Simulink's other value,
Vector, concatenates 1-D signals – a kind of signal
ICore does not have, since every ICore signal is a 2-D [m,n] matrix. Writing the
value explicitly is what keeps the exported block on the behaviour implemented
here instead of leaving Simulink on a default the two would read
differently.
The Simulink block has no SampleTime parameter, so the
rate is not carried across: it stays on the ICore side, and a block
configured with an explicit positive rate reports that the rate did not
cross.
simulink/Signal Routing/Vector Concatenate imports as this block too.
It is the same Simulink block under a second library path – same
Concatenate type, same three parameters – offered with
Mode preset to Vector and the dimension to
1. An imported one becomes a Matrix Concatenate stacking
rows, which is what its vector concatenation was doing; exporting again
writes the Math Operations path.
Notes
- Algebraic, with no state – pure signal joining; nothing is scaled or reordered.
- The output size is derived from the inputs, so it is only settled once the upstream blocks have resolved theirs.
- Deliberately carries no state space, even though concatenation is linear: it RESHAPES, and a D matrix can rescale a signal but never re-index one into a larger one.
- Stacking rows computes what Mux computes. They are separate blocks in Simulink and separate here: Mux is signal routing with no parameter behind its width, and this is the Math Operations block whose direction is a config.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Base_Blocks/Matrix_Concatenate |
| family | Control_Systems/Base_Blocks |
| solver environment class | ICoreBlock_0_Control_Systems_1_Base_Blocks_2_Matrix_Concatenate |
| source | src/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Base_Blocks/Matrix_Concatenate/ICoreBlock_0_Control_Systems_1_Base_Blocks_2_Matrix_Concatenate.cpp |
| header | src/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Base_Blocks/Matrix_Concatenate/ICoreBlock_0_Control_Systems_1_Base_Blocks_2_Matrix_Concatenate.h |
| default size on canvas | 80 × 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 | — |
| 2 | in | ICoreDouble | — |
| 3 | 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 |
|---|---|---|
Concatenate Dimension | 1 - Vertical (stack rows)%~%2 - Horizontal (stack columns… | ConcatenateDimension |
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/Math Operations/Matrix Concatenate |
| port-count rule | PortsParam::ConcatNumInputs |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
| always set | Mode = Multidimensional array |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
Concatenate Dimension | ConcatenateDimension | 1 - Vertical (stack rows) → 1, 2 - Horizontal (stack columns) → 2 |
Caveat (shown to the user): the two dimensions map 1:1 onto Simulink's, so the choice is lossless in both directions; Simulink's Vector mode concatenates 1-D signals, which ICore does not have - every ICore signal is a 2-D matrix - so the multidimensional mode is always written out
Catalog contract: src/ICoreSDK/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The checker has a blind spot here — it could not resolve something (a grouped port bullet, a computed config name), which is reported and never counted as a pass. A reader has to settle it:
B0Ports lists 2 entries for 3 ports (2 in, 1 out) — grouped, or one undocumented? a reader must say
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).
Matrix Concatenate -- several signals joined into one along a chosen dimension Measured against Simulink R2026a, [1 2; 3 4] and [5 6; 7 8]:
ConcatenateDimension = 1 -> [4,2] 1 2 ConcatenateDimension = 2 -> [2,4] 3 4 1 2 5 6 5 6 3 4 7 8 7 8
So dimension 1 stacks ROWS and dimension 2 stacks COLUMNS, and each requires the inputs to agree on the OTHER extent.
SIMULINK'S "Mode" IS NOT OFFERED, and the entry says so with a fixedParam. Its two values are "Vector" and "Multidimensional array"; the vector mode exists to concatenate 1-D signals, a thing Simulink has and ICore does not - every ICore signal is a 2-D [m,n] matrix, so there is no 1-D case for it to mean anything different in. Writing "Multidimensional array" unconditionally is what makes the export land on the behaviour this block actually implements, rather than leaving Simulink on its own default and having the two disagree about what a column vector is.
Note the deliberate overlap with Mux: Mux is signal ROUTING and always stacks vertically with no parameter behind it, so this block's dimension-1 case computes the same thing. They are separate blocks in Simulink and separate here, and the descriptions say so.
HDL is SYNTHESIZABLE: concatenation is data movement, so the values stay in the Q16.16 datapath and nothing is converted or approximated.
Algebraic and stateless. No state space -- see the header for why.
Sample results#
| t | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 [1x2] entry 0 |
|---|---|---|---|
| 0 | -2 | -2 | [-2, -2] |
| 0.4 | 0.5 | 0.5 | [0.5, 0.5] |
| 0.8 | -2 | -2 | [-2, -2] |
| 1.2 | 0.5 | 0.5 | [0.5, 0.5] |
| 1.6 | -2 | -2 | [-2, -2] |
| 2 | 0.5 | 0.5 | [0.5, 0.5] |
| 2.4 | -2 | -2 | [-2, -2] |
| 2.8 | 0.5 | 0.5 | [0.5, 0.5] |
| 3.2 | -2 | -2 | [-2, -2] |
| 3.6 | 0.5 | 0.5 | [0.5, 0.5] |
| 4 | -2 | -2 | [-2, -2] |
| 4.4 | 0.5 | 0.5 | [0.5, 0.5] |
| 4.8 | -2 | -2 | [-2, -2] |
| 5.2 | 0.5 | 0.5 | [0.5, 0.5] |
Every 4th of 60 samples, from the table stimulus.
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 … 5.8 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -1 … 0.9996 |
step | Step: 0 -> 1 at t = 1 s | 0 … 1 |
Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample
Category static · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Control_Systems__Base_Blocks__Matrix_Concatenate.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).