classdef (Hidden = true) TargetInterface < rtw.connectivity.CodeInfoWriterProject
%TARGETINTERFACE creates a target interface file defined by CodeInfo
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2008-2020 The MathWorks, Inc.

    % properties
    properties (SetAccess = 'private', GetAccess = 'protected')
        SILPILWrapperUtils;     
        InSwitchStatement;
    end
    
    properties (SetAccess = 'private', GetAccess = 'private')
        DefinedDataList;
        AccessFcnsData = {};
        HasModelWideEvents = false;
        SelfCoderDataGroup;
        AllocatesCppObject = false;
    end
    
    methods
        % constructor
        function this = TargetInterface(silPILWrapperUtils, ...
                                        targetInterfacePath)
            narginchk(2, 2);
            % validate arg            
            rtw.connectivity.Utils.validateArg(silPILWrapperUtils, ...
                'rtw.pil.SILPILWrapperUtils');
            
            % call super class constructor                             
            this@rtw.connectivity.CodeInfoWriterProject(silPILWrapperUtils.getCodeInfoUtils, ...
                                                        targetInterfacePath);              
            % store SILPILWrapperUtils
            this.SILPILWrapperUtils = silPILWrapperUtils;
                                   
            % validate interface
            this.validateInterface;
            
            % Set model wide events
            this.HasModelWideEvents = this.getCodeInfoUtils.hasModelWideEvents();
        end

        % get an expression for a DataElement of an Implementation
        %
        % index argument is only used for vector or array expressions
        function expression = getElementExpression(this, ...
                                                   implementation, ...
                                                   cData, ...
                                                   index)            
            % get DataElement sub-expression
            isPointerTypeSupported = true;
            isLeafPointerAllowed = true;
            subExpression = cData.getElementExpression(index, isPointerTypeSupported, isLeafPointerAllowed);
            % see if subExpression starts with an index
            if ~isempty(subExpression) && (subExpression(1) == '[')        
                subExpressionStartsWithIndex = true;                
            else
                subExpressionStartsWithIndex = false; 
            end
            
            switch this.codeInfoUtils.class(implementation)                                    
                case 'ArrayExpression'                    
                    % get the base storage implementation in order to
                    % handle indexing correctly
                    baseStorageImplementation = this.getBaseStorageImplementation(implementation);
                    targetIdentifier = this.getExpression(baseStorageImplementation);                                         
                    expression = [targetIdentifier '[' int2str(implementation.Offset) ']'];
                    % if subExpression starts with an index then we need
                    % to switch to pointer arithmetic
                    if subExpressionStartsWithIndex                    
                        expression = ['(&' expression ')'];                    
                    end                                        
                case 'PointerVariable'                
                    assert(logical(implementation.Type.isPointer), 'PointerVariable must have pointer type.');
                    expression = this.getExpression(implementation);                                        
                    % if subExpression does not start with an index then we
                    % need to dereference the scalar pointer!                    
                    if ~subExpressionStartsWithIndex
                        expression = ['(*' expression ')'];
                    end                    
                case 'TypedCollection'
                    dataElementList = cData.getDataInterface.getTargetDataElements;
                    %Only ever going to be 2 storage implementations from a
                    %a typed collection. Last one are always size as will
                    %the last data element associated with that interface.
                    if cData==dataElementList(end)
                        elementIndex = 2;
                    else
                        elementIndex = 1;
                    end
                    elements = this.codeInfoUtils.getArray(implementation.Elements);
                    expression = this.getExpression(elements(elementIndex));
                case 'AutosarCalibration'     
                    baseRegion = this.getCodeInfoUtils.getAutosarCalibrationBaseRegion(implementation);
                    if ~isempty(baseRegion)
                        % for subcomponent/model reference simulations, 
                        % get the expression of the BaseRegion, e.g. the
                        % struct containing the parameters
                        expression = this.getExpression(baseRegion);
                    else
                        % for component simulations, no implementation in
                        % this case, getExpression returns the global data
                        % defined in the stub e.g. Rte_CData_*_data
                        expression = this.getExpression(implementation);
                    end
                otherwise                    
                    expression = this.getExpression(implementation);
            end                                                            
            % add subExpression
            expression = [expression subExpression];            
        end
        
        function data = getAccessFunctionsData(this)
            data = this.AccessFcnsData;
        end
        
                
        function expression = getExpression(this, implementation)
            switch this.codeInfoUtils.class(implementation)
                case 'BasicAccessFunctionExpression'
                    ioAccessMode = implementation.IOAccessMode;
                    switch ioAccessMode
                        case {'ByReference', 'BY_REFERENCE', ...
                                'ByValue', 'BY_VALUE'}
                            expression = rtw.pil.AccessFunctions.getExpression(implementation.Prototype);
                        otherwise
                            assert(false, 'Only ByReference or ByValue access mode are supported');
                    end
                otherwise
                    expression = implementation.getExpression;
                    if rtw.pil.TargetInterface.isGetSetFunctionAccess(implementation) || ...
                            this.codeInfoUtils.isa(implementation,'ClassMethodExpression')
                        
                        if this.codeInfoUtils.isa(implementation,'ClassMethodExpression')
                            % For class methods, need to strip off model object
                            % and trailing round braces to allow it's use as a
                            % valid variable.
                            % This is needed to transform rtObject.getIn1() into
                            % getIn1 which can be used (with the modification
                            % below) as a valid identifier.
                            expression = regexprep(expression,'^[^.]*\.','');
                            expression = regexprep(expression,'\(\)$','');
                        end
                        % use a different name to the read / write functions
                        expression = ['xsd_' expression];
                    end
                    % remove the hidden root subsystem, see g1314398
                    expression = strrep(expression,'.TmpModelReferenceSubsystem','');
            end
        end
    end        
    
    methods (Access = 'public', Static)
        function isArray = isArrayInCode(argInfoOrEmbTypeObj, dimensions)
            isArray = (dimensions > 1) || rtw.pil.TargetInterface.isArrSizeOne(argInfoOrEmbTypeObj);
        end
        
        function isArrSzeOne = isArrSizeOne(argInfoOrEmbTypeObj)
            if (isstruct(argInfoOrEmbTypeObj))
                embT = argInfoOrEmbTypeObj.Implementation.Type;
            else
                embT = argInfoOrEmbTypeObj;
            end
            
            assert(rtw.connectivity.CodeInfoTypeUtils.isaType(embT,'Type'));
            isArrSzeOne = embT.isMatrix && embT.ArrSizeOne;
        end
    end
        
    methods (Access = 'protected')   
        function writeOutputBody(this)
            this.writeSectionHeader;
            this.writeSectionIncludes;
            this.writeSectionDefines;
            this.writeSectionStorage;
            this.writeSectionStorageForAssignmentData;
            this.writeSectionXILIOData;   
            this.writeSectionGetSetFunctions;
            this.writeSectionDynamicAccessFunctions;
            this.writeSectionUserFunctions;
            this.writeSectionProcessParams;
            this.writeSectionUploadProfilingDataPoint;
            this.writeSectionGetDataTypeInfo;
            this.writeSectionCallerFunctions;
            this.writeSectionInit;
            this.writeSectionPause;
            this.writeSectionSystemInitialize;
            this.writeSectionSystemReset;
            this.writeSectionInitUDataProcessing;
            this.writeSectionInitSDataProcessing;
            this.writeSectionOutput;
            this.writeSectionUpdate;
            this.writeSectionInitYDataProcessing;
            this.writeSectionTerminate;
            this.writeSectionEnable;
            this.writeSectionDisable;
        end
        
        function writeSectionHeader(this)
            this.writer.writeCellLines({'/*';
                [' * File: ' this.SILPILWrapperUtils.getSILPILInterface.getWrapperSourceInfo];
                ' *';
                [' * ' this.SILPILWrapperUtils.getSILPILInterface.getSimModeAcronym ...
                ' generated interface for code: "' this.codeInfo.Name '"'];
                ' *';
                ' */'});
            this.writer.newLine;
        end
        
        function writeSectionIncludes(this)
            
            if ~isempty(this.SILPILWrapperUtils.getLoggingData)
                %Write out xil header file
                this.writer.wLine('#include <stddef.h>');
            end

            %Construct the list of model Header files based on header files
            %listed in CodeInfo
            %Get all functions from CodeInfo. This list needs maintaining
            %as codeInfo changes.
            allFcns = [this.codeInfo.InitializeFunctions, ...
                this.codeInfo.OutputFunctions, ...
                this.codeInfo.UpdateFunctions, ...
                this.codeInfo.TerminateFunctions];
            
            %Construct a full list of header files, then remove duplicates
            headerList = cell(numel(allFcns),1);
            for fcnIdx  = 1 : numel(allFcns)
                headerList{fcnIdx} = allFcns(fcnIdx).Prototype.HeaderFile;
            end
            
            % Explicitly add model.h as it may not be part of header files
            % listed in codeInfo (e.g. for AUTOSAR)
            modelH = this.codeInfoUtils.getModelHeader;
            
            % Imported Grouped storage classes might also have a header
            % associated
            lDIs = [this.codeInfo.Inports, ...
                this.codeInfo.Outports, ...
                this.codeInfo.DataStores, ...
                this.codeInfo.Parameters];
            lImportedGroupedHeaderFiles = this.getGroupedCSCHeaderFiles(lDIs);
            
            lPrivateHader = this.SILPILWrapperUtils.getClientInterface.getPrivateHeader(this.SILPILWrapperUtils);
            
            % include rte header file
            if this.codeInfoUtils.UseMF0CodeDescriptor && ...
                    ~isempty(this.codeInfo.RuntimeEnvironment)
                headerList{end+1} = 'rte.h';
            end
            
            %Model.h should be included before SimulinkFunctions headers
            %because it includes the Variants definitions if any.
            headerList = unique([ ...
                modelH; ...
                headerList; ...
                lImportedGroupedHeaderFiles(:); ...
                lPrivateHader], 'stable');
                                   
            %Write out individual header files.
            for headerIdx = 1:numel(headerList)
                % For AUTOSAR target, the runnable declarations are not
                % generated and hence the HeaderFile would be empty
                if ~isempty(headerList{headerIdx})
                    this.writer.wLine('#include "%s"',headerList{headerIdx});
                end
            end
            
            %Write out xil header file. Put this after any generated
            %application includes to avoid redefinitions
            this.writer.wLine('#include "xil_interface.h"');
            
            isCodeInstrumentationEnabled =...
                this.SILPILWrapperUtils.isCodeInstrumentationEnabled ||...
                this.SILPILWrapperUtils.isProfilingEnabled;
            
            if isCodeInstrumentationEnabled
                [~, ~, ~, hProfilingHeaderFull] = this.SILPILWrapperUtils.getSILPILInterface...
                    .getSourceFileProfiling;
                [~,f,e] = fileparts(hProfilingHeaderFull);
                hProfilingHeaderShort = [f e];
                
                this.writer.writeLine('#include "%s"', ...
                                      hProfilingHeaderShort);
            end    


            
            this.writer.writeCellLines...
                ({'/* Functions with a C call interface */';
                '#ifdef __cplusplus';
                'extern "C" {';
                '#endif';
                '#include "xil_data_stream.h"'});                 
            if isCodeInstrumentationEnabled
                this.writer.wLine('#include "codeinstr_data_stream.h"');
            end
            this.writer.writeCellLines...
                ({'#ifdef __cplusplus';
                '}';
                '#endif'});
            
            % check for and include unique header files for Get/Set
            % functions                        
            dataInterfaces = this.getDataInterfacesForGetSetFunctionProcessing;
            headerFiles = {};
            for dataInterfaceIdx = 1:length(dataInterfaces)
                dataInterface = dataInterfaces(dataInterfaceIdx);
                implementation = dataInterface.Implementation;
                if rtw.pil.TargetInterface.isGetSetFunctionAccess(implementation)
                    headerFile = implementation.HeaderFile;
                    if isempty(headerFile)
                        % get set belong to different interface types, i.e.
                        % inports, outports, parameters and datastore
                        interfaceType = 'unspecified';
                        diString = this.codeInfoUtils.getDataInterfaceString(dataInterface, interfaceType);
                        DAStudio.error('PIL:pil:GetSetHeaderFileMissing', ...
                                       diString);                        
                    else
                        headerFiles{end+1} = headerFile; %#ok<AGROW>
                    end
                end
            end
            % remove duplicates
            headerFiles = unique(headerFiles);
            % include
            if ~isempty(headerFiles)
                this.writer.writeLine('/* Get/Set function header file(s) */');      
                for headerFileIdx = 1:length(headerFiles)
                    headerFile = headerFiles{headerFileIdx};
                   this.writer.writeLine('#include "%s"', headerFile);
                end             
            end
            this.writer.newLine;                        
        end                
        
        function writeSectionDefines(this)             
            if this.SILPILWrapperUtils.isMatFileLoggingEnabled
                this.writeSectionDefinesMatFileLogging();
            end
        end
        
        function writeSectionStorage(this)  
            % reset list of defined data so we can track duplicates
            this.DefinedDataList = containers.Map.empty;                
                        
            % arguments of constructor function (C++ only)
            dataInterfaces = rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfoUtils.getClassConstructorActualArgs), 'constructorArg');
            this.writeSectionStorageForData(dataInterfaces);
            
            % signals
            %
            % need to define inactive data
            filterInactiveInterfaces = false;
            dataInterfaces = [rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                this.getInportIndicesForStorage, ...
                'input', filterInactiveInterfaces), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfo.Outports), ...
                'output', filterInactiveInterfaces)];
            
            % we need to allocate memory for IO ports even if they are not
            % active, because they may be mapped as arguments in an active
            % function interface (e.g. in model step)
            mustAllocate = true;
            this.writeSectionStorageForData(dataInterfaces, mustAllocate);            

            % all shared data stores
            dataInterfaces = [rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getReaderSharedDataStores), 'dsread'), ...
                            rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getWriterSharedDataStores), 'dswrite'), ...
                            rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getSharedLocalDataStores), 'sharedLocalDataStore')];            
            this.writeSectionStorageForData(dataInterfaces);
                                        
            % params                        
            dataInterfaces = rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                                this.getParamIndicesForStorage, 'parameter');
            this.writeSectionStorageForData(dataInterfaces);
            
            %Allocation Function Required Data
            allocFunction = this.codeInfo.AllocationFunction;
            if ~isempty(allocFunction) && ...
                    strcmp(this.SILPILWrapperUtils.getConfigInterface.getParam('SuppressErrorStatus'), 'off')
                this.writer.wLine('const char *RT_MEMORY_ALLOCATION_ERROR = "memory allocation error";');
            end
            
            % Need to define storage for server functions input and output
            % args.
            serverFunctionsInfo = this.SILPILWrapperUtils.getServerFunctionsInfo;
            dataInterfaces = rtw.pil.DataInterface.empty;
            for svrIdx = 1:length(serverFunctionsInfo)
                currServerFcn = serverFunctionsInfo(svrIdx);
                % Storage is not needed for:
                % - local server functions
                % - global functions that are not called from outside the component
                if ~(currServerFcn.IsScoped || currServerFcn.IsInvokedInModelTaskOnly)
                    dataInterfaces = [dataInterfaces serverFunctionsInfo(svrIdx).getAllCodeInfoDataInterfaces]; %#ok<AGROW>
                end
            end
            this.writeSectionStorageForData(dataInterfaces);
            
            % Need to define storage for caller functions input and output
            % args
            callerFunctionsInfo = this.getCallerFunctionsInfo;
            dataInterfaces = rtw.pil.DataInterface.empty;
            for callIdx = 1:length(callerFunctionsInfo)
                dataInterfaces = [dataInterfaces callerFunctionsInfo(callIdx).getAllCodeInfoDataInterfaces]; %#ok<AGROW>
            end
            this.writeSectionStorageForData(dataInterfaces);

            % allocate storage for more data if necessary
            this.writeSectionStorageHook;
        end
        
        function writeSectionStorageInternalData(this)                      
            % allocate storage for internal data            
            dataInterfaces = rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfo.InternalData), ...
                'internal');
            this.writeSectionStorageForData(dataInterfaces);
        end
        
        function callerFunctionsInfo = getCallerFunctionsInfo(this)
            % return the caller functions which are not defined on the
            % target. SIL/PIL needs to provide the implementation by going
            % back to Simulink and requesting the service
            callerFunctionsInfo = this.getCodeInfoUtils.getUndefinedCallerFunctionsInfo;
        end
        
        function needsDeclaration = needsExternDeclaration(this, implementation)
            % For exported CSC data, the DEFINITION is always generated
            % into the source file.  However, code placement options in the
            % configuration set allows the (extern) DECLARATION to either
            % be generated into a header file (and the header file
            % #included by any source files that use the data) or you can
            % put a separate extern declaration into each of the source
            % files. In the latter case, the SIL/PIL wrapper would need to
            % these CSCs as extern to avoid compilation errors.
            
            needsDeclaration = false; %assume            
            storageSpecifier = this.getCodeInfoUtils.getStorageSpecifier(...
                implementation);
            if strcmp(storageSpecifier, 'extern')
                baseImplementation = this.getBaseStorageImplementation(implementation);
                if isempty(baseImplementation.DeclarationFile) &&...
                        ~isempty(baseImplementation.DefinitionFile)
                    needsDeclaration = true;
                end
            end
        end
        
        function hasOnlyLocalAccess = hasOnlyLocalAccess(this, dataInterface)
            % override in subclass
            
            % determine the data interfaces for all the server and caller
            % functions arguments
            serverFunctionsInfo = this.SILPILWrapperUtils.getServerFunctionsInfo;
            callerFunctionsInfo = this.getCallerFunctionsInfo;
            serverAndCallerFunctionsDataInterfaces = [];            
            for svrIdx = 1:length(serverFunctionsInfo)
                serverAndCallerFunctionsDataInterfaces =...
                    [serverAndCallerFunctionsDataInterfaces...
                    serverFunctionsInfo(svrIdx).getAllDataInterfaces]; %#ok<AGROW>
            end
            
            for callerIdx = 1:length(callerFunctionsInfo)
                serverAndCallerFunctionsDataInterfaces =...
                    [serverAndCallerFunctionsDataInterfaces...
                    callerFunctionsInfo(callerIdx).getAllDataInterfaces]; %#ok<AGROW>
            end

            foundInterface = false;
            for idx = 1:numel(serverAndCallerFunctionsDataInterfaces)
                if isequal(serverAndCallerFunctionsDataInterfaces(idx), dataInterface)
                    foundInterface = true;
                    break;
                end
            end
            if rtw.pil.TargetInterface.isGetSetFunctionAccess(dataInterface.Implementation) || ...
                    foundInterface || ...
                    isa(dataInterface.Implementation, 'coder.descriptor.ClassMethodAccess')
                % 1) intermediate get/set function storage is static
                % 2) Caller and server functions arguments are static
                hasOnlyLocalAccess = true;
            else                    
                hasOnlyLocalAccess = false;
            end
        end
        
        % add identifier prefix if applicable
        function writeSectionStorageForDataAddPrefix(this, dataInterface, implementation)
            if this.hasOnlyLocalAccess(dataInterface) && ...
                    ~(this.codeInfoUtils.isa(implementation, 'CustomExpression') || ...
                    this.codeInfoUtils.isa(implementation, 'ClassMethodExpression'))
                % prepend a tag to the identifier to de-risk name
                % clashes with global storage defined by the
                % component
                %
                % Note: we assume that writeSectionStorageForData
                % is called before any other functions that access
                % the Identifier.
                %
                % "xsd" stands for "XIL static data"
                tag = 'xsd_';
                id = implementation.Identifier;
                if isempty(regexp(id, ['^' tag], 'once'))
                    implementation.Identifier = [tag implementation.Identifier];
                end
            end
        end
        
        function writeSectionStorageForData(this, xilDataInterfaces, varargin)
            assert(isa(xilDataInterfaces, 'rtw.pil.DataInterface'), ...
                'xilDataInterfaces must be an rtw.pil.DataInterface');
            % parse varargin
            p = inputParser;
            p.addOptional('MustAllocate', false, ...
                @(x)validateattributes(x,{'logical'},{'scalar'}));
            p.parse(varargin{:});
            mustAllocate = p.Results.MustAllocate;
            
            for i=1:length(xilDataInterfaces)
                xilDataInterface = xilDataInterfaces(i);
                dataInterface = xilDataInterface.getRTWDataInterface;
                % if the Data belongs to an active variant or if we need to
                % allocate it anyway
                if mustAllocate || (rtw.pil.CodeInfoData.isInterfaceActive(dataInterface) &&...
                        rtw.connectivity.CodeInfoUtils.isDataInterfaceAccessible(dataInterface))
                    
                    if this.codeInfoUtils.isa(dataInterface.Implementation, 'TypedCollection')
                        implementationList = this.codeInfoUtils.getArray(dataInterface.Implementation.Elements);
                    else
                        implementationList = dataInterface.Implementation;
                    end
                    
                    for idx = 1:length(implementationList)
                        implementation = implementationList(idx);
                        if ~this.isDefined(implementation) && ~this.codeInfoUtils.isImportedGroupedCSC(implementation)
                            this.writeInterfaceData(xilDataInterface, implementation);
                        elseif this.codeInfoUtils.isa(implementation,'ClassMethodExpression')
                            this.writeInterfaceData(xilDataInterface, implementation);
                        else
                            if this.needsExternDeclaration(implementation)
                                this.writeDeclarationData(xilDataInterface, implementation);
                            end
                        end
                        
                        if (this.codeInfoUtils.isa(implementation,'PointerExpression') && ...
                                ~implementation.TargetRegion.isDefined) && ...
                                isempty(this.codeInfo.AllocationFunction)
                            
                            assert(this.codeInfoUtils.isa(implementation.TargetRegion, 'Variable'))
                            this.writeInterfaceData(xilDataInterface, implementation.TargetRegion);
                            
                        end
                        
                        if xilDataInterface.NeedsIOTargetTransform
                            if rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(implementation)
                                continue; %Skip processing for items with Opaque Regions
                            elseif ~isempty(implementation.DynamicMemory)
                                %Define additional extra variables located in
                                %the dynamic region for the interface.
                                staticVarList = implementation.DynamicMemory.StaticVariables;
                                if ~isempty(staticVarList)
                                    elements = this.codeInfoUtils.getArray(staticVarList.Elements);
                                    for eleIdx = 1:numel(elements)
                                        if ~this.isDefined(elements(eleIdx))
                                            this.writeInterfaceData(xilDataInterface, elements(eleIdx));
                                            this.setImplementationOwner(elements(eleIdx), 'PIL');
                                        end
                                    end
                                else
                                    staticVar = implementation.DynamicMemory.StaticVariable;
                                    if ~isempty(staticVar) && ~this.isDefined(staticVar)
                                        this.writeInterfaceData(xilDataInterface, staticVar);
                                        this.setImplementationOwner(staticVar, 'PIL');
                                    end
                                end
                                sizeVar = implementation.DynamicMemory.SizeVariable;
                                if ~isempty(sizeVar) && ~this.isDefined(sizeVar)
                                    this.writeInterfaceData(xilDataInterface, sizeVar);
                                end
                                indexVar = implementation.DynamicMemory.IndexVariable;
                                if ~isempty(indexVar) && ~this.isDefined(indexVar)
                                    this.writeInterfaceData(xilDataInterface, indexVar);
                                end
                            end
                        end
                    end
                end
            end
            
            % Handle any grouped CSCs
            if mustAllocate
                this.writeGroupedInterfaceData(xilDataInterfaces);
            end
        end
          
        function writeDeclarationData(this, xilDataInterface, implementation)
            assert(isa(xilDataInterface, 'rtw.pil.DataInterface'), ...
                'xilDataInterface must be an rtw.pil.DataInterface');
            % get the base storage implementation (e.g. base struct storage,
            % base array expression storage, target variable, etc)
            storageImplementation = this.getBaseStorageImplementation(implementation);
            this.validateBaseStorageImplementation(implementation, storageImplementation);
            declareExpression = this.getDeclareExpression(storageImplementation);
            this.writer.writeLine('%s', declareExpression);
            
            % add newline
            this.writer.newLine;
        end
        
        function writeInterfaceData(this, xilDataInterface, implementation)            
            assert(isa(xilDataInterface, 'rtw.pil.DataInterface'), ...
                'xilDataInterface must be an rtw.pil.DataInterface');       
            dataInterface = xilDataInterface.getRTWDataInterface;
            % get the base storage implementation (e.g. base struct storage,
            % base array expression storage, target variable, etc)
            storageImplementation = this.getBaseStorageImplementation(implementation);
            this.validateBaseStorageImplementation(implementation, storageImplementation);
            
            % add identifier prefix if applicable
            this.writeSectionStorageForDataAddPrefix(dataInterface, storageImplementation);                             
            
            needsReplaceType = true;
            isPointerToMatrix = this.getIsPointerToMatrix(storageImplementation, dataInterface);
            
            % One case where the Implementation can be undefined and
            % the storageImplementation can be defined is:
            % (With reusable code) InternalData(i) corresponding to Parameter  
            % is a PointerExpression and InternalData(i+idx) 
            % corresponding to RTM is a PointerVariable. At "i" 
            % Implementation.BaseRegion.TargetVariable gets defined.
            % At "i+idx", Implementation is undefined but 
            % Implementation.TargetVariable is already defined.
            if (~this.isDefined(storageImplementation))
                % In-the-Loop owner
                this.setImplementationOwner(storageImplementation, ...
                                            'PIL');
                this.writeCPPModelObjForIOAccess(storageImplementation);
            end
            isPointerVariable = this.codeInfoUtils.isa(implementation, 'PointerVariable');
            definedData = {};
            definedExpressions = {};
                        
            if this.isCPPModelObject(storageImplementation)
                storage_size_str = this.getStorageSizeStrAndIndex(...
                    storageImplementation, xilDataInterface.DimsPreserved);
                storageVolatile = this.getStorageVolatileQualifier(storageImplementation);
                storageSpecifier = this.getStorageSpecifier(dataInterface);
                
                assert(~isPointerVariable, 'CPP Model Object was a pointer');
                assert(isempty(storageSpecifier), 'CPP Model Object has storage specifier.');
                assert(isempty(storageVolatile), 'CPP Model Object is volatile.');
                assert(isempty(storage_size_str), 'CPP Model Object is non-scalar.');
                
                %Write out model object
                dataExpression = this.getExpression(storageImplementation);
                typeIdentifier = this.getStorageTypeIdentifier( ...
                    storageImplementation, ...
                    needsReplaceType, ...
                    isPointerToMatrix);
                this.writeModelObjectAccessFunction(dataExpression, typeIdentifier);
                
                definedData{end+1} = dataExpression;
                definedExpressions{end+1} = {};
            else
                isPointerAllocd = this.isPointerVariableAllocd(dataInterface, isPointerVariable);
                
                if ~isPointerAllocd
                    
                    fullExpression = this.getDefineExpression(storageImplementation, xilDataInterface);
                    
                    if ~(this.codeInfoUtils.isa(storageImplementation, 'BasicAccessFunctionExpression') && xilDataInterface.NeedsIOTargetTransform)
                        this.writer.writeLine('/* interface data */');
                        this.writer.writeLine(fullExpression);
                    end

                    if (this.codeInfoUtils.isa(storageImplementation, 'BasicAccessFunctionExpression') && ~xilDataInterface.NeedsIOTargetTransform)
                        this.AccessFcnsData{end+1} = fullExpression;
                    end


                    % add newline
                    this.writer.newLine;
                    
                    dataExpression = this.getExpression(storageImplementation);
                    definedData{end+1} = dataExpression;
                    
                    if this.codeInfoUtils.isa(storageImplementation, 'ClassMethodExpression') || ...
                            this.codeInfoUtils.isa(storageImplementation, 'StructAccessorVariable')

                        % For Get/Set Method access, cache away get/set
                        % call lines whilst creating interface data
                        
                        getArgModifier = '';
                        if this.codeInfoUtils.isa(storageImplementation, 'StructAccessorVariable')
                            % for struct accessor methods, get accessor
                            % methods from parent application, and modify
                            % get method argument call qualifier
                            storageImplementation = storageImplementation.Accessor;
                            getArgModifier = '&';
                        end
                        expressionValue = struct('Get','','Set','','OKToSetBeforeInit',true);
                        if ~isempty(storageImplementation.getReadExpression)                            
                            if isPointerToMatrix
                                typeIdentifier = this.getStorageTypeIdentifier( ...
                                    storageImplementation, ...
                                    needsReplaceType, ...
                                    isPointerToMatrix);
                                typeCast = sprintf('( %s ) ', typeIdentifier);
                            else
                                typeCast = '';
                            end
                            expressionValue.Get = sprintf('%s = %s%s', dataExpression, typeCast, storageImplementation.getReadExpression);
                        end
                        if ~isempty(storageImplementation.getWriteExpression)
                            
                            expressionValue.Set = sprintf('%s( %s%s )', ...
                                storageImplementation.getWriteExpression, ...
                                getArgModifier, dataExpression);
                            if isempty(dataInterface.Timing)
                                % Need to spot Parameter set methods. Using
                                % a lack of timing information to do
                                % that. These won't be updated prior to
                                % calling Initialize, so we don't override
                                % any values in the generated code.
                                expressionValue.OKToSetBeforeInit = false;
                            end
                        end
                        definedExpressions{end+1} = expressionValue;
                    else
                        %Otherwise nothing to add
                        definedExpressions{end+1} = {};
                    end
                end
                        
                if isPointerVariable
                    % Not aware of any cases that the pointer itself is
                    % volatile, so not going to support that concept
                    % (not sure if volatile would be flagged on the
                    % pointertype or on its basetype)
                    %
                    % storageVolatile will be used to correctly define
                    % the pointer as a pointer to volatile if
                    % required
                    
                    % In-the-Loop owner
                    this.setImplementationOwner(implementation, ...
                                                'PIL');
                    dataExpressionPtr = this.getExpression(implementation);
                    definedData{end+1} = dataExpressionPtr;                    
                    definedExpressions{end+1} = '';
                    
                     % define pointer
                    this.writer.writeLine('/* interface (pointer) data */');
                    pointerDefineExpr = this.getPointerDefineExpression(implementation, storageImplementation, xilDataInterface.DimsPreserved, isPointerAllocd, isPointerToMatrix);
                    this.writer.writeLine('%s', pointerDefineExpr);
                    
                    % add newline
                    this.writer.newLine;                    
                end
            end            
            % check for duplicate definitions
            %
            % make sure we captured the defined data for all code paths
            % above
            assert(~isempty(definedData), ...
                'Some data must be defined.');
            assert(~any(this.DefinedDataList.isKey(definedData)), ...
                'Duplicate data defined: %s', char(definedData));
            for idx = 1:numel(definedData)
                this.DefinedDataList(definedData{idx}) = definedExpressions{idx};
            end
        end
        
        function writeGroupedInterfaceData(this, xilDataInterfaces)
            % Write definitions for imported grouped CSCs
            lRTWDIs = [];
            for i = 1:numel(xilDataInterfaces)
                lRTWDIs = [lRTWDIs, xilDataInterfaces(i).getRTWDataInterface];%#ok
            end
            lImplementations = this.getUniqueRTWDataImplementationWithGroupedCSC(lRTWDIs);
            arrayfun( @(x)this.writer.wLine('%s', this.getGroupedDefineExpression(x)), ...
                    lImplementations);
        end
        
        function indices = getParamIndicesForStorage(this)
            indices = [this.codeInfoUtils.getModelArgParams ...
                       this.SILPILWrapperUtils.getSupportedTunableWorkspaceParams];
        end 
        
        function indices = getInportIndicesForStorage(this)
           % by default, process all Inports
           % may be overridden in subclass to change behavior
           indices = 1:length(this.codeInfo.Inports); 
        end
        
        function writeSectionStorageForAssignmentData(this)
            % list of data interfaces to process assignment storage requirements for                                                                                             
            serverFunctionsInfo = this.SILPILWrapperUtils.getServerFunctionsInfo;            
            serverFunctionsDataElements = [];
            for svrIdx = 1:length(serverFunctionsInfo)
                serverFunctionsDataElements = [serverFunctionsDataElements serverFunctionsInfo(svrIdx).getAllDataElements]; %#ok<AGROW>
            end
            
            callerFunctionsInfo = this.getCallerFunctionsInfo;                     
            callerFunctionsDataElements = [];
            for svrIdx = 1:length(callerFunctionsInfo)
                callerFunctionsDataElements = [callerFunctionsDataElements callerFunctionsInfo(svrIdx).getAllDataElements]; %#ok<AGROW>
            end
            
            dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfo.Inports), 'input'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfo.Outports), 'output'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                this.getParamIndicesForStorage, 'parameter'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getReaderSharedDataStores), 'dsread'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getWriterSharedDataStores), 'dswrite'),...
                                serverFunctionsDataElements,...
                                callerFunctionsDataElements];
            % process all logging data
            loggingData = this.SILPILWrapperUtils.getLoggingData;                            
            for i=1:length(loggingData)
                dataElements = [dataElements loggingData(i).getTargetDataElements]; %#ok<AGROW>
            end
                               
            for dataElementIdx = 1:length(dataElements)
                cData = dataElements(dataElementIdx);                                
                if ~cData.isGetSetViaPointer
                    % get assignment data info
                    [dataType, identifier] = cData.getDataInterface.getAssignmentDataInfo(cData);
                    if cData.getNumInstances == 1
                        cDataInstancesStr = '';
                    else
                        cDataInstancesStr = ['[' cData.getSymbolicNumInstances ']'];
                    end
                    storageSize.Numerical = cData.getNumElements;
                    storageSize.Symbolic = cData.getSymbolicNumElements;
                    
                    if storageSize.Numerical == 1
                        storage_size_str = '';
                    else
                        storage_size_str = ['[' storageSize.Symbolic ']'];
                    end
                    
                    this.writer.writeLine('/* storage for assignment data transfer */');
                    this.writer.writeLine(['static ' dataType ' ' ...
                        identifier ...
                        cDataInstancesStr ...
                        storage_size_str ';']);
                    % add newline
                    this.writer.newLine;
                end
            end
        end
        
        function writeSectionXILIOData(this)
            % Process input signals with DirectFeedThrough
            this.writeIOxilIOData('mdlOutputsInputs', ...
                'output_U');
            
            % Process input signals with no DirectFeedThrough
            this.writeIOxilIOData('mdlUpdateInputs',...
                'update_U');
            
            % Process output signals
            this.writeIOxilIOData('Outputs', 'Y');
            % Process output states
            this.writeIOxilIOData('States', 'S');
            
            % process const output signals
            this.writeConstOutputxilIOData;            
            % process init I/O
            this.writeFuncXILIOData(this.codeInfo.InitializeFunctions, 'init');            
            % process term I/O
            this.writeFuncXILIOData(this.codeInfo.TerminateFunctions, 'term');      
            % process workspace parameters for dynamic initialization /
            % tuning
            tag = '_ws_params';            
            tunableParamsIndices = this.SILPILWrapperUtils.getSupportedTunableWorkspaceParams;
            inTheLoopType = this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType;
            instPArgsIndices = this.codeInfoUtils.getInstPArgumentIndices(inTheLoopType);
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                [tunableParamsIndices, instPArgsIndices], 'parameter');
            if ~isempty(dataElements)
                this.writePILIODataTable(dataElements, tag);
            end
        end
        
        function writeSectionUserFunctions(this)             %#ok<MANU>
        end
        
        function writeSectionProcessParams(this)
            this.writer.writeCellLines({'/* In-the-Loop Interface functions - see xil_interface.h */';
                '';
                'XIL_INTERFACE_ERROR_CODE xilProcessParams(uint32_T xilFcnId) {';});
            this.writer.incIndent;
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}';});            
            this.writeSectionProcessParamsBody;
            this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
            this.writer.decIndent;            
            this.writer.writeLine('}');                        
            this.writer.newLine;
        end
        
        function writeSectionProcessParamsBody(this)                                    
            % make any required assignments                                    
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(...
                this.SILPILWrapperUtils, ...
                this.getParamIndicesForStorage, 'parameter');
            this.writeInputAssignments(dataElements);                                    
        end    
        
        function fcnCallExpression = getFunctionCallExpressionForServerFunction(this, serverFunctionInfo)
            % getFunctionCall errors out for Simulink Functions, so we will
            % implement this ourselves.
            
            codeInfoPrototype = serverFunctionInfo.getCodeInfoData.Prototype;
            
            % When we're interfacing with the generated code, 
            % codeInfoPrototype is authoritative for both
            % ERT and AUTOSAR workflows.
            serverFunctionName = codeInfoPrototype.Name;
            
            if this.SILPILWrapperUtils.isCPPEncap
                modelObjectInterface = this.getCPPModelObject;
                modelObjectVariable = modelObjectInterface.Implementation.getExpression;
                serverFunctionName = sprintf('%s.%s',modelObjectVariable,serverFunctionName);
            end
            
            % come up with the left-hand side expression as we could 
            % have a LHS return argument
            if serverFunctionInfo.hasReturnArg                
                returnArg = serverFunctionInfo.getDataInterfacesForArgs(...
                    serverFunctionInfo.getCodeArgPosition(codeInfoPrototype.Return));
                lhsExpression = this.getExpression(returnArg.Implementation);
                lhsExpression = [lhsExpression ' = '];
            else
                lhsExpression = '';
            end
            
            % add function name
            fcnCallExpression = [lhsExpression serverFunctionName '('];
            
            % add RHS arguments
            args = this.codeInfoUtils.getArray(codeInfoPrototype.Arguments);
            for codeInfoArgIdx = 1:length(args)
                arg = args(codeInfoArgIdx);
                codeArgPosition = serverFunctionInfo.getCodeArgPosition(arg);
                if serverFunctionInfo.isArgMultiInstanceRTM(codeArgPosition, arg)
                    % first argument to multi instance simulink function might be the self pointer
                    data = this.getCodeInfo.InternalData;
                    for i=1:length(data)
                        currData = data(i);
                        isRTMpointer = strcmp('RTModel', currData.GraphicalName);
                        if isRTMpointer
                            argDataInterface = currData;
                            assert(this.codeInfoUtils.isa(argDataInterface.Implementation,...
                                'PointerVariable'),...
                                'RTModel should be a pointer variable');
                            break;
                        end
                    end
                else
                    isRTMpointer = false;
                    argDataInterface = serverFunctionInfo.getDataInterfacesForArgs(codeArgPosition);
                end
                argImp = argDataInterface.Implementation;
                isInputArg = strcmp(arg.IOType, 'INPUT');
                if isInputArg
                    storageImplementation = this.SILPILWrapperUtils.getBaseStorageImplementation(argImp);
                    isScalar = ~rtw.connectivity.CodeInfoTypeUtils.isVector(...
                        storageImplementation.Type, this.SILPILWrapperUtils.getClientInterface);
                    if rtw.connectivity.CodeInfoTypeUtils.isBusOrStruct(argImp.Type) && isScalar
                        fcnCallExpression = [fcnCallExpression '&' getExpression(this, argImp) ',']; %#ok<AGROW>
                    elseif isScalar && (argImp.Type.isPointer || arg.Type.isPointer) && ~isRTMpointer
                        % Scalar pass-by-pointer
                        fcnCallExpression = [fcnCallExpression '&' getExpression(this, argImp) ',']; %#ok<AGROW>
                    else
                        fcnCallExpression = [fcnCallExpression getExpression(this, argImp) ',']; %#ok<AGROW>
                    end
                else % output or input-output
                    isVector = rtw.connectivity.CodeInfoTypeUtils.isVector(argImp.Type, ...
                        this.SILPILWrapperUtils.getSILPILInterface.getClientInterface);
                    if isVector
                        fcnCallExpression = [fcnCallExpression getExpression(this, argImp) ',']; %#ok<AGROW>
                    else
                        fcnCallExpression = [fcnCallExpression '&' getExpression(this, argImp) ',']; %#ok<AGROW>
                    end
                end
            end
            
            if strcmp(fcnCallExpression(end), ',')
                fcnCallExpression = fcnCallExpression(1:end-1);
            end
            fcnCallExpression = [fcnCallExpression ')'];
        end
        
        function memcpyExpression = getCallerInputArgMemcpyExpression(this,...
                argInfo, argDataInterface, argCData)
            % return the appropriate memcpy expression            
            
            if argInfo.IsInOutArg
                [inputAddressValue, outputAddressValue] =....
                    getCallerOutputArgMemcpyInAndOutAddressValues(this,...
                    argInfo, argDataInterface, argCData);
            else
                [inputAddressValue, outputAddressValue] =....
                    getCallerInputArgMemcpyInAndOutAddressValues(this,...
                    argInfo, argDataInterface, argCData);
            end
            memcpyExpression = ['memcpy(' outputAddressValue ', '...
                inputAddressValue ', (' argInfo.NumElements ...
                ') * sizeof(' argInfo.ArgBaseType '));'];
        end
        
        function [inputAddressValue, outputAddressValue] =....
                getCallerInputArgMemcpyInAndOutAddressValues(this,...
                argInfo, argDataInterface, argCData) %#ok<INUSL>
            
            tgtVarName = argInfo.Implementation.Identifier;
            srcArgVarExpr = argInfo.ArgName;
            if(argInfo.IsVoidPointer)
                % cast to actual type to avoid compiler warnings regarding
                % "void *" type and also allow non-zero indexing for arrays
                srcArgVarExpr = sprintf('((%s)%s)',argInfo.ArgType,srcArgVarExpr);
            end
            if argInfo.IsBus
                inputAddressValue = srcArgVarExpr;
                outputAddressValue = ['&(' tgtVarName ')'];
            else
                outputAddressValue = ['&(' this.getElementExpression(argInfo.Implementation, ...
                    argCData, ...
                    0)  ')'];
                if argInfo.Implementation.Type.isPointer || this.isArrSizeOne(argInfo)
                    % Follow FPC Type
                    inputAddressValue = srcArgVarExpr;
                else
                    inputAddressValue = strrep(outputAddressValue, tgtVarName, srcArgVarExpr);
                end
            end
        end
        
        function [inputType, baseInputType] = getCallerInputArgType(...
                this, argInfo, argDataInterface, argCData) %#ok<INUSL>
            % determine the caller input argument type we will use
            if argCData.isStructElement
                baseInputType = argCData.getStructArrayTopLevelInfo;
            else
                baseInputType = argCData.getType.Identifier;
            end
            
            % determine whether we need pointer "*" in this input argument
            % type's expression. The default rules are:
            %
            %  Scalar inputs are passed by value 
            %  Complex inputs are passed by value
            %  Enum inputs are passed by value
            % 
            %  Array inputs are passed using expression "T a[N]"
            %
            %  Bus Inputs are passed by pointer
            if argInfo.IsBus && ~argInfo.IsVector
                inputType = [baseInputType '*'];
            elseif argInfo.Implementation.Type.isPointer
                % Follow Function Prototype Control type
                inputType = [baseInputType '*'];
            else
                inputType = baseInputType;
            end
            
            % Add const qualifier
            % determine whether we need the const qualifier for this caller
            % argument            
            if (argInfo.IsInArg)
                argImplType = argInfo.Implementation.Type;
                if (argInfo.IsVector || argInfo.IsBus)
                    inputType = ['const ' inputType];
                else
                  if argImplType.isPointer 
                      if argImplType.BaseType.ReadOnly
                          inputType = ['const ' inputType];
                      end
                  elseif argImplType.ReadOnly
                      inputType = ['const ' inputType];
                  end
                end
                
                if argImplType.isPointer && argImplType.ReadOnly
                    inputType = [inputType 'const'];
                end
            end
        end
        
        function [inputAddressValue, outputAddressValue] =....
                getCallerOutputArgMemcpyInAndOutAddressValues(this,...
                argInfo, argDataInterface, argCData) %#ok<INUSL>
            srcArgVarExpr = argInfo.ArgName;
            if(argInfo.IsVoidPointer)
                % cast to actual type to avoid compiler warnings regarding
                % "void *" type and also allow non-zero indexing for arrays
                srcArgVarExpr = sprintf('((%s)%s)',argInfo.ArgType,srcArgVarExpr);
            end
            tgtVarName = argInfo.Implementation.Identifier;
            if (argInfo.IsVector && ~argInfo.IsBus)
                outputAddressValue = ['&(' this.getElementExpression(argInfo.Implementation, ...
                    argCData, ...
                    0)  ')'];
                if(this.isArrSizeOne(argInfo))
                    % Follow FPC Type
                    inputAddressValue = srcArgVarExpr;
                else
                    inputAddressValue = strrep(outputAddressValue, tgtVarName, srcArgVarExpr);
                end
            else
                inputAddressValue = srcArgVarExpr;
                outputAddressValue = ['&(' tgtVarName ')'];
            end
        end
            
        function memcpyExpression = getCallerOutputArgMemcpyExpression(this,...
                argInfo, argDataInterface, argCData)
            [inputAddressValue, outputAddressValue] =....
                getCallerOutputArgMemcpyInAndOutAddressValues(this,...
                argInfo, argDataInterface, argCData);
            memcpyExpression = ['memcpy(' inputAddressValue ...
                ', ' outputAddressValue  ', (' argInfo.NumElements ...
                ') * sizeof(' argInfo.ArgBaseType '));'];
        end
        
        function [outputType, baseOutputType] = getCallerOutputArgType(this,...
                argInfo, argDataInterface, argCData) %#ok<INUSL>
            % determine the caller output argument type we will use
            if argCData.isStructElement
                baseOutputType = argCData.getStructArrayTopLevelInfo;
            else
                baseOutputType = argCData.getType.Identifier;
            end
            
            % determine whether we need pointer "*" in this output argument
            % type's expression.       
            if argInfo.IsVector
                outputType = baseOutputType;
            else
                outputType = [baseOutputType '*'];
            end            
        end
        
        function storageSizeStr = getCallerArgStorageSizeString(this, argInfo) %#ok<INUSL>
            % return the storage size string for multidimensional signals
            if argInfo.IsVector
                storageSizeStr = ['[' argInfo.NumElements ']'];
            else
                storageSizeStr = '';
            end
        end
        
        function returnArgType = getCallerFunctionReturnArgType(this, callerFunctionInfo)            
            [hasReturnArg, codeInfoArg] = callerFunctionInfo.hasReturnArg;
            if hasReturnArg
                codeArgPosition = callerFunctionInfo.getCodeArgPosition(codeInfoArg);
                argInfo = this.getCallerArgInfo(callerFunctionInfo, codeInfoArg, codeArgPosition);
                returnArgType = argInfo.ArgBaseType;
            else
                % function does not have a return argument
                returnArgType = 'void';
            end
        end
        
        function writeSectionCallerFunctionTrailerReturnArg(this, callerFunctionInfo)
            if callerFunctionInfo.hasReturnArg
                codeInfoPrototype = callerFunctionInfo.getCodeInfoData.Prototype;
                returnArgDataInterface = callerFunctionInfo.getDataInterfacesForArgs(...
                    callerFunctionInfo.getCodeArgPosition(codeInfoPrototype.Return));
                returnArgExpression = this.getExpression(returnArgDataInterface.Implementation);
                this.writer.wLine('return %s;', returnArgExpression);
            else
                % no return
            end
        end
        
        function argInfo = getCallerArgInfo(this, callerFunctionInfo, codeInfoArg, codeArgPosition)
            
            % initialize structure
            argInfo = struct(...
                'ArgName',[],...
                'IsInArg',[],...
                'IsOutArg',[],...
                'IsInOutArg',[],...
                'IsVector',[],...
                'IsVoidPointer',[],...
                'NumElements', [], ...
                'IsBus',[],...                
                'StorageSizeStr',[],...
                'ArgType',[],...
                'ArgBaseType',[],...
                'MemcpyExpression',[],...
                'Implementation',[]...
                );
            
            
            argDataInterface = callerFunctionInfo.getDataInterfacesForArgs(codeArgPosition);
            argDataElements = callerFunctionInfo.getDataElementsForArgs(codeArgPosition);
            
            % the first data element has all the info we need
            argCData = argDataElements(1);
            
            % Determine the argument name and whether it is input or output
            % argument            
            argInfo.ArgName = callerFunctionInfo.getCodeInfoArgumentName(codeInfoArg);
            argInfo.IsInArg = strcmp(codeInfoArg.IOType, 'INPUT');
            argInfo.IsOutArg = strcmp(codeInfoArg.IOType, 'OUTPUT');
            argInfo.IsInOutArg = strcmp(codeInfoArg.IOType, 'INPUT_OUTPUT');
            argInfo.IsVoidPointer = callerFunctionInfo.isVoidPointerTypedArg(codeArgPosition);
            
            % determine whether we need the const qualifier for input args
            argInfo.Implementation = argDataInterface.Implementation;
            storageImplementation =...
                this.SILPILWrapperUtils.getBaseStorageImplementation(...
                argInfo.Implementation);
            argInfo.IsVector = rtw.connectivity.CodeInfoTypeUtils.isVector(...
                storageImplementation.Type, ...
                this.SILPILWrapperUtils.getClientInterface);            
            argInfo.NumElements = rtw.connectivity.CodeInfoTypeUtils.getSymbolicWidth(storageImplementation.Type);
            argInfo.IsBus = rtw.connectivity.CodeInfoTypeUtils.isBusOrStruct(...
                argInfo.Implementation.Type);
            
            % determine the storage size string for multidimensional
            % signals
            argInfo.StorageSizeStr = this.getCallerArgStorageSizeString(argInfo);
            
            % determine the argument type and memcpy expressions            
            if argInfo.IsInOutArg
                % code treats this as an output arg
                [argInfo.ArgType,argInfo.ArgBaseType] =...
                    this.getCallerOutputArgType(argInfo, argDataInterface, argCData);
                % copy from input to xil storage
                argInfo.MemcpyExpression{1} =...
                    this.getCallerInputArgMemcpyExpression(argInfo, argDataInterface, argCData);
                % copy from xil storage to output
                argInfo.MemcpyExpression{2} =...
                    this.getCallerOutputArgMemcpyExpression(argInfo, argDataInterface, argCData);
            else
                if argInfo.IsInArg
                    [argInfo.ArgType,argInfo.ArgBaseType] =...
                        this.getCallerInputArgType(argInfo, argDataInterface, argCData);
                    argInfo.MemcpyExpression =...
                        this.getCallerInputArgMemcpyExpression(argInfo, argDataInterface, argCData);
                else
                    [argInfo.ArgType,argInfo.ArgBaseType] =...
                        this.getCallerOutputArgType(argInfo, argDataInterface, argCData);
                    argInfo.MemcpyExpression =...
                        this.getCallerOutputArgMemcpyExpression(argInfo, argDataInterface, argCData);
                end
            end
        end
        
        
        function writeSectionCallerFunctionHeader(this, callerFunctionInfo, inArgsMemcpy)
            % copy input arguments to XIL local storage
            for ii = 1:(callerFunctionInfo.getNumInArgs + callerFunctionInfo.getNumInOutArgs)
                this.writer.writeLine(inArgsMemcpy{ii});
            end
            
            % make any required assignments
            % Note that if server is on target, there is no need to write 
            % output assignments because in this case there is no transfer
            % of data between host/target
            if ~callerFunctionInfo.getIsDefined
                dataElements = this.getCallerFunctionTargetToHostDataElements(callerFunctionInfo);
                this.writeOutputAssignments(dataElements);
            end
        end
        
        function writeSectionCallerFunctionBody(this, callerFunctionInfo)
            isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;                
            if isExecutionProfilingEnabled                        
                this.writer.wComment('freeze execution profiling timer during the call to Simulink');
                this.writer.writeLine('xilProfilingTimerFreeze();');
            end
            
            % execute function
            this.writer.wBlockStart('');
            this.writer.writeLine('uint32_T xilFcnId = 0;');
            this.writer.writeLine('uint32_T xilCommandIdx = %d;', callerFunctionInfo.getTID);
            this.writer.writeLine('uint32_T serverFcnId = %d;', callerFunctionInfo.getFcnId);
            this.writer.writeLine(['xilCommandDispatchAndResponse(XIL_CLIENT_SERVER_COMMAND,'...
                'xilCommandIdx, xilFcnId, serverFcnId, XIL_RESPONSE_CS_REQUEST_SERVICE, xilGetCurrentProcessMsgContext());']);
            this.writer.wBlockEnd(); % if xilCommandDispatchAndResponse
            
            if isExecutionProfilingEnabled
                this.writer.wComment('unfreeze execution profiling timer');
                this.writer.writeLine('xilProfilingTimerUnFreeze();');
            end
        end
        
        function writeSectionCallerFunctionTrailer(this, callerFunctionInfo, outArgsMemcpy)
            % make any required assignments
            dataElements = this.getCallerFunctionHostToTargetDataElements(callerFunctionInfo);
            
            % If this function is defined, there is no need to 
            % make any assignments for the return argument because
            % we simply return back the LHS of the called function.
            % For example, in code we will have something like this:
            %
            %    errorStatus = Rte_Call_Foo(...) {
            %
            %    /* call Foo on the target */
            %    xsd_errorStatus = Foo(...)
            %
            %    return xsd_errorStatus;
            %   }            
            if callerFunctionInfo.getIsDefined
                [hasReturnArg, codeInfoArg] = callerFunctionInfo.hasReturnArg;
                if hasReturnArg
                    returnArgDataElements = callerFunctionInfo.getDataElementsForArgs(...
                        callerFunctionInfo.getCodeArgPosition(codeInfoArg));
                    dataElements = setdiff(dataElements, returnArgDataElements, 'stable');
                end
            end
            
            % if server is on target, there is no need to write input
            % assignments because in this case there is no transfer of data
            % between host/target
            if ~callerFunctionInfo.getIsDefined
                this.writeInputAssignments(dataElements);
            end
            
            % copy from XIL local storage to output arguments
            for ii = 1:(callerFunctionInfo.getNumOutArgs + callerFunctionInfo.getNumInOutArgs)
                this.writer.writeLine(outArgsMemcpy{ii});
            end
            
            % finally, return the LHS argument if required
            this.writeSectionCallerFunctionTrailerReturnArg(callerFunctionInfo);                        
        end
        
        function writeSectionCallerFunctions(this)
            functionNames = {};
            callerFunctionsInfo = this.getCallerFunctionsInfo;
            for callerFcnIdx = 1:length(callerFunctionsInfo)
                callerFunctionInfo = callerFunctionsInfo(callerFcnIdx);
                functionName = callerFunctionInfo.getTargetFunctionName;
                
                if ~ismember(functionName, functionNames)
                    % filter duplicates
                    functionNames{end+1} = functionName; %#ok<AGROW>
                    
                    % create argString
                    argString = '';
                    inArgsMemcpy = [];
                    outArgsMemcpy = [];
                    args = this.codeInfoUtils.getArray(callerFunctionInfo.getCodeInfoData.Prototype.Arguments);
                    for codeArgPosition = 1:length(args)
                        % get the DataInterface and DataElements for this
                        % arg
                        arg = args(codeArgPosition);
                        argInfo = this.getCallerArgInfo(callerFunctionInfo, arg, codeArgPosition);
                        
                        if codeArgPosition ~= 1
                            % comma separate args
                            argString = [argString ', '];  %#ok<AGROW>
                        end
                        
                        % put it all together
                        if argInfo.IsVoidPointer
                            voidPtrTypeStr = strrep(argInfo.ArgType,argInfo.ArgBaseType,'void');
                            argString = [argString voidPtrTypeStr ' ' argInfo.ArgName]; %#ok<AGROW>
                        else
                            argString = [argString argInfo.ArgType ...
                                ' ' argInfo.ArgName argInfo.StorageSizeStr]; %#ok<AGROW>
                        end
                        
                        % collect memcpy expressions
                        IsInOutArg =  strcmp(arg.IOType, 'INPUT_OUTPUT') || isequal(arg.IOType, ...
                            coder.descriptor.types.IOTypes.INPUT_OUTPUT);
                        if IsInOutArg
                            inArgsMemcpy{end+1} = argInfo.MemcpyExpression{1}; %#ok<AGROW>
                            outArgsMemcpy{end+1} = argInfo.MemcpyExpression{2}; %#ok<AGROW>
                        else
                            if argInfo.IsInArg
                                inArgsMemcpy{end+1} = argInfo.MemcpyExpression; %#ok<AGROW>
                            else
                                outArgsMemcpy{end+1} = argInfo.MemcpyExpression; %#ok<AGROW>
                            end
                        end
                    end
                    
                    % get the caller function return argument type
                    returnArgType = this.getCallerFunctionReturnArgType(callerFunctionInfo);
                    
                    % write function prototype
                    this.writer.wBlockStart('%s %s(%s)', returnArgType, functionName, argString);
                    this.writer.newLine;
                    
                    % write header
                    this.writeSectionCallerFunctionHeader(callerFunctionInfo, inArgsMemcpy);
                    
                    % write body
                    this.writeSectionCallerFunctionBody(callerFunctionInfo);
                    
                    % write trailer
                    this.writeSectionCallerFunctionTrailer(callerFunctionInfo, outArgsMemcpy);
                                                       
                    this.writer.wBlockEnd(); %function
                end
            end
        end
        
        function writeSectionGetDataTypeInfo(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilGetDataTypeInfo(void) {');
            this.writer.incIndent;            
            this.writeSectionGetDataTypeInfoBody;
            this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
            this.writer.decIndent;
            this.writer.writeLine('}');                        
            this.writer.newLine;                        
        end
        
        function writeSectionGetDataTypeInfoBody(this)
            % send back XIL_RESPONSE_TYPE_SIZE for required floating point
            % types                                    
            this.writeSectionResponseTypeSize('single');
            this.writeSectionResponseTypeSize('double');            
        end                
        
        function writeSectionInit(this)
            this.writer.wBlockStart('XIL_INTERFACE_ERROR_CODE xilInitialize(uint32_T xilFcnId)');            
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writeSectionInitBody;
            this.writer.writeLine('return errorCode;');
            this.writer.wBlockEnd(); % XIL_INTERFACE_ERROR_CODE xilInitialize(uint32_T xilFcnId)
        end
                
        
        function [sectionIdForFunction, instrumentationCalls] = getProfilingIdentifier...
                (this, codeInfoForFunction)
            
            functionInfoForProfiling = coder.profile.ExecTimeConfig ...
                .getFunctionInfoForProfiling(codeInfoForFunction);
            functionName = functionInfoForProfiling.taskName;
            % Get the model name or (in the case of right-click build) SID for the subsystem
            % path
            [~, lComponentRef] = this.SILPILWrapperUtils.getComponentName;
            lTaskRegistry = this.SILPILWrapperUtils.getProfilingTaskRegistry;
            sectionIdForFunction =...
                lTaskRegistry.requestIdentifierForTask(functionName, ...
                                                       functionInfoForProfiling, ...
                                                       lComponentRef);
            instrumentationCalls = lTaskRegistry.instrumentationFnc(...
                coder_profile_ProbeType.TASK_TIME_PROBE,...
                sectionIdForFunction);
            
        end
        
        function [groupsInterf, othersInterf] = getCoderDataGroupsFromInternalData(this)
            % return the DataInterfaces that describe CoderDataGroups in
            % the InternalData, also return the other interfaces as second
            % output
                            
            groups = this.SILPILWrapperUtils.getCodeInfoUtils.CoderGroupsInfo;
            internalData = this.codeInfo.InternalData;
            keepIdx = false(1, numel(internalData));
            
            if groups.ProcessGroups
                for idx_ci = 1:numel(internalData)
                    % get the type identifier for the current interface
                    type_id = this.codeInfoUtils.getTypeIdentifier(internalData(idx_ci));
                    % compare the type id of the current interface to the 
                    % group types from CodeDescriptor
                    for idx_cd = 1:groups.NumGroups
                        currGroupRootType = groups.Groups(idx_cd).RootTypeName;
                        if strcmp(type_id, currGroupRootType)
                            keepIdx(idx_ci) = true;
                            break
                        end
                    end
                end
            end
            
            groupsInterf = internalData(keepIdx);
            othersInterf = internalData(~keepIdx);
        end
        
        function writeSectionOverheadConfiguration(this)
            cfg = this.SILPILWrapperUtils.getTargetServices().getProfilingService();
            if cfg.isOverheadFilteringActive
                this.writer.wBlockStart('');
                if cfg.isOverheadBenchmarkExecuted
                    [~, lComponentRef] = this.SILPILWrapperUtils.getComponentName;
                    % retrieve the Overhead Registry, deploy the specific
                    % probes and generate the call to run the benchmark
                    lOverheadRegistry = this.SILPILWrapperUtils.getProfilingOverheadRegistry;
                    idDmy = lOverheadRegistry.requestDummyOverheadProbe(lComponentRef);
                    idTmr = lOverheadRegistry.requestTimerOverheadProbe(lComponentRef);
                    idFrz = lOverheadRegistry.requestFreezeOverheadProbe(lComponentRef);
                    idTsk = lOverheadRegistry.requestTaskOverheadProbe(lComponentRef);
                    idFcn = lOverheadRegistry.requestFunctionOverheadProbe(lComponentRef);
                    numIterations = cfg.getOverheadBenchmarkSteps();
                    this.writer.wLine('xilProfilingEstimateOverhead(%d, %d, %d, %d, %d, %d);', ...
                        idTmr, idFrz, idTsk, idFcn, idDmy, numIterations);
                else
                    % use the average overheads that are already set in
                    % TargetConnectivity
                    [tsk, fcn, frz] = cfg.getOverheads();
                    this.writer.wLine('xilProfilingOverheadSetting(%d, %d, %d);', ...
                        tsk, fcn, frz);
                end
                this.writer.wBlockEnd; % overhead setting block
            end
        end
        
        function writeSectionAssignCoderDataGroupsPointers(this)
            % assign the rtM pointers to the variables allocated for the
            % groups
            if isempty(this.codeInfo.AllocationFunction)
                groups = this.getCoderDataGroupsFromInternalData;
                if any(arrayfun(@(x)this.codeInfoUtils.isa(x.Implementation, ...
                        'PointerExpression'), groups))
                    this.writer.wComment('Assign rtM pointers to group structs');
                    this.writer.wBlockStart('');
                    this.writePointerExpressionAssignments(groups);
                    this.writer.wBlockEnd();
                end
            end
        end
        
        function writePointerExpressionAssignments(this, interfaces)
            % write assignments for PointerExpression interfaces
            for i = 1:length(interfaces)
                impl = interfaces(i).Implementation;
                if this.codeInfoUtils.isa(impl,'PointerExpression')
                    srcExpr = impl.getExpression;
                    tgtExpr = impl.TargetRegion.getExpression;
                    this.writer.writeLine('%s = &%s;', ...
                        srcExpr, ...
                        tgtExpr);
                end
            end
        end
        
        function writeSectionInitBody(this)
            % initialize any output storage owned by In-the-Loop
            this.writeSectionExternalInitialization;
            
            % call allocation function if one exists before calling initialize
            % functions
            this.writeSectionAllocationFunction;
            
            % assign rtM pointers to the memory allocated for CoderDataGroups 
            this.writeSectionAssignCoderDataGroupsPointers;
            
            isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled; 
            
            if isExecutionProfilingEnabled
                % if profiling is active, the overhead can be filtered
                this.writeSectionOverheadConfiguration();
            end
            
            % call initialize functions
            initFuns = this.codeInfo.InitializeFunctions;           

            % Pack model data into RTM (for reusable code case w/o allocation function)
            % Do not assign group pointers at this stage as it has
            % already been done in writeSectionAssignCoderDataGroupsPointers.
            % (SLDV requires these assignments to be outside of the next
            % generated if statement to avoid some imprecision during
            % the alias analysis)
            if ~isempty(initFuns) && isempty(this.codeInfo.AllocationFunction)
                % initialize the RTM pointer if necessary
                this.writeRTMPointerAssigmentHook;
                % assign groups to RTM fields
                [~, internalData] = this.getCoderDataGroupsFromInternalData;
                this.writePointerExpressionAssignments(internalData);
            end

            % SLDV case: don't perform the function id check to avoid 
            % imprecision during the alias analysis
            if ~this.SILPILWrapperUtils.getSILPILInterface.IsForSldvAnalysis
                this.writer.writeCellLines({'/* Single In-the-Loop Component */' ...
                    'if (xilFcnId == 0) {'});
                this.writer.incIndent;
            else
                this.writer.writeLine('(void)xilFcnId;');
            end

            if isempty(initFuns)
                this.writeEmptyFunctionCall;                
            else
                
                % make any required assignments
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                             this.codeInfoUtils.getDataInterfacesForFunction(initFuns, 'input'), 'input');
                this.writeInputAssignments(dataElements);
                % params
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                this.getParamIndicesForFunction(initFuns), 'parameter');
                this.writeInputAssignments(dataElements);                
                isSetBeforeInit = true; %Signal this is a call Set functions in Init.
                this.writeClassGetSetCalls('Set', isSetBeforeInit); %Must be final call before function is called.
                for i=1:length(initFuns)
                    fun = initFuns(i);    
                    
                    if isExecutionProfilingEnabled
                        
                        [sectionIdForFunction, instrCalls] =...
                            this.getProfilingIdentifier(fun);
                            
                        this.writer.writeLine('%s(%dU);', ...
                                              instrCalls{1}, ...
                                              sectionIdForFunction);                        
                    end
                    
                    this.writeSectionInitializeFunctionCall(fun);
                    
                    if isExecutionProfilingEnabled
                        this.writer.writeLine('%s(%dU);',...
                                              instrCalls{2},...
                                              sectionIdForFunction);
                    end
                end
            end
            isSetBeforeInit = false;
            this.writeClassGetSetCalls('Get', isSetBeforeInit); %Must be first code after function called
                
            % make any required assignments
            %
            % constant and non-constant outputs may have been updated; make
            % sure I/O data for ALL outports is up to date 
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                         1:length(this.codeInfo.Outports), 'output');

            % also include constant logging data which is set during
            % initialization
            loggingData = this.SILPILWrapperUtils.getLoggingData;
            loggingDataIndices = this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('ONESHOT'));
            selectedLoggingData = loggingData(loggingDataIndices);
            for i=1:length(selectedLoggingData)
                dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
            end
                                                     
            this.writeOutputAssignments(dataElements);

            % Update the error status for the non-SLDV case
            if ~this.SILPILWrapperUtils.getSILPILInterface.IsForSldvAnalysis
                this.writer.decIndent;
                this.writer.writeCellLines({'}' ...
                    'else {'});
                this.writer.incIndent;
                this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end                              
        
        function writeSectionExternalInitialization(this)  
                this.writer.writeLine('/* initialize output storage owned by In-the-Loop */');
           
                %Initialize all local method access variables
            
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    1:length(this.codeInfo.Outports), 'output');
                this.writeDataInterfaceDefaultInitialization(dataElements);
                % initialize output sizes owned by In-the-Loop
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    1:length(this.codeInfoUtils.getOutportSizes), ...
                    'outputsize');
                this.writeDataInterfaceDefaultInitialization(dataElements);
        end
        
        % generate default initialization for a data interface 
        % (e.g. outport) implementation owned by the In-the-Loop application
        function writeDataInterfaceDefaultInitialization(this, dataElements)                        

            for dataElementIdx = 1:length(dataElements)                
                cData = dataElements(dataElementIdx);
                
                implementation = cData.getDataInterface.getRTWDataInterface.Implementation;
                if this.codeInfoUtils.isa(implementation, 'ClassMethodExpression') || ...
                        (this.codeInfoUtils.isa(implementation, 'BasicAccessFunctionExpression') && ...
                            (cData.getDataInterface.NeedsIOTargetTransform)) || ...
                            (rtw.connectivity.CodeInfoTypeUtils.isaType(implementation.Type, 'Class') && ...
                            (strcmp(implementation.Type.Identifier,'std::string') || ...
                             contains(implementation.Type.Identifier,'std::array') || ...
                             contains(implementation.Type.Identifier,'std::vector'))) || ...
                            (cData.getDataInterface.NeedsIOTargetTransform && ...
                            (strcmp(cData.SimulinkType,'char'))) || ...
                            cData.getDataInterface.HasTargetSideOpaqueRegion || ...
                            (strcmp(cData.SimulinkType,'half'))
                    % If this is a class method, then it's already been
                    % initialized above.                    
                    continue;
                end
                
                baseStorageImplementationList = this.getBaseStorageImplementation(implementation);
                %If more than on StorageImplementation returned, need to
                %identify which one matches this element.
                if numel(baseStorageImplementationList)>1
                    dataElementList = cData.getDataInterface.getTargetDataElements;
                    %Only ever going to be 2 storage implementations from a
                    %typed collection and last one is always size. Last Data
                    %Element will also be size.
                    if cData==dataElementList(end)
                        elementIndex = 2;
                    else
                        elementIndex = 1;
                    end
                    %Need the correct element as well.
                    implementation = baseStorageImplementationList(elementIndex);
                else
                    elementIndex = 1;
                end
                
                baseStorageImplementation = baseStorageImplementationList(elementIndex);                
                owner = this.getImplementationOwner(baseStorageImplementation);             
                if strcmp(owner, 'PIL')
                    % open any required loops
                    cData.writeStructArrayHeader(this.writer);                    
                    initString = cData.getDefaultInitialization(this.SILPILWrapperUtils);
                    isArray = rtw.pil.TargetInterface.isArrayInCode(...
                        implementation.Type, cData.getNumElements);
                    if isArray
                        this.writer.wBlockStart('');
                        
                        % enum ImportFromFile parameter
                        elementIndexVariables = ...
                            cData.writeElementLoopHeader(this.writer);
                        
                        % get an expression to use in the loop
                        initExpression = this.getInitializeExpression(implementation, cData, elementIndexVariables, initString);
                        this.writer.writeLine('%s', initExpression);
                        
                        cData.writeElementLoopTrailer(this.writer);                        
                                                
                        this.writer.wBlockEnd;
                    else
                        initExpression = this.getInitializeExpression(implementation, cData, 0, initString);
                        this.writer.writeLine('%s', initExpression);
                    end
                    % close any required loops
                    cData.writeStructArrayTrailer(this.writer);
                end                                                
            end                                                                     
        end
        
        function writeSectionAllocationFunction(this)
            %Call Allocation Function and check memory allocation was
            %successful.
            
            allocFunction = this.codeInfo.AllocationFunction;
            if ~isempty(allocFunction)
                
                %Should only ever have one Allocation Function, code below
                %will need to change if that isn't true anymore.
                assert( numel(allocFunction)==1 , 'SIL only supports one Allocation Function');
                
                this.writer.wComment('Allocate model data');
                this.writer.wLine('%s;', allocFunction.getFunctionCall);
                allocReturn = allocFunction.ActualReturn.Implementation.Identifier;
                this.writer.wBlockStart('if ( %s == NULL )', allocReturn);
                this.writer.wLine('return XIL_INTERFACE_MEMORY_ALLOCATION_ERROR;');
                this.writer.wBlockEnd;
                
                if strcmp(this.SILPILWrapperUtils.getConfigInterface.getParam('SuppressErrorStatus'), 'off')
                    this.writer.wBlockStart('if ( rtmGetErrorStatus( %s ) != NULL )', allocReturn);
                    this.writer.wLine('return XIL_INTERFACE_MEMORY_ALLOCATION_ERROR;');
                    this.writer.wBlockEnd;
                end
                
            end
            
        end                
        
        function writeEmptyFunctionCall(this)
            this.writer.writeLine('/* No Function to Call */');            
        end
        
        function writeSectionSystemResetFunction(this)
            % may be overridden
            this.writeEmptyFunctionCall;
        end                         
        
        function writeSectionSystemInitializeFunction(this)
            % may be overridden
            this.writeEmptyFunctionCall;
        end
        
        function writeSectionSystemInitializeTrailer(this) %#ok<MANU>
            % see subclass            
        end
        
        function writeSectionInitUDataProcessing(this)
            this.writer.wBlockStart(['XIL_INTERFACE_ERROR_CODE xilGetHostToTargetData(uint32_T xilFcnId, ' ...
                                                                           'XIL_COMMAND_TYPE_ENUM xilCommandType, ' ...
                                                                           'uint32_T xilCommandIdx, ' ...
                                                                           'XILIOData ** xilIOData)']);
            this.writer.writeCellLines({'XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;';
                '*xilIOData = 0;';
                '/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   errorCode = XIL_INTERFACE_UNKNOWN_FCNID;';
                '   return errorCode;';
                '}'});

            % add the cases
            this.InSwitchStatement = false;
            commandIdxSwitchGenerated = this.writeSectionInitUDataProcessingCases;

            if this.InSwitchStatement
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');  

                if ~commandIdxSwitchGenerated
                    % mark xilIOData arg as unused
                    this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                end
            else
                this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_TID;');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandType);');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
            end
            
            this.writer.writeLine('return errorCode;');
            this.writer.wBlockEnd; %HostToTarget Function
            this.writer.newLine;
        end

        function writeSectionInitDataProcessingSwitchStart(this)
            if (~this.InSwitchStatement)
                % write the start of the switch case statement:
                this.writer.writeLine('switch(xilCommandType) {');
                % switch indent
                this.writer.incIndent;    
                this.InSwitchStatement = true;
            end
        end
 
        function paramIndices = getParamIndicesForFunction(this, functionInterface)
            % use codeInfoUtils implementation to get parameters
            paramIndices = this.codeInfoUtils.getParamIndicesForFunctionImpl(...
                functionInterface);
        end
        
        function commandIdxSwitchGenerated = writeSectionInitUDataProcessingCases(this)
            % support for InitializeFunction inputs
            writeSectionInitUDataProcessingCase(this, ...
                                                this.codeInfo.InitializeFunctions, ...
                                                'XIL_INITIALIZE_COMMAND', ...
                                                'xil_fcnid0_init');
            
            % support for TerminateFunctions inputs
            writeSectionInitUDataProcessingCase(this, ...
                                                this.codeInfo.TerminateFunctions, ...
                                                'XIL_TERMINATE_COMMAND', ...
                                                'xil_fcnid0_term');
                                                      
            tunableParamsIndices = this.SILPILWrapperUtils.getSupportedTunableWorkspaceParams;
            inTheLoopType = this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType;
            instPArgsIndices = this.codeInfoUtils.getInstPArgumentIndices(inTheLoopType);
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                [tunableParamsIndices, instPArgsIndices], 'parameter');
            
            if ~isempty(dataElements)
                isTargetToHost = false;
                this.writeSectionInitDataProcessingSwitchStart();
                % support for PROCESS_PARAMS command
                this.writeSectionInitDataProcessingCase('XIL_PROCESS_PARAMS_COMMAND', ...
                    'xil_fcnid0_ws_params', ...
                    dataElements, isTargetToHost);
            end
            
            % support for Task inputs
            
            tableName = 'xil_fcnid0_task';
            tasksOutput = this.getOutputTasks;
            commandIdxSwitchGeneratedForTasksOutput = writeSectionInitUDataProcessingCaseWithCommandIdxSwitch(this, ...
                tasksOutput, ...
                'XIL_OUTPUT_COMMAND', ...
                tableName);
            
            
            updateTasks = this.codeInfoUtils.getUpdateTasks;            
            commandIdxSwitchGeneratedForTasksUpdate = writeSectionInitUDataProcessingCaseWithCommandIdxSwitch(this, ...
                updateTasks, ...
                'XIL_UPDATE_COMMAND', ...
                tableName);            
  
            commandIdxSwitchGeneratedForCallers = false;
            callerFunctionsInfo = this.getCallerFunctionsInfo;
            if ~isempty(callerFunctionsInfo)
                tableNamePostFix = '_y';
                numCallerFcns = length(callerFunctionsInfo);                
                for callerFcnIdx = 1:numCallerFcns
                    callerFunctionInfo = callerFunctionsInfo(callerFcnIdx);
                    dataElements = this.getCallerFunctionHostToTargetDataElements(callerFunctionInfo);                
                    commandIdxSwitchGeneratedForCallers =...
                        writeSectionInitUDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numCallerFcns, ...
                        num2str(callerFunctionInfo.getTID), ...
                        'XIL_CLIENT_SERVER_COMMAND', ...
                        dataElements,...
                        tableName,...
                        tableNamePostFix,...
                        commandIdxSwitchGeneratedForCallers);
                end
                if commandIdxSwitchGeneratedForCallers
                    % generate default
                    this.writer.writeCellLines({'default:';
                        '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                        '   break;'});
                    % switch indent
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                    this.writer.writeLine('break;');
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                end
            end
            
            commandIdxSwitchGenerated = commandIdxSwitchGeneratedForCallers || ...
                commandIdxSwitchGeneratedForTasksOutput || commandIdxSwitchGeneratedForTasksUpdate ;
            
            if this.codeInfoUtils.isExportFunctions
                % support for Export Functions DisableFunction inputs
                %
                % map directly on to inputs for step command
                %
                this.writeSectionInitDataProcessingSwitchStart();
                this.writer.writeLine('case XIL_DISABLE_COMMAND: {');
                this.writer.incIndent;
                this.writer.writeLine('errorCode = xilGetHostToTargetData(xilFcnId, XIL_OUTPUT_COMMAND, xilCommandIdx, xilIOData);');
                this.writer.writeLine('return errorCode;');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end                
               
        function writeSectionInitUDataProcessingCase(this, ...
                                                     functionInterfaces, ...                                                     
                                                     command, ...
                                                     tableName)
            % process inports and params              
            inportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, ...
                                                                                 'inputsize'); 
            outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, ...
                                                                                 'outputsize');                                                                              
            inputIndices = this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, 'input');
            if strcmp(command,'XIL_INITIALIZE_COMMAND')
                inputIndices = this.codeInfoUtils.getAdditionalInitializeInportIndices(inputIndices);                
            elseif strcmp(command,'XIL_TERMINATE_COMMAND')
                inputIndices = unique([inputIndices, ...
                                this.codeInfoUtils.getInputPortsForRates(this.codeInfoUtils.getRates('TERMINATE'))]);
            end
                    
            
            dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                inputIndices, 'input'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                inportSizesIndices, 'inputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                outportSizesIndices, 'outputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                this.getParamIndicesForFunction(functionInterfaces), 'parameter')];
            
            if ismember(command, {'XIL_INITIALIZE_COMMAND', 'XIL_SYSTEM_INITIALIZE_COMMAND'})
                %Add Data Store Reads to INITIALIZE call
                initTask = this.codeInfoUtils.getInitializeTasks();
                dsReadIndices = [];
                for taskIdx = 1:length(initTask)
                    dsReadIndices = [dsReadIndices, ...                         
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(initTask(taskIdx), 'init'), 'dsread')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                end
                dataElements = [dataElements, ...
                    rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    unique(dsReadIndices, 'stable'), 'dsread')];    
                if strcmp(command, 'XIL_INITIALIZE_COMMAND')
                    %Add InstP model arguments
                    inTheLoopType = this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType;
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.codeInfoUtils.getInstPArgumentIndices(inTheLoopType), 'parameter')];
                end
            elseif strcmp(command,'XIL_TERMINATE_COMMAND')
                %Add Data store Reads to TERMINATE Calls
                termTask = this.codeInfoUtils.getTerminateTasks();
                if ~isempty(termTask) && ...
                        this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType~=rtw.pil.InTheLoopType.Block
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(termTask, 'term'), 'dsread'), 'dsread')]; %Extra Argument needed to workaround G1411813
                end
            end
                            
            if ~isempty(dataElements)           
                isTargetToHost = false;
                this.writeSectionInitDataProcessingSwitchStart();
                this.writeSectionInitDataProcessingCase(command, ...
                    [tableName '_u'], ...
                    dataElements, isTargetToHost);
            end
        end        
        
        function commandIdxSwitchGenerated = writeSectionInitUDataProcessingCaseWithCommandIdxSwitch(this, ...
                                                                                                     tasks, ...
                                                                                                     command, ...
                                                                                                     tableName)                                                                                                                                      
            numTasks = length(tasks);             
            commandIdxSwitchGenerated = false;                                               
            for taskIdx = 1:numTasks
                task = tasks(taskIdx);                                
                
                id = int2str(task.id);
                % process inports, params and dsread
                % ports may not be part of the function signature
                
                
                portIndices = this.codeInfoUtils.getInputPortsForTask(task);                
                if (strcmp(command,'XIL_UPDATE_COMMAND')  )
                    
                    iotag = '_update_u';
                    portIndices = this.SILPILWrapperUtils.getNonDFInputPortsIndices(portIndices);
                    inportSizesIndices = this.codeInfoUtils.getInportSizesIndices(portIndices);
                    dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        portIndices, 'input'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        inportSizesIndices, 'inputsize')];
                    
                else
                    iotag = '_output_u';
                    
                    portIndices = this.SILPILWrapperUtils.getDFInputPortsIndices(portIndices);         
                    inportSizesIndices = this.codeInfoUtils.getInportSizesIndices(portIndices);
                    outportIndices = this.codeInfoUtils.getOutputPortsForTask(task);
                    outportSizesIndices = this.codeInfoUtils.getOutportSizesIndices(outportIndices);
                    % process the dsRead for this task
                    dsReadIndices =  this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(task), 'dsread');
                    
                    dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        portIndices, 'input'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        inportSizesIndices, 'inputsize'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        outportSizesIndices, 'outputsize'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.getParamIndicesForFunction(task.codeInfoData), 'parameter'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, dsReadIndices, 'dsread'),...
                        this.getServerInputArgsDataElement(task)];
                end
                
                if ~isempty(dataElements)
                    commandIdxSwitchGenerated =...
                        writeSectionInitUDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numTasks, ...
                        id, ...
                        command, ...
                        dataElements,...
                        tableName,...
                        iotag,...
                        commandIdxSwitchGenerated);
                end
            end
            
            if commandIdxSwitchGenerated
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');
                this.writer.writeLine('break;');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end

        function commandIdxSwitchGenerated = writeSectionInitUDataProcessingCaseWithCommandIdxSwitchBody(this, ...
                                                                                                         numTasks, ...
                                                                                                         taskId, ...
                                                                                                         command, ...
                                                                                                         dataElements,...
                                                                                                         tableName,...
                                                                                                         tableNamePostFix,...
                                                                                                         commandIdxSwitchGenerated)            
            if numTasks > 1
                if ~commandIdxSwitchGenerated
                    this.writeSectionInitDataProcessingSwitchStart();
                    this.writer.writeLine(['case ' command ': {']);
                    this.writer.incIndent;
                    % apply nested switch on "xilCommandIdx"
                    this.writer.writeLine('switch(xilCommandIdx) {');
                    % switch indent
                    this.writer.incIndent;
                    commandIdxSwitchGenerated = true;
                end
                caseId = taskId;
            else
                caseId = command;
            end
            isTargetToHost = false;
            this.writeSectionInitDataProcessingSwitchStart();
            this.writeSectionInitDataProcessingCase(caseId, ...
                [tableName taskId tableNamePostFix], ...
                dataElements, isTargetToHost);                        
        end
        
        function writeSectionInitDataProcessingCase(this, caseId, tableName, dataElements, isTargetToHost)           
            this.writer.wBlockStart(['case ' caseId ':']);
            
            this.writePILIODataTableInitialization(dataElements, tableName);
            this.writeTransferCalls(dataElements, isTargetToHost);
            this.writer.writeCellLines({['*xilIOData = &' tableName '[0];'];
                'break;'});
            
            this.writer.wBlockEnd; % case caseId
        end
                
        function writeSectionUploadProfilingDataPoint(this)

            isCodeInstrumentationEnabled =...
                this.SILPILWrapperUtils.isCodeInstrumentationEnabled ||...
                    this.SILPILWrapperUtils.isProfilingEnabled;
            if isCodeInstrumentationEnabled
                lTargetWordSize = this.SILPILWrapperUtils.getParam('TargetWordSize');
                lCodeInstrSizeCDataType = ...
                    coder.profile.ExecTimeConfig.getCodeInstrNumElsCDataType...
                    (lTargetWordSize);
                lIdType = ...
                    coder.profile.ExecTimeConfig.getIdentifierCDataType...
                    (lTargetWordSize);
                this.writer.writeLine...
                    ('void %s(void * pData, %s numMemUnits, %s sectionId) {', ...
                     rtw.pil.SILPILInterface.TargetCodeInstrFcnName,...
                     lCodeInstrSizeCDataType, lIdType);
                this.writer.incIndent;
                this.writer.newLine;
                               
                this.writer.writeLine('/* Send code instrumentation data to host */');                            
                dataForHost = {sprintf('(MemUnit_T *) &numMemUnits'), 'sizeof(numMemUnits)'};
                
                dataForHost(end+1,:) = ...
                    {sprintf('(MemUnit_T *) &sectionId'), ...
                     sprintf('sizeof(%s)', lIdType)};
                
                dataForHost(end+1,:) = ...
                    {sprintf('(MemUnit_T *) pData'), 'numMemUnits'};
                
                numDataItems = length(dataForHost(:,1));
                for i=1:numDataItems
                    tmpAddr=dataForHost{i,1};
                    tmpSize=dataForHost{i,2};
                    this.writer.writeLine...
                        (['if (codeInstrWriteData(%s, %s) '...
                          '!= XIL_DATA_STREAM_SUCCESS) {'], ...
                         tmpAddr, tmpSize);
                    this.writer.incIndent;              
                    this.writer.writeLine('for(;;);');
                    this.writer.decIndent;              
                    this.writer.writeLine('}');
                end
                this.writer.decIndent;                
                this.writer.writeLine('}');
                this.writer.newLine;
                
            end
        end
        
        function writeSectionPause(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilPause(uint32_T xilFcnId) {');
            this.writer.incIndent;
            this.writeSectionPauseBody;
            this.writer.decIndent;
            this.writer.writeLine('}');                        
            this.writer.newLine;
        end
        
        function writeSectionPauseBody(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writer.wBlockStart('if (xilFcnId == 0)');
            if this.SILPILWrapperUtils.AreEventCallbacksRequired
                this.writer.writeLine('PauseEvent();');
            else
                this.writer.writeLine('/* Nothing to do */');
            end
            this.writer.wBlockMiddle('else');
            this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.wBlockEnd;
            this.writer.writeLine('return errorCode;');
        end

        function writeSectionOutput(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilOutput(uint32_T xilFcnId, uint32_T xilTID) {');
            % fun indent
            this.writer.incIndent;
            this.writeSectionOutputBody;                        
            % fun indent
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end                          
        
        function writeSectionOutputBody(this)
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}';});
            
            outputTasks = this.getOutputTasks;
            
            if isempty(outputTasks)
                this.writer.writeLine('/* No Output Function - this function should never be called */');
                this.writer.writeLine('UNUSED_PARAMETER(xilTID);');
                this.writer.writeLine('return XIL_INTERFACE_UNKNOWN_TID;');
            else
                
                this.writer.writeLine('switch(xilTID) {');
                % switch indent
                this.writer.incIndent;
                                               
                isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
                
                % check to make sure we never get an update function for C++ encapsulated mode:
                if this.SILPILWrapperUtils.isCPPEncap
                    assert(isempty(this.codeInfoUtils.getUpdateTasks), ...
                           'Expected no update tasks for C++ class generation');
                    isCombinedOutputUpdate = true;
                else
                    isCombinedOutputUpdate = strcmp(this.SILPILWrapperUtils.getParam( ...
                        'CombineOutputUpdateFcns'), 'on');
                end

                for taskIdx = 1:length(outputTasks)
                    outputTask = outputTasks(taskIdx);                    
                                                                                
                    this.writer.writeLine(['case ' int2str(outputTask.id) ':']);
                    this.writer.incIndent;
                    

                    if rtw.pil.CodeInfoData.isInterfaceActive(outputTask.codeInfoData)
                        % make any required assignments
                        this.writeSectionOutputInputAssignments(outputTask);
                        isSetBeforeInit = false;
                        this.writeClassGetSetCalls('Set', isSetBeforeInit); %Must be final thing before function called
                        if isExecutionProfilingEnabled

                            [sectionIdForFunction, instrCalls] =...
                                this.getProfilingIdentifier(...
                                    outputTask.codeInfoData);

                            this.writer.writeLine('%s(%dU);', ...
                                                  instrCalls{1},...
                                                  sectionIdForFunction);
                        end

                        % call output function
                        this.writeSectionOutputFunctionCall(taskIdx, outputTask);
                        if isExecutionProfilingEnabled
                            this.writer.writeLine('%s(%dU);',...
                                                  instrCalls{2},...
                                                  sectionIdForFunction);
                        end
                        isSetBeforeInit = false;
                        this.writeClassGetSetCalls('Get', isSetBeforeInit); %Must be first thing after function called
                        % make any required assignments
                        this.writeSectionOutputOutputAssignments(outputTask);


                        % we only need to call rt_UpdateTXYLogVars if 
                        % CombineOutputUpdate is off. Also, we only call this
                        % for the base rate
                        if this.SILPILWrapperUtils.isMatFileLoggingEnabled &&...
                                (taskIdx==1) && ~isCombinedOutputUpdate
                            this.writeSectionOutputBodyMatFileLogging();
                        end
                    end
                    
                    if this.codeInfoUtils.UseMF0CodeDescriptor && ...
                            ~isempty(this.codeInfo.RuntimeEnvironment)
                        this.writeTimingServiceCode(outputTask);
                    end
                    
                    this.writer.writeLine('break;');
                    this.writer.decIndent;
                end
                
                % generate default
                this.writer.writeCellLines({'default:';
                    '   return XIL_INTERFACE_UNKNOWN_TID;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');

                if this.SILPILWrapperUtils.AreEventCallbacksRequired
                    this.writer.writeLine('StepCompletedEvent();');
                    this.writer.newLine;
                end
                
                this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
            end            
        end
        
        function writeSectionOutputInputAssignments(this, outputTask)
            
            % Update all input setters
            
            inportIndices = this.SILPILWrapperUtils.getDFInputPortsIndices(this.codeInfoUtils.getInputPortsForTask(outputTask));
            
            % inputs
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                         inportIndices, 'input');
            this.writeInputAssignments(dataElements);
            % params
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                         this.getParamIndicesForFunction(outputTask.codeInfoData), 'parameter');
            this.writeInputAssignments(dataElements);
            % datastores
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                this.codeInfoUtils.getReaderSharedDataStoresForTask(outputTask), 'dsread'), 'dsread');
            this.writeInputAssignments(dataElements);

            % server input arguments
            dataElements = this.getServerInputArgsDataElement(outputTask);
            this.writeInputAssignments(dataElements);                        
        end
        
        function writeSectionUpdateInputAssignments(this, updateTask)
            inportIndices = this.SILPILWrapperUtils.getNonDFInputPortsIndices(this.codeInfoUtils.getInputPortsForTask(updateTask));
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, inportIndices, 'input');
            this.writeInputAssignments(dataElements);
        end
                        
        function writeSectionOutputFunctionCall(this, taskIdx, outputTask)  %#ok<INUSL>                        
            [isServerFunction, serverFunctionInfo] =...
                isServerOutputFunction(this.codeInfoUtils, outputTask.codeInfoData);            
            if isServerFunction
                % g1051905 (item 5): getFunctionCall errors out for server OutputFunction in codeInfo.
                this.writer.writeLine([this.getFunctionCallExpressionForServerFunction(serverFunctionInfo) ';']);
            else
                this.writer.writeLine([outputTask.codeInfoData.getFunctionCall ';']);
            end
        end
        
        function writeSectionInitializeFunctionCall(this, initFun)
            this.writer.writeLine([initFun.getFunctionCall ';']);
        end

        function writeSectionOutputOutputAssignments(this, outputTask)
            % outputs
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                         this.codeInfoUtils.getOutputPortsForTask(outputTask), 'output');
            this.writeOutputAssignments(dataElements);
            % datastores
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                this.codeInfoUtils.getWriterSharedDataStoresForTask(outputTask), 'dswrite'), 'dswrite');
            % logging data             
            loggingData = this.SILPILWrapperUtils.getLoggingData;                
            loggingDataIndices = this.codeInfoUtils.getLoggingForTask(outputTask);                                                
            selectedLoggingData = loggingData(loggingDataIndices);                                
            for i=1:length(selectedLoggingData)
                dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
            end
            this.writeOutputAssignments(dataElements);
            
            dataElements = this.getServerOutputArgsDataElement(outputTask);
            this.writeOutputAssignments(dataElements);
        end
        
        function writeSectionUpdate(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilUpdate(uint32_T xilFcnId, uint32_T xilTID) {');
            this.writer.incIndent;
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}'});
            
            updateTasks = this.codeInfoUtils.getUpdateTasks;
            
            if isempty(updateTasks)
                this.writer.writeLine('/* No Update Function */');
                this.writer.writeLine('UNUSED_PARAMETER(xilTID);');
            else
                this.writer.writeLine('switch(xilTID) {');
                this.writer.incIndent;                
                isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
                
                for taskIdx = 1:length(updateTasks)
                    updateTask = updateTasks(taskIdx);
                    
                    this.writer.writeLine(['case ' int2str(updateTask.id) ':']);
                    this.writer.incIndent;
                    
                    % make any required assignments
                    this.writeSectionUpdateInputAssignments(updateTask);
                    
                    if isExecutionProfilingEnabled
                        
                        [sectionIdForFunction, instrCalls] =...
                            this.getProfilingIdentifier...
                            (updateTask.codeInfoData);
                        this.writer.writeLine('%s(%dU);', ...
                                              instrCalls{1}, ...
                                              sectionIdForFunction);                
                    end
                    
                    this.writeSectionUpdateFunctionCall(taskIdx, updateTask);
                    if isExecutionProfilingEnabled
                        this.writer.writeLine('%s(%dU);',...
                                              instrCalls{2}, ...
                                              sectionIdForFunction);
                    end
                    this.writer.writeLine('break;');
                    this.writer.decIndent;
                end
                % generate default
                this.writer.writeCellLines({'default:';
                    '   return XIL_INTERFACE_UNKNOWN_TID;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
            this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
            % fun indent
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end
        
        function writeSectionUpdateFunctionCall(this, taskIdx, updateTask)  %#ok<INUSL>
           this.writer.writeLine([updateTask.codeInfoData.getFunctionCall ';']); 
        end
        
        function writeSectionInitSDataProcessing(this)
            this.writer.wBlockStart(['XIL_INTERFACE_ERROR_CODE xilGetTargetToHostPreData(uint32_T xilFcnId, ' ...
                                                                          'XIL_COMMAND_TYPE_ENUM xilCommandType, ' ...
                                                                          'uint32_T xilCommandIdx, ' ...
                                                                          'XILIOData ** xilIOData, ' ...
                                                                          'MemUnit_T responseId, ' ...
                                                                          'uint32_T serverFcnId)']);
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writer.writeLine('*xilIOData = 0;');
            this.writer.wBlockStart('if (xilFcnId != 0)');
            this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.writeLine('return errorCode;');
            this.writer.wBlockEnd();

            this.InSwitchStatement = false;
            tableName = 'xil_fcnid0_task';
            tasks = this.getOutputTasks;
            commandIdxSwitchGeneratedForTasks = writeSectionInitSDataProcessingCaseWithCommandIdxSwitch(this, ...
                tasks, ...
                'XIL_OUTPUT_COMMAND', ...
                 tableName);
             
             if this.InSwitchStatement
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');

                if ~commandIdxSwitchGeneratedForTasks
                    % mark xilIOData arg as unused
                    this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                    this.writer.writeLine('UNUSED_PARAMETER(responseId);');
                    this.writer.writeLine('UNUSED_PARAMETER(serverFcnId);');
                end
            else
                % mark xilIOData arg as unused
                this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_TID;');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandType);');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                this.writer.writeLine('UNUSED_PARAMETER(responseId);');
                this.writer.writeLine('UNUSED_PARAMETER(serverFcnId);');
            end
                             
            this.writer.writeLine('return errorCode;');
            this.writer.wBlockEnd();
            this.writer.newLine;
        end
        
        function writeSectionInitYDataProcessing(this)
            this.writer.writeLine(['XIL_INTERFACE_ERROR_CODE xilGetTargetToHostData(uint32_T xilFcnId, ' ...
                                                                          'XIL_COMMAND_TYPE_ENUM xilCommandType, ' ...
                                                                          'uint32_T xilCommandIdx, ' ...
                                                                          'XILIOData ** xilIOData, ' ...
                                                                          'MemUnit_T responseId, ' ...
                                                                          'uint32_T serverFcnId) {']);
            this.writer.incIndent;
            this.writer.writeCellLines({'XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;';
                '/* Single In-the-Loop Component */';
                '*xilIOData = 0;';
                'if (xilFcnId != 0) {';
                '   errorCode = XIL_INTERFACE_UNKNOWN_FCNID;';
                '   return errorCode;';
                '}'});
            
            % add the cases
            this.InSwitchStatement = false;
            commandIdxSwitchGenerated = this.writeSectionInitYDataProcessingCases;                        
            
            if this.InSwitchStatement
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');

                if ~commandIdxSwitchGenerated
                    % mark xilIOData arg as unused
                    this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                    this.writer.writeLine('UNUSED_PARAMETER(responseId);');
                    this.writer.writeLine('UNUSED_PARAMETER(serverFcnId);');
                end
            else
                % mark xilIOData arg as unused
                this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_TID;');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandType);');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                this.writer.writeLine('UNUSED_PARAMETER(responseId);');
                this.writer.writeLine('UNUSED_PARAMETER(serverFcnId);');
            end
            this.writer.writeLine('return errorCode;');
            % fun indent
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end
        
        function commandIdxSwitchGenerated = writeSectionInitYDataProcessingCases(this)
            isTargetToHost = true;
            if this.codeInfoUtils.hasConstOutput
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                             this.codeInfoUtils.getOutputPortsForRates(this.codeInfoUtils.getRates('ONESHOT')), ...
                                                             'output');
                if ~isempty(dataElements)
                    this.writeSectionInitDataProcessingSwitchStart();
                    % support for const outputs
                    this.writeSectionInitDataProcessingCase('XIL_CONST_OUTPUT_COMMAND', ...
                        'xil_fcnid0_const_y', ...
                        dataElements, isTargetToHost);
                end
            end
            
            % support for InitializeFunction outputs
            if this.SILPILWrapperUtils.getClientInterface.syncIODuringOneShot()
                writeSectionInitYDataProcessingCase(this, ...
                    this.codeInfo.InitializeFunctions, ...
                    'XIL_INITIALIZE_COMMAND', ...
                    'xil_fcnid0_init');
            end                                   
            
            % support for TerminateFunctions outputs
            if this.SILPILWrapperUtils.getClientInterface.syncIODuringOneShot() && this.HasModelWideEvents
                writeSectionInitYDataProcessingCase(this, ...
                    this.codeInfo.TerminateFunctions, ...
                    'XIL_TERMINATE_COMMAND', ...
                    'xil_fcnid0_term');
            end
            
            tableName = 'xil_fcnid0_task';
            tasks = this.getOutputTasks;
            commandIdxSwitchGeneratedForTasks = writeSectionInitYDataProcessingCaseWithCommandIdxSwitch(this, ...
                tasks, ...
                'XIL_OUTPUT_COMMAND', ...
                 tableName);
            
            callerFunctionsInfo = this.getCallerFunctionsInfo;
            commandIdxSwitchGeneratedForCallers = false;
            if ~isempty(callerFunctionsInfo)
                tableNamePostFix = '_output_u';
                numCallerFcns = length(callerFunctionsInfo);                
                for callerFcnIdx = 1:numCallerFcns
                    callerFunctionInfo = callerFunctionsInfo(callerFcnIdx);
                    dataElements = this.getCallerFunctionTargetToHostDataElements(callerFunctionInfo);
                    commandIdxSwitchGeneratedForCallers =...
                        writeSectionInitYDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numCallerFcns, ...
                        num2str(callerFunctionInfo.getTID), ...
                        'XIL_CLIENT_SERVER_COMMAND', ...
                        dataElements,...
                        tableName,...
                        tableNamePostFix,...
                        commandIdxSwitchGeneratedForCallers);
                end
                if commandIdxSwitchGeneratedForCallers
                    % generate default
                    this.writer.writeCellLines({'default:';
                        '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                        '   break;'});
                    % switch indent
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                    this.writer.writeLine('break;');
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                end
            end

            commandIdxSwitchGenerated = commandIdxSwitchGeneratedForCallers ||...
                commandIdxSwitchGeneratedForTasks;
            
            if this.codeInfoUtils.isExportFunctions
                % support for Export Functions DisableFunction outputs
                %
                % map directly on to outputs for step command
                %
                this.writeSectionInitDataProcessingSwitchStart();
                this.writer.writeLine('case XIL_DISABLE_COMMAND: {');
                this.writer.incIndent;
                this.writer.wLine('MemUnit_T lResponseId = XIL_RESPONSE_OUTPUT_DATA;');
                this.writer.wLine('uint32_T lServerFcnId = 0;');
                this.writer.writeLine('errorCode = xilGetTargetToHostData(xilFcnId, XIL_OUTPUT_COMMAND, xilCommandIdx, xilIOData, lResponseId, lServerFcnId);');
                this.writer.writeLine('return errorCode;');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end
        
        function writeSectionInitYDataProcessingCase(this, ...
                                                     functionInterfaces, ...                                                                      
                                                     command, ...
                                                     tableName)                                                              
            isTargetToHost = true;
            uploadConstSigLogsAndSharedLocalDataStores = false;
            uploadInitializeIRTData = false;
            uploadTerminateIRTData = false;
            switch command
                case 'XIL_INITIALIZE_COMMAND'
                    uploadConstSigLogsAndSharedLocalDataStores = true;
                    uploadInitializeIRTData = this.HasModelWideEvents && ...
                       this.SILPILWrapperUtils.getCodeInfoUtils.UseMF0Logging;
                case 'XIL_TERMINATE_COMMAND'
                    uploadTerminateIRTData = this.HasModelWideEvents && ...
                       this.SILPILWrapperUtils.getCodeInfoUtils.UseMF0Logging;
                case {'XIL_SYSTEM_INITIALIZE_COMMAND', ...
                      'XIL_ENABLE_COMMAND', ...
                      'XIL_DISABLE_COMMAND', ...
                      'XIL_SYSTEM_RESET_COMMAND'}
                    % do nothing
                otherwise                    
                    assert(false, 'Unexpected command: %s', command);
            end
                                                 
            % process outports            
            outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, ...
                'outputsize');
            % process output size data prior to processing output data
            % so that the host's row major transform can access the current
            % sizes
            dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                outportSizesIndices, 'outputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, 'output'), ...
                                'output')];
            if uploadConstSigLogsAndSharedLocalDataStores || uploadInitializeIRTData || uploadTerminateIRTData
                loggingData = this.SILPILWrapperUtils.getLoggingData;
                loggingDataIndices = [];
                if uploadConstSigLogsAndSharedLocalDataStores
                    loggingDataIndices = [loggingDataIndices ...
                        this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('ONESHOT'))];
                end
                if uploadInitializeIRTData
                    loggingDataIndices = [loggingDataIndices ...
                        this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('INITIALIZE'))];
                end
                if uploadTerminateIRTData
                    loggingDataIndices = [loggingDataIndices ...
                        this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('TERMINATE'))];
                end
                selectedLoggingData = loggingData(loggingDataIndices);
                for i=1:length(selectedLoggingData)
                    dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
                end
            end
            if strcmp(command,'XIL_TERMINATE_COMMAND')
                termTask = this.codeInfoUtils.getTerminateTasks();
                if ~isempty(termTask) && ...
                        this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType~=rtw.pil.InTheLoopType.Block
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getWriterSharedDataStoresForTask(termTask,'term'), 'dswrite'), 'dswrite')]; %Extra Argument needed to workaround G1411813
                end
            end
            if ismember(command, {'XIL_INITIALIZE_COMMAND', 'XIL_SYSTEM_INITIALIZE_COMMAND'})
                %Add Data Store Writes to INITIALIZE call
                initTask = this.codeInfoUtils.getInitializeTasks();                
                dsWriteIndices = [];
                for taskIdx = 1:length(initTask)
                    dsWriteIndices = [dsWriteIndices, ...                         
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getWriterSharedDataStoresForTask(initTask(taskIdx), 'init'), 'dswrite')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                end
                dataElements = [dataElements, ...
                    rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    unique(dsWriteIndices, 'stable'), 'dswrite')];
                if uploadConstSigLogsAndSharedLocalDataStores
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        1:length(this.codeInfoUtils.getSharedLocalDataStores), 'initializeSharedLocal'), 'sharedLocalDataStore')];
                end
            end
            
            if ~isempty(dataElements)
                this.writeSectionInitDataProcessingSwitchStart();
                this.writeSectionInitDataProcessingCase(command, ...
                    [tableName '_y'], ...
                    dataElements, isTargetToHost);
            end            
        end
        
        function commandIdxSwitchGenerated = writeSectionInitYDataProcessingCaseWithCommandIdxSwitch(this, ...
                                                                                                     tasks, ...
                                                                                                     command, ...
                                                                                                     tableName)
            numTasks = length(tasks);             
            commandIdxSwitchGenerated = false;                                               
            for taskIdx = 1:numTasks                
                task = tasks(taskIdx);
                               
                id = int2str(task.id);
                % process outports, dswrite and timer
                loggingData = this.SILPILWrapperUtils.getLoggingData;                

                % ports may not be part of the function signature
                portIndices = this.codeInfoUtils.getOutputPortsForTask(task);
                outportSizesIndices = this.codeInfoUtils.getOutportSizesIndices(portIndices);
                % process dsWrite for this task
                dsWriteIndices = this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                    this.codeInfoUtils.getWriterSharedDataStoresForTask(task), 'dswrite');
                loggingDataIndices = this.codeInfoUtils.getLoggingForTask(task);
                loggingDataIndices = this.codeInfoUtils.getSignalLoggingIndices(loggingDataIndices);
                % process output size data prior to processing output data
                % so that the host's row major transform can access the current
                % sizes
                dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                    outportSizesIndices, 'outputsize'), ...
                                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                    portIndices, 'output'), ...                                
                                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                    dsWriteIndices, 'dswrite'),...
                                    this.getServerOutputArgsDataElement(task)];
                                
                selectedLoggingData = loggingData(loggingDataIndices);                                
                for i=1:length(selectedLoggingData)
                    dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
                end
                                                                                  
                if ~isempty(dataElements)
                    commandIdxSwitchGenerated =...
                        writeSectionInitYDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numTasks, ...
                        id, ...
                        command, ...
                        dataElements,...
                        tableName,...
                        '_y',...
                        commandIdxSwitchGenerated);
                end
            end
            if commandIdxSwitchGenerated
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');   
                this.writer.writeLine('break;');
                this.writer.decIndent;
                this.writer.writeLine('}');                                                                                                                       
            end
        end        
        
        function commandIdxSwitchGenerated = writeSectionInitSDataProcessingCaseWithCommandIdxSwitch(this, ...
                                                                                                     tasks, ...
                                                                                                     command, ...
                                                                                                     tableName)
            numTasks = length(tasks);             
            commandIdxSwitchGenerated = false;                                               
            for taskIdx = 1:numTasks                
                task = tasks(taskIdx);
                               
                id = int2str(task.id);
                % process states
                loggingData = this.SILPILWrapperUtils.getLoggingData;                
                loggingDataIndices = this.codeInfoUtils.getLoggingForTask(task);
                loggingDataIndices = this.codeInfoUtils.getStateLoggingIndices(loggingDataIndices);
                dataElements = [];                                
                selectedLoggingData = loggingData(loggingDataIndices);                                
                for i=1:length(selectedLoggingData)
                    dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
                end
                                                                                  
                if ~isempty(dataElements)
                    commandIdxSwitchGenerated =...
                        writeSectionInitYDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numTasks, ...
                        id, ...
                        command, ...
                        dataElements,...
                        tableName,...
                        '_s',...
                        commandIdxSwitchGenerated);
                end
            end
            if commandIdxSwitchGenerated
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');   
                this.writer.writeLine('break;');
                this.writer.decIndent;
                this.writer.writeLine('}');                                                                                                                       
            end
        end
        
        function commandIdxSwitchGenerated =...
                writeSectionInitYDataProcessingCaseWithCommandIdxSwitchBody(...
                                                                            this, ...
                                                                            numTasks, ...
                                                                            taskId, ...
                                                                            command, ...
                                                                            dataElements,...
                                                                            tableName,...
                                                                            tableNamePostFix,...
                                                                            commandIdxSwitchGenerated)            
            if numTasks > 1
                if ~commandIdxSwitchGenerated
                    this.writeSectionInitDataProcessingSwitchStart();
                    this.writer.writeLine(['case ' command ': {']);
                    this.writer.incIndent;
                    % apply nested switch on "xilCommandIdx"
                    this.writer.writeLine('switch(xilCommandIdx) {');
                    % switch indent
                    this.writer.incIndent;
                    commandIdxSwitchGenerated = true;
                end
                caseId = taskId;
            else
                caseId = command;
            end
            isTargetToHost = true;
            this.writeSectionInitDataProcessingSwitchStart();
            this.writeSectionInitDataProcessingCase(caseId, ...
                [tableName taskId tableNamePostFix], ...
                dataElements, isTargetToHost);           
        end
        
        function writeSectionTerminate(this)
            
            this.writer.wBlockStart('XIL_INTERFACE_ERROR_CODE xilTerminate(uint32_T xilFcnId)')            
            this.writer.wBlockStart('if (xilFcnId != 0)');
            this.writer.wLine('return XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.wBlockEnd(); %if xilFcnId !=0
            
            this.writer.wComment('Invoke any terminate Function');
            this.writeSectionTerminateBody();
                        
            if this.AllocatesCppObject
                mdlObjectImplementation = this.getCPPModelObject.Implementation;
                mdlObjectName = this.getExpression(mdlObjectImplementation);
                this.writer.writeLine('delete %s();', ...
                    this.getModelObjectPtrAccessorName(mdlObjectName));
            end
            
            this.writer.wLine('return XIL_INTERFACE_SUCCESS;');
            this.writer.wBlockEnd();            
        end
        
        function writeSectionTerminateBody(this)
            
            termFunction = this.codeInfo.TerminateFunctions;
            if isempty(termFunction)
                % no terminate function
                callTerminateFunction = false;
            else
                % expect a single terminate function
                assert(isscalar(termFunction));
                % single terminate function
                callTerminateFunction = true;
            end
            
            if this.SILPILWrapperUtils.isMatFileLoggingEnabled                
                this.writeSectionTerminateMatFileLogging();
            end
            
            %
            isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
            if callTerminateFunction                
                if isExecutionProfilingEnabled
                    [sectionIdForFunction, instrCalls] = this.getProfilingIdentifier...
                        (termFunction);
                    this.writer.writeLine('%s(%dU);', ...
                        instrCalls{1}, ...
                        sectionIdForFunction);
                end
                
                this.writer.wLine('%s;', termFunction.getFunctionCall);
                    
                if isExecutionProfilingEnabled
                    this.writer.writeLine('%s(%dU);',...
                        instrCalls{2},...
                        sectionIdForFunction);
                end
            else
                this.writeEmptyFunctionCall;                
            end
            this.writer.newLine;

            if this.SILPILWrapperUtils.AreEventCallbacksRequired
                this.writer.writeLine('TerminateEvent();');
                this.writer.newLine;
            end
        end
        
        function writeSectionEnable(this)                        
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilEnable(uint32_T xilFcnId, uint32_T xilTID) {');
            this.writer.incIndent;
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}';});            
            this.writeSectionEnableBody;
            this.writer.decIndent;            
            this.writer.writeLine('}');
            this.writer.newLine;
        end
        
        function writeSectionEnableBody(this)
            % no enable function
            this.writer.writeLine('UNUSED_PARAMETER(xilTID);');
            this.writer.writeLine('/* No Enable Function - this function should never be called */');
            this.writer.writeLine('return XIL_INTERFACE_UNKNOWN_TID;');
        end                
        
        function writeSectionDisable(this)            
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilDisable(uint32_T xilFcnId, uint32_T xilTID) {');
            this.writer.incIndent;
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}';});
            this.writeSectionDisableBody;
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;                        
        end
        
        function writeSectionDisableBody(this)
            % no disable function
            this.writer.writeLine('UNUSED_PARAMETER(xilTID);');
            this.writer.writeLine('/* No Disable Function - this function should never be called */');
            this.writer.writeLine('return XIL_INTERFACE_UNKNOWN_TID;');
        end
        
        % write the xilIOData table variable
        function writePILIODataTable(this, dataElements, tag)              
            this.writer.writeLine(['static XILIOData xil_fcnid0' tag '[' ...
                this.getNumxilIODataTableElements(dataElements) '];']);
            this.writer.newLine;
        end
        
        % write xilIOData tables for init functions
        function writeFuncXILIOData(this, funs, name)                          
            switch name
                case 'init'
                    uploadConstSigLogsAndSharedLocalDataStores = true;
                case {'system_initialize', 'term', 'enable', 'disable', 'system_reset'}
                    uploadConstSigLogsAndSharedLocalDataStores = false;
                otherwise                    
                    assert(false, 'Unexpected name: %s', name);
            end
            % process inports and params        
            inportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(funs, ...
                                                                                 'inputsize');
            outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(funs, ...
                                                                                 'outputsize');
            inputIndices = this.codeInfoUtils.getDataInterfacesForFunction(funs, 'input');
            
            paramIndices = this.getParamIndicesForFunction(funs);

            if strcmp(name,'init')
                inputIndices = this.codeInfoUtils.getAdditionalInitializeInportIndices(inputIndices);
                % write xilIOData table entries for InstP model arguments
                inTheLoopType = this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType;
                paramIndices = [paramIndices, this.codeInfoUtils.getInstPArgumentIndices(inTheLoopType)];
            elseif strcmp(name,'term')
                inputIndices = unique([inputIndices, ...
                    this.codeInfoUtils.getInputPortsForRates(this.codeInfoUtils.getRates('TERMINATE'))]);
            end
            dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                inputIndices, 'input'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                inportSizesIndices, 'inputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...                            
                                outportSizesIndices, 'outputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                paramIndices, 'parameter')];
            if ismember(name,{'init', 'system_initialize'}) 
                %Add Data Store reads to INITIALIZE call
                initTask = this.codeInfoUtils.getInitializeTasks();
                dsReadIndices = [];
                for taskIdx = 1:length(initTask)
                    dsReadIndices = [dsReadIndices, ...                         
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(initTask(taskIdx), 'init'), 'dsread')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                end
                dataElements = [dataElements, ...
                    rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    unique(dsReadIndices, 'stable'), 'dsread')];                
            elseif strcmp(name,'term')
                %Add Data store Reads to TERMINATE Calls
                termTask = this.codeInfoUtils.getTerminateTasks();
                if ~isempty(termTask) && ...
                        this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType~=rtw.pil.InTheLoopType.Block
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(termTask, 'term'), 'dsread'), 'dsread')]; %Extra Argument needed to workaround G1411813
                end
            end                
                            
                            
            if ~isempty(dataElements)                
                tag = ['_' name '_u'];
                this.writePILIODataTable(dataElements, tag);
            end
            % process outports
            if (this.SILPILWrapperUtils.getClientInterface.syncIODuringOneShot())                
                if (strcmp(name,'term') && this.HasModelWideEvents) || ~strcmp(name,'term')
                    % process output size data prior to processing output data
                    % so that the host's row major transform can access the current
                    % sizes
                    dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        outportSizesIndices, 'outputsize'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.codeInfoUtils.getDataInterfacesForFunction(funs, 'output'), 'output')];
                    if uploadConstSigLogsAndSharedLocalDataStores
                        loggingData = this.SILPILWrapperUtils.getLoggingData;
                        loggingDataIndices = this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('ONESHOT'));
                        selectedLoggingData = loggingData(loggingDataIndices);
                        for i=1:length(selectedLoggingData)
                            dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
                        end
                    end
                    if strcmp(name,'term')
                        % Add logged signals in TERMINATE subsystem
                        if this.HasModelWideEvents && this.SILPILWrapperUtils.getCodeInfoUtils.UseMF0Logging
                            loggingData = this.SILPILWrapperUtils.getLoggingData;
                            loggingDataIndices = this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('TERMINATE'));
                            selectedLoggingData = loggingData(loggingDataIndices);
                            for i=1:length(selectedLoggingData)
                                dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
                            end
                        end
                        %Add Data store Writes to TERMINATE Calls                        
                        termTask = this.codeInfoUtils.getTerminateTasks();
                        if ~isempty(termTask) && ...
                                this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType~=rtw.pil.InTheLoopType.Block
                            dataElements = [dataElements, ...
                                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                                this.codeInfoUtils.getWriterSharedDataStoresForTask(termTask, 'term'), 'dswrite'), 'dswrite')]; %Extra Argument needed to workaround G1411813
                        end
                    elseif ismember(name,{'init', 'system_initialize'})
                        % Add logged signals in INITIALIZE subsystem
                        if this.HasModelWideEvents && this.SILPILWrapperUtils.getCodeInfoUtils.UseMF0Logging
                            loggingData = this.SILPILWrapperUtils.getLoggingData;
                            loggingDataIndices = this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('INITIALIZE'));
                            selectedLoggingData = loggingData(loggingDataIndices);
                            for i=1:length(selectedLoggingData)
                                dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
                            end
                        end
                        %Add Data Store Writes to INITIALIZE call
                        initTask = this.codeInfoUtils.getInitializeTasks();
                        
                        dsWriteIndices = [];
                        for taskIdx = 1:length(initTask)
                            dsWriteIndices = [dsWriteIndices, ...
                                this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                                this.codeInfoUtils.getWriterSharedDataStoresForTask(initTask(taskIdx), 'init'), 'dswrite')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                        end
                        dataElements = [dataElements, ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            unique(dsWriteIndices, 'stable'), 'dswrite')];
                        if uploadConstSigLogsAndSharedLocalDataStores
                            dataElements = [dataElements, ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                            1:length(this.codeInfoUtils.getSharedLocalDataStores), 'initializeSharedLocal'), 'sharedLocalDataStore')];
                        end
                    end
                    if ~isempty(dataElements)
                        tag = ['_' name '_y'];
                        this.writePILIODataTable(dataElements, tag);
                    end
                end                
            end
        end                                           
        
        function validateInterface(this)
            
            % validate I/O
            this.checkDataInterfacesAreAccessibleAndWritable(this.codeInfo.Inports, 'inport');
            this.checkDataInterfacesAreAccessibleAndWritable(this.codeInfo.Outports, 'outport');
            
            %Validate Data Stores can be accessed
            allSharedDataStores = this.SILPILWrapperUtils.getDataStoresToTransferInterfaces(this.codeInfoUtils.getAllSharedDataStores);
            this.checkDataInterfacesAreAccessibleAndWritable(allSharedDataStores, 'datastore');
            
            % validate parameters
            dataInterfaces = this.codeInfo.Parameters;
            for i=1:length(dataInterfaces)
                dataInterface = dataInterfaces(i);
                this.validateParamDataInterface(dataInterface);            
            end
            
            % validate that the generated code does not contain instance
            % specific parameters that SIL/PIL cannot support
            this.validateInstSpecificParameters;
        end               
        
        function validateParamDataInterface(this, dataInterface)             %#ok<INUSD>
            % override in subclass
        end
        
        % write assignments from the data transfer variable to the
        % target variable
        function writeInputAssignments(this, dataElements)                                                                                                    
            for dataElementIdx = 1:length(dataElements)
                dataElement = dataElements(dataElementIdx);
                dataInterface = dataElement.getDataInterface;
                dataInterface.writeInputAssignment(dataElement, this.writer, this);
            end
        end                    
                        
        % write assignments from the target variable to the data transfer variable
        function writeOutputAssignments(this, dataElements)             
            for dataElementIdx = 1:length(dataElements)
                dataElement = dataElements(dataElementIdx);                                                                                  
                dataInterface = dataElement.getDataInterface;                                                                                
                dataInterface.writeOutputAssignment(dataElement, this.writer, this);                                                
            end
        end 
        
        % return the "base" storage implementation for a root implementation
        function storageImplementation = getBaseStorageImplementation(this, implementation)
            %Call shared implementation for getBaseStorageImplementation in
            %SILPILWrapperUtils.
            storageImplementation = this.SILPILWrapperUtils.getBaseStorageImplementation(implementation);
        end     
        
        function isDef = isDefined(this, implementation) 
            isDef = implementation.isDefined;
            if ~isDef 
                switch this.codeInfoUtils.class(implementation)
                    case 'PointerExpression'
                        %For PointerExpressions, codeInfo only returns true for
                        %isDefined if BaseRegion is defined, however we set the
                        %Owner of the Target Region first. See G1044788
                        isDef = implementation.TargetRegion.isDefined;
                    case 'AutosarCalibration' 
                        %Needed to support Autosar subcomponent model
                        %block, recursively call isDefined on the
                        %calibration base storage
                        isDef = this.isDefined(this.getBaseStorageImplementation(implementation));
                end
            end
        end
        
        
        function setImplementationOwner(~, implementation, owner) 
            rtw.pil.OwnerAccessor.setOwner(implementation, owner);
        end
        
        function owner = getImplementationOwner(~, implementation)
            owner = rtw.pil.OwnerAccessor.getOwner(implementation);
        end
        
        % template method to write required Defines for Matfile logging
        function writeSectionDefinesMatFileLogging(this) %#ok<MANU>
        end

        % template method to write code to update vars for Matfile logging
        function writeSectionOutputBodyMatFileLogging(this) %#ok<MANU>
        end

        % template method to write code to stop Matfile logging
        function writeSectionTerminateMatFileLogging(this) %#ok<MANU>
        end
        
        function modelObject = getCPPModelObject(this)
            assert(this.SILPILWrapperUtils.isCPPEncap, ...
                   'getCPPModelObject is only valid for C++ class generation');
            % assume model object is always the first element
            modelObject = this.getCodeInfo.InternalData(1);   
        end
        
        % default method, pointer variables will normally need to be
        % statically defined.
        function isPointerAllocd = isPointerVariableAllocd(~, ~, ~)
            isPointerAllocd = false;
        end
        
        % return output tasks, filtering server functions if needed
        function tasks = getOutputTasks(this)
            tasks = this.codeInfoUtils.getOutputTasks;
            keepIndices = true(1,length(tasks));
            for taskIdx = 1:length(tasks)
                [isServerFunction, serverFunctionInfo] =...
                    isServerOutputFunction(this.codeInfoUtils, ...
                        tasks(taskIdx).codeInfoData);
                if isServerFunction && ...
                        (~this.SILPILWrapperUtils.needToProcessServerFcns || serverFunctionInfo.IsScoped)
                    % remove all server fcns if not needed and remove all
                    % scoped server fcns
                    keepIndices(taskIdx) = false;
                end
            end
            tasks = tasks(keepIndices);
        end
        
        function validateBaseStorageImplementation(this, implementation, storageImplementation)
            if this.codeInfoUtils.isa(storageImplementation, 'CustomExpression')
                % make sure coder.descriptor.CustomExpression is not referenced
                % from e.g. an coder.descriptor.ArrayExpression or any other
                % parent implementation - it must be at the top-level
                assert(implementation == storageImplementation, ...
                    'Implementation & StorageImplementation must be equal for CustomExpression.');
                if rtw.pil.TargetInterface.isGetSetFunctionAccess(storageImplementation) || ...
                        this.codeInfoUtils.isa(storageImplementation, 'ClassMethodAccess')
                    assert(~rtw.connectivity.CodeInfoTypeUtils.isComplex(storageImplementation.Type), ...
                        'Get/Set CSC cannot be complex.');
                    if implementation.AccessViaMacro
                        % error out for unsupported get/set functions with AccessViaMacro
                        DAStudio.error('PIL:pil:UnsupportedCSCGetSetViaMacro');
                    end
                else
                    % Imported struct, bitfield, etc. should be encoded
                    % as CustomVariable, not CustomExpression.
                    assert(this.isDefined(storageImplementation), ...
                        'CustomExpression not supported for imported data scopes');
                end
            elseif this.codeInfoUtils.isa(storageImplementation, 'BasicAccessFunctionExpression')
                % error out for unsupported storage classes with data
                % access through macro
                ioAccessMode = storageImplementation.IOAccessMode;
                if ismember(ioAccessMode, {'ByMacro' 'BY_MACRO'})
                    DAStudio.error('PIL:pil:UnsupportedAccessViaMacro');
                end
            end
        end
        
        function typeIdentifier = getStorageTypeIdentifier(this, storageImplementation, needsReplaceType, isPointerToMatrix)
            % C storage type gives structtype for struct data and
            % complextype for complex data
            if isempty(storageImplementation)
                typeIdentifier = '';
                return;
            end
            storageType = rtw.connectivity.CodeInfoTypeUtils.getCStorageType(storageImplementation.Type);
            typeIdentifier = storageType.Identifier;
            if needsReplaceType
                typeIdentifier = this.codeInfoUtils.getReplacedTypeIdentifier(typeIdentifier);
            end
            if isPointerToMatrix
                % Modify type for pointer to matrix case
                % Ensure type is includes correct pointer representation.
                % NOTE: The type is only a pointer if the underlying data
                % is a matrix.
                typeIdentifier = sprintf('%s*', typeIdentifier);
            end
        end
                
        
        function storageSpecifier = getStorageSpecifier(this, dataInterface)
            % Returns the storage specifier
            if this.hasOnlyLocalAccess(dataInterface)
                % limit scope of internal variables
                storageSpecifier = 'static ';
            else
                if ~this.SILPILWrapperUtils.isCPPEncap
                    %Only process C storage specifiers
                    tmpStorageSpec = this.codeInfoUtils.getStorageSpecifier(dataInterface.Implementation);
                    if ismember(tmpStorageSpec,{'unknown','extern'})
                        tmpStorageSpec = '';
                    end
                    storageSpecifier = sprintf('%s ', tmpStorageSpec);
                else
                    storageSpecifier = '';
                end
            end
        end
        
        
        function [storage_size_str, storage_index] = getStorageSizeStrAndIndex(this, storageImplementation, lDimsPreserved)
            % Returns strings specifying the storage size and storage
            % index.
            numElements = this.getStorageNumElements(storageImplementation);
            lHasStorageImplementation = ~isempty(storageImplementation);
            if lHasStorageImplementation
                isArray = rtw.connectivity.CodeInfoTypeUtils.isVector(storageImplementation.Type, ...
                    this.SILPILWrapperUtils.getClientInterface);
            else
                isArray = false;
            end
            isScalar = ~isArray;
            if isScalar
                storage_size_str = '';
                storage_index = '';
            else
                storage_index = '[0]';
                if lDimsPreserved
                    if isempty(storageImplementation.CodeType)
                        %Default back to Type, if Code Type is missing (not
                        %all Dynamic Region Variables have it populated)
                        cType = storageImplementation.Type;
                    else
                        cType = storageImplementation.CodeType;
                    end
                    symbolicDims = rtw.connectivity.CodeInfoTypeUtils.getSymbolicDimensions(cType);
                    storage_size_str = '';
                    storage_index = repmat(storage_index, 1, length(symbolicDims));
                    for i=1:length(symbolicDims)
                        storage_size_str = sprintf('%s[%s]', storage_size_str, symbolicDims{i});
                    end
                else
                    storage_size_str = ['[' numElements ']'];
                end
            end
        end
        
        
        function fullExpression = getDeclareExpression(this, storageImplementation)
            % Returns expression for declaring storage variables.
            if this.canGetExpressionFromCodeInfo(storageImplementation)
                fullExpression = storageImplementation.DeclareExpression;
            else
                needsReplaceType = true;
                isPointerToMatrix = false;
                typeIdentifier = this.getStorageTypeIdentifier( ...
                    storageImplementation, ...
                    needsReplaceType, ...
                    isPointerToMatrix);
                
                declarationSpecifier = 'extern ';
                
                lDimsPreserved = false; % Don't preserve symbolic dims when just declaring
                storage_size_str = this.getStorageSizeStrAndIndex(...
                    storageImplementation, lDimsPreserved);
                
                storageVolatile = this.getStorageVolatileQualifier(storageImplementation);
                
                dataExpression = this.getExpression(storageImplementation);
                
                fullExpression = [declarationSpecifier storageVolatile typeIdentifier ' ' ...
                    dataExpression ...
                    storage_size_str ';'];
            end
        end
        
        
        function fullExpression = getDefineExpression(this, storageImplementation, xilDataInterface)
            % GETDEFINEEXPRESSION
            % Returns expression for defining storage variables.
            if this.canGetExpressionFromCodeInfo(storageImplementation)
                fullExpression = storageImplementation.DefineExpression;
            else
                storage_size_str = this.getStorageSizeStrAndIndex(...
                    storageImplementation, xilDataInterface.DimsPreserved);
                
                dataInterface = xilDataInterface.getRTWDataInterface;
                storageSpecifier = this.getStorageSpecifier(dataInterface);
                storageVolatile = this.getStorageVolatileQualifier(storageImplementation);
                
                needsReplaceType = true;
                isPointerToMatrix = this.getIsPointerToMatrix(storageImplementation, dataInterface);
                typeIdentifier = this.getStorageTypeIdentifier( ...
                    storageImplementation, ...
                    needsReplaceType, ...
                    isPointerToMatrix);
                
                dataExpression = this.getExpression(storageImplementation);
                fullExpression = [storageSpecifier storageVolatile typeIdentifier ' ' ...
                    dataExpression ...
                    storage_size_str ';'];
            end
        end
                
        
        function fullExpression = getPointerDefineExpression(this, implementation, storageImplementation, lDimsPreserved, isPointerAllocd, isPointerToMatrix)
            % Returns expression for declaring/initializing pointer
            % variables for storage.
            [~, storage_index] = this.getStorageSizeStrAndIndex(...
                storageImplementation, lDimsPreserved);
            storageVolatile = this.getStorageVolatileQualifier(storageImplementation);
            dataExpression = this.getExpression(storageImplementation);
            dataExpressionPtr = this.getExpression(implementation);
            
            needsReplaceType = true;
            typeIdentifier = this.getStorageTypeIdentifier( ...
                                    storageImplementation, ...
                                    needsReplaceType, ...
                                    isPointerToMatrix);
                                
            if this.canGetExpressionFromCodeInfo(storageImplementation)
                % isPointerVariable is derived based on the class of the
                % implementation object.  For CSCs of type "other",
                % getPointerDefineExpression should not be used.
                assert(false, ...
                    'CSCs of type "Other" with pointer access are not represented by PointerVariable');
            else
                if isPointerAllocd
                    %Allocation Function will dynamically allocate this
                    %variable, so just declare it.
                    fullExpression = sprintf('%s %s * %s;', ...
                        storageVolatile, typeIdentifier, dataExpressionPtr);
                else
                    %Pointer variable needs initializing to point
                    %storage item declared above.
                    fullExpression = [storageVolatile typeIdentifier ' * ' ...
                        dataExpressionPtr ' = &' ...
                        dataExpression ...
                        storage_index ';'];
                end
            end
        end
        
        function fullExpression = getGroupedDefineExpression(this, implementation)
            assert(this.codeInfoUtils.isa(implementation, 'CustomVariable'));
            fullExpression = strtrim(implementation.ClassAccess.GroupDataDefinition);
        end
        
        function initExpression = getInitializeExpression(this, implementation, cData, indices, initString)
            % Returns expression for initializing storage implementation
            %
            % Currently just calls getWriteExpression and writes what the
            % caller passes as initString.  For CSCs of type "Other" in
            % theory we could use the response to the "initialize" request
            % returned by the TLC, but in this case, the "ground" value
            % might be a variable that is not defined in the generated
            % code if we are using a model ref code interface, or have
            % assigned ownership of the variable elsewhere.
            initExpression = this.getWriteExpression(implementation, cData, indices, initString);
        end
        
        % template method to write additional storage allocation
        function writeSectionStorageHook(~)
        end
        
        % template method to write RTW pointer assignment before calling
        % into the init_ function
        function writeRTMPointerAssigmentHook(~)
        end
        
        function isObj = isCPPModelObject(this, storageImplementation)
            isObj = this.SILPILWrapperUtils.isCPPEncap && ...
                (storageImplementation == this.getCPPModelObject.Implementation);
        end
        
        function validateInstSpecificParameters(~)
            % default implementation
        end
    end
    
    
    methods (Access = 'private', Static)
       function isGetSetFunctionAccess = isGetSetFunctionAccess(implementation)
            % determines whether we use get / set functions to access the
            % dataInterface in the generated code            
            if rtw.connectivity.CodeInfoUtils.isa(implementation, 'CustomExpression') && ...
                    ~strcmp(implementation.ReadExpression, implementation.WriteExpression)
                % assume that any time the Read / Write expressions don't
                % match, we have a GetSet CSC
                %
                % CodeInfo has no way to differentiate between the GetSet CSC
                % and a user's custom CSC that could potentially also register
                % different Read / Write expressions
                isGetSetFunctionAccess = true;
            else
                isGetSetFunctionAccess = false;
            end
        end                 
    end
   
    methods (Access = 'private')                     
        function dataElements = getServerInputArgsDataElement(this, task)
            % get data elements for the server function input args            
            [isServerFunction, serverFunctionInfo] =...
                isServerOutputFunction(this.codeInfoUtils, task.codeInfoData);
            if isServerFunction
                assert(this.SILPILWrapperUtils.needToProcessServerFcns, ...
                    'Expect needToProcessServerFcns in getServerInputArgsDataElement');
                dataElements = serverFunctionInfo.getInputArgsDataElements;
            else
                dataElements = rtw.pil.DataElement.empty;
            end
        end
        
        function dataElements = getServerOutputArgsDataElement(this, task)
            % get data elements for the server function output args            
            [isServerFunction, serverFunctionInfo] =...
                isServerOutputFunction(this.codeInfoUtils, task.codeInfoData);
            if isServerFunction
                assert(this.SILPILWrapperUtils.needToProcessServerFcns, ...
                    'Expect needToProcessServerFcns in getServerOutputArgsDataElement');
                dataElements = serverFunctionInfo.getOutputArgsDataElements;
            else 
                dataElements = rtw.pil.DataElement.empty;
            end
        end
        
        function checkDataInterfacesAreAccessibleAndWritable(this, dataInterfaces, dataInterfaceType)
            for i=1:length(dataInterfaces)
                dataInterface = dataInterfaces(i);
                % check data interfaces are not read-only
                %
                % e.g.
                % - In-the-Loop must be able to write to Inports
                % - model.c must be able to write to Outports (compilation should
                %   actually have failed before this check runs)
                if strcmp(dataInterfaceType, 'inport') || ~this.codeInfoUtils.isa(dataInterface.Implementation, 'ClassMethodExpression')
                    isReadOnly = this.codeInfoUtils.isDataInterfaceReadOnly(dataInterface);
                    if isReadOnly
                        diString = this.codeInfoUtils.getDataInterfaceString(dataInterface, dataInterfaceType);
                        rtw.pil.ProductInfo.error('pil', ...
                            'ReadOnlyDataInterface', ...
                            diString);
                    end
                end
                % check ports are accessible (not static)
                %
                % e.g.
                % - In-the-Loop must be able to access Inports and Outports
                isStatic = this.codeInfoUtils.isDataInterfaceStatic(dataInterface);
                if isStatic
                    diString = this.codeInfoUtils.getDataInterfaceString(dataInterface, dataInterfaceType);                                         
                    rtw.pil.ProductInfo.error('pil', ...
                                              'StaticDataInterface', ...
                                              diString);                    
                end
            end
        end
        
        function dataElements = getCallerFunctionTargetToHostDataElements(this, callerFunctionInfo)
            % process in args
            inArgsDataElements = callerFunctionInfo.getInputArgsDataElements;
            
            % process dsWrite
            task = [];
            task.codeInfoData = callerFunctionInfo.getCodeInfoData;
            dsWriteIndices = this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                this.codeInfoUtils.getWriterSharedDataStoresForTask(task), 'dswrite');
            dsWriteDataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                dsWriteIndices, 'dswrite');
            dataElements = [inArgsDataElements dsWriteDataElements];
        end
        
        function dataElements = getCallerFunctionHostToTargetDataElements(this, callerFunctionInfo)
            % process out args
            outArgsDataElements = callerFunctionInfo.getOutputArgsDataElements;
            
            % process dsread
            task = [];
            task.codeInfoData = callerFunctionInfo.getCodeInfoData;
            dsReadIndices = this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                this.codeInfoUtils.getReaderSharedDataStoresForTask(task), 'dsread');
            dsReadDataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                dsReadIndices, 'dsread');
            
            dataElements = [outArgsDataElements dsReadDataElements];
        end                
        
        % return the number of xilIOData table elements for the given
        % dataInterfaces
        function symbNumElements = getNumxilIODataTableElements(this, dataElements)  %#ok<INUSL>
            % always at least the final xilIOData
            numElements = 1;
            symbNumElements = '';
            for dataElementIdx = 1:length(dataElements)
                cData = dataElements(dataElementIdx);                
                % entry for each instances of the contiguous chunk
                if Inf == cData.getNumInstances
                    if isempty(symbNumElements)
                        symbNumElements = cData.getSymbolicNumInstances;
                    else
                        symbNumElements = sprintf( ...
                            '%s + %s', ...
                            symbNumElements,  cData.getSymbolicNumInstances);
                    end
                else
                    numElements = numElements + cData.getNumInstances;
                end                
            end
            if isempty(symbNumElements)
                symbNumElements = int2str(numElements);
            else
                symbNumElements = sprintf( ...
                    '%s + %d', symbNumElements, numElements);
            end
        end                                                     
        
        % write dynamic xilIOData table array initialization
        function writePILIODataTableInitialization(this, dataElements, tableName)
            this.writer.writeLine('static int initComplete = 0;');
            this.writer.wBlockStart('if (!initComplete)');
            this.writer.writeLine('uint32_T tableIdx = 0;');
            for dataElementIdx = 1:length(dataElements)
                cData = dataElements(dataElementIdx);
                
                if ~cData.shouldSkipTargetIOTableEntry
                    % open any required loops
                    cData.writeStructArrayHeader(this.writer);
                    % body
                    cData.getDataInterface.writePILIODataTableEntry(cData, tableName, this.writer, this);
                    % close any required loops
                    cData.writeStructArrayTrailer(this.writer);
                end
            end

            % final xilIOData - null pointer
            this.writer.writeLine([tableName '[tableIdx].memUnitLength = 0;']);
            this.writer.writeLine([tableName '[tableIdx++].address = (MemUnit_T *) 0;']);
            this.writer.writeLine('initComplete = 1;');
            this.writer.wBlockEnd(''); %if (!initComplete)
        end
        
        function writeTransferCalls(this, dataElements, isTargetToHost)
            if isTargetToHost
                this.writeResponseOutputData();
            end
            processedInterfaces = {};
            for idx=1:numel(dataElements)
                
                if dataElements(idx).getDataInterface.HasTargetSideOpaqueRegion
                    dataInterface = dataElements(idx).getDataInterface.getRTWDataInterface();
                    interfaceImplementation = dataElements(idx).getDataInterface.getRTWDataInterfaceImplementation();
                    if ~any(cellfun(@(x)isequal(x,dataElements(idx).getDataInterface),processedInterfaces))
                        processedInterfaces{end+1} = dataElements(idx).getDataInterface; %#ok<AGROW>
                        opaqueRegion = interfaceImplementation.OpaqueRegion;
                        if isTargetToHost
                            this.writer.wLine('%s',opaqueRegion.ReadFromData.getFunctionCall(dataInterface));
                        else
                            this.writer.wLine('%s',opaqueRegion.WriteToData.getFunctionCall(dataInterface));
                        end
                    end
                end
            end
        end
        function writeResponseOutputData(this)
            %Write out calls to send command information headers before
            %sending back output data.            
            this.writer.wBlockStart('');                        
            % Need to send the response id before sending back data
            this.writer.wBlockStart('if(xilWriteData(&responseId, sizeof(responseId)) != XIL_DATA_STREAM_SUCCESS)');
            this.writer.wLine('return XIL_INTERFACE_COMMS_FAILURE;');
            this.writer.wBlockEnd(); %If send response Id.
            
            this.writer.wBlockStart('if(responseId == XIL_RESPONSE_CS_REQUEST_SERVICE)')
            this.writer.wBlockStart('if(xilWriteData((MemUnit_T *) &serverFcnId, sizeof(serverFcnId)) != XIL_DATA_STREAM_SUCCESS)');
            this.writer.wLine('return XIL_INTERFACE_COMMS_FAILURE;');
            this.writer.wBlockEnd(); %If send server function.
            this.writer.wBlockEnd(); %If response is XIL_RESPONSE_CS_REQUEST_SERVICE.
            this.writer.wBlockEnd(); %Code Block
        end
        % helper function to process xilIOData
        function writeIOxilIOData(this, type, iotag)            
            %
            switch (type)
                case {'Outputs','mdlOutputsInputs', 'States'}
                    tasks = this.getOutputTasks;
                case 'mdlUpdateInputs'
                    tasks = this.codeInfoUtils.getUpdateTasks;
                otherwise
                    assert(false, 'Unexpected I/O type during target generation: %s\n', type);
            end
            
            for taskIdx = 1:length(tasks)
                task = tasks(taskIdx);                                
                
                % used for both inputs & outputs
                outportIndices = this.codeInfoUtils.getOutputPortsForTask(task);                                
                
                switch (type)
                    case 'mdlOutputsInputs'
                        
                        % process inports, params and dsread
                        inportIndices = this.codeInfoUtils.getInputPortsForTask(task);
                        inportIndices = this.SILPILWrapperUtils.getDFInputPortsIndices(inportIndices);
                        
                        
                        inportSizesIndices = this.codeInfoUtils.getInportSizesIndices(inportIndices);
                        outportSizesIndices = this.codeInfoUtils.getOutportSizesIndices(outportIndices);
                        dsReadIndices =  this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                            this.codeInfoUtils.getReaderSharedDataStoresForTask(task), 'dsread');
                                                
                        dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            inportIndices, 'input'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            inportSizesIndices, 'inputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            outportSizesIndices, 'outputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            this.getParamIndicesForFunction(task.codeInfoData), 'parameter'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            dsReadIndices, 'dsread'),...
                            this.getServerInputArgsDataElement(task)];
                    case 'mdlUpdateInputs'
                        inportIndices = this.codeInfoUtils.getInputPortsForTask(task);
                        inportIndices  = this.SILPILWrapperUtils.getNonDFInputPortsIndices(inportIndices);
                        inportSizesIndices = this.codeInfoUtils.getInportSizesIndices(inportIndices);
                        dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            inportIndices, 'input'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            inportSizesIndices, 'inputsize')];
                    case 'Outputs'
                        % process outports, dswrite and timer
                        loggingData = this.SILPILWrapperUtils.getLoggingData;
                        loggingDataIndices = this.codeInfoUtils.getLoggingForTask(task);
                        loggingDataIndices = this.codeInfoUtils.getSignalLoggingIndices(loggingDataIndices);
                        outportSizesIndices = this.codeInfoUtils.getOutportSizesIndices(outportIndices);
                        dsWriteIndices = this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                            this.codeInfoUtils.getWriterSharedDataStoresForTask(task), 'dswrite');
                        % process output size data prior to processing output data
                        % so that the host's row major transform can access the current
                        % sizes
                        dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            outportSizesIndices, 'outputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            outportIndices, 'output'), ...                            
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            dsWriteIndices, 'dswrite'),...
                            this.getServerOutputArgsDataElement(task)];
                        selectedLoggingData = loggingData(loggingDataIndices);
                        for i=1:length(selectedLoggingData)
                            dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
                        end
                    case 'States'
                        % process states
                        loggingData = this.SILPILWrapperUtils.getLoggingData;
                        loggingDataIndices = this.codeInfoUtils.getLoggingForTask(task);
                        loggingDataIndices = this.codeInfoUtils.getStateLoggingIndices(loggingDataIndices);
                        dataElements = [];
                        selectedLoggingData = loggingData(loggingDataIndices);
                        for i=1:length(selectedLoggingData)
                            dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
                        end
                    otherwise
                        assert(false, 'Unexpected I/O type during target generation: %s\n', type);
                end
                if isempty(dataElements)
                    % no xilIOData table required
                    continue;
                end
                tag = ['_task' int2str(task.id) '_' lower(iotag)];
                this.writePILIODataTable(dataElements, tag);
            end
            if ~strcmp(type,'mdlUpdateInputs') && ~strcmp(type,'States')
                % not done for States. Check if any action is required
                callerFunctionsInfo = this.getCallerFunctionsInfo;
                for callerFcnIdx = 1:length(callerFunctionsInfo)
                    callerFunctionInfo = callerFunctionsInfo(callerFcnIdx);
                    switch(type)
                        case 'mdlOutputsInputs'
                            dataElements = this.getCallerFunctionTargetToHostDataElements(callerFunctionInfo);
                        case 'Outputs'
                            dataElements = this.getCallerFunctionHostToTargetDataElements(callerFunctionInfo);
                        otherwise
                            assert(false, 'Unexpected I/O type during target generation: %s\n', type);
                    end
                    tag = ['_task' int2str(callerFunctionInfo.getTID) '_' lower(iotag)];
                    this.writePILIODataTable(dataElements, tag);
                end
            end
        end
        
        % write xilIOData table for constant outputs
        function writeConstOutputxilIOData(this)
            if this.codeInfoUtils.hasConstOutput
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                             this.codeInfoUtils.getOutputPortsForRates(this.codeInfoUtils.getRates('ONESHOT')), ...
                                                             'output'); 
                if ~isempty(dataElements)                                     
                    tag = '_const_y';
                    this.writePILIODataTable(dataElements, tag);
                end
            end
        end   
        
        % return the set of dataInterfaces to process for Get/Set function 
        % header files and definitions
        function dataInterfaces = getDataInterfacesForGetSetFunctionProcessing(this)
            allSharedDataStores = this.codeInfoUtils.getAllSharedDataStores;
            dataInterfaces = [this.codeInfo.Inports, ...
                              this.codeInfo.Outports, ...
                              allSharedDataStores, ...
                              this.codeInfo.Parameters(this.getParamIndicesForStorage)];
        end
        
        function writeSectionGetSetFunctions(this)
            % define imported get / set functions referenced 
            % by the component under test                        
            dataInterfaces = this.getDataInterfacesForGetSetFunctionProcessing;
            headerWritten = false;
            processedSetFuns = {};
            processedGetFuns = {};
            for dataInterfaceIdx = 1:length(dataInterfaces)
                dataInterface = dataInterfaces(dataInterfaceIdx);
                implementation = dataInterface.Implementation;
                if rtw.pil.TargetInterface.isGetSetFunctionAccess(implementation)
                    if ~headerWritten
                        this.writer.wLine('/* Get/Set Functions */');
                        headerWritten = true;
                    end
                    
                    storageImplementation = this.getBaseStorageImplementation(implementation);
                    this.validateBaseStorageImplementation(implementation, storageImplementation);
                    
                    needsReplaceType = false;
                    isPointerToMatrix = false;
                    typeIdentifier = this.getStorageTypeIdentifier(storageImplementation, needsReplaceType, isPointerToMatrix);
                    numElements = this.getStorageNumElements(storageImplementation);
                    
                    setFunction = implementation.WriteExpression;

                    % filter duplicates
                    if ismember(setFunction, processedSetFuns) || isempty(setFunction)
                        defineSetFunction = false;
                    else
                        defineSetFunction = true;
                        processedSetFuns{end+1} = setFunction; %#ok<AGROW>
                    end    
                    getFunction = implementation.ReadExpression;

                    % filter duplicates
                    dataExpression = this.getExpression(storageImplementation);
                    if ismember(getFunction, processedGetFuns) || isempty(getFunction)
                        defineGetFunction = false; 
                    else
                        defineGetFunction = true;
                        processedGetFuns{end+1} = getFunction; %#ok<AGROW>
                    end
                    if strcmp(numElements,'1')
                        if defineSetFunction
                            this.writer.wBlockStart('void %s(%s data)', ...
                                setFunction, typeIdentifier);
                            this.writer.wLine('%s = data;', dataExpression );
                            this.writer.wBlockEnd;
                        end                     
                        if defineGetFunction
                            this.writer.wBlockStart('%s %s(void)', ...
                                typeIdentifier, getFunction);
                            this.writer.wLine('return %s;', dataExpression );
                            this.writer.wBlockEnd;
                        end
                    else
                        if defineSetFunction
                            this.writer.wBlockStart('void %s(size_t idx, %s data)', ...
                                setFunction, typeIdentifier);
                            this.writer.wLine('%s[idx] = data;', dataExpression );
                            this.writer.wBlockEnd;
                        end
                        if defineGetFunction
                            this.writer.wBlockStart('%s %s(size_t idx)', ...
                                typeIdentifier, getFunction);
                            this.writer.wLine('return %s[idx];', dataExpression );
                            this.writer.wBlockEnd;
                        end
                    end    
                    if defineSetFunction || defineGetFunction
                        this.writer.newLine;
                    end
                end                
            end
            this.writer.newLine;
        end
        
        function writeSectionDynamicAccessFunctions(this)
            %Check I/O interfaces for dynamic regions. Define any get/set
            %functions attached to those regions.
            
            dataInterfaces = [rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                this.getInportIndicesForStorage, 'input'), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfo.Outports), 'output'), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                this.getParamIndicesForStorage, 'parameter')];
            
            definedFunctions = {}; %Workaround for isDefined not working see g1774074.
            haveDefinedTargetSerializations = false;
            %Keep a list of functions we have defined and only define
            %functions that are different
            for idx=1:numel(dataInterfaces)
                implemenation = dataInterfaces(idx).getRTWDataInterface.Implementation;
                if this.codeInfoUtils.isa(implemenation, 'TypedCollection')
                    implementationList = this.codeInfoUtils.getArray(implemenation.Elements);
                else
                    implementationList = implemenation;
                end
                for implIdx = 1:numel(implementationList)
                    implementation = implementationList(implIdx);
                    if rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(implementation) && ~haveDefinedTargetSerializations
                        this.writer.wLine(this.SILPILWrapperUtils.getCodeInfo.XILTargetSerializationFunctionDefinitions);
                        haveDefinedTargetSerializations = true;
                        
                    elseif ~isempty(implementation.DynamicMemory)
                        dynMem = implementation.DynamicMemory;
                        %Define any functions we previously haven't defined
                        
                        if ~isempty(dynMem.Getter)
                            %workaround method naming issue
                            isDefinedMethod = getIsDefinedMethodName(dynMem.Getter);
                            if ~dynMem.Getter.(isDefinedMethod) && ~ismember(dynMem.Getter.Name, definedFunctions)
                                this.writer.wLine(dynMem.getGetterFunctionBody);
                                dynMem.Getter.(isDefinedMethod) = true;
                                definedFunctions{end+1} = dynMem.Getter.Name; %#ok<AGROW>
                            end
                        end

                        if ~isempty(dynMem.Setter)
                            %workaround method naming issue
                            isDefinedMethod = getIsDefinedMethodName(dynMem.Setter);
                            if ~dynMem.Setter.(isDefinedMethod) && ~ismember(dynMem.Setter.Name, definedFunctions)
                                this.writer.wLine(dynMem.getSetterFunctionBody);
                                dynMem.Setter.(isDefinedMethod) = true;
                                definedFunctions{end+1} = dynMem.Setter.Name; %#ok<AGROW>
                            end
                        end
                    end
                end
            end
            
            function name = getIsDefinedMethodName(obj)
                if isprop(obj, 'IsDefined')
                    name = 'IsDefined';
                elseif isprop(obj, 'isDefined')
                    name = 'isDefined';
                else
                    assert(false, 'no property isDefined/IsDefined');
                end
            end
        end
        
        function writeSectionResponseTypeSize(this, hostDataType)
            allIODataTypes = this.SILPILWrapperUtils.getAllIODataTypes;
            if ismember(hostDataType, allIODataTypes)   
                % for targetDataType use native C datatypes to avoid any 
                % possible issues with data type replacement
                %
                % support PIL_FLOAT_SIZE and PIL_DOUBLE_SIZE for testing
                switch hostDataType
                    case 'single'
                        targetDefine = 'PIL_FLOAT_SIZE';
                        targetDataType = 'float';
                    case 'double'
                        targetDefine = 'PIL_DOUBLE_SIZE';
                        targetDataType = 'double';
                    otherwise
                        assert(false, 'Unexpected hostDataType: %s\n', hostDataType);
                end
                
                memUnitData = int2str(this.SILPILWrapperUtils.getTypeUtils.builtinTypeToTypeId(hostDataType));
                
                
                this.writer.writeLine('{');
                this.writer.incIndent;
                this.writer.writeCellLines({'/* send response id code */';
                    'MemUnit_T memUnitData = XIL_RESPONSE_TYPE_SIZE;';
                    'if (xilWriteData(&memUnitData, sizeof(memUnitData)) != XIL_DATA_STREAM_SUCCESS) {'});
                this.writer.incIndent;
                this.writer.writeLine('return XIL_INTERFACE_COMMS_FAILURE;');
                this.writer.decIndent;
                this.writer.writeLine('}');
                this.writer.writeCellLines({'/* send type id */';
                    ['memUnitData = ' memUnitData ';'];
                    'if (xilWriteData(&memUnitData, sizeof(memUnitData)) != XIL_DATA_STREAM_SUCCESS) {'});
                this.writer.incIndent;
                this.writer.writeLine('return XIL_INTERFACE_COMMS_FAILURE;');
                this.writer.decIndent;
                this.writer.writeLine('}');
                this.writer.writeLine(['/* ' targetDefine ' should only be already defined for MathWorks testing */']);
                this.writer.writeLine(['#ifndef ' targetDefine]);
                this.writer.incIndent;
                this.writer.writeLine(['#define ' targetDefine ' sizeof(' targetDataType ')']);
                this.writer.decIndent;
                this.writer.writeLine('#endif');                
                this.writer.writeCellLines({'/* send size in bytes */';
                    ['memUnitData = (MemUnit_T) ' targetDefine ';'];
                     '#ifndef HOST_WORD_ADDRESSABLE_TESTING';
                     '/* convert MemUnits to bytes */';
                     'memUnitData *= MEM_UNIT_BYTES;';
                     '#endif'});                                
                this.writer.writeLine('if (xilWriteData(&memUnitData, sizeof(memUnitData)) != XIL_DATA_STREAM_SUCCESS) {');
                this.writer.incIndent;
                this.writer.writeLine('return XIL_INTERFACE_COMMS_FAILURE;');
                this.writer.decIndent;
                this.writer.writeLine('}');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end
        
        function writeModelObjectAccessFunction(this, modelObjName, modelObjType)
            % Use an accessor function to control
            % initialization of the model object.   This is
            % essential to support LDRA code coverage which
            % calls targetFopen during execution of
            % the rtObject the constructor. We must delay this
            % until after xilInit to a point where the host
            % s-function is ready to accept the incoming data.
            % Top-tester for LDRA is 
            % codecoverage/sharedtests/tcodecov_basic_topModel.m:simple_top_model_sil_cpp_encap
            this.AllocatesCppObject = true;
            modelObjPtrAccessor = this.getModelObjectPtrAccessorName(modelObjName);
            this.writer.wComment('Map model object name to dereferenced pointer to model object');
            this.writer.wLine('#define %s (*%s())', modelObjName, modelObjPtrAccessor);
            this.writer.newLine;
            
            this.writer.wComment('Accessor function to control initialization of model object');
            this.writer.wBlockStart('%s * %s(void)', ...
                modelObjType, ...
                modelObjPtrAccessor);
            % call the constructor function to instantiate the obj
            ctor = this.codeInfo.ConstructorFunction;
            assert(~isempty(ctor), 'ConstructorFunction should not be empty');
            ctorCall = ctor.getFunctionCall;
            objPtr = ['xil_' modelObjName '_ptr'];
            this.writer.wLine('static %s* %s = new %s;', modelObjType, objPtr, ctorCall);
            this.writer.wLine('return %s;', objPtr);
            this.writer.wBlockEnd;
        end
        
        % Write all cached Get or Set calls.
        function writeClassGetSetCalls(this, getOrSet, isSetBeforeInit)
            allKeys = this.DefinedDataList.keys;
            
            if ~isempty(allKeys)
                this.writer.wComment(sprintf('Call all %s class methods', getOrSet));
            end
            for idx = 1 : this.DefinedDataList.length
                curValue = this.DefinedDataList(allKeys{idx});
                if ~isempty(curValue) && ~isempty(curValue.(getOrSet)) && ...
                        (~isSetBeforeInit || curValue.OKToSetBeforeInit) %Skip call if this is a parameter set method in Init
                    this.writer.wLine('%s;',curValue.(getOrSet));
                end
            end
            if ~isempty(allKeys)
                this.writer.newLine;
            end

        end
        
        function writeSectionSystemInitialize(this)            
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilSystemInitialize(uint32_T xilFcnId) {');                            
            this.writer.incIndent;
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writeSectionSystemInitializeBody;
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end  
        
        function writeSectionSystemInitializeBody(this)                        
            this.writer.writeCellLines({'/* Single In-the-Loop Component */' ...
                'if (xilFcnId == 0) {'});
            this.writer.incIndent;                                            
            
            % call SystemInitialize if applicable
            this.writeSectionSystemInitializeFunction;
                                                           
            this.writer.decIndent;
            this.writer.writeCellLines({'}' ...
                                        'else {'});
            this.writer.incIndent;
            this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.decIndent;
            this.writer.writeLine('}');                        
            % add optional trailer
            this.writeSectionSystemInitializeTrailer;            
            this.writer.writeLine('return errorCode;');
        end   
        
        function writeSectionSystemReset(this)                        
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilSystemReset(uint32_T xilFcnId) {');
            this.writer.incIndent;
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writeSectionSystemResetBody;
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end
        
        function writeSectionSystemResetBody(this)
            this.writer.writeCellLines({'/* Single In-the-Loop Component */' ...
                'if (xilFcnId == 0) {'});
            this.writer.incIndent;  
            
            % call SystemReset if applicable
            this.writeSectionSystemResetFunction;
                                                           
            this.writer.decIndent;
            this.writer.writeCellLines({'}' ...
                                        'else {'});
            this.writer.incIndent;
            this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.decIndent;
            this.writer.writeLine('}');                        
            this.writer.writeLine('return errorCode;');
        end        
        
        function ret = getUniqueRTWDataImplementationWithGroupedCSC(this, lRTWDataInterfaces)
            % Return a list of DataImplementation that represent
            % a set of unique grouped CSCs.
            lGroupedRTWDIs = this.getRTWDataInterfacesWithGroupedCSC(lRTWDataInterfaces);
            lGroupedCSCs = table( ...
                arrayfun(@(x)x.Implementation.ClassAccess.Package, lGroupedRTWDIs, 'UniformOutput', false)', ...
                arrayfun(@(x)x.Implementation.ClassAccess.ClassName, lGroupedRTWDIs, 'UniformOutput', false)');
            if ~isempty(lGroupedCSCs)
                [~, lUniqueIdx] = unique(lGroupedCSCs, 'stable');
                ret = [lGroupedRTWDIs(lUniqueIdx).Implementation];
            else
                ret = this.getCodeInfoUtils.getEmptyInterface('DataImplementation');
            end
        end
    
        function lHeaderFiles = getGroupedCSCHeaderFiles(this, lRTWDataInterfaces)
            % Return the list of (non-unique) header files for grouped
            % CSCs.
            lGroupedRTWDIs = this.getRTWDataInterfacesWithGroupedCSC(lRTWDataInterfaces);
            lHeaderFiles = arrayfun( ...
                @(x)x.Implementation.DeclarationFile, ...
                lGroupedRTWDIs, ...
                'UniformOutput', false);
    
            % Filter out any empty header file specifications
            lHeaderFiles(strcmp(lHeaderFiles, '')) = [];
            end
    
        function ret = getRTWDataInterfacesWithGroupedCSC(this, lRTWDataInterfaces)
            % Return the subset of data interfaces that are implemented with
            % a grouped CSCs
            ret = lRTWDataInterfaces( ...
                  arrayfun( ...
                  @(x)this.codeInfoUtils.isImportedGroupedCSC(x.Implementation), ...
                  lRTWDataInterfaces));
        end
        
        function writeCPPModelObjForIOAccess(this, storageImplementation)
            % write the Model Object access function if required for
            % IOAccess, this code is triggered when the ExternalIOAccess
            % parameter is set to Method or Structure-based method.
            
            if this.SILPILWrapperUtils.isCPPEncap
                switch this.codeInfoUtils.class(storageImplementation)
                    case 'ClassMethodExpression'
                        % for a Class Method Expression implementation always
                        % write out model object. The isDefined guard in the 
                        % caller means it won't be defined more than once. 
                        % i.e. storageImplementation is the rtObject.
                        modelObjectImplementation = storageImplementation.BaseRegion; %#ok<NASGU>
                        writeOutModelObj = true;
                        % Note: Strictly in this case we set the Owner twice, 
                        % to the same value, (in the caller and when we write 
                        % out the model object). This has been left as I 
                        % believe the code is more clear.
                    case 'StructAccessorVariable'
                        % for Struct Accessor Variable implementation only
                        % write out model object if it hasn't been defined
                        % already.
                        % Port data base structure (rtU/rtY) will be defined as
                        % normal below. i.e. storageImplementation is rtU/rtY
                        %                     writeOutModelObj = ~this.getCPPModelObject.Implementation.isDefined;
                        modelObjectImplementation = storageImplementation.Accessor.BaseRegion;
                        writeOutModelObj = ~modelObjectImplementation.isDefined;
                    otherwise
                        % no access method
                        writeOutModelObj = false;
                end
                
                if writeOutModelObj
                    % write out model object access function (rtObject) to
                    % allow method calls against it.
                    modelObjectImplementation = this.getCPPModelObject.Implementation;
                    
                    %Set owner on model object
                    this.setImplementationOwner(modelObjectImplementation, 'PIL');
                    dataExpression = this.getExpression(modelObjectImplementation);
                    this.writeModelObjectAccessFunction(dataExpression, modelObjectImplementation.Type.Identifier);
                end
            end
        end
        
        function ptrName = getModelObjectPtrAccessorName(~, modelObjName)
            ptrName = ['get_' modelObjName '_ptr'];
        end
    end
        
    methods (Access = public)
        % getReadExpression needs to be public, because it gets called in
        % rtw.pil.CodeInfoData.writeOutputAssignment
        function readExpression = getReadExpression(this, implementation, cData, indices)
            % Returns the expression for reading from storage implementation
            if this.canGetExpressionFromCodeInfo(implementation)
                readExpression = this.getExpressionForTLCCSC( ...
                    'ReadExpression', ...
                    implementation, ...
                    cData, ...
                    indices);
            else
                readExpression = this.getElementExpression(implementation, cData, indices);
            end
        end
        
        function addressExpression = getAddressExpression(this, implementation, cData, indices)
            % Returns the expression for reading from storage implementation
            if this.canGetExpressionFromCodeInfo(implementation)
                addressExpression = this.getExpressionForTLCCSC( ...
                    'AddressExpression', ...
                    implementation, ...
                    cData, ...
                    indices);
            else
                addressExpression = sprintf('&(%s)', this.getElementExpression(implementation, cData, indices));    
            end
        end
        
        function writeExpression = getWriteExpression(this, implementation, cData, indices, writeString)
            % Returns the expression for writing to storage implementation
            if this.canGetExpressionFromCodeInfo(implementation)
                writeExpression = this.getExpressionForTLCCSC( ...
                    'WriteExpression', ...
                    implementation, ...
                    cData, ...
                    indices);
                writeExpression = strrep(writeExpression, '$(NEWVALUE_TOKEN)', writeString);
            else
                expression = this.getElementExpression(implementation, cData, indices);
                writeExpression = [expression ' = ' writeString ';'];
            end
        end
    end

    methods (Access = protected)
        function isPointerToMatrix = getIsPointerToMatrix(this, storageImplementation, dataInterface)
            % Returns true if storageImplementation is a pointer to a
            % matrix.
            isPointerToMatrix = storageImplementation.Type.isPointer && ...
                ~isempty(dataInterface.Type) && ...
                dataInterface.Type.isMatrix && ...
                (this.SILPILWrapperUtils.getClientInterface.isArray(this.codeInfoUtils.getArray(dataInterface.Type.Dimensions)) || dataInterface.Type.ArrSizeOne);
        end
        
        function writeTimingServiceCode(this, outputTask)
            for fcnIdx = 1:this.codeInfo.RuntimeEnvironment.TimingService.FunctionTimingServices.Size
                fcnTiming = this.codeInfo.RuntimeEnvironment.TimingService.FunctionTimingServices(fcnIdx);
                if outputTask.codeInfoData.Timing.TaskIndex == fcnTiming.Index                                
                    timingServices = fcnTiming.RequestedTimingServices.toArray;
                    
                    % check need time tick code
                    needTimeTick = false; % assume
                    % check need delta time tick code
                    needDeltaTimeTick = false; % assume
                    
                    for serviceIdx = 1:length(timingServices)
                        service = timingServices(serviceIdx);
                        % If the function needs either Time or TimeTick service, the TimeTick incremental code is needed.
                        % Note that Time is computed as time resolution times time tick.
                        if (service == coder.descriptor.TimingServiceType.Time) || ...
                           (service == coder.descriptor.TimingServiceType.TimeTick)
                            needTimeTick = true;
                            
                        % If the function needs DeltaTimeTick service, or it is an asynchronous task and it needs DeltaTime,
                        % the DeltaTimeTick incremental code is need.                        
                        % Note that for an asynchronous task, delta time is computed as time resolution times delta time
                        % tick; while for a synchronous task, delta time is simply its period, so
                        % DeltaTimeTick is not needed.
                        elseif (service == coder.descriptor.TimingServiceType.DeltaTimeTick) || ...
                               (service == coder.descriptor.TimingServiceType.DeltaTime && fcnTiming.IsAsynchronous)
                            needDeltaTimeTick = true;
                        end
                    end
                    if needTimeTick
                        this.writer.wLine('RTE_TimingService_private.timeTick%d++;', fcnTiming.Index);
                    end
                    if needDeltaTimeTick
                        this.writer.wLine('RTE_TimingService_private.deltaTimeTick%d++;', fcnTiming.Index);
                    end
                end                            
            end
        end
    end
    
    methods (Access = protected, Static)
        function numElements = getStorageNumElements(storageImplementation)
            % Return the number of storage elements
            if isempty(storageImplementation)
                numElements = 0;
                return;
            end
            numElements = rtw.connectivity.CodeInfoTypeUtils.getSymbolicWidth(storageImplementation.Type);
        end
        
        
        function storageVolatile = getStorageVolatileQualifier(storageImplementation)
            % Returns the storage volatile qualifier when necessary
            isVolatile = ~isempty(storageImplementation) && ...
                rtw.connectivity.CodeInfoTypeUtils.isVolatile(storageImplementation.Type);
            if isVolatile
                % add volatile qualifier
                storageVolatile = 'volatile ';
            else
                storageVolatile = '';
            end
        end
        
        function ret = canGetExpressionFromCodeInfo(storageImplementation)
            % Returns true if the expressions for 
            % define/declare/read access/write access are pre-built in
            % CodeInfo/CodeDescriptor
            ret = rtw.connectivity.CodeInfoUtils.isa(storageImplementation, 'CustomVariable') || ...
                (rtw.connectivity.CodeInfoUtils.isa(storageImplementation, 'ArrayExpression') && ...
                rtw.connectivity.CodeInfoUtils.isa(storageImplementation.BaseRegion, 'CustomVariable'));
        end
        
        function retExpression = getExpressionForTLCCSC(expressionType, implementation, cData, lOriginalLeafIndices)
            % Get expression for accessing data element for CSC of type
            % "Other".
            [lOriginalElementSubExpression, nOriginalLevels] = cData.getElementExpression(lOriginalLeafIndices, true, true);
            if cData.getDataInterface.DimsPreserved
                lLevelIndexExprPreFormatting = 'i%d_0';
            else
                lLevelIndexExprPreFormatting = 'i%d';
            end
            lSymbolicZeroLevelIndexExpr = sprintf(lLevelIndexExprPreFormatting,0);
            
            isArrayExpression =  rtw.connectivity.CodeInfoUtils.isa(implementation, 'ArrayExpression');
            if isArrayExpression
                % Handle the case where the CustomVariable is an element of an
                % ArrayExpression.
                %
                % For example, if the underlying variable is:
                %   real_T exampleSignal[10];
                %
                % But the current port is a 5-element array:
                %   exampleSignal[5] -- exampleSignal[9]
                %
                lOffset = implementation.Offset;
                implementation = implementation.BaseRegion;
                
                % Determine whether we need to prepend a leaf index, or a
                % non-leaf index.
                %
                [needToPrependLeafIndex, needToPrependNonLeafIndex] = ...
                    rtw.pil.TargetInterface.needToPrependForArrayExpression(lOriginalElementSubExpression);
                
                % If we prepended a non-leaf index, then update the number
                % of levels
                if needToPrependNonLeafIndex
                    nLevels = nOriginalLevels + 1;
                    % Hard-code the index for the offset, since there won't
                    % be a loop generated for it.
                    lZeroLevelIndexExpr = sprintf('%d', lOffset);
                else
                    nLevels = nOriginalLevels;
                    lZeroLevelIndexExpr = lSymbolicZeroLevelIndexExpr;
                end
                
                lElementSubExpression = ...
                    rtw.pil.TargetInterface.updateElementSubExpressionForArrayExpression( ...
                        lOriginalElementSubExpression, ...
                        lOriginalLeafIndices, ...
                        lZeroLevelIndexExpr, ...
                        needToPrependLeafIndex, ...
                        needToPrependNonLeafIndex);
                
                % Update the indices expression
                lUpdatedLeafIndices = ...
                    rtw.pil.TargetInterface.updateLeafIndicesForArrayExpression( ...
                    lOriginalLeafIndices, ...
                    nLevels, ...
                    lOffset);
                
                % Update the zero level index expression
                if ~needToPrependNonLeafIndex && ~needToPrependLeafIndex && lOffset ~= 0
                    % If an array expression, and we didn't prepend anything
                    % we need to add the offset to the lowest level non-leaf
                    % index if the offset is non-zero
                    lUpdatedZeroLevelIndexExpr = sprintf('(%s + %d)', lZeroLevelIndexExpr, lOffset);
                else
                    lUpdatedZeroLevelIndexExpr = lZeroLevelIndexExpr;
                end
            else
                needToPrependNonLeafIndex = false;
                nLevels = nOriginalLevels;
                lZeroLevelIndexExpr = lSymbolicZeroLevelIndexExpr;
                lUpdatedZeroLevelIndexExpr = lZeroLevelIndexExpr;
                lUpdatedLeafIndices = lOriginalLeafIndices;
                lElementSubExpression = lOriginalElementSubExpression;
            end
            
            % Get the "candidate" element identifiers from the
            % CustomVariable object - need to use the "original" indices
            % here, since that is what will have been used in the original
            % lElementSubExpression
            dataElements = rtw.connectivity.CodeInfoUtils.getArray(implementation.DataElements);
            lCandidateElementIdentifiers = arrayfun(@(x)(x.DataElementIdentifier), dataElements, 'UniformOutput', false);
            lCandidateElementIdentifiers = cellfun(@(x)rtw.pil.TargetInterface.substituteTokens(x, nLevels, lOriginalLeafIndices, lZeroLevelIndexExpr, lLevelIndexExprPreFormatting, needToPrependNonLeafIndex), ...
                lCandidateElementIdentifiers, ...
                'UniformOutput', false);
            
            % Find the correct identifier - the one that matches
            % lElementSubExpression.
            lCurrentElement = dataElements(strcmp(lCandidateElementIdentifiers, lElementSubExpression));
            assert(~isempty(lCurrentElement), 'Could not find codeinfo data for %s', lElementSubExpression);
            retExpression = lCurrentElement.(expressionType);
            
            % Substitute index tokens into the final expression
            retExpression = rtw.pil.TargetInterface.substituteTokens( retExpression, nLevels, lUpdatedLeafIndices, lUpdatedZeroLevelIndexExpr, lLevelIndexExprPreFormatting, needToPrependNonLeafIndex);
        end
        
        function [needToPrependLeafIndex, needToPrependNonLeafIndex] = needToPrependForArrayExpression(lOriginalElementSubExpression)
            % Determine whether we need to prepend an index into an array
            % for an ArrayExpression.
            %
            % Prepend a "leaf" index if the port corresponds to
            % a real-valued scalar element of the underlying vector.
            % 
            %   e.g., for an underlying variable
            %       real_T myVariable[3];
            %
            %   The port corresponds to myVariable[1]
            needToPrependLeafIndex = isempty(lOriginalElementSubExpression);
            
            % Prepend a "non-leaf" index if the port corresponds to an
            % element of a composite data type
            %
            % For example, for the underlying variable
            %       myBus myBusInstance[3];
            %
            % The port corresponds to myBusInstance[1].myField[xyz]
            %
            needToPrependNonLeafIndex = ~needToPrependLeafIndex && ~startsWith(lOriginalElementSubExpression, '[');
        end
            
        function updatedLeafIndices = updateLeafIndicesForArrayExpression(leafIndices, nLevels, lOffset)
            % When working with an array expression, update the leaf
            % indices based on the number of levels in the DataElement
            % hierarchy and the offset in the array expression
            updatedLeafIndices = leafIndices;
            if nLevels == 0
                if lOffset ~= 0
                    if isnumeric(updatedLeafIndices)
                        % If indices is numeric, then add the offset
                        % so that it can be hard-coded in the returned
                        % expression.
                        %
                        % Example:
                        %
                        % Suppose that our ports correspond to elements
                        % 5-9 of the underlying variable:
                        %   real_T myVariable[10];
                        % Then to access element 2 of the port
                        %   leafIndices = 2, 
                        %   lOffset = 5,
                        %  and we compute:
                        %   updatedLeafIndices = 7;
                        updatedLeafIndices(1) = updatedLeafIndices(1) + lOffset;
                    else
                        % Example:
                        %
                        % for a port that corresponds of elements 5-9 of an
                        % underlying variable:
                        %   real_T myVariable[10]
                        % To access element "myElem" of the port, return 
                        % "(myElem + 5)"
                        %
                        assert(ischar(updatedLeafIndices));
                        updatedLeafIndices = sprintf('(%s + %d)', updatedLeafIndices, lOffset);
                    end
                end
            end
        end
        
        function lUpdatedElementSubExpression = updateElementSubExpressionForArrayExpression(lElementSubExpression, lOriginalLeafIndices, lZeroLevelIndexExpr, needToPrependLeafIndex, needToPrependNonLeafIndex)
            % If the ArrayExpression only indexes one element of
            % the array, then even though the underlying data is an
            % index into an array, the "Type" associated with the
            % ArrayExpression is scalar.  So we need to patch the
            % element sub expression with an index into the first
            % element of the array.
            %
            % We might be indexing into the "leaf" data element, or the
            % lowest "level" of a hierarchy of non-leaf data elements.
            if needToPrependLeafIndex
                if isnumeric(lOriginalLeafIndices)
                    lUpdatedElementSubExpression = sprintf('[%d]%s', lOriginalLeafIndices, lElementSubExpression);
                else
                    assert(ischar(lOriginalLeafIndices));
                    lUpdatedElementSubExpression = sprintf('[%s]%s', lOriginalLeafIndices, lElementSubExpression);
                end
            elseif needToPrependNonLeafIndex
                % We need to update the sub expression by prepending
                % with an explicit index into the base variable.
                lUpdatedElementSubExpression = sprintf('[%s]%s', lZeroLevelIndexExpr, lElementSubExpression);
            else
                % Return the original expression.
                lUpdatedElementSubExpression = lElementSubExpression;
            end
        end
        
        function lExpression = substituteTokens( lExpression, nLevels, indices, lZeroLevelIndexExpr, lLevelIndexExprPreFormatting, indexWasPrepended)
            % Fill in the tokens for ancestor indices
            for kLevel = 0:nLevels-1
                if kLevel == 0
                    lIToken = lZeroLevelIndexExpr;
                elseif indexWasPrepended
                    % If we prepended a hard-coded, this won't be included in
                    % the "for" loop that is generated in xil_interface.c.
                    % So we need to subtract 1 from the identifier in 
                    % expressions "i1", "i2", "i3", etc.  i.e.,
                    % "$(INDEX_TOKEN_1)" will map to "i0".
                    lIToken = sprintf(lLevelIndexExprPreFormatting, kLevel-1);
                else
                    lIToken = sprintf(lLevelIndexExprPreFormatting, kLevel);
                end 
                lExpression = strrep( ...
                    lExpression, ...
                    sprintf('$(INDEX_TOKEN_%d)', kLevel), ...
                    lIToken);
            end
            
            % Fill in the tokens for the leaf index
            if iscell(indices)
                assert(numel(indices) == 1, 'PreserveDimensions not supported for CSCs of type Other');
                indices = indices{1};
            end

            if isnumeric(indices)
                indices = sprintf('%d', indices);
            end 
            lExpression = strrep( ...
                lExpression, ...
                sprintf('$(INDEX_TOKEN_%d)', nLevels), ...
                indices);
        end
    end
end
