Generated reference › Product — Control Systems/Base Blocks
kind: generated#block#control-systems-base-blocks

Product — Control Systems/Base Blocks

Control_Systems/Base_Blocks/Product · 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.

Product

Control Systems / Base Blocks

Multiplies its inputs, either entry by entry or as matrices, chosen by the Multiplication Type parameter: y = u₁ · u₂ … element by element, or y = u₁ × u₂ as a true matrix product. The choice decides the block's shape as well as its arithmetic.

Ports

  • Inputs – two by default. In Element-wise the count is user-editable and every input must carry the SAME signal size; this block does not expand a scalar over a matrix. In Matrix there are exactly two, and their inner dimensions must agree: a [m,n] first input needs a [n,p] second.
  • Output – the product y. The shared input size in Element-wise; [m,p] in Matrix, so a matrix product may change the signal's dimensions.

Operations

An input's operation is its port description label, not a parameter: click the label on the canvas and type * or /. A blank label counts as *. A fresh block comes up as * then *, matching Simulink's default for this block – Divide is the same block with * then /. A leading / is a reciprocal, so a /* port list computes (1/u₁)·u₂. Because the operation lives on the port it travels with copy, undo and save, and can never fall out of step with the port count – the same arrangement Sum uses for its signs. Only Element-wise accepts a /; see Parameters.

Parameters

  • Multiplication Type – which product is computed. This selects the arithmetic rather than retuning it, so each option is a separate code path.
    • Element-wise (.*) – entry by entry, over any number of inputs, each taken with its own * or /. This is the default, as in Simulink. All inputs share one size and the output takes it.
    • Matrix (*) – the matrix product y = u₁ × u₂, summing over the inner index. Exactly two inputs, both *. A third input, or a / on either port, is reported when the model is built: chain a second Product block for a longer product, and use Divide or Math Function for a quotient. A [m,n] and a [n,p] input give a [m,p] output, so this is the one mode that can change the signal's dimensions.
  • 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. Both the multiplication type and the per-port operations are fixed into the generated arithmetic at export time; there is nothing left to retune on the generated core.

The seven software targets agree exactly, including division by zero in Element-wise, which gives ±Inf (or NaN for 0/0) in all seven.

The three HDL targets differ by mode, and deliberately. Matrix is ordinary Q16.16 fixed point and is synthesizable: it is a sum of products, the same arithmetic Gain's matrix modes emit. Element-wise is simulation-only, because a / is reachable there and Q16.16 cannot hold a quotient whose divisor has come near zero – one quantum of the divisor is 1.5×10⁻⁵, so an ordinary signal crossing zero produces a result far outside the format. Those cores convert at the port boundary and evaluate in real arithmetic; having no infinity, they answer 0 where a divisor is exactly zero.

Simulink bridge

Import and export, mapped to simulink/Math Operations/Product. "Multiplication Type" to Multiplication, one option for one option (Element-wise(.*), Matrix(*)), so that round trip is lossless. There is no parameter behind the per-port operations on either side: the port labels become Simulink's Inputs string (**), and an imported string – or an imported input count, which Simulink also accepts there – becomes this port list. "Sampling Time (s)" goes to SampleTime, as on every block.

Simulink's Divide is this same block with an Inputs of */, and imports as ICore's Divide; its Product of Elements is a different block, and is ICore's Product Of Elements. A Simulink Product set to Matrix(*) with more than two inputs, or with a / among them, is reported on import rather than silently truncated.

Notes

  • Algebraic, with no state.
  • Not linear in either mode – a product of two signals is not – so the block deliberately carries no state space and model reduction reports it as unmergeable. Gain is the linear sibling, a signal times a constant, and that one can be absorbed; it is also the block to reach for when one operand is a constant matrix, since its Matrix (K*u) mode is synthesizable in every target.
  • At least two inputs are required, as for Sum and Divide.
  • Matrix is not commutative: swapping the two inputs generally changes the answer, and changes the output's shape unless both are square.

Code facts#

FactValue
registered typeControl_Systems/Base_Blocks/Product
familyControl_Systems/Base_Blocks
solver environment classICoreBlock_0_Control_Systems_1_Base_Blocks_2_Product
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Base_Blocks/Product/ICoreBlock_0_Control_Systems_1_Base_Blocks_2_Product.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Base_Blocks/Product/ICoreBlock_0_Control_Systems_1_Base_Blocks_2_Product.h
default size on canvas70 × 70 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
1inICoreDoubleOP_MULTIPLY
2inICoreDoubleOP_MULTIPLY
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
Multiplication TypeElement-wise (.*)%~%Matrix (*)~~Element-wise (.*)Multiplication

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/Math Operations/Product
port-count rulePortsParam::DivideSigns
SampleTime parameteryes
ICore configSimulink parameterValue translation
Multiplication TypeMultiplicationElement-wise (.*)Element-wise(.*), Matrix (*)Matrix(*)

Caveat (shown to the user): the two multiplication types map 1:1 onto Simulink's Multiplication values, so the choice is lossless in both directions; the per-input operations are port labels rather than config, and cross as Simulink's Inputs string. Matrix() here takes exactly two '' inputs -- a longer chain or a '/' among them is reported rather than silently truncated, and is expressed by cascading a second Product block

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:

  • B0 Ports 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).

Product block -- the multiplying half of Simulink's Product/Divide pair "Multiplication Type" selects WHICH code runs, so it is a mode rather than a tuning, and it decides the block's SHAPE as well as its arithmetic:

Element-wise (.) y = u1 (op) u2 (op) ... entry by entry, any number of inputs, each carrying "" or "/" in its port DESCRIPTION LABEL. Identical arithmetic to Divide, deliberately: the two are ONE Simulink block, and a user moving between them should not meet a second convention. Matrix () y = u1 * u2, the true matrix product of two SIGNALS. [m,n] x [n,p] leaves as [m,p]. Exactly two inputs, both "".

WHY THE MATRIX MODE TAKES EXACTLY TWO INPUTS. Simulink chains N matrices left to right, and each step of such a chain produces an intermediate of a DIFFERENT shape than both its operands. The seven software targets could hold those intermediates; a VHDL block body cannot -- it runs inside one clocked process and may declare nothing, and the shared scratch (acc, acc2, iacc) is scalar. Emitting a chain the HDL backends could not follow would mean either a language that silently does something else or a mode with no rig behind it, and ADDING_NEW_BLOCKS.md is explicit that an untested branch is worse than a documented limit. So the third input is refused with a message that names the cascade as the way to get it.

HDL, and why the two modes differ there:

Element-wise is SIMULATION-ONLY real arithmetic, for exactly the reason Divide gives. A "/" port is reachable on this block too, and a Q16.16 quotient overflows as soon as the divisor drops below |numerator| / 32768 -- one quantum of the divisor is 1.5e-5, so an ordinary signal crossing zero produces a quotient the datapath cannot hold. One emitted body per target, converting at the port boundary, rather than a fixed-point path selected only by an all-"*" port list.

Matrix is FIXED POINT and synthesizable. It is "*"-only by construction, so it has no dynamic-range argument against it -- it is the same sum-of-products Gain's Matrix (K*u) already emits and already passes. It is also a rig of its own (PT_Product__Matrix), so unlike the branch Divide declined to write, this one is selected and compared in all ten languages.

Algebraic and stateless. NO state space in either mode: a product of two SIGNALS is not linear. See the header.

Sample results#

Product — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sampleProduct — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample05012345t (s)in ICoreDouble-Out-0in ICoreDouble-Out-0out ICoreDouble-Out-0
tin ICoreDouble-Out-0in ICoreDouble-Out-0out ICoreDouble-Out-0
0-2-24
0.40.50.50.25
0.8-2-24
1.20.50.50.25
1.6-2-24
20.50.50.25
2.4-2-24
2.80.50.50.25
3.2-2-24
3.60.50.50.25
4-2-24
4.40.50.50.25
4.8-2-24
5.20.50.50.25

Every 4th of 60 samples, from the table stimulus.

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 1
rampRamp: slope 1 from t = 00 … 33.64
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias0 … 1
stepStep: 0 -> 1 at t = 1 s0 … 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__Product.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).