Main Content

Import Calls to External Code into Generated Code with Legacy Code Tool

Legacy Code Tool and Code Generation

You can use the Simulink® Legacy Code Tool to generate fully inlined C MEX S-functions for legacy or custom code. The S-functions are optimized for embedded components, such as device drivers and lookup tables, and they call existing C or C++ functions.

Note

The Legacy Code Tool can interface with C++ functions, but not C++ objects. To work around this issue so that the tool can interface with C++ objects, see Legacy Code Tool Limitations.

You can use the tool to:

  • Compile and build the generated S-function for simulation.

  • Generate a masked S-Function block that is configured to call the existing external code.

If you want to include these types of S-functions in models for which you intend to generate code, use the tool to generate a TLC block file. The TLC block file specifies how the generated code for a model calls the existing C or C++ function.

If the S-function depends on files in folders other than the folder containing the S-function dynamically loadable executable file, use the tool to generate an sFunction_makecfg.m or rtwmakecfg.m file for the S-function. Generating the file maintains those dependencies when you build a model that includes the S-function. For example, for some applications, such as custom targets, you might want to locate files in a target-specific location. The build process looks for sFunction_makecfg.m or rtwmakecfg.m in the same folder as the S-function dynamically loadable executable and calls the function in the file.

For more information, see Integrate C Functions Using Legacy Code Tool.

Generate Inlined S-Function Files for Code Generation

Depending on the code generation requirements of your application, to generate code for a model that uses the S-function, do either of the following:

  • Generate one .cpp file for the inlined S-function. In the Legacy Code Tool data structure, set the value of the Options.singleCPPMexFile field to true before generating the S-function source file from your existing C function. For example:

    def.Options.singleCPPMexFile = true;
    legacy_code('sfcn_cmex_generate', def);

  • Generate a source file and a TLC block file for the inlined S-function. For example:

    def.Options.singleCPPMexFile = false;
    legacy_code('sfcn_cmex_generate', def);
    legacy_code('sfcn_tlc_generate', def);

singleCPPMexFile Limitations

You cannot set the singleCPPMexFile field to true if

  • Options.language='C++'

  • You use one of the following Simulink objects with the IsAlias property set to true:

    • Simulink.Bus

    • Simulink.AliasType

    • Simulink.NumericType

  • The Legacy Code Tool function specification includes a void* or void** to represent scalar work data for a state argument

  • HeaderFiles field of the Legacy Code Tool structure specifies multiple header files

Apply Code Style Settings to Legacy Functions

To apply the model configuration parameters for code style to a legacy function:

  1. Initialize the Legacy Code Tool data structure. For example:

    def = legacy_code('initialize');
    
  2. In the data structure, set the value of the Options.singleCPPMexFile field to true. For example:

    def.Options.singleCPPMexFile = true;

To check the setting, enter:

def.Options.singleCPPMexFile

Address Dependencies on Files in Different Locations

By default, the Legacy Code Tool assumes that files on which an S-function depends reside in the same folder as the dynamically loadable executable file for the S-function. If your S-function depends on files that reside elsewhere and you are using the template makefile build process, generate an sFunction_makecfg.m or rtwmakecfg.m file for the S-function. For example, you might generate this file if your Legacy Code Tool data structure defines compilation resources as path names.

To generate the sFunction_makecfg.m or rtwmakecfg.m file, call the legacy_code function with 'sfcn_makecfg_generate' or 'rtwmakecfg_generate' as the first argument, and the name of the Legacy Code Tool data structure as the second argument. For example:

legacy_code('sfcn_makecfg_generate', lct_spec);

If you use multiple registration files in the same folder and generate an S-function for each file with a single call to legacy_code, the call to legacy_code that specifies 'sfcn_makecfg_generate' or 'rtwmakecfg_generate' must be common to all registration files. For more information, see Handling Multiple Registration Files.

For example, if you define defs as an array of Legacy Code Tool structures, you call legacy_code with 'sfcn_makecfg_generate' once.

defs = [defs1(:);defs2(:);defs3(:)];
legacy_code('sfcn_makecfg_generate', defs);

For more information, see Build Support for S-Functions.

Deploy S-Functions for Simulation and Code Generation

You can deploy the S-functions that you generate with the Legacy Code Tool so that other people can use them. To deploy an S-function for simulation and code generation, share the following files:

  • Registration file

  • Compiled dynamically loadable executable

  • TLC block file

  • sFunction_makecfg.m or rtwmakecfg.m file

  • Header, source, and include files on which the generated S-function depends

When you use these deployed files:

  • Before using the deployed files in a Simulink model, add the folder that contains the S-function files to the MATLAB® path.

  • If the Legacy Code Tool data structure registers required files as absolute paths and the location of the files changes, regenerate the sFunction_makecfg.m or rtwmakecfg.m file.

Integrate External C++ Objects

The Legacy Code Tool can interface with C++ functions, but not C++ objects. Using the previous example as a starting point, here is an example of how you can work around this limitation.

  • Modify the class definition for adder in a new file adder_cpp.hpp. Add three new macros that dynamically allocate a new adder object, invoke the method add_one(), and free the memory allocated. Each macro takes a pointer to an adder object. Because each function called by the Legacy Code Tool must have a C-like signature, the pointer is cached and passed as a void*. Then you must explicitly cast to adder* in the macro. The new class definition for adder:

    #ifndef _ADDER_CPP_
    #define _ADDER_CPP_
    
    class adder {
    private:
    	int int_state;
    public:
    	adder(): int_state(0) {};
    	int add_one(int increment);
    	int get_val() {return int_state;};
    };
    
    // Method wrappers implemented as macros
    #define createAdder(work1) \
        *(work1) = new adder
    
    #define deleteAdder(work1) \
        delete(static_cast<adder*>(*(work1)))
    
    #define adderOutput(work1, u1) \
        (static_cast<adder*> ((work1)))->add_one(u1)
    
    #endif /* _ADDER_CPP_ */
  • Update adder_cpp.cpp. With the class modification, instead of one global instance, each generated S-function manages its own adder object.

    #include "adder_cpp.hpp"
    
    int adder::add_one(int increment)
    {
    	int_state += increment;
        return int_state;
    }
  • Update adder_cpp.cpp with the following changes:

    • StartFcnSpec calls the macro that allocates a new adder object and caches the pointer.

      def.StartFcnSpec  = 'createAdder(void **work1)';
      
    • OutputFcnSpec calls the macro that invokes the method add_one() and provides the S-function specific adder pointer object.

      def.OutputFcnSpec = 'int32 y1 = adderOutput(void *work1, int32 u1)';
    • TerminateFcnSpec calls the macro that frees the memory.

      def.TerminateFcnSpec = 'deleteAdder(void **work1)';

See Also

Related Topics