ICore Blocks / Features / Ten targets

One model.
Ten languages.
Every one verifiable.

Deploy turns a subsystem, or the whole model, into a stand-alone program. Whichever of the ten languages you pick, it has the same shape: a core that advances the model by exactly one sample per call, a testbench that drives it, and a build file so the folder compiles as it is. Then ICore can build it with your own toolchain and compare it with the simulation, sample by sample.

DPT_Feedback_Discrete the model
In1 Σ +− 1.8 0.4 z⁻¹ 1 − 0.6 z⁻¹ Out Error Ctrl_Gain Plant feedback

The same five-block loop is exported on every target page. On the right is what Deploy wrote for its Ctrl_Gain block in each of the ten languages, with nothing edited.

DPT_Feedback_Discrete_deployableCore.cdouble
/* blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain */
static void blk2_solve(DeployableCore* core) {
    double input[1][1];
    memcpy(input, core->signals.sig1, sizeof(input));
    double output[1][1] = {0};
    for (int i = 0; i < 1; i++) {
        for (int j = 0; j < 1; j++) {
            output[i][j] = input[i][j] * core->params.blk2_gain[0][0];
        }
    }
    memcpy(core->signals.sig2, output, sizeof(output));
}
DPT_Feedback_Discrete_deployableCore.hppdouble
// blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain
struct Blk2 {
    void solve(Signals& signals, const Params& params) {
        auto gain = params.blk2_gain;
        auto input = signals.sig1;
        Mat<1, 1> output {};
        for (std::size_t i = 0; i < output.size(); i++) {
            for (std::size_t j = 0; j < output[0].size(); j++) {
                output[i][j] = input[i][j] * gain[0][0];
            }
        }
        signals.sig2 = output;
    }
};
DPT_Feedback_Discrete_deployableCore.rsf64
// blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain
struct Blk2 {}

impl Blk2 {
    fn new() -> Blk2 { Blk2 {} }

    fn solve(&mut self, signals: &mut Signals, params: &Params) {
        let gain = params.blk2_gain;
        let input = signals.sig1;
        let mut output = [[0.0_f64; 1]; 1];
        for i in 0..output.len() {
            for j in 0..output[0].len() {
                output[i][j] = input[i][j] * gain[0][0];
            }
        }
        signals.sig2 = output;
    }
}
DPT_Feedback_Discrete_deployableCore.pynumpy · float64
class Blk2:
    def solve(self):
        output = signals.sig1 * params.blk2_gain[0][0]
        signals.sig2 = output
DPT_Feedback_Discrete_deployableCore.mdouble
        function blk2_solve(obj)

            gain = obj.params.blk2_gain;
            input = obj.signals.sig1;
            output = input * gain(1, 1);
            obj.signals.sig2 = output;
        end
DPT_Feedback_Discrete_deployableCore.javadouble
    // blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain
    class Blk2 {

        void solve() {
            double[][] gain = params.blk2_gain;
            double[][] input = signals.sig1;
            double[][] output = new double[1][1];
            for (int i = 0; i < output.length; i++) {
                for (int j = 0; j < output[0].length; j++) {
                    output[i][j] = input[i][j] * gain[0][0];
                }
            }
            signals.sig2 = output;
        }
    }
DPT_Feedback_Discrete.vhdQ16.16 · synthesizable
-- blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain
for i in 0 to 0 loop
    for j in 0 to 0 loop
        s.sig2(i, j) := resize(s.sig1(i, j) * blk2_gain(0, 0), s.sig2(i, j));
    end loop;
end loop;
DPT_Feedback_Discrete.vQ16.16 · synthesizable
// blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain
acc = $signed(w_sig1[0*`ICORE_WIDTH +: `ICORE_WIDTH]) * $signed(blk2_gain[0*`ICORE_WIDTH +: `ICORE_WIDTH]);
w_sig2[0*`ICORE_WIDTH +: `ICORE_WIDTH] = acc >>> `ICORE_FRAC_BITS;
DPT_Feedback_Discrete.svQ16.16 · synthesizable
// blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain
acc = $signed(w_sig1[0][0]) * $signed(blk2_gain[0][0]);
w_sig2[0][0] = acc >>> `ICORE_FRAC_BITS;
DPT_Feedback_Discrete_deployableCore.stLREAL
(* blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain *)
sig2[0, 0] := sig1[0, 0] * gain_blk2[0, 0];
BlockCtrl_Gain · 1.8
Parameterblk2_gain
Tunableafter export
Targets
10
seven software languages, three hardware description languages
On all ten
749/ 806
library blocks that implement every target
Software residual
0%
Gain rig against the simulation, tolerance 0.1 %
HDL residual
0.001%
the Q16.16 quantum, tolerance 1 %
01 · One shape, ten times

A core, a testbench, a build file.

Every export is organised the same way, whatever the language. One call to the core advances the model by one sample: one clock edge on the HDL targets, one scan on a PLC. Storage is fixed-size and nothing is allocated at run time. The order the blocks run in comes from the diagram.

params

Tunable after export

Change a gain on the deployed core without regenerating it. In the loop above, blk2_gain holds the 1.8.

signals

One matrix per output port

sig0, sig1, and so on. Each is a fixed-size matrix, commented with the block and port it comes from.

inputs

Set before each call

Every top-level input gate copies its field into signal storage at the start of the step.

state

Persistent block state

Delay lines, filter histories and integrators live inside the core. The plant's u_hist and y_hist are here.

Software targets compute in double, or LREAL in Structured Text, unless a port carries another signal type. The three HDL targets compute in fixed point.

02 · The ten targets

What Deploy writes, target by target.

TargetDeploy writesOne call advances one sample
C<name>_deployableCore.h/.c, testbench, CMakeLists.txtDeployableCore_init(&core), then execute_blocks(&core)
C++<name>_deployableCore.hpp, testbench, CMakeLists.txtstruct DeployableCore · execute_blocks()
Rust<name>_deployableCore.rs, testbench, Cargo.tomlexecute_blocks(&mut self)
Python<name>_deployableCore.py, testbenchexecute_blocks() · needs numpy
MATLAB<name>_deployableCore.m, testbenchclassdef … < handle · core.execute_blocks()
Java<name>_deployableCore.java, testbenchexecuteBlocks()
VHDLicore_pkg.vhd, entity, testbench, Makefile (GHDL)one rising clk edge
Verilogicore_defs.vh, module, testbench, Makefile (Icarus)one clk edge
SystemVerilogicore_defs.svh, module, testbench, Makefile (Icarus)one clk edge
PLC · ST<name>_deployableCore.st, testbenchFUNCTION_BLOCK FB_<name> - one scan
Deploy target · Scripting
A deploy target's Scripting list open, showing all ten export languages A deploy target's Scripting list open, showing all ten export languages
One deploy target's Scripting list, showing the same ten targets as the table. The target's verification level and source subsystem are set right beside it.

Which blocks support which target

Measured over the 806 library blocks, 749 support all ten targets.

  • Python803
  • C801
  • MATLAB800
  • C++ · Rust · Java800
  • PLC · ST756
  • VHDL · Verilog · SV749

The exceptions are blocks whose meaning ties them to software:

  • the user-code blocks: the C block exports to C, and the Python block and the two Python-model blocks export to Python;
  • 44 blocks that export to the six general-purpose languages: fourteen of the string blocks, the two bus blocks, the five message blocks, the fourteen incremental-learning blocks, the label encoder, the model type converter, and seven ephemeris, sun-tracking and CubeSat models;
  • seven blocks that reach PLC Structured Text but stop short of the HDL targets: Bitwise Operator, Peak Finder, Parameter Writer, String Display, and three Earth-orientation and space-weather lookups;
  • the Hit Scheduler, which steers a variable-step solver that exported code does not have.

The subsystem block is counted too, but it writes no code of its own, because its contents do.

Each block's own page names the targets it implements, and every documentation build checks that list against the block's code.

03 · Verification

Built by your toolchain. Graded by the simulation.

Choose any verification level other than None and Deploy checks its own work before it writes anything. The export is compiled and run on your machine, then compared with ICore's simulation of the same subsystem on the same stimulus.

01 · Stimulus

A random input

Uniform between Testing Amplitude Min and Max, one fresh draw per sample, written to <name>_input.csv.

02 · Build

Your compiler

A recording testbench is exported and built with the toolchain that Auto-scan finds, or the one you point it at.

03 · Simulate

The same input

ICore runs its own simulation of that subsystem on the same CSV.

04 · Compare

The residual

The largest absolute difference, as a percentage of the simulated peak, is checked against your tolerance.

$ exportVerify … --blocks Gain  · the Gain rig, from the manual
language        pass  residual%   tol%
Python          PASS  0           0.1
C++             PASS  0           0.1
C               PASS  0           0.1
Rust            PASS  1.76e-16    0.1
Java, MATLAB    PASS  0           0.1
VHDL            PASS  0.00105     1
Verilog         PASS  0.001069    1
System Verilog  PASS  0.001062    1
PLC - ST        PASS  0           0.1
The software targets reproduce the simulation exactly or to rounding. The HDL targets land about 0.001 % away, which is the size of one Q16.16 step, not a defect. The Rust row is the manual's re-run with rustc on the path.
  • Pythonpython3 with numpy
  • C · C++gcc · g++
  • Rustrustc
  • Javaa JDK, javac and java
  • MATLABmatlab
  • VHDLghdl
  • Verilog · SViverilog and vvp
  • PLC · STmatiec's iec2c, built in the app's own tree, and gcc

If a toolchain is missing, the result says so by name, for example rustc not found., instead of failing in some vaguer way.

Four levels: None Output Gates Only Output Gates and Sink Blocks Strict - All Signals. If a check fails, Deploy asks before it continues. A green result means precisely this: the exported code, compiled by your toolchain, reproduces ICore's simulation of this subsystem on this stimulus, to within the tolerance.

04 · Fixed point on the HDL targets

Every HDL signal is Q16.16.

Range
−32768
to +32767.99998
Step
2⁻¹⁶
about 1.53e-5
Products
Q32.32
formed at double width, shifted back once

VHDL, Verilog and SystemVerilog carry every signal in this one format, declared in the generated icore_pkg.vhd, icore_defs.vh or icore_defs.svh. Their residual against the simulation is therefore set by the number format, not by the generator.

Synthesizable

Arithmetic that exists in Q16.16

.vhds.sig2(i, j) := resize(s.sig1(i, j) * blk2_gain(0, 0), s.sig2(i, j));

Add, multiply, compare, delay and saturate get a body that a synthesis tool builds into logic. The gain above is one of them.

Simulation-only, and labelled

Arithmetic that doesn't

.vhd-- Cartesian To Polar: SIMULATION-ONLY real arithmetic

A square root, a four-quadrant arctangent, sin/cos, or a division by a signal gets a body in the simulator's real type. It simulates and passes verification under GHDL or Icarus, but it won't synthesize, and both the generated file and the block's page say so.

05 · The rules

What the export refuses, and why.

Each of these stops the export with a message that names the cause. Nothing is quietly approximated.

  • Fixed-step or discreteThe generated core has no notion of a variable step, so a variable-step model is refused: "set the global solver to fixed-step or discrete and deploy again."
  • One sampling rateEvery block in the exported subtree must share one rate. A model with several rates deploys as several targets; see multi-target deploy.
  • Signal typesA port whose signal type the chosen language can't carry stops that target before any code is written. The other targets in the queue still run.
  • Commented-outA commented-out subsystem is left out, along with everything inside it, and can't be chosen as the source.
  • Identifier stemThe model name becomes the file and identifier stem, sanitised to what VHDL and Structured Text accept. They are the strictest of the ten, so they set the rule for all of them.
  • Model frozenWhile an export or a verification runs, edits are refused. You can still scroll, zoom, switch tabs and save.
  • HDL overflowThe Verilog bodies wrap on overflow instead of saturating, and they floor where VHDL rounds. Because of that, the two can differ by one quantum on the same diagram.
  • What green meansVerification compares the generated code with ICore's own simulation. It doesn't prove the simulation is right, and it says nothing about inputs the stimulus never produced.

Where to find it

  • DeployThe Deploy To Hardware panel, or Code Engine → Export → Deploy Targets. Press Create Target, then Deploy or Deploy All.
  • VerifierCode Engine → Code Export Verifier, with plots of emulated vs simulated output and the residuals, plus the full compiler log
  • DocsExporting code · Numerics and limits · Signal types

See also: Verification  ·  One model, every rate, every target

Get started

See it run on your own model.

Download the application from the customer portal, or read the documentation first - the manual, a page for every block, and the full command reference are public.