Matlab Deploy Embedded AI
MathWorksOfficialCreated by MathWorks
- Skill
- Workflow
- Software development
- BSD-2-Clause
- No issues found
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
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/matlab/matlab-deploy-embedded-ai/downloadUnzip 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 modelsChoosing 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/fitrnetneural network models ARE covered by this skillGenerating 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. Seereferences/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
| Pattern | When to Use | Primary Toolchain |
|---|---|---|
| 1 — MATLAB Network Codegen | Model trained in MATLAB, OR external model needing compression/quantization/Simulink export/weight inspection, OR Cortex-M targets | MATLAB Coder™ / Embedded Coder™ |
| 2 — PyTorch/LiteRT Direct Codegen | External PyTorch (.pt2) or LiteRT (.tflite) model on x86/GPU/Cortex-A targets; shorter path to C code without compression | MATLAB 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
| Capability | Pattern 1 (dlnetwork) | Pattern 2 (PyTorch/LiteRT direct) |
|---|---|---|
| C code generation | Yes | Yes |
| Target: Cortex-M, Cortex-A/R, x86, GPU | Yes | Yes |
| Weight inspection / modification | Yes | No |
| dlquantizer (INT8) | Yes | No |
| Projection (compressNetworkUsingProjection) | Yes | No |
| Pruning | Yes | No |
| Simulink integration | Yes (exportNetworkToSimulink) | Yes (PyTorch SPKG Simulink blocks) |
| Combined compression | Yes | No |
| Speed to first C code | Slower | Faster |
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):
Environment Discovery (silent): Load
references/shared/environment-setup.mdProject Discovery (interactive): Load
references/shared/project-discovery.md
Project Discovery determines the workflow pattern via the decision tree above.
Do Not Use (Legacy Functions)
| Legacy | Modern Replacement |
|---|---|
trainNetwork / train (for DL) | trainnet |
DAGNetwork / SeriesNetwork / network | dlnetwork |
taylorPrunableNetwork / updateScore / updatePrunables | compressNetworkUsingTaylorPruning (when trainable with trainnet); use taylorPrunableNetwork for custom training loops |
csvread / xlsread | readmatrix / readtable |
datenum | datetime |
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_toolboxesand support packages viamatlabshared.supportpkg.getInstalledbefore any workflow stepIf 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
singleprecision for all inference inputsScript-based execution: For each workflow step done in MATLAB, create a
.mscript file and execute it withrun_matlab_fileorevaluate_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_filesets the working directory to the script's folder. Always use absolute paths (viafullfile) for model files, data, and saved outputs — never rely onpwdor 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:
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.)
Run the same inputs through the rebuilt native MATLAB model and compare against ground truth
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/dispstrings, because re-running the script with different inputs or a different model will then print stale numbers.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.,ValidationThresholdfor accuracy-budgeted pruning). Lookup procedure:First try
help <function>in the live MATLAB session. Fast and reflects the actually-installed release of the toolbox or support package.If
helpreturns 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 athttps://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.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.mdand 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, ornetworkobjectsRun 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.mdUse 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 inferencePass a
dlnetworktoprepareNetwork— in R2026a the function takes adlquantizerobject (prepareNetwork(quantObj)) and mutates it in place. The legacynet = prepareNetwork(net)form is no longer defined
Related Workflows (Out of Scope)
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:
| Target | Toolchain | Notes |
|---|---|---|
| FPGA / SoC | HDL Coder + Deep Learning HDL Toolbox | Generates HDL (VHDL/Verilog) from dlnetwork |
| PLC | Simulink PLC Coder | Generates 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
- ATTRIBUTION.mdAdded by AutoGineer1.0 KiB
- LICENSELicence file added by AutoGineer1.4 KiB
- SKILL.md18.1 KiB
- manifest.yaml588 B
- references/pattern1/codegen-embedded.md19.3 KiB
- references/pattern1/compression-classical-ml.md2.2 KiB
- references/pattern1/compression-decision.md9.8 KiB
- references/pattern1/compression-projection.md7.9 KiB
- references/pattern1/compression-pruning.md2.7 KiB
- references/pattern1/compression-quantization.md13.6 KiB
- references/pattern1/compression.md13.1 KiB
- references/pattern1/custom-layers-codegen.md7.9 KiB
- references/pattern1/data-preparation.md5.3 KiB
- references/pattern1/native-rebuild-patterns.md9.4 KiB
- references/pattern1/native-rebuild-preparation.md14.7 KiB
- references/pattern1/placeholder-blocks.md14.6 KiB
- references/pattern1/simulink-integration.md22.9 KiB
- references/pattern1/troubleshooting.md18.3 KiB
- references/pattern1/workflow.md12.1 KiB
- references/pattern2/architecture-patterns.md5.0 KiB
- references/pattern2/coder-configuration.md6.8 KiB
- references/pattern2/pytorch-export.md4.7 KiB
- references/pattern2/troubleshooting.md7.1 KiB
- references/pattern2/verification-testing.md10.7 KiB
- references/pattern2/workflow.md5.6 KiB
- references/shared/ai-verification.md4.3 KiB
- references/shared/environment-setup.md4.6 KiB
- references/shared/project-discovery.md4.2 KiB
references/pattern1/troubleshooting.md
Troubleshooting -- Pattern 1
Common errors in the import, native dlnetwork rebuild, compression, code generation, and Simulink workflow for embedded AI deployment.
Import and Rebuild Errors
"Layer X does not support code generation"
Cause: The network still contains autogenerated custom layers from import.
Fix: You have not finished the rebuild. Find and replace all custom layers:
for i = 1:numel(net.Layers)
cls = class(net.Layers(i));
if startsWith(cls, "nnet.onnx.") || startsWith(cls, "nnet.pytorch.") || ...
startsWith(cls, "nnet.tensorflow.") || contains(cls, "PlaceholderLayer")
fprintf('Still custom: %s [%s]\n', net.Layers(i).Name, cls);
end
endReplace each one with a native equivalent. See native-rebuild-patterns.md and
custom-layers-codegen.md.
PyTorch import failure -- fallback to ONNX
If importNetworkFromPyTorch fails or produces custom autogenerated layers that
are NOT supported for code generation (check with analyzeNetwork):
Do NOT attempt to fix the layers manually in MATLAB as a first step.
Use
/matlab-import-external-ai-modelto guide the user through exporting the network to ONNX format from Python and importing viaimportNetworkFromONNX.If the ONNX import also produces unsupported layers, proceed to a full native rebuild using
native-rebuild-patterns.md.
Post-import validation -- detecting unsupported layers
After importing any external model, always check for placeholder, autogenerated, or unsupported layers:
analyzeNetwork(net)
numLayers = numel(net.Layers);
isUnsupported = false(numLayers, 1);
for i = 1:numLayers
layerClass = class(net.Layers(i));
if contains(layerClass, "PlaceholderLayer") || ...
contains(layerClass, "nnet.onnx") || ...
contains(layerClass, "nnet.pytorch") || ...
contains(layerClass, "nnet.tensorflow") || ...
contains(layerClass, "autogenerated")
warning("Layer '%s' (%s) may not support code generation.", ...
net.Layers(i).Name, layerClass);
isUnsupported(i) = true;
end
end
unsupportedLayers = find(isUnsupported);
if ~isempty(unsupportedLayers)
fprintf("Found %d layers that may not support code generation.\n", ...
numel(unsupportedLayers));
endWeight shape mismatch during transfer
Cause: PyTorch and MATLAB use different dimension orderings for some layer types.
Common fixes:
% Conv weights: PyTorch [O,I,H,W] -> MATLAB [H,W,I,O]
matlabW = permute(pytorchW, [3, 4, 2, 1]);
% FC weights from ONNX MatMul: [input, output] -> MATLAB [output, input]
matlabW = onnxW';
% Position embeddings: [numTokens, embedDim] -> [embedDim, numTokens]
matlabPE = pytorchPE';Numerical mismatch after rebuild (error > 1e-3)
Possible causes:
Missing weight: A learnable parameter was not transferred. Check that
net.Learnablesrow counts match between the imported and rebuilt networks.Wrong weight mapped: Layer name matching was ambiguous. Use explicit index-based transfer.
FC OperationDimension: After replacing mean with GAP+flatten in CNNs, the FC layer's
OperationDimensionis still set for PyTorch convention. Rebuild the FC layer from scratch:fcNew = fullyConnectedLayer(fcOld.OutputSize, 'Name', fcOld.Name, ... 'Weights', fcOld.Weights, 'Bias', fcOld.Bias); lgraph = replaceLayer(lgraph, fcOld.Name, fcNew);dlarray format mismatch: The rebuilt network expects a different format string. Check what
analyzeNetwork(net)shows for input expectations.
analyzeNetworkForCodegen reports issues
Fix: Run and read each issue:
analyzeNetworkForCodegen(net, TargetLibrary='none');Common issues and fixes:
| Issue | Fix |
|---|---|
| "Layer not supported for target 'none'" | Replace with codegen-compatible equivalent |
| "State properties not supported" | Remove layers with State (or use stateless equivalent) |
| "Variable-size not supported" | Ensure all dimensions are fixed in coder.typeof |
Compression Errors
fi() type error during calibrate()
Cause: prepareNetwork() was not called on the quantizer before calibration.
Fix:
qObj = dlquantizer(net, 'ExecutionEnvironment', 'MATLAB');
prepareNetwork(qObj); % Takes the dlquantizer object, mutates in place
calibrate(qObj, ds);dlquantizer fails on network with custom layers
Cause: Custom autogenerated layers do not support the quantization framework.
Fix: This is the whole reason for the native rebuild. Ensure ALL layers in the network are standard MATLAB types. If you still have custom layers, the rebuild is incomplete.
INT8 quantization has 50%+ error
Possible causes (check in order):
Wide output range: If
(max - min) / 256 > acceptable_error, INT8 cannot represent the output accurately. Check:outputs = zeros(numTests, 1); for i = 1:numTests y = predict(baseNet, dlarray(single(testInputs{i}), fmt)); outputs(i) = extractdata(y); end fprintf('Output range: [%.1f, %.1f]\n', min(outputs), max(outputs));If range exceeds ~100, consider INT16 (via Fixed-Point Designer) or stay FP32.
Unrepresentative calibration data: If the calibration set does not cover the real input distribution, scale/zero-point estimates are wrong. Ensure at least 100 representative samples from the deployment domain.
Sensitive intermediate layers: Some layers (e.g., normalization, attention projections) lose more precision than others. Use
quantizationDetails(qNet)to inspect per-layer ranges, and consider excluding high-error layers via theExcludedLayersoption.Missing
prepareNetwork: Without preparation, batch normalization layers are not fused and layer equalization is not applied, leading to cascading quantization error.prepareNetwork(quantObj)handles both fusion and equalization.
quantize() output is NOT supported for code generation
Cause: The output of quantize() is for simulation and emulation only.
MATLAB Coder does NOT support code generation from quantized networks produced
by quantize().
Fix: For embedded deployment, use the uncompressed, pruned, or unpacked
projected network for code generation. Quantization via dlquantizer validates
quantization effects, but the quantized dlnetwork cannot be deployed through
coder.loadDeepLearningNetwork.
Projection fails accuracy budget
Cause: Compression goal is too aggressive for the data distribution.
Fix:
Reduce
LearnablesReductionGoal, or omit it entirely and let the default (driven byExplainedVarianceGoal=0.95) choose. If you don't know the right goal, sweep a few values and compare accuracy.Verify calibration data covers the real input distribution
Do not use synthetic random data for fine-tuning
Combine projection at a conservative goal with INT8 quantization instead
compressNetworkUsingProjection returns worse accuracy at any goal
Cause: The test/calibration data has an extremely narrow distribution. Even small dimensionality reduction loses critical information.
Fix: Use INT8 quantization alone (without projection). The 75% savings from INT8 may be sufficient without adding projection.
ProjectedLayer wrapping a stateful LSTM/GRU fails code generation
Cause: lstmProjectedLayer and gruProjectedLayer (the unpacked
recurrent forms) are supported for generic C/C++ codegen via
coder.loadDeepLearningNetwork. A ProjectedLayer wrapper is supported
only when its contents are stateless — conv/FC, or LSTM/GRU configured in
stateful-I/O mode (HasStateInputs=true, HasStateOutputs=true). A
wrapped stateful LSTM/GRU does not generate code.
Fix: Unpack to the recurrent form before saving for
coder.loadDeepLearningNetwork:
% Unpack projected layers — produces lstmProjectedLayer / gruProjectedLayer
unpackedNet = unpackProjectedLayers(projectedNet);
save("deployableNet.mat", "unpackedNet");
% Alternative: set UnpackProjectedLayers=true during projection
projectedNet = compressNetworkUsingProjection(net, calibData, ...
UnpackProjectedLayers=true);Simulink Errors
exportNetworkToSimulink generates placeholder blocks
Cause: Network contains custom layers that cannot be mapped to native Simulink
layer blocks. The export does not fail — it succeeds but inserts placeholder
subsystems (empty stubs with Inport -> Assertion -> Outport) for each
unsupported layer. These placeholders block both simulation and code generation.
Fix: Either complete the native rebuild (replace all custom layers with
standard MATLAB types), or implement the placeholder layers manually using the
workflow in placeholder-blocks.md.
Cause 2: Stale Simulink artifacts. Fix:
if exist([mdl '.slx'], 'file'), delete([mdl '.slx']); end
if exist([mdl '.slxc'], 'file'), delete([mdl '.slxc']); end
if bdIsLoaded(mdl), close_system(mdl, 0); end
exportNetworkToSimulink(net, 'ModelName', mdl);Cause 3: Quantized network not exportable. Fix: Fall back to float32 baseline, then apply quantization via Simulink tools instead.
Simulink simulation output is NaN
Possible causes:
NaN or Inf in input data: Check
any(isnan(inputData(:)) | isinf(inputData(:))). Missing sensor readings or division-by-zero in preprocessing propagate as NaN through the network.Input timeseries has wrong time vector: Must start at 0 and match StopTime.
Input shape does not match what the block expects: Verify dimensions with
analyzeNetwork(net)and compare to the signal feeding the block.
slbuild fails on Embedded Coder generation
Fix:
% Ensure ERT target is set
set_param(mdl, 'SystemTargetFile', 'ert.tlc');
set_param(mdl, 'TargetLang', 'C');
% Enable portable word sizes for cross-compilation
set_param(getActiveConfigSet(mdl), 'PortableWordSizes', 'on');Code Generation Errors
DeepLearningConfig not set
% Always set explicitly for library-free C
cfg.DeepLearningConfig = coder.DeepLearningConfig('none');InstructionSetExtensions on wrong object
% WRONG -- dlcfg doesn't have this property
dlcfg.InstructionSetExtensions = 'Neon v7';
% CORRECT -- property of cfg
cfg.InstructionSetExtensions = 'Neon v7';Linker errors with LargeConstantThreshold = 0
On bare-metal MCU without filesystem, the generated code tries to fopen .bin
weight files.
Fix: Do not set LargeConstantThreshold -- let weights embed inline in the
.c file. Or provide a filesystem abstraction on the MCU.
Checking for Hardware Support Packages
Many embedded deployment workflows require hardware support packages (HSPs) in
addition to toolboxes. HSPs are not detected by ver or detect_matlab_toolboxes.
% Check for a specific HSP (e.g., ARM Cortex-M support)
pkgs = matlabshared.supportpkg.getInstalled;
if isempty(pkgs)
fprintf('No support packages installed.\n');
else
hasARM = any(contains({pkgs.Name}, "ARM Cortex-M"));
fprintf('ARM Cortex-M HSP: %s\n', string(hasARM));
endIf the required HSP is missing, direct the user to install it via MATLAB Add-On Explorer. Do not attempt to install support packages programmatically.
Embedded Coder not available
Embedded Coder is optional — MATLAB Coder alone is sufficient for generating C/C++ code from deep learning networks. If Embedded Coder is available, it provides additional optimizations (smaller code footprint, target-specific tuning, MISRA compliance). Proceed with MATLAB Coder if EC is not installed.
Codegen predict function requirements
The entry-point function for code generation must load the network internally
(not accept it as an input) and wrap the raw input in dlarray with the correct
format label. Type information can be provided via the -args flag to codegen
or via an arguments block in the entry-point function (both are valid).
Correct pattern (dlnetwork):
function y = predictDLNet(x)
%#codegen
persistent net;
if isempty(net)
net = coder.loadDeepLearningNetwork('trainedNet.mat');
end
dlX = dlarray(x, 'BC'); % x is [1 x numFeatures] (Batch x Channel)
dlY = predict(net, dlX);
y = extractdata(dlY);
endInvoking codegen with -args:
inputType = coder.typeof(single(0), [1 numFeatures]);
codegen predictDLNet -args {inputType} -config cfgThe -args flag specifies the input type and size. Alternatively, an arguments
block inside the entry-point function can provide type information — if -args is
omitted, MATLAB Coder uses the arguments block for type inference. Both approaches
are valid; -args is more flexible for variable-size specifications.
Common failure: Missing dlarray wrapping inside the function. Without it,
codegen fails with: "Input data argument to predict must be dlarray type."
Input specification errors during codegen
Symptom: codegen fails with errors about input size, type, or format mismatch.
Common causes and fixes:
Wrong
-argsspecification: The-argstype must match what the entry-point function actually receives. For a[numFeatures × 1]column vector input:inputType = coder.typeof(single(0), [numFeatures 1]); codegen predictDLNet -args {inputType} -config cfgVariable-size inputs: Some targets support variable-size inputs, but for resource-constrained MCUs, fixed-size is typically preferred. Use
coder.typeof(single(0), [numFeatures 1])for fixed-size, orcoder.typeof(single(0), [numFeatures 1], [true false])for variable first dim.dlarray format mismatch: The format label in
dlarray(x, 'BC')must match the network's expected input format. Check with:net.Layers(1) % Shows expected input size and formatBatch dimension confusion: For single-sample inference on embedded targets, the batch dimension is 1. Use
"BC"(batch=1 × channels) for feature inputs,"SSCB"for image inputs,"CB"for single-timestep stateful sequence inputs.
Network codegen compatibility matrix
| Network Type | Codegen Support | Action Required |
|---|---|---|
Standard dlnetwork | Supported | None |
Projected network with lstmProjectedLayer / gruProjectedLayer | Supported (generic C/C++ only) | None |
Projected network with ProjectedLayer over conv/FC or stateful-I/O LSTM/GRU | Supported (generic C/C++ only) | None |
Projected network with ProjectedLayer over a stateful LSTM/GRU | NOT supported | Call unpackProjectedLayers(net) first |
Quantized network from quantize() | NOT supported | Use the pre-quantized network for codegen |
Diagnostic Commands
% List all layers and their types
for i = 1:numel(net.Layers)
fprintf('%3d %-40s [%s]\n', i, net.Layers(i).Name, class(net.Layers(i)));
end
% Check codegen compatibility
analyzeNetworkForCodegen(net, TargetLibrary='none');
% Check single custom layer
checkLayer(myLayer, inputSize, 'CheckCodegenCompatibility', true)
% List supported layers for target
coder.getDeepLearningLayers('none')
% Inspect Learnables table
disp(net.Learnables)
% Count parameters
totalParams = 0;
for i = 1:height(net.Learnables)
totalParams = totalParams + numel(net.Learnables.Value{i});
end
fprintf('Total parameters: %d (%.1f KB float32)\n', totalParams, totalParams*4/1024);
% Check for installed compression library
pkgs = matlabshared.supportpkg.getInstalled;
hasCompLib = false;
if ~isempty(pkgs)
hasCompLib = any(contains({pkgs.Name}, "Model Compression"));
end
% Estimate model size after code generation
codeDir = "codegen/lib/predictDLNet/";
files = dir(fullfile(codeDir, "*.c"));
totalCodeSize = sum([files.bytes]);
dataFiles = dir(fullfile(codeDir, "*_data.c"));
totalDataSize = sum([dataFiles.bytes]);
fprintf("Generated code size: %.1f KB\n", totalCodeSize / 1024);
fprintf("Data (weights) size: %.1f KB\n", totalDataSize / 1024);
fprintf("Total: %.1f KB\n", (totalCodeSize + totalDataSize) / 1024);Quick-Reference Decision Matrix
This matrix covers the most common single-cause failures. For persistent issues,
also check: input data quality (NaN/Inf), dlarray format labels, and whether
prepareNetwork was called before calibration.
| Symptom | Cause | Fix |
|---|---|---|
| Codegen fails on layer | Custom autogenerated layer | Replace with native (see custom-layers-codegen.md) |
| PyTorch import produces unsupported layers | Autogenerated layers from PyTorch converter | Use /matlab-import-external-ai-model to fall back to ONNX path |
| dlquantizer calibrate fails | prepareNetwork not called | Create the dlquantizer first, then call prepareNetwork(quantObj) (R2026a mutates the object in place) |
| INT8 accuracy terrible | Wide output range | Use FP32 or reduce output scaling |
quantize() output fails codegen | Quantized nets not supported for codegen | Use uncompressed/pruned/unpacked net for codegen |
| Projection fails accuracy | Goal too aggressive | Lower LearnablesReductionGoal, or omit it and use the default (ExplainedVarianceGoal=0.95); sweep goals to find the budget |
| ProjectedLayer codegen error | ProjectedLayer wrapping a stateful LSTM/GRU is not codegen-compatible | Call unpackProjectedLayers(net) to expose the underlying lstmProjectedLayer/gruProjectedLayer (which are codegen-supported) |
| Simulink export fails | Custom layers remain | Complete native rebuild |
| Simulink NaN output | Input shape or time vector mismatch | Check timeseries format and solver settings |
slbuild fails | ERT target or config issue | Set SystemTargetFile to ert.tlc, enable PortableWordSizes |
| DeepLearningConfig not set | Missing codegen config | Set cfg.DeepLearningConfig = coder.DeepLearningConfig('none') |
| Linker errors on MCU | LargeConstantThreshold = 0 causes fopen | Remove threshold, let weights embed inline |
| C output wrong values | Column-major mismatch | Transpose input in C harness |
| Weight transfer wrong | Dimension ordering | Check PyTorch vs MATLAB conventions (permute/transpose) |
| Predict function codegen fails | Missing dlarray format or wrong -args | Wrap input in dlarray with format label; check -args type matches |
| Embedded Coder not installed | Missing toolbox | Warn user; code may be unoptimized for target |
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.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-deploy-embedded-ai.zip