MATLAB Examples

Convert Neural Network Algorithms to Fixed-Point using fxpopt and Generate HDL Code

This example shows how to convert a neural network regression model in Simulink to fixed point using the fxpopt function and Lookup Table Optimizer.

Contents

Overview

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 fxpopt function optimizes data types in a model based on specified system behavioral constraints. For additional information, refer to the documentation link https://www.mathworks.com/help/fixedpoint/ref/fxpopt.html 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.

Data and Neural Network Training

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.

% 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.
load engine_dataset;
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');

Model Preparation for Fixed-Point Conversion

Once the network is trained, use the gensim function from the Neural Network Toolbox™ to generate a Simulink model.

[sysName, netName] = gensim(net, 'Name', 'mTrainedNN'); %#ok

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);

% Set up model for HDL code generation
hdlsetup(model);

Simulate the model to observe model performance when using double-precision floating-point data types.

sim_out = sim(model, 'SaveFormat', 'Dataset');

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

Define System Behavioral Constraints for Fixed Point Conversion

opts = fxpOptimizationOptions();
opts.addTolerance(system_under_design, 1, 'RelTol', 0.05);
opts.addTolerance(system_under_design, 1, 'AbsTol', 50)
opts.AllowableWordLengths = 8:32;

Optimize Data Types

Use the fxpopt function to optimize the data types in the system under design and explore the solution. The software analyzes the range of objects in system_under_design and wordlength and tolerance constraints specified in opts to apply heterogeneous data types to the model while minimizing total bit width.

solution  = fxpopt(model, system_under_design, opts);
best_solution = solution.explore; %#ok
	+ Preprocessing
	+ Modeling the optimization problem
		- Constructing decision variables
	+ Running the optimization solver
		- Evaluating new solution: cost 480, does not meet the tolerances.
		- Evaluating new solution: cost 540, does not meet the tolerances.
		- Evaluating new solution: cost 600, does not meet the tolerances.
		- Evaluating new solution: cost 660, does not meet the tolerances.
		- Evaluating new solution: cost 720, does not meet the tolerances.
		- Evaluating new solution: cost 780, does not meet the tolerances.
		- Evaluating new solution: cost 840, does not meet the tolerances.
		- Evaluating new solution: cost 900, meets the tolerances.
		- Updated best found solution, cost: 900
		- Evaluating new solution: cost 896, meets the tolerances.
		- Updated best found solution, cost: 896
		- Evaluating new solution: cost 886, meets the tolerances.
		- Updated best found solution, cost: 886
		- Evaluating new solution: cost 885, meets the tolerances.
		- Updated best found solution, cost: 885
		- Evaluating new solution: cost 884, meets the tolerances.
		- Updated best found solution, cost: 884
		- Evaluating new solution: cost 883, meets the tolerances.
		- Updated best found solution, cost: 883
		- Evaluating new solution: cost 882, meets the tolerances.
		- Updated best found solution, cost: 882
		- Evaluating new solution: cost 881, meets the tolerances.
		- Updated best found solution, cost: 881
		- Evaluating new solution: cost 880, meets the tolerances.
		- Updated best found solution, cost: 880
		- Evaluating new solution: cost 879, meets the tolerances.
		- Updated best found solution, cost: 879
		- Evaluating new solution: cost 878, meets the tolerances.
		- Updated best found solution, cost: 878
		- Evaluating new solution: cost 868, meets the tolerances.
		- Updated best found solution, cost: 868
		- Evaluating new solution: cost 867, meets the tolerances.
		- Updated best found solution, cost: 867
		- Evaluating new solution: cost 866, meets the tolerances.
		- Updated best found solution, cost: 866
		- Evaluating new solution: cost 865, meets the tolerances.
		- Updated best found solution, cost: 865
		- Evaluating new solution: cost 864, meets the tolerances.
		- Updated best found solution, cost: 864
		- Evaluating new solution: cost 863, meets the tolerances.
		- Updated best found solution, cost: 863
		- Evaluating new solution: cost 862, meets the tolerances.
		- Updated best found solution, cost: 862
		- Evaluating new solution: cost 857, meets the tolerances.
		- Updated best found solution, cost: 857
		- Evaluating new solution: cost 856, meets the tolerances.
		- Updated best found solution, cost: 856
		- Evaluating new solution: cost 855, meets the tolerances.
		- Updated best found solution, cost: 855
		- Evaluating new solution: cost 850, does not meet the tolerances.
		- Evaluating new solution: cost 854, meets the tolerances.
		- Updated best found solution, cost: 854
		- Evaluating new solution: cost 846, meets the tolerances.
		- Updated best found solution, cost: 846
		- Evaluating new solution: cost 845, meets the tolerances.
		- Updated best found solution, cost: 845
		- Evaluating new solution: cost 841, does not meet the tolerances.
		- Evaluating new solution: cost 835, meets the tolerances.
		- Updated best found solution, cost: 835
		- Evaluating new solution: cost 834, meets the tolerances.
		- Updated best found solution, cost: 834
		- Evaluating new solution: cost 833, meets the tolerances.
		- Updated best found solution, cost: 833
		- Evaluating new solution: cost 832, meets the tolerances.
		- Updated best found solution, cost: 832
		- Evaluating new solution: cost 831, meets the tolerances.
		- Updated best found solution, cost: 831
		- Evaluating new solution: cost 830, meets the tolerances.
		- Updated best found solution, cost: 830
		- Evaluating new solution: cost 829, meets the tolerances.
		- Updated best found solution, cost: 829
		- Evaluating new solution: cost 828, meets the tolerances.
		- Updated best found solution, cost: 828
		- Evaluating new solution: cost 827, meets the tolerances.
		- Updated best found solution, cost: 827
		- Evaluating new solution: cost 817, meets the tolerances.
		- Updated best found solution, cost: 817
		- Evaluating new solution: cost 816, meets the tolerances.
		- Updated best found solution, cost: 816
		- Evaluating new solution: cost 815, meets the tolerances.
		- Updated best found solution, cost: 815
		- Evaluating new solution: cost 814, meets the tolerances.
		- Updated best found solution, cost: 814
		- Evaluating new solution: cost 813, meets the tolerances.
		- Updated best found solution, cost: 813
		- Evaluating new solution: cost 812, meets the tolerances.
		- Updated best found solution, cost: 812
		- Evaluating new solution: cost 811, meets the tolerances.
		- Updated best found solution, cost: 811
		- Evaluating new solution: cost 806, meets the tolerances.
		- Updated best found solution, cost: 806
		- Evaluating new solution: cost 805, meets the tolerances.
		- Updated best found solution, cost: 805
		- Evaluating new solution: cost 804, meets the tolerances.
		- Updated best found solution, cost: 804
		- Evaluating new solution: cost 799, does not meet the tolerances.
		- Evaluating new solution: cost 803, does not meet the tolerances.
		- Evaluating new solution: cost 796, meets the tolerances.
		- Updated best found solution, cost: 796
		- Evaluating new solution: cost 795, meets the tolerances.
		- Updated best found solution, cost: 795
		- Evaluating new solution: cost 791, meets the tolerances.
		- Updated best found solution, cost: 791
		- Evaluating new solution: cost 781, does not meet the tolerances.
	+ Optimization has finished.
		- Neighborhood search complete.
		- Maximum number of iterations completed.
	+ Fixed-point implementation that met the tolerances found.
		- Total cost: 791
		- Maximum absolute difference: 53.582715
		- Use the explore method of the result to explore the implementation.

Verify model accuracy after conversion by simulating the model

sim_out = sim(model, 'SaveFormat', 'Dataset');

Plot the regression accuracy of the fixed-point model.

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

Replace Activation Function With an Optimized Lookup Table

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 = 8:32;
p.Options.BreakpointSpecification = 'EvenPow2Spacing';
solution  = p.solve;
solution.replaceWithApproximate;
|  ID | Memory (bits) | Feasible | Table Size | Breakpoints WLs | TableData WL | BreakpointSpecification |             Error(Max,Current) | 
|   0 |            48 |        0 |          2 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 1.000000e+00 |
|   1 |            32 |        0 |          2 |               8 |            8 |         EvenPow2Spacing |     7.812500e-03, 1.000000e+00 |
|   2 |            66 |        0 |          4 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 9.091797e-01 |
|   3 |            48 |        0 |          4 |               8 |            8 |         EvenPow2Spacing |     7.812500e-03, 9.091797e-01 |
|   4 |           102 |        0 |          8 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 8.398438e-01 |
|   5 |            80 |        0 |          8 |               8 |            8 |         EvenPow2Spacing |     7.812500e-03, 8.398438e-01 |
|   6 |           174 |        0 |         16 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 7.231445e-01 |
|   7 |           144 |        0 |         16 |               8 |            8 |         EvenPow2Spacing |     7.812500e-03, 7.231445e-01 |
|   8 |           318 |        0 |         32 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 5.371094e-01 |
|   9 |           272 |        0 |         32 |               8 |            8 |         EvenPow2Spacing |     7.812500e-03, 5.371094e-01 |
|  10 |           606 |        0 |         64 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 2.841797e-01 |
|  11 |           592 |        0 |         64 |               8 |            9 |         EvenPow2Spacing |     7.812500e-03, 2.841797e-01 |
|  12 |          1182 |        0 |        128 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 8.349609e-02 |
|  13 |          1168 |        0 |        128 |               8 |            9 |         EvenPow2Spacing |     7.812500e-03, 8.349609e-02 |
|  14 |          2334 |        0 |        256 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 2.587891e-02 |
|  15 |          2320 |        0 |        256 |               8 |            9 |         EvenPow2Spacing |     7.812500e-03, 2.587891e-02 |
|  16 |          4638 |        0 |        512 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 8.300781e-03 |
|  17 |          4626 |        0 |        512 |               9 |            9 |         EvenPow2Spacing |     7.812500e-03, 8.300781e-03 |
|  18 |          9246 |        1 |       1024 |              15 |            9 |         EvenPow2Spacing |     7.812500e-03, 4.394531e-03 |
|  19 |          9236 |        1 |       1024 |              10 |            9 |         EvenPow2Spacing |     7.812500e-03, 4.394531e-03 |
|  20 |            50 |        0 |          2 |              15 |           10 |         EvenPow2Spacing |     7.812500e-03, 1.000000e+00 |
|  21 |            70 |        0 |          4 |              15 |           10 |         EvenPow2Spacing |     7.812500e-03, 9.091797e-01 |
|  22 |           110 |        0 |          8 |              15 |           10 |         EvenPow2Spacing |     7.812500e-03, 8.398438e-01 |
|  23 |           190 |        0 |         16 |              15 |           10 |         EvenPow2Spacing |     7.812500e-03, 7.231445e-01 |
|  24 |           350 |        0 |         32 |              15 |           10 |         EvenPow2Spacing |     7.812500e-03, 5.371094e-01 |
|  25 |           670 |        0 |         64 |              15 |           10 |         EvenPow2Spacing |     7.812500e-03, 2.827148e-01 |
|  26 |           656 |        0 |         64 |               8 |           10 |         EvenPow2Spacing |     7.812500e-03, 2.827148e-01 |
|  27 |          1310 |        0 |        128 |              15 |           10 |         EvenPow2Spacing |     7.812500e-03, 8.203125e-02 |
|  28 |          1296 |        0 |        128 |               8 |           10 |         EvenPow2Spacing |     7.812500e-03, 8.203125e-02 |
|  29 |          2590 |        0 |        256 |              15 |           10 |         EvenPow2Spacing |     7.812500e-03, 2.441406e-02 |
|  30 |          2576 |        0 |        256 |               8 |           10 |         EvenPow2Spacing |     7.812500e-03, 2.441406e-02 |
|  31 |          5150 |        1 |        512 |              15 |           10 |         EvenPow2Spacing |     7.812500e-03, 5.859375e-03 |
|  32 |          5138 |        1 |        512 |               9 |           10 |         EvenPow2Spacing |     7.812500e-03, 5.859375e-03 |

Best Solution
|  ID | Memory (bits) | Feasible | Table Size | Breakpoints WLs | TableData WL | BreakpointSpecification |             Error(Max,Current) |
|  32 |          5138 |        1 |        512 |               9 |           10 |         EvenPow2Spacing |     7.812500e-03, 5.859375e-03 |

Verify model accuracy after function replacement

sim_out = sim(model, 'SaveFormat', 'Dataset');
Warning: Wrap on overflow detected. This originated from
'fxpdemo_neuralnet_regression_toconvert/Function Fitting Neural Network/Layer
1/tansig/LUT'
Suggested Actions:
    •  - Suppress
 

Plot regression accuracy after function replacement

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

Generate HDL Code and Test Bench

Generating HDL code requires an HDL Coder™ license.

Choose the model for which to generate HDL code and a test bench.

systemname = 'fxpdemo_neuralnet_regression_toconvert/Function Fitting Neural Network';

Use a temporary directory for the generated files.

workingdir = tempname;

You can run the following command to check for HDL code generation compatibility.

checkhdl(systemname,'TargetDirectory',workingdir);

Run the following command to generate HDL code.

makehdl(systemname,'TargetDirectory',workingdir);

Run the following command to generate the test bench.

makehdltb(systemname,'TargetDirectory',workingdir);

Clean up

close all;
Simulink.sdi.close;
clear engineInputs engineTargets net x t
clear h1 h2 h3
clear sim_out logsout nn_out yarr_out ypred actual
clear solution opts p
close_system(model, 0);
close_system(sysName, 0);
clear system_under_design model block_path
clear netName sysName
clear best_solution baseline_output
cd(current_dir);
status = rmdir(fxpnn_temp_dir, 's'); %#ok
clear fxpnn_demo_dir fxpnn_temp_dir current_dir status

Helper functions

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