ICore Blocks / Targets / Software

C++

A header-only core as a single struct — one object, one call per sample.

Deploy writes
  • <name>_deployableCore.hpp
  • <name>_testbench.cpp
  • CMakeLists.txt
One call, one sample

DeployableCore core; core.execute_blocks();

Verified with

g++ or clang

Numbers

double

Library blocks

306 of 308

The whole core is a struct in one header, so adopting it is an include and a member. Params, signals, inputs and state are all members you can read and write directly; there is no wrapper class to learn and nothing to link.

The generated CMakeLists.txt builds the testbench as-is, which makes the exported folder a working project rather than a pile of sources to wire up yourself.

What the export looks like

Every target page shows the same model, so the ten are directly comparable: an input, an error junction, a gain of 1.8, a discrete plant 0.4z⁻¹ / (1 − 0.6z⁻¹), an output — and the plant's output fed back into the junction. Five blocks, in a diagram the export-verification suite calls DPT_Feedback_Discrete — which is where the names in the file come from. Below is what Deploy writes for this target, with only the file's header banner removed.

DPT_Feedback_Discrete_deployableCore.hpp

#pragma once

#include <array>
#include <cmath>
#include <cstddef>
#include <iostream>

// Fixed-size matrix: R rows x C cols, stack/struct-allocated (no heap)
template <std::size_t R, std::size_t C>
using Mat = std::array<std::array<double, C>, R>;

// Tunable parameters (editable from the testbench)
struct Params {
    Mat<1, 1> blk2_gain = {{ {{1.8}} }};
};

// External inputs: set these before execute_blocks; each top-level input gate
// copies its field into signal storage.
struct Inputs {
    Mat<1, 1> sig0 {};
};

// Generated signal storage - one fixed-size matrix per output port
struct Signals {
    // sig0: ICore Blocks/Home/DPT_Feedback_Discrete/In1/ICoreDouble-Out-0
    Mat<1, 1> sig0 {};
    // sig1: ICore Blocks/Home/DPT_Feedback_Discrete/Error/ICoreDouble-Out-0
    Mat<1, 1> sig1 {};
    // sig2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain/ICoreDouble-Out-0
    Mat<1, 1> sig2 {};
    // sig3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant/ICoreDouble-Out-0
    Mat<1, 1> sig3 {};
    // sig4: ICore Blocks/Home/DPT_Feedback_Discrete/ICoreDouble-Out-0
    Mat<1, 1> sig4 {};
    Inputs inputs {};
};

// blk0: ICore Blocks/Home/DPT_Feedback_Discrete/In1
struct Blk0 {
    void solve(Signals& signals, const Params& params) {
        (void)params;
        signals.sig0 = signals.inputs.sig0;
    }
};


// blk1: ICore Blocks/Home/DPT_Feedback_Discrete/Error
struct Blk1 {
    void solve(Signals& signals, const Params& params) {
        (void)params;
        Mat<1, 1> output {};
        auto in0 = signals.sig0;
        for (std::size_t i = 0; i < output.size(); i++) {
            for (std::size_t j = 0; j < output[0].size(); j++) {
                output[i][j] += in0[i][j];
            }
        }
        auto in1 = signals.sig3;
        for (std::size_t i = 0; i < output.size(); i++) {
            for (std::size_t j = 0; j < output[0].size(); j++) {
                output[i][j] -= in1[i][j];
            }
        }
        signals.sig1 = output;
    }
};


// 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;
    }
};


// blk3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant
struct Blk3 {
    double u_hist[1][1][2] {};
    double y_hist[1][1][1] {};
    void solve(Signals& signals, const Params& params) {
        static constexpr double num[2] = {0, 0.40000000000000002};
        static constexpr double den[1] = {-0.59999999999999998};
        auto input = signals.sig2;
        for (std::size_t r = 0; r < 1; r++) {
            for (std::size_t c = 0; c < 1; c++) {
                const double uk = input[r][c];
                for (std::size_t k = 1; k > 0; k--) u_hist[r][c][k] = u_hist[r][c][k - 1];
                u_hist[r][c][0] = uk;
                double yk = 0.0;
                for (std::size_t i = 0; i < 2; i++) yk += num[i] * u_hist[r][c][i];
                for (std::size_t i = 0; i < 1; i++) yk -= den[i] * y_hist[r][c][i];
                y_hist[r][c][0] = yk;
                signals.sig3[r][c] = yk;
            }
        }
    }
};


// blk4: ICore Blocks/Home/DPT_Feedback_Discrete/Out1
struct Blk4 {
    void solve(Signals& signals, const Params& params) {
        (void)params;
        signals.sig4 = signals.sig3;
    }
};


// Deployable core: owns params, signals and every stateful block instance
struct DeployableCore {
    Params  params {};
    Signals signals {};
    Blk0 blk0 {};
    Blk1 blk1 {};
    Blk2 blk2 {};
    Blk3 blk3 {};
    Blk4 blk4 {};

    // Execution order generated automatically from block diagram
    void execute_blocks() {
        // blk0: ICore Blocks/Home/DPT_Feedback_Discrete/In1
        blk0.solve(signals, params);
        // blk1: ICore Blocks/Home/DPT_Feedback_Discrete/Error
        blk1.solve(signals, params);
        // blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain
        blk2.solve(signals, params);
        // blk3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant
        blk3.solve(signals, params);
        // blk4: ICore Blocks/Home/DPT_Feedback_Discrete/Out1
        blk4.solve(signals, params);
    }
};

The same core as one struct per block, with std::array for every matrix. Parameters carry their diagram values as member initialisers — blk2_gain is 1.8 the moment the object exists — so there is no init call to forget, and the whole thing still fits in a header you can include from anywhere.

How it is checked

Every one of the ten targets is verifiable, and this one is no exception: the export is compiled with the toolchain above, run across the simulation window, and compared against the solver sample by sample. Software targets pass at around 1e-11 % against a 0.1 % tolerance; the HDL targets are bounded by their fixed-point quantum instead. See verification.

See also: Code export  ·  Multi-target, multi-rate deploy

Get started

See it run on your own model.

Download the application from the customer portal, or read the documentation first — the manual, every block with its measured response, and the full command reference are public.