During the build process, the code generator uses:
Preset match criteria to identify functions and operators for which application-specific implementations replace default implementations.
Preset replacement function signatures.
It is possible that preset match criteria and preset replacement function signatures do not completely meet your function and operator replacement needs. For example:
You want to replace an operator with a particular fixed-point implementation function only when fraction lengths are within a particular range.
When a match occurs, you want to modify your replacement function signature based on compile-time information, such as passing fraction-length values into the function.
To add extra logic into the code replacement match and replacement process, create custom code replacement table entries. With custom entries, you can specify additional match criteria and modify the replacement function signature to meet application needs.
To create a custom code replacement entry:
Create a custom code replacement entry class, derived
from RTW.TflCFunctionEntryML (for function
replacement) or RTW.TflCOperationEntryML
(for operator replacement).
In your derived class, implement a do_match
method
with a fixed preset signature as a MATLAB® function. In your do_match
method,
provide either or both of the following customizations that instantiate
the class:
Add match criteria that the base class does not provide. The base class provides a match based on:
Argument number
Argument name
Signedness
Word size
Slope (if not specified with wildcards)
Bias (if not specified with wildcards)
Math modes, such as saturation and rounding
Operator or function key
Modify the implementation signature by adding additional arguments or setting constant input argument values. You can inject a constant value, such as an input scaling value, as an additional argument to the replacement function.
Create code replacement entries that instantiate the custom entry class.
Register a library containing the code replacement table that includes your entries.
During code generation, the code replacement match process tries
to match function or operator call sites with the base class of your
derived entry class. If the process finds a match, the software calls
your do_match
method to execute your additional
match logic (if any) and your replacement
function customizations (if any).
This example shows how to use custom code replacement table entries to refine the match and replacement logic for functions. The example shows how to:
Modify a sine function replacement only if the integer size on the current target platform is 32 bits.
Change the replacement such that the implementation function passes in a degrees-versus-radians flag as an input argument.
To exercise the table entries that you create in this example, create an ERT-based model with a sine function block. For example:
In the Inport block parameters, set the signal Data type to
double
. If the value selected for Configuration Parameters > Hardware Implementation > Device type supports an integer size other than 32, do one of the following:
Select a temporary target platform with a 32-bit integer size.
Modify the code to match the integer size of your target platform.
Create a class, for example TflCustomFunctionEntry
, that is
derived from the base class RTW.TflCFunctionEntryML
. The derived
class defines a do_match
method with the signature:
function ent = do_match(hThis, ... hCSO, ... targetBitPerChar, ... targetBitPerShort, ... targetBitPerInt, ... targetBitPerLong, ... targetBitPerLongLong)
In the do_match
signature:
ent
is the return handle, which is returned either as empty
(indicating that the match failed) or as a TflCFunctionEntry
handle.
hThis
is a handle to the class instance.
hCSO
is a handle to an object that the code generator creates
for querying the library for a replacement.
Remaining arguments are the number of bits for various data types of the current target.
The do_match
method:
Adds required additional match criteria that the base class does not provide.
Makes required modifications to the implementation signature.
In this case, the do_match
method must match only
targetBitPerInt
, representing the number of bits in the C
int
data type for the current target, to the value 32. If the code
generator finds a match, the method sets the return handle and creates and adds an input
argument. The input argument represents whether units are expressed as degrees or
radians, to the replacement function signature.
Alternatively, create and add the additional implementation function argument for passing a units flag in each code replacement table definition file that instantiates this class. In that case, this class definition code does not create the argument. That code sets only the argument value. For an example of creating and adding additional implementation function arguments in a table definition file, see Customize Code Match and Replacement for Scalar Operations.
classdef TflCustomFunctionEntry < RTW.TflCFunctionEntryML methods function ent = do_match(hThis, ... hCSO, ... %#ok targetBitPerChar, ... %#ok targetBitPerShort, ... %#ok targetBitPerInt, ... %#ok targetBitPerLong, ... %#ok targetBitPerLongLong) %#ok % DO_MATCH - Create a custom match function. The base class % checks the types of the arguments prior to calling this % method. This will check additional data and perhaps modify % the implementation function. ent = []; % default the return to empty, indicating the match failed. % Match sine function only if the target int size is 32 bits if targetBitPerInt == 32 % Need to modify the default implementation, starting from a copy % of the standard TflCFunctionEntry. ent = RTW.TflCFunctionEntry(hThis); % If the target int size is 32 bits, the implementation function % takes an additional input flag argument indicating degress vs. % radians. The additional argument can be created and added either % in the CRL table definition file that instantiates this class, or % here in the class definition, as follows: createAndAddImplementationArg(ent, 'RTW.TflArgNumericConstant', ... 'Name', 'u2', ... 'IsSigned', true, ... 'WordLength', 32, ... 'FractionLength', 0, ... 'Value', 1); end end end end
Exit the class folder and return to the previous working folder.
Create and save the following code replacement table definition file,
crl_table_custom_sinfcn_double.m
. This file defines a code
replacement table that contains a function table entry for sine with
double
input and output. This entry instantiates the derived class
from the previous step, TflCustomFunctionEntry
.
function hTable = crl_table_custom_sinfcn_double hTable = RTW.TflTable; %% Add TflCustomFunctionEntry fcn_entry = TflCustomFunctionEntry; setTflCFunctionEntryParameters(fcn_entry, ... 'Key', 'sin', ... 'Priority', 30, ... 'ImplementationName', 'mySin', ... 'ImplementationHeaderFile', 'mySin.h', ... 'ImplementationSourceFile', 'mySin.c'); createAndAddConceptualArg(fcn_entry, 'RTW.TflArgNumeric', ... 'Name', 'y1', ... 'IOType', 'RTW_IO_OUTPUT', ... 'DataTypeMode', 'double'); createAndAddConceptualArg(fcn_entry, 'RTW.TflArgNumeric', ... 'Name', 'u1', ... 'IOType', 'RTW_IO_INPUT', ... 'DataTypeMode', 'double'); % TflCustomFunctionEntry class do_match method will create and add % an implementation function argument during code generation if % the supported integer size on the current target is 32 bits. copyConceptualArgsToImplementation(fcn_entry); addEntry(hTable, fcn_entry);
Check the validity of the code replacement table entry.
At the command prompt, invoke the table definition file.
tbl = crl_table_custom_sinfcn_double
In the Code Replacement Viewer, view the table definition file.
crviewer(crl_table_custom_sinfcn_double)
This example shows how to create custom code replacement entries that add logic to the code match and replacement process for a nonscalar operation. Custom entries specify additional match criteria or modify the replacement function signature to meet application needs.
This example restricts the match criteria for an element-wise
multiplication replacement to entries with a specific dimension range.
When a match occurs, the custom do_match
method
modifies the replacement signature to pass the number of elements
into the function.
Files for developing and testing this code replacement library
example are available in matlab/help/toolbox/ecoder/examples/code_replacement/custom_elemmult
:
do_match
method — @MyElemMultEntry/MyElemMultEntry.m
Replacement function source and header files — src/myMulImplLib.c
and src/myMulImplLib.h
Model — myElemMul.slx
Code replacement table definition — myElemMultCrlTable.m
Registration file — rtwTargetInfo.m
To create custom code replacement entries that add logic to the code replacement match and replacement process:
Create a class, for example MyElemMultEntry
,
which is derived from the base class RTW.TflCOperationEntryML
.
The derived class defines a do_match
method with
the following signature:
function ent = do_match(hThis, ... hCSO, ... targetBitPerChar, ... targetBitPerShort, ... targetBitPerInt, ... targetBitPerLong, ... targetBitPerLongLong)
In the do_match
signature:
ent
is the return handle, which
is returned as empty (indicating that the match failed) or as a TflCOperationEntry
handle.
hThis
is the handle to the derived
instance.
hCSO
is a handle to an object that
the code generator creates for querying the library for a replacement.
Remaining arguments are the number of bits for various data types of the current target.
The do_match
method:
Adds match criteria that the base class does not provide.
Makes changes to the implementation signature.
The do_match
method relies on the base
class for checking data types and dimension ranges. If the code generator
finds a match, do_match
:
Sets the return handle.
Uses the conceptual arguments to compute the number of elements in the array. In the replacement entry returned, sets the value of the constant implementation argument as the number of elements of the array.
Updates the code replacement entry such that it matches CSOs that have the same argument dimensions.
classdef MyElemMultEntry < RTW.TflCOperationEntryML methods function obj = MyElemMultEntry(varargin) mlock; obj@RTW.TflCOperationEntryML(varargin{:}); end function ent = do_match(hThis, ... hCSO, ... %#ok targetBitPerChar, ... %#ok targetBitPerShort, ... %#ok targetBitPerInt, ... %#ok targetBitPerLong, ... %#ok targetBitPerLongLong ) %#ok % Fourth implementation arg represents number of elements for producing matches. assert(strcmp(hThis.Implementation.Arguments(4).Name,'numElements')); ent = RTW.TflCOperationEntry(hThis); % Calculate number of elements and set value of injected constant. ent.Implementation.Arguments(4).Value = prod(hCSO.ConceptualArgs(1).DimRange(1,:)); % Since implementation has been modified for specific DimRange, update % returned entry to match similar CSOs only. for idx =1:3 ent.ConceptualArgs(idx).DimRange = hCSO.ConceptualArgs(idx).DimRange; end end end end
Create and save the following code replacement table
definition file, myElemMultCrlTable.m
. This file
defines a code replacement table that contains an operator entry generator
for element-wise multiplication. The table entry:
Instantiates the derived class myElemMultEntry
from
the previous step.
Sets operator entry parameters with the call to the setTflCOperationEntryParameters
function.
Creates conceptual arguments y1
, u1
,
and u2
. The argument class RTW.TflArgMatrix
specifies
matrix arguments to match. The three arguments are set up to match
2-dimensional matrices with at least two elements in each dimension.
Calls the getTflArgFromString
function
to create a return value and four implementation arguments. Arguments u1
and u2
are
the operands, y1
is the product, and the fourth
argument is the number of elements.
Alternatively, the do_match
method of the
derived class myElemMultEntry
can create and
add the implementation arguments. When the number of additional implementation
arguments required can vary based on compile-time information, use
the alternative approach.
Calls addEntry
to
add the entry to a code replacement table.
function hLib = myElemMultCrlTable libPath = fullfile(fileparts(which(mfilename)),'src'); hLib = RTW.TflTable; %---------- entry: RTW_OP_ELEM_MUL ----------- hEnt = MyElemMultEntry; hEnt.setTflCOperationEntryParameters( ... 'Key', 'RTW_OP_ELEM_MUL', ... 'Priority', 100, ... 'SaturationMode', 'RTW_WRAP_ON_OVERFLOW', ... 'ImplementationName', 'myElemMul_s32', ... 'ImplementationSourceFile', 'myMulImplLib.c', ... 'ImplementationSourcePath', libPath, ... 'ImplementationHeaderFile', 'myMulImplLib.h', ... 'ImplementationHeaderPath', libPath, ... 'SideEffects', true, ... 'GenCallback','RTW.copyFileToBuildDir'); % Conceptual Args arg = RTW.TflArgMatrix('y1', 'RTW_IO_OUTPUT', 'int32'); arg.DimRange = [2 2; Inf Inf]; hEnt.addConceptualArg(arg); arg = RTW.TflArgMatrix('u1', 'RTW_IO_INPUT', 'int32'); arg.DimRange = [2 2; Inf Inf]; hEnt.addConceptualArg(arg); arg = RTW.TflArgMatrix('u2', 'RTW_IO_INPUT', 'int32'); arg.DimRange = [2 2; Inf Inf]; hEnt.addConceptualArg(arg); % Implementation Args arg = hEnt.getTflArgFromString('unused','void'); arg.IOType = 'RTW_IO_OUTPUT'; hEnt.Implementation.setReturn(arg); arg = hEnt.getTflArgFromString('u1','int32*'); hEnt.Implementation.addArgument(arg); arg = hEnt.getTflArgFromString('u2','int32*'); hEnt.Implementation.addArgument(arg); arg = hEnt.getTflArgFromString('y1','int32*'); arg.IOType = 'RTW_IO_OUTPUT'; hEnt.Implementation.addArgument(arg); arg = hEnt.getTflArgFromString('numElements','uint32',0); hEnt.Implementation.addArgument(arg); hLib.addEntry( hEnt );
Check the validity of the code replacement table entry.
At the command prompt, invoke the table definition file.
tbl = myElemMultCrlTable
In the Code Replacement Viewer, view the table definition file.
crviewer(myElemMultCrlTable)
This example shows how to create custom code replacement entries that add logic to the code match and replacement process for a scalar operation. Custom entries specify additional match criteria or modify the replacement function signature to meet application needs.
For example:
When fraction lengths are within a specific range, replace an operator with a fixed-point implementation function.
When a match occurs, modify the replacement function signature based on compile-time information, such as passing fraction-length values into the function.
This example modifies a fixed-point addition replacement such that the implementation function passes in the fraction lengths of the input and output data types as arguments.
To create custom code replacement entries that add logic to the code replacement match and replacement process:
Create a class, for example TflCustomOperationEntry
,
that is derived from the base class RTW.TflCOperationEntryML
.
The derived class defines a do_match
method with
the following signature:
function ent = do_match(hThis, ... hCSO, ... targetBitPerChar, ... targetBitPerShort, ... targetBitPerInt, ... targetBitPerLong, ... targetBitPerLongLong)
In the do_match
signature:
ent
is the return handle, which
is returned as empty (indicating that the match failed) or as a TflCOperationEntry
handle.
hThis
is the handle to the class
instance.
hCSO
is a handle to an object that
the code generator creates for querying the library for a replacement.
Remaining arguments are the number of bits for various data types of the current target.
The do_match
method adds match criteria
that the base class does not provide. The method makes modifications
to the implementation signature. In this case, the do_match
method
relies on the base class for checking word size and signedness. do_match
must
match only the number of conceptual arguments to the value 3 (two
inputs and one output) and the bias for each argument to value 0.
If the code generator finds a match, do_match
:
Sets the return handle.
Removes slope and bias wild cards from the conceptual arguments (the match is for specific slope and bias values).
Writes fraction-length values for the inputs and output into replacement function arguments 3, 4, and 5.
You can create and add three additional implementation function arguments for passing fraction lengths in the class definition or in each code replacement entry definition that instantiates this class. This example creates the arguments, adds them to a code replacement table definition file, and sets them to specific values in the class definition code.
classdef TflCustomOperationEntry < RTW.TflCOperationEntryML methods function ent = do_match(hThis, ... hCSO, ... %#ok targetBitPerChar, ... %#ok targetBitPerShort, ... %#ok targetBitPerInt, ... %#ok targetBitPerLong, ... %#ok targetBitPerLongLong) %#ok % DO_MATCH - Create a custom match function. The base class % checks the types of the arguments prior to calling this % method. This class will check additional data and can % modify the implementation function. % The base class checks word size and signedness. Slopes and biases % have been wildcarded, so the only additional checking to do is % to check that the biases are zero and that there are only three % conceptual arguments (one output, two inputs) ent = []; % default the return to empty, indicating the match failed if length(hCSO.ConceptualArgs) == 3 && ... hCSO.ConceptualArgs(1).Type.Bias == 0 && ... hCSO.ConceptualArgs(2).Type.Bias == 0 && ... hCSO.ConceptualArgs(3).Type.Bias == 0 % Modify the default implementation. Since this is a % generator entry, a concrete entry is created using this entry % as a template. The type of entry being created is a standard % TflCOperationEntry. Using the standard operation entry % provides required information, and you do not need % a custom match function. ent = RTW.TflCOperationEntry(hThis); % Since this entry is modifying the implementation for specific % fraction-length values (arguments 3, 4, and 5), the conceptual argument % wild cards must be removed (the wildcards were inherited from the % generator when it was used as a template for the concrete entry). % This concrete entry is now for a specific slope and bias. % hCSO holds the slope and bias values (created by the code generator). for idx=1:3 ent.ConceptualArgs(idx).CheckSlope = true; ent.ConceptualArgs(idx).CheckBias = true; % Set the specific Slope and Biases ent.ConceptualArgs(idx).Type.Slope = hCSO.ConceptualArgs(idx).Type.Slope; ent.ConceptualArgs(idx).Type.Bias = 0; end % Set the fraction-length values in the implementation function. ent.Implementation.Arguments(3).Value = ... -1.0*hCSO.ConceptualArgs(2).Type.FixedExponent; ent.Implementation.Arguments(4).Value = ... -1.0*hCSO.ConceptualArgs(3).Type.FixedExponent; ent.Implementation.Arguments(5).Value = ... -1.0*hCSO.ConceptualArgs(1).Type.FixedExponent; end end end end
Exit the class folder and return to the previous working folder.
Create and save the following code replacement table definition file,
crl_table_custom_add_ufix32.m
. This file defines a code
replacement table that contains a single operator entry, an entry generator for
unsigned 32-bit fixed-point addition operations, with arbitrary fraction-length
values on the inputs and the output. The table entry:
Instantiates the derived class TflCustomOperationEntry
from the previous step. If you want to replace word sizes and signedness
attributes, you can use the same derived class, but not the same entry,
because you cannot use a wild card with the WordLength
and IsSigned
arguments. For example, to support
uint8
, int8
,
uint16
, int16
, and
int32
, add five other distinct entries. To use
different implementation functions for saturation and rounding modes other
than overflow and round to floor, add entries for those match
permutations.
Sets operator entry parameters with the call to the setTflCOperationEntryParameters
function.
Calls the createAndAddConceptualArg
function to create conceptual arguments y1
,
u1
, and u2
.
Calls createAndSetCImplementationReturn
and createAndAddImplementationArg
to define the signature for
the replacement function. Three of the calls to
createAndAddImplementationArg
create implementation
arguments to hold the fraction-length values for the inputs and output.
Alternatively, the entry can omit those argument definitions. Instead, the
do_match
method of the derived class
TflCustomOperationEntry
can create and add the
three implementation arguments. When the number of additional implementation
arguments required can vary based on compile-time information, use the
alternative approach.
Calls addEntry
to add the entry
to a code replacement table.
function hTable = crl_table_custom_add_ufix32 hTable = RTW.TflTable; % Add TflCustomOperationEntry op_entry = TflCustomOperationEntry; setTflCOperationEntryParameters(op_entry, ... 'Key', 'RTW_OP_ADD', ... 'Priority', 30, ... 'SaturationMode', 'RTW_SATURATE_ON_OVERFLOW', ... 'RoundingModes', {'RTW_ROUND_FLOOR'}, ... 'ImplementationName', 'myFixptAdd', ... 'ImplementationHeaderFile', 'myFixptAdd.h', ... 'ImplementationSourceFile', 'myFixptAdd.c'); createAndAddConceptualArg(op_entry, 'RTW.TflArgNumeric', ... 'Name', 'y1', ... 'IOType', 'RTW_IO_OUTPUT', ... 'CheckSlope', false, ... 'CheckBias', false, ... 'DataType', 'Fixed', ... 'Scaling', 'BinaryPoint', ... 'IsSigned', false, ... 'WordLength', 32); createAndAddConceptualArg(op_entry, 'RTW.TflArgNumeric', ... 'Name', 'u1', ... 'IOType', 'RTW_IO_INPUT', ... 'CheckSlope', false, ... 'CheckBias', false, ... 'DataType', 'Fixed', ... 'Scaling', 'BinaryPoint', ... 'IsSigned', false, ... 'WordLength', 32); createAndAddConceptualArg(op_entry, 'RTW.TflArgNumeric', ... 'Name', 'u2', ... 'IOType', 'RTW_IO_INPUT', ... 'CheckSlope', false, ... 'CheckBias', false, ... 'DataType', 'Fixed', ... 'Scaling', 'BinaryPoint', ... 'IsSigned', false, ... 'WordLength', 32); % Specify replacement function signature createAndSetCImplementationReturn(op_entry, 'RTW.TflArgNumeric', ... 'Name', 'y1', ... 'IOType', 'RTW_IO_OUTPUT', ... 'IsSigned', false, ... 'WordLength', 32, ... 'FractionLength', 0); createAndAddImplementationArg(op_entry, 'RTW.TflArgNumeric', ... 'Name', 'u1', ... 'IOType', 'RTW_IO_INPUT', ... 'IsSigned', false, ... 'WordLength', 32, ... 'FractionLength', 0); createAndAddImplementationArg(op_entry, 'RTW.TflArgNumeric', ... 'Name', 'u2', ... 'IOType', 'RTW_IO_INPUT', ... 'IsSigned', false, ... 'WordLength', 32, ... 'FractionLength', 0); % Add 3 fraction-length args. Actual values are set during code generation. createAndAddImplementationArg(op_entry, 'RTW.TflArgNumericConstant', ... 'Name', 'fl_in1', ... 'IOType', 'RTW_IO_INPUT', ... 'IsSigned', false, ... 'WordLength', 32, ... 'FractionLength', 0, ... 'Value', 0); createAndAddImplementationArg(op_entry, 'RTW.TflArgNumericConstant', ... 'Name', 'fl_in2', ... 'IOType', 'RTW_IO_INPUT', ... 'IsSigned', false, ... 'WordLength', 32, ... 'FractionLength', 0, ... 'Value', 0); createAndAddImplementationArg(op_entry, 'RTW.TflArgNumericConstant', ... 'Name', 'fl_out', ... 'IOType', 'RTW_IO_INPUT', ... 'IsSigned', false, ... 'WordLength', 32, ... 'FractionLength', 0, ... 'Value', 0); addEntry(hTable, op_entry);
Check the validity of the operator entry.
At the command prompt, invoke the table definition file.
tbl = crl_table_custom_add_ufix32
In the Code Replacement Viewer, view the table definition file.
crviewer(crl_table_custom_add_ufix32)