Replace Code Generated from Discrete FIR Filter Blocks
This example shows you how to replace code generated from a Discrete FIR Filter block with custom implementation code. To override the default code generation behavior of a block and use a custom implementation, use block replacement. When replacing code generated from blocks, you can:
Specify criteria that blocks must match for replacement, such as input and output types or block dialog parameter settings.
Use a different implementation function for each system function generated from a block, such as initialize, output, and terminate functions.
Use temporary variables between the different implementation functions for a block.
Include states in the generated code by defining custom DWork vectors.
Include preprocessing of block parameters in the generated code.
For more information about block replacement, supported blocks, and limitations, see Block Replacement for Code Optimization.
Explore The Model
For this example, replace the code generated from the model
BlockReplacementDiscreteFIR
.
The model contains one Discrete FIR Filter block. The block has the block dialog property settings shown in this table.
Property | Setting |
---|---|
Coefficient source | Dialog parameters |
Filter structure | Direct form |
Input processing | Columns as channels (frame based) |
Integer rounding mode | Floor |
Saturate on integer overflow | off |
The model has one input with Data type set to
single
and Port dimensions set to [16
1]
, and one output with inherited data type and dimensions.
Define The Implementation Code
For this example, replace the code for the Discrete FIR Filter block with
calls to the custom implementation functions MyDiscreteFIR_out
and
MyDiscreteFIR_init
. The generated model step function calls the
MyDiscreteFIR_out
function and the generated initialization function
calls the MyDiscreteFIR_init
function. The custom implementation
functions have these signatures:
void MyDiscreteFIR_init( CrlDWork *localDW, real32_T_CRL* coeffs, real32_T_CRL* initStates, uint16_T_CRL coeffsSize); void MyDiscreteFIR_out( const real32_T_CRL rtu_In1[16], real32_T_CRL rty_Out1[16], CrlDWork *localDW);
The initialization function uses the values of the inputs coeffs
,
initStates
, and coeffsSize
to initialize the
corresponding fields of the DWork structure localDW
. The output function,
which uses the values of the fields, takes the DWork structure as one argument instead of
taking the three values as separate arguments. The values of these fields are calculated
from the values of the block properties Coefficients
and
InitialStates
.
The data types used by the implementation functions, including the DWork structure, are
defined in the custom header file MyDiscreteFIR.h
. This code segment
shows the lines of the header file that define these data
types:
typedef float real32_T_CRL; typedef unsigned short uint16_T_CRL; struct CrlDWork { real32_T_CRL DiscreteFir_Coefficients[2]; real32_T_CRL DiscreteFir_InitialStates; uint16_T_CRL DiscreteFir_CoeffsSize; };
Create a Code Replacement Table and Entry
Create a new function file with the name of your code replacement table, for example,
crl_table_block_discrete_fir.m
.Create a code replacement table.
Create a function with the name of your code replacement library table that does not have arguments and returns a table object. You can use this function to call your code replacement library table. Write the code in the following steps and sections in this function.
Create a table object by calling
RTW.TflTable
.function hTable = crl_table_block_discrete_fir hTable = RTW.TflTable;
Create a block replacement entry. Because this example replaces code generated from a block, create the entry in your table by calling the block replacement entry function
RTW.TflBlockEntry
.hEntry = RTW.TflBlockEntry;
Define the Conceptual Representation
The conceptual representation in a block replacement entry describes the block that you want to match for replacement.
Specify the block key. The
Key
properties identifies the type of block that the entry replaces code for. For this example, specify'DiscreteFir'
to indicate that the entry replaces code from Discrete FIR Filter blocks. You can optionally specify other properties of the entry.hEntry.Key = 'DiscreteFir'; hEntry.Priority = 1; hEntry.SideEffects = true; hEntry.AdditionalHeaderFiles = {'customtypedefinitions.h'};
Specify the block dialog property settings for the entry to match. You must specify certain parameter settings, depending on the block type, so that the code generator matches only blocks that support your implementation code. To specify a block dialog parameter setting:
Create a property specification by calling
RTW.BlockProperty
. Specify the name and value of the block dialog property that you want to match.Add the property specification to the block replacement entry by calling
addBlockProperty
.
For this example, specify that the entry matches blocks with the settings described in Explore The Model.
bp1 = RTW.BlockProperty('CoefSource','Dialog parameters'); addBlockProperty(hEntry,bp1); bp2 = RTW.BlockProperty('FilterStructure','Direct form'); addBlockProperty(hEntry,bp2); bp3 = RTW.BlockProperty('InputProcessing','Columns as channels (frame based)'); addBlockProperty(hEntry,bp3);
Specify block input and output data types as conceptual arguments. Create an argument handle by calling
getTflArgFromString
or an argument class constructor such asRTW.TflArgMatrix
. Add the argument to the entry by callingaddConceptualArg
. For this example, the entry matches blocks that have one matrix input and one matrix output of base typesingle
.hArgOut = RTW.TflArgMatrix('y1','RTW_IO_OUTPUT','single'); hArgOut.DimRange = [1 1; inf inf]; addConceptualArg(hEntry,hArgOut); hArgIn = RTW.TflArgMatrix('u1','RTW_IO_INPUT','single'); hArgIn.DimRange = [1 1; inf inf]; addConceptualArg(hEntry,hArgIn);
Specify block parameter arguments as conceptual arguments. Block parameter arguments describe the block dialog parameters whose values are used by the implementation functions, either directly (by referencing the parameter) or indirectly (by referencing a derived parameter that uses the block parameter argument). For more information, see Block Parameter Arguments.
To add a block parameter argument, create an argument handle and call
addBlockParamArg
. For this example, the replacement code uses the values of theInitialStates
parameter and theCoefficients
parameter. Specify the data types for these parameters as block parameter arguments.hParamArg1 = hTable.getTflArgFromString('InitialStates', 'single'); addBlockParamArg(hEntry,hParamArg1); hParamArg2 = RTW.TflArgMatrix('Coefficients', 'RTW_IO_INPUT', 'single'); hParamArg2.DimRange = [1 1; inf inf]; addBlockParamArg(hEntry,hParamArg2);
For more information about the parts of the conceptual representation, see Conceptual Information for Matching Blocks.
Define the Implementation Representation
The implementation representation in a block replacement entry describes the custom code that replaces the default code generated from the block.
Create an
RTW.CImplementation
object to represent one of your implementation functions.initImplementation = RTW.CImplementation;
Specify the name of the implementation function and, if required, build information.
initImplementation.Name = 'MyDiscreteFIR_init'; initImplementation.HeaderFile = 'MyDiscreteFIR.h'; initImplementation.SourceFile = 'MyDiscreteFIR_init.c';
If your implementation uses derived data that is calculated from block inputs, outputs, or dialog parameter values, define the data as a derived parameter and add the derived parameter to the block replacement entry. For this example, the implementation functions use these derived parameters:
DiscreteFir_Coefficients
— The list of coefficients specified in the block parameter dialog boxDiscreteFir_CoeffsSize
— The number of coefficientsDiscreteFir_InitialStates
— The initial states specified in the block parameter dialog box
For each derived parameter, add the expression that calculates the parameter value to the
DerivedBlockParams
list in the block replacement entry. For more information about derived parameters, see Derived Parameters for Implementation Functions and Use Derived Data in Replacement Code Implementations.hEntry.DerivedBlockParams{end+1} = 'DiscreteFir_Coefficients = <%Coefficients>'; hEntry.DerivedBlockParams{end+1} = 'DiscreteFir_CoeffsSize = length(<%Coefficients>)'; hEntry.DerivedBlockParams{end+1} = 'DiscreteFir_InitialStates = <%InitialStates>';
If your implementation uses a DWork argument, you must define the DWork argument and add it to the block entry before you can add it to the implementation specification. For this example, you define the DWork argument for the structure described in Define The Implementation Code.
Set up the fields of the structure type used by the DWork. For each field of the structure, create a structure that indicates the identifier and type of the field.
se1.Identifier = 'Coeffs'; se1.Type = 'single*'; se2.Identifier = 'InitStates'; se2.Type = 'single'; se3.Identifier = 'CoeffsSize'; se3.Type = 'uint16';
Set up the structure type used by the DWork. Specify the name of the type as the identifier and specify the structures that you created in the previous step as the fields of the structure.
ssTypeP.Identifier = 'InitStruct'; ssTypeP.Elements = [se1, se2, se3];
Create the argument handle for the DWork argument by calling the
createDWorkArg
method and specifying the structure type that you created in the previous step. Then add the argument to the entry by calling theaddDWorkArg
function.d1 = createDWorkArg(hEntry,'struct','Name','CrlDWork','StructData',ssTypeP); addDWorkArg(hEntry,d1);
Now you can use the DWork argument when you specify arguments for the implementation representation.
Add the arguments to the implementation representation. For this example, the replacement initialization function uses these arguments:
d2
— A pointer that uses the DWork argumentd1
, which you defined in the previous step.DiscreteFir_Coefficients
— A derived parameter argument that you create for the corresponding derived parameter defined earlier.DiscreteFir_InitialStates
— A derived parameter argument that you create for the corresponding derived parameter defined earlier.DiscreteFir_CoeffsSize
— A derived parameter argument that you create for the corresponding derived parameter defined earlier.A
void
output argument. Specify this as the return argument by callingsetReturn
.
For the arguments that you have not yet defined, create the argument handles by calling
getTflArgFromString
orcreateDWorkArg
. For derived parameters, specify the name and data type of the derived parameter. Add the arguments to the implementation object by callingaddArgument
.% DWork pointer argument d2 = createDWorkArg(hEntry,'Pointer','Name','CrlDWork','BaseType','struct','StructData',ssTypeP); initImplementation.addArgument(d2); % arguments for derived parameters arg = getTflArgFromString(hTable,'DiscreteFir_Coefficients', 'single*'); initImplementation.addArgument(arg); arg = getTflArgFromString(hTable,'DiscreteFir_InitialStates', 'single*'); initImplementation.addArgument(arg); arg = getTflArgFromString(hTable,'DiscreteFir_CoeffsSize', 'uint16'); initImplementation.addArgument(arg); % return argument arg = getTflArgFromString(hTable,'void','void'); arg.IOType = 'RTW_IO_OUTPUT'; initImplementation.setReturn(arg);
Add the implementation function to the block replacement entry. Use the
addImplementation
method and specify the system function that should use the implementation function.addImplementation(hEntry,'initialize',initImplementation);
Repeat these steps for the other implementation functions. For this example, add the
output
implementation function.%% Output implementation function outImplementation = RTW.CImplementation; % Implementation name and build information outImplementation.Name = 'MyDiscreteFIR_out'; outImplementation.HeaderFile = 'MyDiscreteFIR.h'; outImplementation.SourceFile = 'MyDiscreteFIR_out.c'; % Add input and output arguments argIn = getTflArgFromString(hTable,'u1','single*'); argOut = getTflArgFromString(hTable,'y1','single*'); argOut.IOType = 'RTW_IO_OUTPUT'; addArgument(outImplementation,argIn); addArgument(outImplementation,argOut); % Add DWork argument addArgument(outImplementation,d2); % Add return argument arg = getTflArgFromString(hTable,'void','void'); arg.IOType = 'RTW_IO_OUTPUT'; outImplementation.setReturn(arg); % Add implementation function to entry addImplementation(hEntry,'output', outImplementation);
Add the entry to the table by calling the function
addEntry
.%% Add the entry to the table addEntry(hTable,hEntry);
Save the file. The function file that you created is the code replacement table.
Validate and Register the Code Replacement Library
From the command line, validate the code replacement library table by calling it:
>> hTable = crl_table_block_discrete_fir
Register the code replacement library. Registration creates a code replacement library by defining the library name, code replacement tables, and other information. Create a registration file (a new function file) with these specifications:
function rtwTargetInfo(cm) cm.registerTargetInfo(@loc_register_crl); end function this = loc_register_crl this(1) = RTW.TflRegistry; %instantiates a registration entry this(1).Name = 'Block Replacement Library'; this(1).TableList = {'crl_table_block_discrete_fir.m'}; % table created in this example this(1).TargetHWDeviceType = {'*'}; this(1).Description = 'This registers an example library'; end
To use your code replacement library, refresh your current MATLAB® session by using the command:
>> sl_refresh_customizations
Verify the code replacement library. From the MATLAB command line, open the library by using the Code Replacement Viewer and verify that the table and entry are specified. For more information, see Verify Code Replacement Library. Configure your model to use the code replacement library, generate code, and verify that replacement occurs as expected. If unexpected behavior occurs, examine the hit and miss logs to troubleshoot the issues.
Generate Code by Using a Block Replacement Library
In this section, you generate code by using the code replacement library that you created in the first section of this example. The code generator replaces code from the Discrete FIR Filter block with calls to custom initialization and output functions in the generated code.
Example Model
Open the model BlockReplacementDiscreteFIR
for configuring the code replacement library.
model = 'BlockReplacementDiscreteFIR'; open_system(model); copyfile blockDFIRRtwTargetInfo.txt rtwTargetInfo.m
Run the sl_refresh_customizations
function to register the library.
sl_refresh_customizations;
Enable the Code Replacement Library
Open the Configuration Parameters dialog box.
On the Interface pane, set Code Replacement Library by clicking Select and adding
Block Replacement Library
to the Selected code replacement libraries - prioritized list pane. Alternatively, use the command-line API to enable the code replacement.
set_param(model, 'CodeReplacementLibrary', 'Block Replacement Library');
Generate code from the model.
evalc('slbuild(''BlockReplacementDiscreteFIR'')');
View the generated code. Here is a portion of BlockRepalcementDiscreteFIR.c
.
cfile = fullfile('BlockReplacementDiscreteFIR_ert_rtw','BlockReplacementDiscreteFIR.c'); coder.example.extractLines(cfile,'/* Model step function ','/* Model terminate function',1, 1);
/* Model step function */ void BlockReplacementDiscreteFIR_step(void) { /* DiscreteFir: '<Root>/Discrete FIR Filter' incorporates: * CrlBlock generated from: '<Root>/Discrete FIR Filter' * Inport: '<Root>/In1' * Outport: '<Root>/Out1' */ MyDiscreteFIR_out(&BlockReplacementDiscreteFIR_U.In1[0], &BlockReplacementDiscreteFIR_Y.Out1[0], &BlockReplacementDiscreteFIR_DW.CrlDWork); } /* Model initialize function */ void BlockReplacementDiscreteFIR_initialize(void) { /* Registration code */ /* initialize error status */ rtmSetErrorStatus(BlockReplacementDiscreteFIR_M, (NULL)); /* states (dwork) */ (void) memset((void *)&BlockReplacementDiscreteFIR_DW, 0, sizeof(DW_BlockReplacementDiscreteFI_T)); /* external inputs */ (void)memset(&BlockReplacementDiscreteFIR_U, 0, sizeof (ExtU_BlockReplacementDiscrete_T)); /* external outputs */ (void)memset(&BlockReplacementDiscreteFIR_Y, 0, sizeof (ExtY_BlockReplacementDiscrete_T)); { real32_T DiscreteFir_Coefficients[2]; real32_T DiscreteFir_InitialStates; /* InitializeConditions for DiscreteFir: '<Root>/Discrete FIR Filter' */ DiscreteFir_Coefficients[0] = BlockReplacementDiscreteFIR_P.DiscreteFIRFilter_Coefficients[0]; DiscreteFir_Coefficients[1] = BlockReplacementDiscreteFIR_P.DiscreteFIRFilter_Coefficients[1]; DiscreteFir_InitialStates = BlockReplacementDiscreteFIR_P.DiscreteFIRFilter_InitialStates; MyDiscreteFIR_init(&BlockReplacementDiscreteFIR_DW.CrlDWork, &DiscreteFir_Coefficients[0], &DiscreteFir_InitialStates, 2U); } }
See Also
RTW.TflBlockEntry
| RTW.BlockProperty
| RTW.CImplementation