Main Content

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.

Model containing a Discrete FIR Filter block with one input and one output.

The model contains one Discrete FIR Filter block. The block has the block dialog property settings shown in this table.

PropertySetting
Coefficient sourceDialog parameters
Filter structureDirect form
Input processingColumns as channels (frame based)
Integer rounding modeFloor
Saturate on integer overflowoff

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

  1. Create a new function file with the name of your code replacement table, for example, crl_table_block_discrete_fir.m.

  2. Create a code replacement table.

    1. 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.

    2. Create a table object by calling RTW.TflTable.

      function hTable = crl_table_block_discrete_fir
      
      hTable = RTW.TflTable;

  3. 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.

  1. 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'};
  2. 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:

    1. Create a property specification by calling RTW.BlockProperty. Specify the name and value of the block dialog property that you want to match.

    2. 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);
  3. 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);

  4. 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 the InitialStates parameter and the Coefficients 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.

  1. Create an RTW.CImplementation object to represent one of your implementation functions.

    initImplementation = RTW.CImplementation;

  2. 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';

  3. 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 box

    • DiscreteFir_CoeffsSize — The number of coefficients

    • DiscreteFir_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>';

  4. 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.

    1. 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';

    2. 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];

    3. 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 the addDWorkArg 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.

  5. Add the arguments to the implementation representation. For this example, the replacement initialization function uses these arguments:

    • d2 — A pointer that uses the DWork argument d1, 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 calling addArgument.

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

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

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

  8. 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.

 Code Replacement Table File

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

  1. Open the Configuration Parameters dialog box.

  2. 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

| |

Related Topics