ICore Blocks / Targets / Software

C

A C core with fixed-size storage and no allocation at run time — the target most microcontrollers and RTOS tasks actually want.

Deploy writes
  • <name>_deployableCore.h / .c
  • <name>_testbench.c
  • CMakeLists.txt
One call, one sample

DeployableCore_init(&core); … execute_blocks(&core);

Verified with

gcc or clang

Numbers

double

Library blocks

307 of 308

The core is a plain struct plus functions. You initialise it once, set the inputs, and call execute_blocks once per sample; the outputs are struct members you read straight after. There is no allocator, no hidden state and no dependency beyond the standard library, so it drops into a bare-metal build as easily as a desktop one.

C is one of the two targets that can carry a C block — the user-code block that runs your own source inside the loop. That is why C supports 307 of the 308 library blocks rather than 306: everything the others support, plus the block that is C by definition.

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.h

#ifndef DPT_FEEDBACK_DISCRETE_DEPLOYABLECORE_H
#define DPT_FEEDBACK_DISCRETE_DEPLOYABLECORE_H

/* Tunable parameters (editable from the testbench) */
typedef struct {
    double blk2_gain[1][1];
} Params;

/* Generated signal storage - one fixed-size matrix per output port */
typedef struct {
    /* sig0: ICore Blocks/Home/DPT_Feedback_Discrete/In1/ICoreDouble-Out-0 */
    double sig0[1][1];
    /* sig1: ICore Blocks/Home/DPT_Feedback_Discrete/Error/ICoreDouble-Out-0 */
    double sig1[1][1];
    /* sig2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain/ICoreDouble-Out-0 */
    double sig2[1][1];
    /* sig3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant/ICoreDouble-Out-0 */
    double sig3[1][1];
    /* sig4: ICore Blocks/Home/DPT_Feedback_Discrete/ICoreDouble-Out-0 */
    double sig4[1][1];
} Signals;

/* External inputs: set these before execute_blocks; each top-level input gate
   copies its field into signal storage. */
typedef struct {
    double sig0[1][1];
} Inputs;

/* Persistent block state */
typedef struct {
    double blk3_u_hist[1][1][2];
    double blk3_y_hist[1][1][1];
} State;

/* Deployable core: owns params, signals and persistent block state */
typedef struct {
    Params  params;
    Signals signals;
    Inputs  inputs;
    State   state;
} DeployableCore;

void DeployableCore_init(DeployableCore* core);
void execute_blocks(DeployableCore* core);

#endif

DPT_Feedback_Discrete_deployableCore.c

#include "DPT_Feedback_Discrete_deployableCore.h"
#include <string.h>
#include <math.h>
#include <stdio.h>

/* blk0: ICore Blocks/Home/DPT_Feedback_Discrete/In1 */
static void blk0_solve(DeployableCore* core) {
    memcpy(core->signals.sig0, core->inputs.sig0, sizeof(core->signals.sig0));
}


/* blk1: ICore Blocks/Home/DPT_Feedback_Discrete/Error */
static void blk1_solve(DeployableCore* core) {
    double output[1][1] = {0};
    double in0[1][1];
    memcpy(in0, core->signals.sig0, sizeof(in0));
    for (int i = 0; i < 1; i++) {
        for (int j = 0; j < 1; j++) {
            output[i][j] += in0[i][j];
        }
    }
    double in1[1][1];
    memcpy(in1, core->signals.sig3, sizeof(in1));
    for (int i = 0; i < 1; i++) {
        for (int j = 0; j < 1; j++) {
            output[i][j] -= in1[i][j];
        }
    }
    memcpy(core->signals.sig1, output, sizeof(output));
}


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


/* blk3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant */
static void blk3_solve(DeployableCore* core) {
    static const double num[2] = {0, 0.40000000000000002};
    static const double den[1] = {-0.59999999999999998};
    for (int r = 0; r < 1; r++) {
        for (int c = 0; c < 1; c++) {
            const double uk = core->signals.sig2[r][c];
            for (int k = 1; k > 0; k--) core->state.blk3_u_hist[r][c][k] = core->state.blk3_u_hist[r][c][k - 1];
            core->state.blk3_u_hist[r][c][0] = uk;
            double yk = 0.0;
            for (int i = 0; i < 2; i++) yk += num[i] * core->state.blk3_u_hist[r][c][i];
            for (int i = 0; i < 1; i++) yk -= den[i] * core->state.blk3_y_hist[r][c][i];
            core->state.blk3_y_hist[r][c][0] = yk;
            core->signals.sig3[r][c] = yk;
        }
    }
}


/* blk4: ICore Blocks/Home/DPT_Feedback_Discrete/Out1 */
static void blk4_solve(DeployableCore* core) {
    memcpy(core->signals.sig4, core->signals.sig3, sizeof(core->signals.sig4));
}


void DeployableCore_init(DeployableCore* core) {
    memset(&core->signals, 0, sizeof(core->signals));
    memset(&core->inputs,  0, sizeof(core->inputs));
    memset(&core->state,   0, sizeof(core->state));
    /* Default parameters (override from the testbench after init) */
    core->params.blk2_gain[0][0] = 1.800000; 
}

/* Execution order generated automatically from block diagram */
void execute_blocks(DeployableCore* core) {
    /* blk0: ICore Blocks/Home/DPT_Feedback_Discrete/In1 */
    blk0_solve(core);
    /* blk1: ICore Blocks/Home/DPT_Feedback_Discrete/Error */
    blk1_solve(core);
    /* blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain */
    blk2_solve(core);
    /* blk3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant */
    blk3_solve(core);
    /* blk4: ICore Blocks/Home/DPT_Feedback_Discrete/Out1 */
    blk4_solve(core);
}

Params, signals, inputs and state are four plain structs inside one DeployableCore; each block is a static function over it, and execute_blocks is the order the diagram implies, resolved once at export time. The feedback is visible in blk1_solve: it reads sig3, the plant output that blk3_solve writes later in the same pass — so the loop carries the previous sample, which is what a discrete feedback loop means. Nothing here allocates, and the only headers are string.h, math.h and stdio.h.

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.