Main Content

Create Custom Target IDE for Code Generation

Simulink® PLC Coder™ currently supports plugin-based target IDEs such as Selectron CAP1131, Omron Sysmac Studio, and so on. For your plugin-based custom target IDEs that are not supported, generate PLCOpen XML-or ASCII-compliant structured text code. Customize the generated code to meet your target IDE requirements, by leveraging the built-in plugin options in Simulink PLC Coder.

Plugin-Based Code Generation Workflow

To generate code for your custom plugin-based target IDEs:

Flowchart displaying steps to generate code for plugin based custom target IDEs

Follow the flowchart to create the plc_custom_ide.m and if required the plc_precg_callback_IDEname.m, and plc_postcg_callback_IDEname.m files. To use these files to create and generate code for your custom target IDE, see Generate Code by Using Plugin-Based Target IDE.

Create plc_custom_ide.m file

Create plc_custom_ide.m by using this template:

function plc_ide_list = plc_custom_ide
%   Copyright 2012-2021 The MathWorks, Inc.
    plc_ide_list(1) = get_ide_info_myplcopen;  
end

function ide_info = get_ide_info_myplcopen
    ide_info.name = 'myplcopen';
    ide_info.description = 'My PLCopen XML';
    ide_info.path = ''; % IDE path
    ide_info.format = 'xml'; % generic|xml
    ide_info.fileExtension = 'xml';
    ide_info.cfg = get_ide_cfg_myplcopen;
    ide_info.precg_callback = 'plc_precg_callback_myplcopen'; 
    ide_info.postcg_callback = 'plc_postcg_callback_myplcopen';
    ide_info.xmltree_callback = PLCCoder.PLCCGMgr.PLC_PLUGIN_CG_CALLBACK_EMPTY;
    ide_info.pluginVersion = 2.2;
    ide_info.compatibleBuildVersion = 1.6;
end

function cfg = get_ide_cfg_myplcopen
    cfg.fConvertDoubleToSingle = true;
    cfg.fConvertNamedConstantToInteger = true;
    cfg.fConvertEnumToInteger = true;
    cfg.fConvertOutputInitValueToAssignment = true;
    cfg.fConvertTunableParamToInputVariable = true;
    cfg.fSimplifyFunctionCallExpr = true;
    cfg.fConvertOutputInitValueToAssignment = true;
end

  • Set the name of your target IDE, by using ide_info.name. The Target IDE configuration setting displays the name set in ide_info.description.

  • If your target IDE is compliant with Generic ST standards, set ide_info.format = 'generic'. If your target IDE is compliant with PLCOpen XML, set ide_info.format = 'xml'.

  • Set where the generated code files are placed by using ide_info.path.

  • Set the extension for your target IDE files by using ide_info.fileExtension.

In the function_cfg section of the file, set your plugin options. To enable the plugin, set the plugin option to true. For example, cfg.fArrayInitialValueBrackets = true; enables the plugin. To disable the plugin, set the plugin option to false. To decide which plugin options you need in the plc_custom_ide.m file, see Plugin Options.

Create Callback Files

If your custom IDE requires pre- and post-code generation processing, create plc_precg_callback_IDEname.m and plc_postcg_callback_IDEname.m files.

Preprocessing Callback File.  The plc_precg_callback_IDEname.m file provides access to the intermediate generated code representation. The intermediate code is stored in the controller struct data type, which contains information such as name of the subsystem block, inputs, outputs, generated code body, and so on. This callback is executed before the target-specific emitter function is called. To create a plc_precg_callback_IDEname.m file, use this template:

function controller = plc_precg_callback_myplcopen(controller)
%   Copyright 2012-2020 The MathWorks, Inc.
    
    % do modifications to the controller struct here, f.ex.:
    for i = 1:length(controller.components)
        controller.components(i).body = sprintf('<<header_placeholder>>\r\n%s',controller.components(i).body);
    end

end

Postprocessing Callback File.  The plc_postcg_callback_IDEname.m file provides access to the generated code file. Use this file to make the generated code match the syntax requirements for your custom target IDE. This file reads in and makes changes to the generated code file, which is read in as a string file. To create a plc_postcg_callback_IDEname.m file, use this template:

function generatedFiles = plc_postcg_callback_myplcopen(fileNames)
%   Copyright 2012-2020 The MathWorks, Inc.

    fileName = fileNames{1};
    str = fileread(fileName);
%   do modifications to str here, f.ex.:
%   str = regexprep(str,'BOOL_TO_LREAL','BOOL_TO_INT');
%   str = regexprep(str,'<USINT/>','<INT/>');
%   str = regexprep(str, 'END_STRUCT','END_STRUCT;');

    [sHeader,eHeader] = regexp(str,'\(\*.*?\*\)');
    header = str(sHeader:eHeader);
    
    str = regexprep(str,'<<header_placeholder>>',header);
    
    sfprivate ('str2file', str, fileName);
    generatedFiles = {fileName};
end

Plugin Options

Generate custom code for your custom target IDE, by selecting from the plugin options listed in the table.

Plugins for Data Type Transformation

Plugin NamePlugin PurposeWhen to Use PluginEffect of Plugin on Generated Code
fConvertBooleanCast

Convert boolean type cast function to an if-else assignment.

If your target IDE does not support boolean type cast function or operator.

Generated code with plugin disabled:

Out1 := DINT_TO_INT(BOOL_TO_DINT(In1) +...
 BOOL_TO_DINT(In2));

Generated code with plugin enabled:

IF In1 THEN 
    temp1 := DINT#1;
ELSE 
    temp1 := DINT#0;
END_IF;


IF In2 THEN 
    temp2 := DINT#1;
ELSE 
    temp2 := DINT#0;
END_IF;

Out1 := DINT_TO_INT(temp1 + temp2);

fConvertDoubleToSingle

Convert double data type to single data type.

If your target IDE does not support double data types.

The generated code double data type variables are converted to single data types. A warning message is generated during code generation that data types that are not supported have been found and converted. LREAL data types are replaced by REAL data types.

The values in the generated code may be different from the simulation values.

Generated code with plugin disabled:

VAR_INPUT
    ssMethodType: SINT;
    U: LREAL;
END_VAR
VAR_OUTPUT
    Y: LREAL;
END_VAR

Generated code with plugin enabled:

VAR_INPUT
    ssMethodType: SINT;
    U: REAL;
END_VAR
VAR_OUTPUT
    Y: REAL;
END_VAR

fConvertDoubleToSingleEmitter

Convert double data types to single data types.

If your target IDE does not support double data types and you want to preserve values after conversion.

The generated code double data type variables are converted to single data types. A warning message is generated during code generation that data types that are not supported have been found and converted. LREAL data types are replaced by REAL data types.

The values in the generated code match the simulation values unless they exceed the REALMAX bounds.

Generated code with plugin disabled:

VAR_INPUT
    ssMethodType: SINT;
    U: LREAL;
END_VAR
VAR_OUTPUT
    Y: LREAL;
END_VAR

Generated code with plugin enabled:

VAR_INPUT
    ssMethodType: SINT;
    U: REAL;
END_VAR
VAR_OUTPUT
    Y: REAL;
END_VAR

fConvertEnumToInteger

Convert enum data types to integer data types.

If your target IDE does not support enum data types.

Generated code with plugin disabled for a target IDE that supports enum data type:

VAR_TEMP
    rtb_Switch: myEnum;
    in: myEnum;
END_VAR

Generated code with plugin enabled:

VAR_TEMP
    rtb_Switch: DINT;
    in: DINT;
END_VAR

fConvertUnsignedIntToSignedInt

Converts unsigned integer to signed integer.

If your target does not support unsigned integer data type.

Generated code with plugin disabled:

FUNCTION_BLOCK Subsystem
VAR_INPUT
    In1: UDINT;
    In2: UDINT;
END_VAR
VAR_OUTPUT
    Out1: UDINT;
END_VAR

Generated code with plugin enabled:

FUNCTION_BLOCK Subsystem
VAR_INPUT
    In1: DINT;
    In2: DINT;
END_VAR
VAR_OUTPUT
    Out1: DINT;
END_VAR

fInt32AsBaseInt

Sets int32 data type as the default integer data type.

Setting int32 as the default internal integer data type might reduce the number of type cast operations in the generated code.

Generated code with plugin disabled:

FUNCTION_BLOCK Subsystem
VAR_INPUT
    In1: SINT;
    In2: SINT;
END_VAR
VAR_OUTPUT
    Out1: SINT;
END_VAR
Out1 := DINT_TO_SINT(SINT_TO_DINT(In1) +...
 SINT_TO_DINT(In2));
END_FUNCTION_BLOCK
The generated code contains additional data type conversion code.

Generated code with plugin enabled:

FUNCTION_BLOCK Subsystem
VAR_INPUT
    In1: DINT;
    In2: DINT;
END_VAR
VAR_OUTPUT
    Out1: DINT;
END_VAR
Out1 := In1 + In2));
END_FUNCTION_BLOCK
The generated code does not contain additional data type conversion code.

fEmitEnumTypeIntegerValue

Displays the enum value and corresponding integer value in the generated code.

To display the enum values and their matching integer values in the generated code.

Generated code with plugin disabled:

TYPE PLCCommandState:
    (FILL, HOLD, EMPTY, ACTIVATE);
END_TYPE
TYPE PLCVesselState:
    (EMPTIED, NOT_FULL, FULL);
END_TYPE
TYPE PLCValveState:
    (SHUT, OPEN);
END_TYPE

Generated code with plugin enabled:

TYPE PLCCommandState:
    (FILL:=0, HOLD:=1, EMPTY:=2, ACTIVATE:=3);
END_TYPE
TYPE PLCVesselState:
    (EMPTIED:=0, NOT_FULL:=1, FULL:=2);
END_TYPE
TYPE PLCValveState:
    (SHUT:=0, OPEN:=1);
END_TYPE

Plugins for Syntax Change

Plugin NamePlugin PurposeWhen to Use PluginEffect of Plugin on Generated Code
fArrayInitialValueBrackets

Encloses array initialization in the declaration area within brackets.

If your target IDE requires enclosing array initialization in the declaration area in brackets.

Generated code with plugin disabled:

 EnableSetpoint_ZCE: ARRAY [0..2] OF USINT:=3,3,3

Generated code with plugin enabled::

 EnableSetpoint_ZCE: ARRAY [0..2] OF USINT:=[3,3,3]

fConvertAggregateInitValueToAssignment

Converts initial values for aggregate data types to an assignment statement.

Target IDE does not support array initialization in the declaration area.

Generated code with plugin disabled:

ARRAY [0..1] OF LREAL := LREAL#0.0,LREAL#0.0998

Generated code with plugin enabled:

tb_U[0] := 0.0;
tb_U[1] := 0.0998;
    

fConvertAggregateTypeFunctionToFB

Converts functions with aggregate data types to a function block (FB).

Target IDE supports aggregate data types for function blocks only.

Generated code with plugin disabled:

function foo(...):ARRAY [0..10] OF LREAL

Generated code with plugin enabled:

FUNCTION_BLOCK foo
VAR_OUTPUT
out1: ARRAY [0..10] OF LREAL;
END_VAR

fConvertFunctionToFB

Convert function to function block (FB).

Target IDE does not support FUN notation but supports FB notation.

 
fConvertOutputInitValueToAssignment

Convert output variable initialization to an assignment.

Target IDE does not allow initial value definition and requires an assignment statement.

Generated code with plugin disabled:

FUNCTION_BLOCK foo
VAR_OUTPUT
somevalue: DINT := 100;
END_VAR

Generated code with plugin enabled:

FUNCTION_BLOCK foo
VAR_OUTPUT
somevalue: DINT;
END_VAR
somevalue := 100;

fEmitVarDeclarationBeforeDescription

Toggles whether the variable description appears before or after the variable declaration.

Target IDE requires variable declaration before the variable description.

Generated code with plugin disabled:

VAR_GLOBAL CONSTANT
   K3: REAL := 0.3;
  END_VAR

Generated code with plugin enabled:

VAR_GLOBAL CONSTANT
   K3: REAL := 0.3;
   END_VAR

fErrorOnTrailingUS

Code generation fails when it encounters variable names with a trailing underscore.

Target IDE does not support names with a trailing underscore.

Code generation fails with this message $name$ has a trailing '_'. 'IDE name' names must not end with '_'.

fHoistArrayIndexExprs

Moves expressions out of array indices and creates a temporary variable for the expression.

Target IDE does not support expressions for array indices.

Generated code with plugin disabled:

EnvCur[TRUNC(j) - 1]

Generated code with plugin enabled:

temp1 := TRUNC(j) - 1;
EnvCur[temp1]

fSimplifyFunctionCallExpr

Simplifies the function call for simple functions.

Target IDE does not allow assignment expressions in function calls.

Generated code with plugin disabled:

y := simplefunction(u_0 := u);

Generated code with plugin enabled:

y := simplefunction(u);

fUseQualifiedTypeConstant

Appends data type to constant declaration.

Target IDE requires data type for constants.

Generated code with plugin disabled:

a := 11;

Generated code with plugin enabled:

a := DINT#11;

Plugins for Interface Changes

Plugin NamePlugin PurposeWhen to Use PluginEffect of Plugin on Generated Code
fConvertTunableParamToInputVariable

Converts tunable parameters to function block (FB) input variables.

You want to convert tunable parameters to function block inputs. This allows you to call a POU with different parameter sets.

Generated code with plugin disabled:

FUNCTION_BLOCK Tunable_Param_to_Input
   24   VAR_INPUT
   25       ssMethodType: SINT;
   26       Input1: REAL;
   27   END_VAR
   28   VAR_OUTPUT
   29       Output1: REAL;
   30   END_VAR
   31   VAR
   32       DSTATE: REAL;
   33   END_VAR
   34   'ST'
   35   BODY
   36   CASE ssMethodType OF
   37       0:            
   40           UnitDelay_DSTATE := 0.0;
   41           
   42       1: 
   43           Output1 := (Input1 - DSTATE) *
                          TunableParam;
   48           DSTATE := Output1;
   50   END_CASE;
   52   END_BODY
   53   END_FUNCTION_BLOCK
   54   
The TunableParam variable is not declared as an input to the function block.

Generated code with plugin enabled:

FUNCTION_BLOCK Tunable_Param_to_Input
   24   VAR_INPUT
   25       TunableParam: LREAL;
   26       ssMethodType: SINT;
   27       Input1: REAL;
   28   END_VAR
   29   VAR_OUTPUT
   30       Output1: REAL;
   31   END_VAR
   32   VAR
   33       DSTATE: REAL;
   34   END_VAR
   35   'ST'
   36   BODY
   37   CASE ssMethodType OF
   38       0: 
   39          DSTATE := 0.0;
   42           
   43       1: 
   44           Output1 := (Input1 - DSTATE) * 
                            TunableParam;
   49           DSTATE := Output1;
   51           
   52   END_CASE;
   53   END_BODY
   54   END_FUNCTION_BLOCK
   55   
The TunableParam variable is declared as an input to the function block.

fDefineFBExternalConstVariable

Defines external variables in VAR_GLOBAL CONSTANT.

Target IDE requires declaration of external constant variables in VAR_GLOBAL CONSTANT.

Generated code with plugin disabled:

VAR_GLOBAL CONSTANT
    SS_INITIALIZE: SINT := 0;
    K3: LREAL := 0.3;
    SS_STEP: SINT := 1;
END_VAR

Generated code with plugin enabled:

VAR_GLOBAL CONSTANT
    K3: REAL := 0.3;

fDefineFBExternalVariable

Defines external constants in VAR_EXTERNAL.

Target IDE requires declaration of external constants in VAR_EXTERNAL.

Generated code with plugin disabled:

VAR
    UnitDelay_DSTATE: LREAL;
    i0_ExternallyDefinedBlock: ExternallyDefinedBlock;
END_VAR

Generated code with plugin enabled:

VAR_EXTERNAL
    K1: REAL;
END_VAR

fReplaceShiftFunctions

Replaces target IDE shift functions with functions to match Simulink shift function behavior.

When the number of shifts is greater than the length of the data type, there is a mismatch between output of Simulink shift block and target IDE shift block. Use this plugin to replace target IDE shift blocks with Simulink shift blocks in the generated code.

Generated code with plugin disabled:

Out10 := WORD_TO_INT(SHL(IN:=INT_TO_WORD(In1),...
  N:=Out2_tmp));
The generated code uses the target IDE SHL shift function.

Generated code with plugin enabled:

Out10 := WORD_TO_INT(PLC_SHL(INT_TO_WORD(In1),...
 DINT_TO_USINT(Out2_tmp)));
FUNCTION PLC_SHL: WORD
VAR_INPUT
    in1: WORD;
    in2: USINT;
END_VAR
'ST'
BODY
IF in2 > 16 THEN 
    PLC_SHL := 16#0;
ELSE 
    PLC_SHL := SHL(in1, in2);
END_IF;
END_BODY
END_FUNCTION
The generated code contains the function PLC_SHL, which replicates the Simulink shift block in the target IDE.

Plugins for Intrinsic Transformation Functions

Plugin NamePlugin PurposeWhen to Use PluginEffect of Plugin on Generated Code
fSimplifyAllIntrinsicFcn

Simplifies the inputs of all intrinsic functions.

Target IDE does not allow compound expressions as a part of intrinsic function arguments.

Generated code with plugin disabled:

a := SQRT(x*y);

Generated code with plugin enabled:

t1 := x*y;
a := SQRT(t1);

fSimplifyIntrinsicFcn

Simplifies intrinsic functions that are arguments of fSimplifyIntrinsicFcnNameList.

Target IDE does not allow compound expressions as a part of intrinsic function arguments.

Generated code with plugin disabled:

a := SQRT(x*y);

Generated code with plugin enabled:

cfg.fSimplifyIntrinsicFcnNameList = { 'SQRT' \}
cfg.fSimplifyIntrinsicFcn = true;
t1 := x*y;
a := SQRT(t1);

fSimplifyIntrinsicFcnNameList

Creates a list of intrinsic functions. Inputs to these intrinsic functions are simplified by using fSimplifyIntrinsicFcn.

Target IDE does not allow compound expressions as a part of intrinsic function arguments.

Generated code with plugin disabled:

a := SQRT(x*y);

Generated code with plugin enabled:

cfg.fSimplifyIntrinsicFcnNameList = { 'SQRT' \}
cfg.fSimplifyIntrinsicFcn = true;
t1 := x*y;
a := SQRT(t1);

fSimplifyOperator

Simplifies inputs of operator functions listed by using fSimplifyOperatorNameList.

Target IDE does not allow compound expressions as a part of operator function arguments.

Generated code with plugin disabled:

a := SHL(x*y);

Generated code with plugin enabled:

cfg.fSimplifyOperatorNameList = { 'SHL' \}
cfg.fSimplifyOperator = true;
t1 := x*y;
a := SHL(t1);

fSimplifyOperatorNameList

Creates a list of operator functions. Inputs to these operator functions are simplified by using fSimplifyOperator.

Target IDE does not allow compound expressions as a part of operator function arguments.

Generated code with plugin disabled:

a := SHL(x*y);

Generated code with plugin enabled:

cfg.fSimplifyOperatorNameList = { 'SHL' \}
cfg.fSimplifyOperator = true;
t1 := x*y;
a := SHL(t1);

fSimplifyTrunc

Simplifies inputs of the TRUNC function.

Target IDE does not allow compound expressions as arguments for the TRUNC function.

Generated code with plugin disabled:

a := TRUNC(x*y);

Generated code with plugin enabled:

cfg.fSimplifyTrunc = true;
t1 := x*y;
a := SHL(t1);

Generate Code by Using Plugin-Based Target IDE

This example shows how to generate code for a custom target IDE called my PLCopen XMLby using plugins.

  1. Create a folder called myplcopen. Create a plc_custom_ide.m file in the folder by using this template:

    function plc_ide_list = plc_custom_ide
    %   Copyright 2012-2021 The MathWorks, Inc.
        plc_ide_list(1) = get_ide_info_myplcopen;  
    end
    
    function ide_info = get_ide_info_myplcopen
        ide_info.name = 'myplcopen';
        ide_info.description = 'My PLCopen XML';
        ide_info.path = ''; % IDE path
        ide_info.format = 'xml'; % generic|xml
        ide_info.fileExtension = 'xml';
        ide_info.cfg = get_ide_cfg_myplcopen;
        ide_info.precg_callback = 'plc_precg_callback_myplcopen'; 
        ide_info.postcg_callback = 'plc_postcg_callback_myplcopen';
        ide_info.xmltree_callback = PLCCoder.PLCCGMgr.PLC_PLUGIN_CG_CALLBACK_EMPTY;
        ide_info.pluginVersion = 2.2;
        ide_info.compatibleBuildVersion = 1.6;
    end
    
    function cfg = get_ide_cfg_myplcopen
        cfg.fConvertDoubleToSingle = true;
        cfg.fConvertNamedConstantToInteger = true;
        cfg.fConvertEnumToInteger = true;
        cfg.fConvertOutputInitValueToAssignment = true;
        cfg.fConvertTunableParamToInputVariable = true;
        cfg.fSimplifyFunctionCallExpr = true;
        cfg.fConvertOutputInitValueToAssignment = true;
    end
    

    Set your plugin options in the function_cfg section of the file. To enable the plugin set the plugin option to true. For example,cfg.fArrayInitialValueBrackets = true; enables the plugin. To disable the plugin, set the plugin option to false.

  2. Create plc_precg_callback_IDEname.m and plc_postcg_callback_IDEname.m files by using these templates:

    function controller = plc_precg_callback_myplcopen(controller)
    %   Copyright 2012-2020 The MathWorks, Inc.
        
        % do modifications to the controller struct here, f.ex.:
        for i = 1:length(controller.components)
            controller.components(i).body = sprintf('<<header_placeholder>>\r\n%s',controller.components(i).body);
        end
    
    end
    function generatedFiles = plc_postcg_callback_myplcopen(fileNames)
    %   Copyright 2012-2020 The MathWorks, Inc.
    
        fileName = fileNames{1};
        str = fileread(fileName);
    %   do modifications to str here, f.ex.:
    %   str = regexprep(str,'BOOL_TO_LREAL','BOOL_TO_INT');
    %   str = regexprep(str,'<USINT/>','<INT/>');
    %   str = regexprep(str, 'END_STRUCT','END_STRUCT;');
    
        [sHeader,eHeader] = regexp(str,'\(\*.*?\*\)');
        header = str(sHeader:eHeader);
        
        str = regexprep(str,'<<header_placeholder>>',header);
        
        sfprivate ('str2file', str, fileName);
        generatedFiles = {fileName};
    end
    
    

  3. Create a plc_header_hook.m file by using this template:

    function headerCommentText = plc_header_hook(filePath, blockH, headerCommentText)
    
    
    headerCommentText = [headerCommentText(1:end-7) ...
        sprintf([' * Plugin Header Copy              : Yes \n']) ...
        headerCommentText(end-6:end)];
    
    end

    The plc_header_hook.m file copies the header information at the beginning of the generated code file to every function block instance.

  4. Add the new folder and files to the MATLAB path.

    • Right-click the folder and select Add to Path > Selected Folders and Subfolders.

    • Use the addpath function. For example, addpath(genpath('path to your folder')).

  5. Run this command:

    plccoderpref('plctargetidepaths','default')

    Restart your MATLAB session.

  6. Open your model and select the model component for code generation. Open the PLC Coder app. Click Settings. On the PLC Code Generation pane, in Target IDE, select My PLCopen XML. Click OK.

  7. In the PLC Coder app, PLC Code tab, click Generate PLC Code to generate code for your custom target IDE. The generated code files are placed in the path specified in ide_info.path = ''; % IDE path.

Related Topics