Replace Code Generated from Discrete FIR Filter Blocks - MATLAB & Simulink (original) (raw)
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 modelBlockReplacementDiscreteFIR
.
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 tosingle
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
andMyDiscreteFIR_init
. The generated model step function calls theMyDiscreteFIR_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
andInitialStates
.
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 functionRTW.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 fromDiscrete 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 callingaddBlockProperty.
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 as RTW.TflArgMatrix. Add the argument to the entry by calling addConceptualArg. For this example, the entry matches blocks that have one matrix input and one matrix output of base type
single
.
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 theDerivedBlockParams
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.
- 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.
- 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 calling setReturn.
For the arguments that you have not yet defined, create the argument handles by calling getTflArgFromString or createDWorkArg. 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.
This is the code replacement table code that you wrote in the previous steps.
function hTable = crl_table_block_discrete_fir
hTable = RTW.TflTable;
hEntry = RTW.TflBlockEntry;
hEntry.Key = 'DiscreteFir'; hEntry.Priority = 1; hEntry.SideEffects = true; hEntry.AdditionalHeaderFiles = {'customtypedefinitions.h'};
% Block dialog property criteria to match 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); bp4 = RTW.BlockProperty('RndMeth', 'Floor'); addBlockProperty(hEntry,bp4); bp5 = RTW.BlockProperty('SaturateOnIntegerOverflow', 'off'); addBlockProperty(hEntry,bp5);
%% Conceptual Signature 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);
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);
%% Implementation initImplementation = RTW.CImplementation; initImplementation.Name = 'MyDiscreteFIR_init'; initImplementation.HeaderFile = 'MyDiscreteFIR.h'; initImplementation.SourceFile = 'MyDiscreteFIR_init.c';
hEntry.DerivedBlockParams{end+1} = 'DiscreteFir_Coefficients = <%Coefficients>'; hEntry.DerivedBlockParams{end+1} = 'DiscreteFir_InitialStates = <%InitialStates>'; hEntry.DerivedBlockParams{end+1} = 'DiscreteFir_CoeffsSize = length(<%Coefficients>)';
% Set up DWork type ssTypeP se1.Identifier = 'Coeffs'; se1.Type = 'single*';
se2.Identifier = 'InitStates'; se2.Type = 'single';
se3.Identifier = 'CoeffsSize'; se3.Type = 'uint16';
ssTypeP.Identifier = 'InitStruct'; ssTypeP.Elements = [se1, se2, se3];
d1 = createDWorkArg(hEntry,'struct','Name','CrlDWork','StructData',ssTypeP); addDWorkArg(hEntry,d1);
d2 = createDWorkArg(hEntry,'Pointer','Name','CrlDWork','BaseType','struct','StructData',ssTypeP); initImplementation.addArgument(d2); 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);
arg = getTflArgFromString(hTable,'void','void'); arg.IOType = 'RTW_IO_OUTPUT'; initImplementation.setReturn(arg); addImplementation(hEntry,'initialize',initImplementation);
% Output outImplementation = RTW.CImplementation; outImplementation.Name = 'MyDiscreteFIR_out'; outImplementation.HeaderFile = 'MyDiscreteFIR.h'; outImplementation.SourceFile = 'MyDiscreteFIR_out.c';
argIn = getTflArgFromString(hTable,'u1','single*'); argOut = getTflArgFromString(hTable,'y1','single*'); argOut.IOType = 'RTW_IO_OUTPUT'; addArgument(outImplementation,argIn); addArgument(outImplementation,argOut);
addArgument(outImplementation,d2);
arg = getTflArgFromString(hTable,'void','void'); arg.IOType = 'RTW_IO_OUTPUT'; outImplementation.setReturn(arg); addImplementation(hEntry,'output', outImplementation);
%% Add the entry to the library addEntry(hTable,hEntry);
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: '/Discrete FIR Filter' incorporates:
- CrlBlock generated from: '/Discrete FIR Filter'
- Inport: '/In1'
- Outport: '/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