This example shows how to convert a neural network regression model in Simulink to fixed point using the Fixed-Point Tool and Lookup Table Optimizer and generate C code using Simulink Coder.

Fixed-Point Designer provides work-flows via the Fixed Point Tool that can convert a design from floating-point data types to fixed-point data types. The Lookup Table Optimizer generates memory-efficient lookup table replacements for unbounded functions such as `exp`

and `log2`

. Using these tools, this example showcases how to convert a trained floating-point neural network regression model to use embedded-efficient fixed-point data types.

Neural Network Toolbox ships with engine_dataset which contains data representing the relationship between the fuel rate and speed of the engine, and its torque and gas emissions.

% This dataset can be loaded using the following command: load engine_dataset; % Use the Function fitting tool |nftool| from Neural Network Toolbox(TM) to % train a neural network to estimate torque and gas emissions of an engine % given the fuel rate and speed. Use the following commands to train % the neural network. x = engineInputs; t = engineTargets; net = fitnet(10); net = train(net,x,t); view(net)

Close all windows of training tool and view of the network

```
nnet.guis.closeAllViews();
nntraintool('close');
```

Once the network is trained, use the `gensim`

function from the Neural Network Toolbox™ to generate a simulink model.

sys_name = gensim(net, 'Name', 'mTrainedNN');

The model generated by the `gensim`

function contains the neural network with trained weights and biases. To prepare this generated model for fixed-point conversion, follow the preparation steps in the best practices guidelines. https://www.mathworks.com/help/fixedpoint/ug/best-practices-for-using-the-fixed-point-tool-to-propose-data-types-for-your-simulink-model.html

After applying these principles, the trained neural network is further modified to enable signal logging at the output of the network, add input stimuli and verification blocks. The modified model is saved as fxpdemo_neuralnet_regression

Copy the model to a temporary writable directory.

model = 'fxpdemo_neuralnet_regression'; current_dir = pwd; fxpnn_demo_dir = fullfile(matlabroot, 'toolbox', 'simulink', 'fixedandfloat', 'fxpdemos'); fxpnn_temp_dir = [tempdir 'fxpnn_dir']; cd(tempdir); [~, ~, ~] = rmdir(fxpnn_temp_dir, 's'); mkdir(fxpnn_temp_dir); cd(fxpnn_temp_dir); copyfile(fullfile(fxpnn_demo_dir, [model,'.slx']), fullfile(fxpnn_temp_dir, [model '_toconvert.slx']));

Open and Inspect model

model = [model '_toconvert']; system_under_design = [model '/Function Fitting Neural Network']; baseline_output = [model '/yarr']; open_system(model);

To open the Fixed-Point Tool, right click on the Function Fitting Neural Network subsystem and select `Fixed-Point Tool`

. Alternatively, use the command-line interface of the Fixed-Point Tool. Fixed Point Tool and the command-line interface provide workflow steps for model preparation for fixed point conversion range and overflow instrumentation of objects via simulation and range analysis, homogeneous wordlength exploration for fixed point data typing and additional overflow diagnostics.

converter = DataTypeWorkflow.Converter(system_under_design);

Simulate the model with instrumentation to collect ranges. This is achieved by clicking the **Collect Ranges** button in the tool or the following commands.

converter.applySettingsFromShortcut('Range collection using double override'); % Save simulation run name generated as collect_ranges. This run name is used in % later steps to propose fixed point data types. collect_ranges = converter.CurrentRunName; sim_out = converter.simulateSystem();

Plot the regression accuracy before the conversion.

```
plotRegression(sim_out, baseline_output, system_under_design, 'Regression before conversion');
```

Range information obtained from simulation can be used by the Fixed-Point Tool to propose fixed-point data types for blocks in the system under design. In this example, to ensure that the tools propose signed data types for all blocks in the subsystem, disable the `ProposeSignedness`

option in the `ProposalSettings`

object.

ps = DataTypeWorkflow.ProposalSettings; ps.ProposeSignedness = false; converter.proposeDataTypes(collect_ranges, ps);

By default, the Fixed-Point Tool applies all of the proposed data types. Use the `applyDataTypes`

method to apply the data types. If you want to only apply a subset of the proposals, in the Fixed-Point Tool use the **Accept** check box to specify the proposals that you want to apply.

converter.applyDataTypes(collect_ranges);

Proposed types should handle all possible inputs correctly. Set the model to simulate using the newly applied types, simulate the model, and observe that the neural network regression accuracy is retained post fixed-point conversion.

```
converter.applySettingsFromShortcut('Range collection with specified data types');
sim_out = converter.simulateSystem();
```

Plot the regression accuracy of the fixed-point model.

```
plotRegression(sim_out, baseline_output, system_under_design, 'Regression after conversion');
```

The Tanh Activation function in Layer 1 can be replaced with either a lookup table or a CORDIC implementation for more efficient fixed-point code generation. In this example, we will be using the Lookup Table Optimizer to get a lookup table as a replacement for `tanh`

. We will be using `EvenPow2Spacing`

for faster execution speed. For more information, see https://www.mathworks.com/help/fixedpoint/ref/functionapproximation.options-class.html.

block_path = [system_under_design '/Layer 1/tansig']; p = FunctionApproximation.Problem(block_path); p.Options.WordLengths = 16; p.Options.BreakpointSpecification = 'EvenPow2Spacing'; solution = p.solve; solution.replaceWithApproximate;

| ID | Memory (bits) | Feasible | Table Size | Breakpoints WLs | TableData WL | BreakpointSpecification | Error(Max,Current) | | 0 | 64 | 0 | 2 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 1.000000e+00 | | 1 | 96 | 0 | 4 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 9.086914e-01 | | 2 | 160 | 0 | 8 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 8.392944e-01 | | 3 | 288 | 0 | 16 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 7.229614e-01 | | 4 | 544 | 0 | 32 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 5.369873e-01 | | 5 | 1056 | 0 | 64 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 2.825317e-01 | | 6 | 2080 | 0 | 128 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 8.178711e-02 | | 7 | 4128 | 0 | 256 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 2.331543e-02 | | 8 | 8224 | 1 | 512 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 5.981445e-03 | Best Solution | ID | Memory (bits) | Feasible | Table Size | Breakpoints WLs | TableData WL | BreakpointSpecification | Error(Max,Current) | | 8 | 8224 | 1 | 512 | 16 | 16 | EvenPow2Spacing | 7.812500e-03, 5.981445e-03 |

Verify model accuracy after function approximation replacement

converter.applySettingsFromShortcut(converter.ShortcutsForSelectedSystem{2}); sim_out = converter.simulateSystem;

Plot regression accuracy.

```
plotRegression(sim_out, baseline_output, system_under_design, 'Regression after function replacement');
```

To generate C code, right-click on the Function Fitting Neural Network subsystem, select **C/C++ Code > Build Subsystem**, then click the **Build** button when prompted for tunable parameters. You can also generate code by using the following command: `rtwbuild('fxpdemo_neuralnet_regression_toconvert/Function Fitting Neural Network')`

close all; clear h1 h2 h3 clear converter collect_ranges ps clear p solution block_path clear yarrOut y_pred actual clear sim_out nn_out yarr_out close_system(model, 0); close_system(sys_name, 0); clear system_under_design model sys_name clear x t net engineInputs engineTargets clear fid cd(current_dir); status = rmdir(fxpnn_temp_dir, 's'); %#ok clear fxpnn_demo_dir fxpnn_temp_dir current_dir status

Create a function to plot regression data

function plotRegression(sim_out, baseline_path, neural_network_output_path, plotTitle) nn_out = find(sim_out.logsout, 'BlockPath', neural_network_output_path); yarr_out = find(sim_out.logsout, 'BlockPath', baseline_path); ypred = nn_out{1}.Values.Data; actual = yarr_out{1}.Values.Data; figure; plotregression(double(ypred), actual, plotTitle); end