Generated banner for Matlab Deploy Embedded AI
Generated logo for Matlab Deploy Embedded AI

Matlab Deploy Embedded AI

MathWorksOfficialCreated by MathWorks

  • Skill
  • Workflow
  • Software development
  • BSD-2-Clause
  • No issues found

From the author

Deploy AI models to embedded hardware using MathWorks tools (MATLAB, Simulink, Embedded Coder). Covers two workflow patterns: (1) MathWorks-native or imported models rebuilt as dlnetwork for lean hardware, (2) direct C/C++ code generation from PyTorch and LiteRT models. Both patterns support all targets (Cortex-M/A/R, x86, GPU). Trigger when: user wants to deploy AI to embedded targets; generate C/CUDA from neural networks; compress AI models for MCU; integrate AI in Simulink for system-level simulation; import PyTorch/ONNX/TensorFlow models for embedded deployment; optimize AI for resource-constrained hardware; or use loadPyTorchExportedProgram, loadLiteRTModel, importNetworkFromPyTorch, importNetworkFromONNX, importNetworkFromTensorFlow, importNetworkFromKeras, dlquantizer, exportNetworkToSimulink, or Embedded Coder with AI models.
— the description by MathWorks

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach for deploying AI models to embedded targets such as Cortex-M, Cortex-A/R, x86, and GPU. It supports two primary deployment patterns: rebuilding models as native dlnetwork objects for optimized hardware deployment, or direct C/C++ code generation from PyTorch and LiteRT models. The skill covers the entire lifecycle, including model import, compression (quantization, pruning, projection), system-level simulation in Simulink, and code generation using MATLAB Coder and Embedded Coder.

What it does

  • Imports and converts external AI models (PyTorch, ONNX, TensorFlow, Keras) into MATLAB-native formats.
  • Optimizes models for resource-constrained hardware via quantization, pruning, and projection.
  • Integrates AI inference into Simulink models for closed-loop system simulation.
  • Generates optimized C/C++ or CUDA code for various embedded hardware targets.
  • Validates numerical equivalence between original models and generated code.
  • Provides a decision framework to select the optimal deployment pattern based on hardware and performance needs.

When to use it

  • When you need to deploy a trained neural network to embedded hardware.
  • When you must compress an AI model to fit within memory or latency constraints of an MCU.
  • When you need to integrate AI inference into a Simulink system model.
  • When you need to generate C/C++ code from PyTorch or LiteRT models for production deployment.
  • When you require verification of numerical accuracy across model transformations.

Advantages

  • Supports a wide range of embedded targets including ARM Cortex and GPU architectures.
  • Enables system-level verification through Simulink integration before hardware deployment.
  • Provides advanced compression techniques to optimize performance on resource-constrained devices.
  • Ensures reproducibility through script-based execution and numerical validation steps.
  • Offers flexible deployment paths for both native MATLAB models and external framework models.

Usage examples

“How do I deploy my trained MATLAB neural network to an ARM Cortex-M processor?”
The agent will initiate the Project Discovery process to confirm the target hardware and model type, then guide you through the Pattern 1 workflow, including potential compression and code generation steps.
“I have a PyTorch model and need to generate C++ code for an x86 target. Which pattern should I use?”
The agent will identify this as a Pattern 2 scenario, confirm the requirements, and walk you through the direct code generation workflow using the PyTorch support package.
“Can you help me quantize my imported ONNX model for an embedded device?”
The agent will guide you through importing the model as a dlnetwork, performing quantization using the dlquantizer, and validating the accuracy before generating the final code.
“I need to verify if my model is compatible with code generation.”
The agent will run the analysis function to check for unsupported layers and provide a report on compatibility for your specific target library.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/matlab/matlab-deploy-embedded-ai/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip matlab-deploy-embedded-ai.zip -d .agents/skills

Documentation by MathWorks

Embedded AI for Engineered Systems

Deploy AI models to embedded hardware using MATLAB® and Simulink®. This skill is written specifically for MATLAB R2026a and uses APIs, functions, and workflows introduced in that release. It covers the complete lifecycle: model creation or import, verification, compression, system-level simulation, and code generation for resource-constrained targets.

Requires MATLAB R2026a or newer. Core toolboxes: Deep Learning Toolbox, Statistics and Machine Learning Toolbox, MATLAB Coder, Embedded Coder, Simulink, and Fixed-Point Designer. Workflow-specific support packages are checked during Environment Discovery. The MATLAB and Simulink Agentic Toolkits must be available so the agent can drive a live MATLAB and Simulink session through MCP tools.

When to Use

  • Deploying a trained neural network (MATLAB-native or imported) to embedded hardware

  • Generating C or CUDA code from a deep learning model for ARM Cortex-M/A/R, x86, or GPU targets

  • Deploying imported PyTorch, ONNX, TensorFlow, or LiteRT models to embedded targets (import step handled by /matlab-import-external-ai-model)

  • Compressing AI models (quantization, pruning, projection) to fit resource-constrained hardware

  • Integrating AI inference into a Simulink system model for closed-loop simulation before code generation

  • Using loadPyTorchExportedProgram, importNetworkFromPyTorch, importNetworkFromONNX, importNetworkFromTensorFlow, dlquantizer, exportNetworkToSimulink, or Embedded Coder with AI models

  • Choosing between MathWorks-native code generation and direct PyTorch/LiteRT code generation

When NOT to Use

  • Training a model purely for research with no deployment target — use Deep Learning Toolbox documentation directly

  • Deploying to cloud/server endpoints (no embedded target) — use MATLAB Production Server or MATLAB Compiler SDK

  • Working with classical ML models (decision trees, SVMs, ensembles) that aren't neural networks — use Statistics and Machine Learning Toolbox codegen workflows directly. Note: fitcnet/fitrnet neural network models ARE covered by this skill

  • Generating code for non-AI Simulink models — use standard Embedded Coder workflows

  • Converting between model formats without an embedded deployment goal (e.g., ONNX to MATLAB for desktop inference only)

Workflow Pattern Selection

This skill uses two deployment patterns:

  • Pattern 1 — MATLAB Network Codegen: Import or build a dlnetwork, optionally compress it, then generate C/C++ via MATLAB Coder or export to Simulink. See references/pattern1/workflow.md.

  • Pattern 2 — PyTorch/LiteRT Direct Codegen: Load a PyTorch (.pt2) or LiteRT (.tflite) model directly and generate C/C++ without converting to a dlnetwork. See references/pattern2/workflow.md.

Decision Tree

Primary discriminator for external models: deployment capabilities + hardware class.

Q1: Where does the AI model come from?
 |
 +-- Trained in MATLAB, or requires training in MATLAB -------> Pattern 1
 |
 +-- External framework (PyTorch, TF, ONNX, Keras) --> Q2
      |
      Q2: Does the deployment need any of these?
       |  - Quantization (INT8 via dlquantizer)
       |  - Pruning or projection
       |  - Weight inspection / modification
       |  - exportNetworkToSimulink integration
       |
       +-- YES --> Pattern 1 (import as dlnetwork)
       |
       +-- NO ---> Q3
            |
            Q3: What is the deployment target?
             |
             +-- Cortex-M: Pattern 1
             |     (compression and Simulink verification typically needed)
             |
             +-- x86 / GPU:
             |     +-- PyTorch (.pt2) or LiteRT (.tflite) --> Pattern 2
             |     +-- ONNX, TF, Keras --> Pattern 1 (convert to ONNX recommended)
             |
             +-- Cortex-A/R:
                   +-- Small model --> Pattern 1
                   |     (import as dlnetwork, then codegen)
                   +-- Large model:
                        +-- PyTorch (.pt2) or LiteRT (.tflite) --> Pattern 2
                        +-- ONNX, TF, Keras --> Pattern 1 (convert to ONNX recommended)
Pattern Summary
PatternWhen to UsePrimary Toolchain
1 — MATLAB Network CodegenModel trained in MATLAB, OR external model needing compression/quantization/Simulink export/weight inspection, OR Cortex-M targetsMATLAB Coder™ / Embedded Coder™
2 — PyTorch/LiteRT Direct CodegenExternal PyTorch (.pt2) or LiteRT (.tflite) model on x86/GPU/Cortex-A targets; shorter path to C code without compressionMATLAB Coder™ + PyTorch & LiteRT SPKG

Pattern 2's generated C is portable to any target, but Cortex-M deployments typically require Pattern 1 capabilities (compression, Simulink verification).

Import step (Pattern 1): For PyTorch/ONNX/Keras/TensorFlow model import, use /matlab-import-external-ai-model. This skill takes over after import for the compression, Simulink integration, and code generation phases.

Pattern 1 vs Pattern 2 Capability Comparison
CapabilityPattern 1 (dlnetwork)Pattern 2 (PyTorch/LiteRT direct)
C code generationYesYes
Target: Cortex-M, Cortex-A/R, x86, GPUYesYes
Weight inspection / modificationYesNo
dlquantizer (INT8)YesNo
Projection (compressNetworkUsingProjection)YesNo
PruningYesNo
Simulink integrationYes (exportNetworkToSimulink)Yes (PyTorch SPKG Simulink blocks)
Combined compressionYesNo
Speed to first C codeSlowerFaster

Rule of thumb: Choose Pattern 1 when you need to compress, quantize, inspect weights, or use exportNetworkToSimulink — or when the model is already a dlnetwork, or when targeting Cortex-M. Choose Pattern 2 when the model is already in PyTorch (.pt2) or LiteRT (.tflite) format and you want the shorter path to C code without compression.

Stats/ML models (fitrnet/fitcnet): These follow Pattern 1 but have their own Simulink integration path. Use the RegressionNeuralNetwork Predict block (for fitrnet) or ClassificationNeuralNetwork Predict block (for fitcnet) from the Statistics and Machine Learning Toolbox library — do NOT use exportNetworkToSimulink (which is for dlnetwork only). Configure simulation programmatically with Simulink.SimulationInput.

Common Start: Prerequisites

Regardless of pattern, always begin with these two prerequisite steps before entering the pattern-specific phases (which start at Phase 1):

  1. Environment Discovery (silent): Load references/shared/environment-setup.md

  2. Project Discovery (interactive): Load references/shared/project-discovery.md

Project Discovery determines the workflow pattern via the decision tree above.

Do Not Use (Legacy Functions)

LegacyModern Replacement
trainNetwork / train (for DL)trainnet
DAGNetwork / SeriesNetwork / networkdlnetwork
taylorPrunableNetwork / updateScore / updatePrunablescompressNetworkUsingTaylorPruning (when trainable with trainnet); use taylorPrunableNetwork for custom training loops
csvread / xlsreadreadmatrix / readtable
datenumdatetime

For legacy import functions (importONNXNetwork, importKerasNetwork, etc.), see /matlab-import-external-ai-model which handles all model import workflows.

Global Rules

Advisory vs Execution Mode

Distinguish between two modes based on the user's intent:

  • Advisory ("How should I approach this?", "What workflow?", "Which pattern?"): Answer the routing question directly — state the recommended Pattern, explain why, and outline the high-level steps. Do NOT enter Environment Discovery or start asking prerequisite questions. After giving the recommendation, ask if the user wants to begin execution.

  • Execution ("Deploy my model", "Walk me through", "Write the script"): Enter the full prerequisite flow (Environment Discovery → Project Discovery → step-by-step phases).

ALWAYS
  • Check toolboxes via detect_matlab_toolboxes and support packages via matlabshared.supportpkg.getInstalled before any workflow step

  • If a support package is missing, ask the user to download from Add-On Explorer -- never install on their behalf

  • Guide the user step-by-step -- one phase at a time

  • Use consistent data partitioning (e.g., fixed indices or a stored partition object) for reproducibility

  • Verify numerical equivalence at each transformation step

  • Generate MEX for desktop validation before generating C code for target

  • Use single precision for all inference inputs

  • Script-based execution: For each workflow step done in MATLAB, create a .m script file and execute it with run_matlab_file or evaluate_matlab_code. Do NOT run ad-hoc MATLAB commands without first writing the script file. If a script needs changes, edit the script file and re-run it. This gives users full visibility into what code is being executed and enables reproducibility. IMPORTANT: run_matlab_file sets the working directory to the script's folder. Always use absolute paths (via fullfile) for model files, data, and saved outputs — never rely on pwd or relative paths.

  • Pause after each workflow step: After every workflow step completes, pause and explicitly ask the user for permission to proceed to the next step. The goal is to let the user read/inspect the MATLAB scripts you created, review results, and ask questions before moving on.

  • Deep Network Designer: When a model is trained in MATLAB, imported, or rebuilt as a native dlnetwork, load it in Deep Network Designer (deepNetworkDesigner(net)) so the user can visually inspect the architecture. Announce this action and wait for user acknowledgment before proceeding.

  • Numerical equivalency tests (import workflows): For any import from PyTorch or ONNX:

    1. Run inference on the original model (via bundled Python for PyTorch, or ONNX runtime) to collect ground-truth reference data. Do NOT use the imported MATLAB model as reference — for PyTorch imports in R2026a, custom autogenerated layers may not support code generation and the model may need to be rebuilt natively. (ONNX autogenerated layers have supported code generation since R2025a and may not require a rebuild.)

    2. Run the same inputs through the rebuilt native MATLAB model and compare against ground truth

    3. After compression, report the accuracy delta vs. the uncompressed baseline (MAE, max error, % accuracy drop). Compute these from variables in the current run — never hardcode numeric values into fprintf/disp strings, because re-running the script with different inputs or a different model will then print stale numbers.

    4. Run tests to validate numerical equivalence between: compressed model in MATLAB, compressed model in Simulink, and final generated code

  • Test count proposal: Before running numerical equivalency tests, propose how many tests you plan to run and explain why (considering model complexity, output range, class count, etc.). Wait for user agreement or correction before proceeding.

  • Code generation report: Do NOT open the code generation report automatically — it opens a GUI that is not useful in an agentic workflow. Only open if the user explicitly requests it.

  • Look up function signatures from MATLAB's help or the online reference page, not from this skill. Argument lists, name-value pair (NVP) defaults, and supported-layer enumerations live in MATLAB's help <function> output and on the function's reference page. Use those as the source of truth instead of any inline parameter table in this skill — inline tables go stale across releases and burn context. This skill only flags name-value arguments that materially change the recipe (e.g., ValidationThreshold for accuracy-budgeted pruning). Lookup procedure:

    1. First try help <function> in the live MATLAB session. Fast and reflects the actually-installed release of the toolbox or support package.

    2. If help returns only a stub like "Run doc <function> for more information." — common for support-package functions whose help redirects to the browser doc — fall back to the agent's web browsing of the online reference page at https://www.mathworks.com/help/<product>/ref/<funcname>.html (lower-case function name). Extract every name-value argument with its default value, formatted as a markdown table, quoting defaults verbatim.

    3. If the function is not found at all (which <func> returns "not found") on a system that has the relevant support package installed, the support package is likely on a stale build. Ask the user to update via Add-On Explorer rather than working around the missing function.

  • Compression decision flow: At the start of Phase 5 (Pattern 1), load references/pattern1/compression-decision.md and walk the user through the question flow (hardware + Simulink availability, primary goal, retraining tolerance). Pick the compression and code generation path based on the answers. Compression is not mandatory and the optimal combination of pruning, projection, and quantization depends on the goal — for example, on Cortex-M with a latency-bound LSTM model, the float32 path with CMSIS-DSP outperforms the quantized path because CMSIS-NN provides no INT8 kernel for recurrent layers.

ASK FIRST
  • Before each phase transition: "Is this step relevant to your project?" (some phases may not apply)

  • Before data splitting: "Do you have existing train/val/test splits, or should I partition?" (avoids overwriting user's partitioning)

  • Before model selection: "What problem type (classification/regression/sequence) and any size constraints?" (determines architecture)

  • Before Simulink: "Do you have an existing Simulink model to integrate into?" (determines export vs standalone)

  • Before quantization: "Does your target support floating-point, or is integer-only required?" (determines quantization path)

  • Before code generation: "What is the target hardware?" (determines Coder configuration and CRL)

  • Before compression and code generation (Pattern 1): walk the user through the decision flow in references/pattern1/compression-decision.md — hardware target + Simulink availability, primary goal, retraining tolerance. The answers determine the compression techniques and the code-replacement library to use. Even if the hardware target is already stated, you MUST still ask about the primary deployment goal (flash, SRAM, latency, accuracy) and present the user with the recommended recipe for confirmation before executing any compression step.

NEVER
  • Present the entire workflow at once

  • Skip Environment Discovery or Project Discovery

  • Open, load, or inspect user data before Project Discovery is confirmed

  • Use legacy functions listed in "Do Not Use" section

  • Assume toolbox or support package availability without checking

  • Install support packages on the user's behalf

  • Promise hardware-agnostic performance or "deploy anywhere"

  • Generate DAGNetwork, SeriesNetwork, or network objects

  • Run MATLAB commands directly in the MCP server without creating a script file first

  • Skip numerical equivalency testing when using imported external models

  • Proceed to the next workflow step without explicit user permission

  • Apply compression without first walking the user through the decision flow in compression-decision.md

  • Use the imported model (with custom autogenerated layers) as numerical ground truth — always validate against the original model via bundled Python

  • Pass a [C × T] array with format "CBT" to a sequence model — always reshape to [C × 1 × T] for single-sequence inference

  • Pass a dlnetwork to prepareNetwork — in R2026a the function takes a dlquantizer object (prepareNetwork(quantObj)) and mutates it in place. The legacy net = prepareNetwork(net) form is no longer defined


This skill covers C/C++ code generation for CPU targets (Cortex-M, Cortex-A/R, x86) via MATLAB Coder and Embedded Coder, and GPU targets (CUDA/TensorRT) via Pattern 2's DeepLearningConfig options. The following deployment targets use different toolchains and are NOT covered:

TargetToolchainNotes
FPGA / SoCHDL Coder + Deep Learning HDL ToolboxGenerates HDL (VHDL/Verilog) from dlnetwork
PLCSimulink PLC CoderGenerates Structured Text for PLCs from Simulink models

If the user's target falls into one of these categories, inform them that this skill does not cover that workflow and suggest the relevant toolchain.


MATLAB and Simulink are registered trademarks of The MathWorks, Inc. See www.mathworks.com/trademarks for a list of additional trademarks. Other product or brand names may be trademarks or registered trademarks of their respective holders.

Copyright 2026 The MathWorks, Inc.

Files

references/pattern1/simulink-integration.md

Export trained AI models to Simulink for closed-loop simulation, hardware-in-the-loop testing, and Embedded Coder C generation. This reference covers integration paths for dlnetwork, fitcnet, and fitrnet models, along with simulation patterns and pre-deployment verification.

Integration Paths by Model Type

Model TypeIntegration MethodNotes
dlnetwork (Deep Learning Toolbox)exportNetworkToSimulink (recommended)Expands network into individual layer blocks; full visibility and per-layer inspection
dlnetwork (Deep Learning Toolbox)Predict / Stateful Predict blockBlack-box inference; avoids placeholder issues; no layer-level visibility
dlnetwork (Deep Learning Toolbox)Classify / Stateful Classify blockSame as Predict but outputs class labels + scores
fitcnet (Stats/ML Toolbox)ClassificationNeuralNetwork Predict block
fitrnet (Stats/ML Toolbox)RegressionNeuralNetwork Predict block

Clean Environment Before Export

Stale Simulink artifacts can cause export failures. Always clean before exporting:

modelName = 'my_dl_model';

% Clean stale files
if exist([modelName '.slx'], 'file'), delete([modelName '.slx']); end
if exist([modelName '.slxc'], 'file'), delete([modelName '.slxc']); end
if bdIsLoaded(modelName), close_system(modelName, 0); end

% Now export
exportNetworkToSimulink(net, ModelName=modelName, ...
    SaveNetworkInModelWorkspace=true);
Basic Export
% Export trained dlnetwork to Simulink (use name-value pairs, not positional model name)
mdlInfo = exportNetworkToSimulink(net, ModelName="myModel", ...
    SaveNetworkInModelWorkspace=true);

This creates a Simulink model with the network expanded into individual layer blocks (FC, LSTM, ReLU, etc.), connected with signal lines matching the dlnetwork graph. Each layer becomes its own Simulink block, giving full visibility into the inference pipeline and enabling per-layer inspection during simulation.

ParameterValuesDefault
ModelNamestring"my_model"
Statefultrue / falseauto (based on net)
SampleTimestring (e.g. "1", "-1")"-1"
InputDataType"uint8", "single", "Inherit: auto""Inherit: auto"
SaveModelToFiletrue / falsetrue
OpenSystemtrue / falsetrue
SaveNetworkInModelWorkspacetrue / falsefalse

Pass the quantized network (output of quantize()) directly to exportNetworkToSimulink. This creates a Simulink model with fixed-point data types pre-configured in block parameters — weights, biases, and accumulators all use embedded.fi types. Embedded Coder then generates integer C code directly.

Reference: https://www.mathworks.com/help/deeplearning/ug/export-quantized-network-to-simulink.html

% Quantize the network
quantObj = dlquantizer(net, ExecutionEnvironment="MATLAB");
calibrate(quantObj, calibDs);
qNet = quantize(quantObj);

% Export quantized network -- creates fixed-point Simulink blocks
exportNetworkToSimulink(qNet, ModelName="model_quantized", ...
    Stateful=true, InputDataType="single", SampleTime="1", ...
    SaveNetworkInModelWorkspace=true);

% Generate fixed-point C code
set_param('model_quantized', 'SystemTargetFile', 'ert.tlc');

% CMSIS-NN optimization: if targeting ARM Cortex-M, set the Code Replacement
% Library to use CMSIS-NN INT8 kernels for Conv2D and FC blocks ONLY (~2.8-3x).
% LSTM/GRU/BiLSTM blocks in the quantized model have no CMSIS-NN INT8 kernel
% in R2026a — they generate as plain fixed-point C. See codegen-embedded.md.
% Requires: Embedded Coder Support Package for ARM Cortex-M Processors.
set_param('model_quantized', 'ProdHWDeviceType', 'ARM Compatible->ARM Cortex-M');
set_param('model_quantized', 'CodeReplacementLibrary', 'ARM Cortex-M');

slbuild('model_quantized');
Exporting Projected Networks (R2026a)

In R2026a, exportNetworkToSimulink accepts networks that contain ProjectedLayer, lstmProjectedLayer, or gruProjectedLayer directly. Calling unpackProjectedLayers before export is no longer required for the Simulink path.

% Project, then export directly — no unpack needed for the Simulink path
projectedNet = compressNetworkUsingProjection(net, calibData);
exportNetworkToSimulink(projectedNet, ModelName="model_projected", ...
    Stateful=true, InputDataType="single", SampleTime="1", ...
    SaveNetworkInModelWorkspace=true);

For the direct MATLAB Coder path, lstmProjectedLayer and gruProjectedLayer are supported by coder.loadDeepLearningNetwork for generic C/C++ codegen. A ProjectedLayer wrapper is supported only when its contents are stateless (conv/FC, or LSTM/GRU in stateful-I/O mode); a wrapped stateful LSTM/GRU is not. When in doubt, call unpackProjectedLayers first to produce the most codegen-friendly form.

Exporting Non-Quantized Compressed Networks

For the float32 path (projected or pruned networks):

exportNetworkToSimulink(compressedNet, ModelName="model_float32", ...
    Stateful=true, InputDataType="single", SampleTime="1", ...
    SaveNetworkInModelWorkspace=true);

When exportNetworkToSimulink encounters layers it cannot map to native Simulink layer blocks (e.g., selfAttentionLayer, custom reshape/permute layers), it creates placeholder subsystems -- empty stubs containing Inport -> Assertion -> Outport with unspecified output dimensions. These block both simulation and code generation.

If placeholder layers appear after export, load placeholder-blocks.md for the complete workflow: identifying placeholders, cleaning stub contents, choosing between Simulink primitives vs MATLAB Function blocks, storing weights in model workspace, and wiring replacements.

Path 1: Use the Predict block (simpler)

The Predict block from the Deep Learning Toolbox handles inference internally without exposing individual network layers. This avoids placeholder layer issues.

% Instead of exportNetworkToSimulink, use the Predict block:
% 1. Open your Simulink model
% 2. From the Library Browser: Deep Learning Toolbox > Predict
% 3. Drag the Predict block into your model
% 4. Double-click and set the network to your workspace variable (e.g., 'net')
% 5. Configure input dimensions to match your signal

This is the recommended path when the primary goal is system-level simulation and the user does not need layer-by-layer visibility in Simulink.

Path 2: Implement placeholder layers with codegen-compatible primitives

If the user needs the full network exposed as individual Simulink blocks (e.g., for layer-by-layer inspection, modification, or mixed-precision analysis):

  1. Identify each placeholder layer and its mathematical operation:

    % List placeholder layers in the exported model
    % Check the original dlnetwork to understand what each layer does
    analyzeNetwork(net)
  2. Replace each placeholder with one of:

    • MATLAB Function block -- Write the operation in MATLAB code that is compatible with code generation (no dynamic allocation, use single types)

    • Simulink math blocks -- Use Sum, Product, Gain, Math Function, etc. to implement the same operation with primitive blocks

  3. Validation: After replacing all placeholder layers, verify:

    % Run simulation and compare outputs
    % Original: run inference in MATLAB
    YMatlab = minibatchpredict(net, XTest);
    
    % Simulink: run the model and collect logged output
    simOut = sim("myModel");
    YSimulink = simOut.logsout.get("AI_Output").Values.Data;
    
    % Compare
    maxDiff = max(abs(YMatlab - YSimulink), [], "all");
    fprintf("Max difference: %.6e\n", maxDiff);
  4. Ensure all replacement blocks are code-generation compatible -- avoid eval, dynamic memory allocation, or variable-size arrays in MATLAB Function blocks.

Configuring the Predict Block

After export, configure the block:

  1. Input format: Set to match your signal dimensions

  2. Data type: Ensure input signals use single precision for compatibility

  3. Sample time: Match the sample time of your system

% Set block parameters programmatically
set_param("myModel/Predict", "SampleTime", "0.01");
Handling Sequence/Time-Series Models (LSTM/GRU)

For recurrent models (LSTM, GRU), set Stateful=true so the exported block maintains hidden state between simulation time steps:

mdlInfo = exportNetworkToSimulink(net, ...
    ModelName="mySeqModel", ...
    Stateful=true, ...
    InputDataType="single", ...
    SampleTime="0.01", ...
    SaveNetworkInModelWorkspace=true);
  • Stateful=true makes the exported block maintain hidden state between time steps, processing one time step per simulation step. Required for LSTM/GRU models.

When to use FrameBased=true: Only for 1D-CNN temporal models (e.g., convolution1dLayer-based networks) where the input is a multi-sample frame rather than a single time step. Do NOT use FrameBased=true for LSTM/GRU models — recurrent layers process one time step at a time via Stateful=true and do not use frame-based signal semantics.

% 1D-CNN temporal model: use FrameBased=true
mdlInfo = exportNetworkToSimulink(cnnTemporalNet, ...
    ModelName="myCNN1DModel", ...
    FrameBased=true, ...
    InputDataType="single", ...
    SampleTime="0.01");

Stats/ML Toolbox Integration

fitcnet -- ClassificationNeuralNetwork Predict Block
  1. Open the Simulink Library Browser

  2. Navigate to: Statistics and Machine Learning Toolbox > Classification

  3. Drag the ClassificationNeuralNetwork Predict block into your model

  4. Double-click to configure:

    • Set the trained model workspace variable name (e.g., mdl)

    • Configure input port dimensions to match your feature vector

% Ensure the trained model is in the base workspace
mdl = fitcnet(XTrain, YTrain, "LayerSizes", [64 32]);

% Or load from file
load("trainedClassifier.mat", "mdl");
fitrnet -- RegressionNeuralNetwork Predict Block
  1. Open the Simulink Library Browser

  2. Navigate to: Statistics and Machine Learning Toolbox > Regression

  3. Drag the RegressionNeuralNetwork Predict block into your model

  4. Configure with the trained model variable


% Open the generated model
open_system(modelName);

% Set solver for embedded deployment
set_param(modelName, ...
    'Solver', 'FixedStepDiscrete', ...
    'FixedStep', '1');          % 1 sample per step (adapt to your app)

% Set stop time for simulation
numSamples = 100;
set_param(modelName, 'StopTime', num2str(numSamples - 1));

When the user does not have a Simulink model, perform open-loop evaluation in MATLAB:

For dlnetwork
% Run inference on test data
YPred = minibatchpredict(net, XTest);

% For classification
[~, YPredLabels] = max(YPred, [], 2);

% Compare with ground truth
accuracy = mean(YPredLabels == YTest);
confMat = confusionmat(YTest, YPredLabels);

figure;
confusionchart(confMat);
title("Confusion Matrix -- Open-Loop Evaluation");
For fitcnet
% Predict classes
YPred = predict(mdl, XTest);
accuracy = mean(YPred == YTest);

% Get scores for ROC analysis
[YPred, scores] = predict(mdl, XTest);

% Confusion matrix
figure;
confusionchart(YTest, YPred);
For fitrnet
% Predict values
YPred = predict(mdl, XTest);

% Regression metrics
rmseVal = rmse(YPred, YTest);
maeVal  = mean(abs(YPred - YTest));
r2Val   = 1 - sum((YTest - YPred).^2) / sum((YTest - mean(YTest)).^2);

% Scatter plot
figure;
scatter(YTest, YPred, 20, "filled", MarkerFaceAlpha=0.5);
hold on;
plot(xlim, xlim, "r--", LineWidth=1.5);
xlabel("Actual");
ylabel("Predicted");
title(sprintf("Regression Results -- RMSE: %.4f, R^2: %.4f", rmseVal, r2Val));

System-Level Simulation Patterns

Connecting AI Block to Control Logic
% Typical connection pattern:
% Sensor Signal -> Preprocessing -> AI Predict Block -> Post-processing -> Controller

% Example: Virtual sensor replacing a physical measurement
% Input: available sensor readings (temperature, pressure, speed)
% Output: estimated quantity (e.g., mass flow rate)
Running Simulation and Collecting Results

Use Simulink.SimulationInput to configure simulations programmatically without modifying the saved model file. This is the recommended pattern for reproducible, scriptable simulation runs.

% Create simulation input object
simIn = Simulink.SimulationInput("myModel");

% Configure simulation parameters
simIn = setModelParameter(simIn, "StopTime", "10");
simIn = setModelParameter(simIn, "SolverType", "Fixed-step");
simIn = setModelParameter(simIn, "FixedStep", "0.001");

% Configure individual block parameters
simIn = setBlockParameter(simIn, "myModel/AI_Predict", ...
    "NetworkVariable", "net");

% Pass workspace variables (e.g., the trained network) to the model
simIn = setVariable(simIn, "net", net, Workspace="myModel");

% Run simulation -- returns Simulink.SimulationOutput
simOut = sim(simIn);

% Extract AI block outputs from logged signals
aiOutput = simOut.logsout.get("AI_Output").Values;

% Plot
figure;
plot(aiOutput.Time, aiOutput.Data);
xlabel("Time (s)");
ylabel("AI Output");
title("AI Model Output During System Simulation");

Key Simulink.SimulationInput methods:

MethodPurpose
setModelParameterSolver, stop time, and model-level settings
setBlockParameterConfigure individual block parameters
setVariablePass workspace variables to the model

Changes made via Simulink.SimulationInput override model values at runtime without dirtying the saved .slx file -- ideal for parameter sweeps and automated testing.

Per-Sample Simulation (Sequence Models)

For LSTM or sequence models, run one simulation per test sample using timeseries input:

IMPORTANT: Stateful LSTM/GRU models output one value per simulation step. The output Data field is 3-D: [1 × 1 × seqLen] (not [seqLen × 1]). Use squeeze() before indexing, or index with (end) not (end, :).

numTests = 100;
simResults = zeros(numTests, 1);

% Configure model for external input
set_param(modelName, 'LoadExternalInput', 'on');
set_param(modelName, 'SaveOutput', 'on');
set_param(modelName, 'OutputSaveName', 'yout');

for i = 1:numTests
    % Create input timeseries for this sample
    inputSeq = single(testInputs{i});  % [seqLen x features]
    time = (0:size(inputSeq, 1) - 1)';
    inputTs = timeseries(inputSeq, time);

    % Configure and run
    set_param(modelName, 'StopTime', num2str(size(inputSeq, 1) - 1));
    set_param(modelName, 'ExternalInput', 'inputTs');
    simOut = sim(modelName);

    % Extract output — use squeeze for stateful LSTM (output is 3-D)
    outData = simOut.yout{1}.Values.Data;
    simResults(i) = outData(end);  % Last time step = final SoC estimate
end

% Compare against reference
errors = abs(simResults - referenceOutputs(:));
mae = mean(errors);
fprintf('Simulink MAE vs reference: %.4e\n', mae);
Batch Simulation (Feedforward Models)

For MLP/CNN models that process independent samples:

% Stack all inputs as timeseries
inputBatch = single(cat(1, testInputs{:}));  % [N x features]
time = (0:size(inputBatch, 1) - 1)';
ts = timeseries(inputBatch, time);

set_param(modelName, 'StopTime', num2str(size(inputBatch, 1) - 1));

simOut = sim(modelName);
simResults = simOut.yout{1}.Values.Data;
Comparing AI vs. Reference Behavior
% Compare AI output against reference (e.g., physical sensor, ground truth)
reference = simOut.logsout.get("Reference_Signal").Values;
aiOutput  = simOut.logsout.get("AI_Output").Values;

% Compute error
error = aiOutput.Data - reference.Data;
rmseVal = sqrt(mean(error.^2));
maxError = max(abs(error));

fprintf("RMSE: %.4f\n", rmseVal);
fprintf("Max absolute error: %.4f\n", maxError);

figure;
tiledlayout(2,1);
nexttile;
plot(reference.Time, reference.Data, "b", aiOutput.Time, aiOutput.Data, "r--");
legend("Reference", "AI Output");
title("AI vs. Reference Signal");

nexttile;
plot(aiOutput.Time, error);
ylabel("Error");
xlabel("Time (s)");
title(sprintf("Prediction Error (RMSE: %.4f)", rmseVal));

Simulink may introduce small numerical differences due to floating-point arithmetic ordering (e.g., fused multiply-add vs. separate multiply then add, different accumulation order across block boundaries). These are inherent to IEEE 754 floating-point and are NOT solver errors — the fixed-step discrete solver performs no numerical integration for inference-only models.

Network TypeFloat32 MAEINT8 MAEFixed-Point MAE
MLP< 1e-6< 2e-3< 5e-3
LSTM< 1e-5< 2e-3< 5e-3
CNN< 1e-5< 3e-3< 5e-3

Configure for Code Generation
% Set ERT target
set_param(modelName, ...
    'SystemTargetFile', 'ert.tlc', ...
    'TargetLang', 'C');

% ARM Cortex-M hardware (bit-per-type params are read-only; auto-set by device type)
cs = getActiveConfigSet(modelName);
set_param(cs, 'ProdHWDeviceType', 'ARM Compatible->ARM Cortex-M');

% Portable word sizes for cross-compilation
set_param(cs, 'PortableWordSizes', 'on');
Generate Code
slbuild(modelName);

Generated code location: <modelName>_ert_rtw/

Key files:

  • <modelName>.c -- Model step function (includes weights for small models)

  • <modelName>.h -- Model interface

  • <modelName>_types.h -- Type definitions

  • *.bin -- Large constant data (weights/biases exceeding LargeConstantThreshold)

  • rtwtypes.h -- Common data types

  • rt_OneStep() -- Real-time entry point

C Integration Patterns
#include "my_dl_model.h"

// Initialize once at startup
my_dl_model_initialize();

// Call at each control loop iteration
void sensor_callback(float* sensor_data) {
    // Set model inputs
    my_dl_model_U.input[0] = sensor_data[0];
    my_dl_model_U.input[1] = sensor_data[1];
    // ... etc

    // Execute one step
    my_dl_model_step();

    // Read prediction
    float prediction = my_dl_model_Y.output;
    apply_control_action(prediction);
}
Fixed-Point Code Generation

For maximum flash reduction on MCU targets, generate fixed-point code from the Simulink model:

% Configure for fixed-point
cfg = getActiveConfigSet(modelName);
set_param(cfg, 'PortableWordSizes', 'on');

% The quantized network's int8/int16 types are preserved through codegen
% Fixed-Point Designer handles the scaling automatically
slbuild(modelName);

Fixed-point C code is typically 60-70% smaller than float32 generated C.


You can generate C from the same network via two paths and compare:

Path 1: Direct MATLAB Coder
% Save network
save('net.mat', 'netNative');

% Write entry-point
% function out = predict_fn(Xin)
% %#codegen
%   persistent net;
%   if isempty(net), net = coder.loadDeepLearningNetwork('net.mat','n'); end
%   out = extractdata(predict(net, dlarray(single(Xin), 'CT')));
% end

cfg = coder.config('lib', 'ecoder', true);
cfg.TargetLang = 'C';
cfg.DeepLearningConfig = coder.DeepLearningConfig('none');
codegen -config cfg predict_fn -args {inputType}
exportNetworkToSimulink(netNative, 'ModelName', 'model_sim');
set_param('model_sim', 'SystemTargetFile', 'ert.tlc');
slbuild('model_sim');

Comparison notes:

  • Simulink adds step/init/terminate harness code (slightly larger)

  • Fixed-point through Simulink can produce smaller MAC cost

  • Direct codegen is simpler for standalone inference

  • Simulink codegen is better for closed-loop systems with other blocks


After integrating the compressed model into Simulink, always validate numerical equivalency between the MATLAB compressed model and the Simulink simulation output. This ensures no numerical drift was introduced during the export and Simulink block execution.

Test Protocol
  1. Propose test count to the user: typically 100 inputs for feedforward models, 50 sequences for LSTM/GRU models. Explain rationale and wait for agreement.

  2. Run comparison tests:

%% Numerical Equivalency: MATLAB vs Simulink
numTests = 100;  % As agreed with user
errors = zeros(numTests, 1);

for i = 1:numTests
    % MATLAB compressed model inference
    xTest = single(testInputs{i});
    yMatlab = predict(compressedNet, dlarray(xTest, formatString));
    yMatlab = extractdata(yMatlab);

    % Simulink inference (via sim)
    simIn = Simulink.SimulationInput(modelName);
    simIn = setVariable(simIn, 'inputData', timeseries(xTest, 0:size(xTest,1)-1));
    simOut = sim(simIn);
    ySimulink = squeeze(simOut.yout{1}.Values.Data);

    % For stateful models, compare last time step only (final prediction).
    % For feedforward models, ySimulink is the full output vector.
    if isvector(ySimulink) && numel(ySimulink) > numel(yMatlab)
        ySimulink = ySimulink(end);
    end

    errors(i) = max(abs(yMatlab(:) - ySimulink(:)));
end

fprintf('MATLAB vs Simulink Numerical Equivalency\n');
fprintf('=========================================\n');
fprintf('Tests run:  %d\n', numTests);
fprintf('MAE:        %.4e\n', mean(errors));
fprintf('Max Error:  %.4e\n', max(errors));
if max(errors) < 1e-4
    fprintf('Pass/Fail:  PASS\n');
else
    fprintf('Pass/Fail:  INVESTIGATE\n');
end
  1. Report results with explicit pass/fail and comparison to expected tolerances (see tolerance table above).


Pre-Deployment Simulation Checklist

Before proceeding to compression and code generation:

  • [ ] AI model produces expected outputs in simulation

  • [ ] Input/output data types are consistent (preferably single)

  • [ ] Sample time is correctly configured

  • [ ] No algebraic loops introduced by the AI block

  • [ ] System behavior is acceptable with AI in the loop

  • [ ] Edge cases and boundary conditions tested

  • [ ] Numerical equivalency validated between MATLAB compressed model and Simulink output

Copyright 2026 The MathWorks, Inc.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an MCP server

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
MathWorks
Publisher
MathWorks (Official)
Source
matlab/matlab-agentic-toolkit · skills-catalog/ai-and-statistics/matlab-deploy-embedded-ai · commit 2a7f215
Licence
BSD 2-Clause License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.md)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-deploy-embedded-ai.zip