C.c
A C core with fixed-size storage and no allocation at run time - the target most microcontrollers and RTOS tasks actually want.
#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
#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); }
What Deploy writes for the reference model below, with only the file's
header banner removed. 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.
From the diagram to your C build.
Deploy writes the folder, ICore checks it against the simulation, and your code calls it once per sample.
Deploy writes
<name>_deployableCore.h / .c<name>_testbench.cCMakeLists.txt
Built, run and compared
Built with gcc or clang, run across the simulation window and compared with the solver sample by sample, against a 0.1 % tolerance.
One call, one sample
Numbers are double, and 801 of 806 library blocks export to C.
DeployableCore_init(&core); … execute_blocks(&core);Where C fits.
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 the only target that can carry a C block - the user-code block that runs your own source inside the loop. It supports 801 of the 806 library blocks: everything except the Python block, the two Python-model blocks, the Hit Scheduler, which steers a variable-step solver that exported code does not have, and the subsystem block, which writes no code of its own because its contents do.
| Target class | Tolerance | Observed |
|---|---|---|
| Software · 7 languages | 0.1 % | ≈1e-11 % |
| HDL · Q16.16 | 1 % | ≈1e-3 % |
One model, ten targets.
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 and an output,
with the plant's output fed back into the junction. The export-verification suite calls it
DPT_Feedback_Discrete - which is where the names in the file come
from.
See also: Code export · One model, every rate, every target
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.