classdef (Hidden = true) ModelBlockSfunction < rtw.pil.Sfunction
%MODELBLOCKSFUNCTION creates an s-function for Model block SIL/PIL
%
%   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 ( SetAccess = immutable, GetAccess = private)
        NeedsInternalQueueFunctions = false;
        GlobalParameterNames
        HasDWork;
    end
    
    properties ( SetAccess = private, GetAccess = private)
        HasVirtualBusOutputs logical = false;
    end
    
    properties (SetAccess = immutable, GetAccess = protected)
        % This separate properties section contains information that we still
        % currently obtain from modelInterface.
        ModelInterfaceUtils;
    end
    
    properties (SetAccess = immutable, GetAccess = private)
        % This separate properties section contains information that we still
        % currently obtain from modelInterface.
        UsePortBasedTs;
        HasNonInlinedSfcn;
    end

    methods
        % constructor
        function this = ModelBlockSfunction(silPILWrapperUtils, ...
                                            sfunSourcePath, ...
                                            sfunOutDir)
            narginchk(3, 3);

            % Note: must not touch class properties until superclass
            % constructor has been called.
            
            % Get reference to MF0
            mf0Contents = silPILWrapperUtils.MF0ModelInfo;
            
            % Package information to pass to super class constructor.
            lHasResetFunction = silPILWrapperUtils.getCodeInfoUtils.hasResetFunction();
            lExposesITPorts = true;
            timingInterfaceFlags = rtw.connectivity.TimingInterfaceFlags.fromMF0(mf0Contents, ...
                lHasResetFunction, ...
                lExposesITPorts);
            
            % call super class constructor
            this@rtw.pil.Sfunction(silPILWrapperUtils, ...
                                   sfunSourcePath, ...
                                   sfunOutDir, ...
                                   'TimingInterfaceFlags', timingInterfaceFlags);            
            
            % Load the list of global parameters from the MF0 file
            oldParams = mf0Contents.interfaceParameterInfo.globalVariables.toArray;           
            this.GlobalParameterNames = {oldParams.parameterName};
            
            modelInterfaceOrig = this.SILPILWrapperUtils.getInfoStruct.modelInterface;
            assert(modelInterfaceOrig.DisallowSampleTimeInheritance == ...
                (mf0Contents.sampleTimeInheritanceRule == rtw.pil.ModelRefTsInhRule.DISALLOW_SAMPLE_TIME_INHERITANCE), ...
                   'Sample time inheritance differs between modelInterface and MF0!');
            
            lDisallowSampleTimeInheritance = this.checkSTInheritanceRule(modelInterfaceOrig.DisallowSampleTimeInheritance, ...
                    modelInterfaceOrig.FixedStepSizeBasedOnHeuristic);
            
            if this.SILPILWrapperUtils.isModelReferenceTarget
                modelInterface = modelInterfaceOrig;
                
                % Until this information is obtained by the Model Block
                % directly from MF0, we should patch values for
                % "nonContinuous" and "fedByBlockWithModesNoZCs" from MF0 
                % into modelInterface, since they are only serialized from
                % TLC for SIM targets.
                %
                % Both of these are almost certainly always "0" for any
                % model used for SIL, since only discrete-time is supported.
                nOutports = mf0Contents.outports.Size;
                if nOutports == 1
                    modelInterface.Outports.FedByBlockWithModesNoZCs = double(mf0Contents.outports.at(1).fedByBlockWithModesNoZCs);
                    modelInterface.Outports.NonContinuous = double(mf0Contents.outports.at(1).nonContinuous);
                elseif nOutports > 1
                    for kPort = 1:nOutports
                        modelInterface.Outports{kPort}.FedByBlockWithModesNoZCs = double(mf0Contents.outports.at(kPort).fedByBlockWithModesNoZCs);
                        modelInterface.Outports{kPort}.NonContinuous = double(mf0Contents.outports.at(kPort).nonContinuous);
                    end
                end
                
                % For input ports, patch in the value of "FeedsDerivPort".
                % This will almost certainly always be "0" for SIL but
                % is the inverse of "IsNotDerivPort".
                nInports = mf0Contents.inports.Size;
                if nInports == 1
                    modelInterface.Inports.FeedsDerivPort = double(~mf0Contents.inports.at(1).isNotDerivPort);
                elseif nInports > 1
                    for kPort = 1:nInports
                        modelInterface.Inports{kPort}.FeedsDerivPort = double(~mf0Contents.inports.at(kPort).isNotDerivPort);
                    end
                end
                    
            else
                % list of required modelInterface fields to avoid hard errors
                modelInterface.HasNonInlinedSfcn = modelInterfaceOrig.HasNonInlinedSfcn;
                modelInterface.HasInternalParameterRate = false; % now sil/pil do not support parameter sample time.
                modelInterface.HasParameterRateOutput = false;
                % hardcode num zero crossings to 0
                modelInterface.ZCVectorLength = 0;
                modelInterface.ParameterChangeEventTID = modelInterfaceOrig.ParameterChangeEventTID;
                modelInterface.IsAPeriodicTriggered = modelInterfaceOrig.IsAPeriodicTriggered;
                modelInterface.IsExportFcnDiagram = modelInterfaceOrig.IsExportFcnDiagram;
                % UsePortBasedTs not available from TLC; calculate from
                % CodeInfo information.
                if isfield(modelInterfaceOrig, 'NumPortlessSimulinkFunctionPortGroups') && ...
                        modelInterfaceOrig.NumPortlessSimulinkFunctionPortGroups > 0
                    modelInterface.NumPortlessSimulinkFunctionPortGroups = ...
                        modelInterfaceOrig.NumPortlessSimulinkFunctionPortGroups;
                    modelInterface.UsePortBasedTs = false;
                else
                    modelInterface.NumPortlessSimulinkFunctionPortGroups = 0;
                    
                    % Note that this.usePortBasedSampleTimes needs the following to
                    % have been already set:
                    %   * this.HasPortlessSimulinkFunctionPortGroups (used to short
                    %       circuit further checks)
                    %   * this.HasContinuousFundamentalSampleTime
                    %   * this.IsConstant
                    modelInterface.UsePortBasedTs = this.usePortBasedSampleTimes;
                end
                modelInterface.DisallowSampleTimeInheritance = lDisallowSampleTimeInheritance;                
                modelInterface.IsConstant = modelInterfaceOrig.IsConstant;
                if ~modelInterface.IsConstant
                    allOutputFunctions = this.codeInfo.OutputFunctions;
                    hasNonConstantRates = false;
                    for idx=1:numel(allOutputFunctions)                        
                        if strcmp(allOutputFunctions(idx).Timing.TimingMode, 'PERIODIC') || ...
                                strcmp(allOutputFunctions(idx).Timing.TimingMode, 'INHERITED') || ...
                                strcmp(allOutputFunctions(idx).Timing.TimingMode, 'ASYNCHRONOUS')
                            hasNonConstantRates = true;
                            break;
                        end
                    end
                    if ~hasNonConstantRates
                        modelInterface.IsConstant = true;
                    end
                end
                modelInterface.ModelRefTsInheritance = modelInterfaceOrig.ModelRefTsInheritance;
                if isfield(modelInterfaceOrig, 'Inports')
                    modelInterface.Inports = modelInterfaceOrig.Inports;
                end
                if isfield(modelInterfaceOrig, 'Outports')
                    modelInterface.Outports = modelInterfaceOrig.Outports;
                end
                modelInterface.MatrixDimensionThreshhold = modelInterfaceOrig.MatrixDimensionThreshhold;
                % export functions
                if isfield(modelInterfaceOrig, 'ExternalPortGroups')
                    modelInterface.ExternalPortGroups = modelInterfaceOrig.ExternalPortGroups;
                end
                if isfield(modelInterfaceOrig, 'FcnCallInputs')
                    modelInterface.FcnCallInputs = modelInterfaceOrig.FcnCallInputs;
                end
                
                if isfield(modelInterfaceOrig, 'LocalMessageQueueInfo')
                    %Check LocalMessageQueueInfo exists, it will be empty
                    %if there are no Receivers (i.e. no Queued Inputs)
                    modelInterface.LocalMessageQueueInfo = modelInterfaceOrig.LocalMessageQueueInfo;
                    this.NeedsInternalQueueFunctions = true;
                end
            end

            if isfield(modelInterfaceOrig,'ModelWideEvents')
                modelInterface.ModelWideEvents=modelInterfaceOrig.ModelWideEvents;
            else
                modelInterface.ModelWideEvents = {};
            end
            
            
            % Cache contents of modelInterface that still gets used
            % directly by ModelBlockSfunction.
            if this.SILPILWrapperUtils.isModelReferenceTarget
                this.UsePortBasedTs = mf0Contents.usePortBasedSampleTime;
            else
                assert(isempty(mf0Contents.usePortBasedSampleTime), ...
                    'Expected usePortBasedSampleTime in MF0 empty for top model code interface')
                % Use the value we previously computed and stored in
                % modelInterface above.
                this.UsePortBasedTs = modelInterface.UsePortBasedTs;
            end
            this.HasNonInlinedSfcn = modelInterface.HasNonInlinedSfcn;
            
            % Compute HasDWork without recourse to modelInterface
            this.HasDWork = this.initializeHasDWork(mf0Contents, this.codeInfo.InternalData);
            
            if this.SILPILWrapperUtils.isModelReferenceTarget
                % Model reference now supports non-inlined s-functions, but
                % SIL/PIL mode does not support them for now because extra
                % interface data is required in xil_interface.c.
                %
                % This is not a problem for Model blocks referencing
                % standalone code.
                if this.HasNonInlinedSfcn
                    rtw.pil.ProductInfo.error('pil','ModelBlockNoninlinedSFunctions', this.SILPILWrapperUtils.getSILPILInterface.getRootModel);
                end
            end

            %
            % Cross validation of modelInterface against CodeInfo:
            %
            %
            % IsExportFcnDiagram is for true export function models with no
            % task specification blocks / periodic rates
            if mf0Contents.configSettingsForConsistencyChecks.isExportFcnDiagram
                numPorts = this.getCodeInfoUtils.getNumExportFunctions + ...
                    length(this.codeInfo.Inports) +...
                    length(this.codeInfo.Outports);
                if this.HasModelWideEvents
                    %UsePortBased Sample times can be true or false when a
                    %system has Model Wide Events.
                    
                elseif (numPorts == 0 || this.HasPortlessSimulinkFunctionPortGroups)
                    % with client/server, it is possible that a model can
                    % contain a number of Simulink functions with no I/O
                    % ports. In this case, we don't want to use
                    % PORT_BASED_SAMPLE_TIMES.
                    % If the exported function diagram contains any simulink function port group
                    % with no ports, don't use PORT_BASED_SAMPLE_TIMES.
                    assert(~logical(this.UsePortBasedTs), ...
                           'IsExportFcnDiagram && UsePortBasedTs');
                else
                    assert(logical(this.UsePortBasedTs), ...
                           'IsExportFcnDiagram && ~UsePortBasedTs');
                end                                   
            end

            % instantiate and store ModelInterfaceUtils
            this.ModelInterfaceUtils =...
                coder.internal.modelreference.ModelInterfaceUtils(modelInterface);          
            
            if ~isempty(this.codeInfo.OutputFunctions) && ...
                    ~isempty(this.codeInfo.OutputFunctions(1).Timing) && ...
                    strcmp(this.codeInfo.OutputFunctions(1).Timing.TimingMode, 'PERIODIC') && ...
                    ~this.IsConstant
                %Enable inclusion of Output Update function. Logic
                %duplicated from model reference function writer.
                this.NeedsPeriodicOutputUpdateFcn = true;
            end
            this.HasExplicitPartitions = mf0Contents.hasExplicitPartitions;
            
            hasVirtualBusOutports = any(mf0Contents.isOrigOutportVirtualBus.toArray);
            this.HasVirtualBusOutputs = hasVirtualBusOutports;
        end
        
        function set.HasVirtualBusOutputs(this, isVirtual)            
            this.HasVirtualBusOutputs = this.HasVirtualBusOutputs || isVirtual;
        end
    end

    methods (Access = 'protected')
        
        function writeSectionInitDataStoreIndices(this)
            dataInterfaces = ...
                this.SILPILWrapperUtils.getDataStoresToTransferInterfaces(this.codeInfoUtils.getAllSharedDataStores);
            numDS = length(dataInterfaces);
            totalNumDSRates = this.getTotalNumDataStoreRates(dataInterfaces);
            if numDS > 0 && totalNumDSRates > 0
                this.writer.writeLine('/* determine DS indices */');
                [DSMVar, DSMIdxVar] = this.getGlobalDSMInfoVars;
                for i=1:numDS
                    ds = dataInterfaces(i);
                    % Extract the data type of the DSM block
                    storageImplementation = rtw.connectivity.CodeInfoTypeUtils.getStorageImplementation(ds.Implementation);
                    dType = rtw.connectivity.CodeInfoTypeUtils.getBaseType(storageImplementation.Type);
                    cidx = num2str(i - 1);
                    this.writer.writeLine(['ssGetSFcnGlobalDataStoreNameAddrIdx(S, "' ...
                        ds.GraphicalName '", "' dType.Name '", &(' DSMVar '[' cidx ']), ' ...
                        '&(' DSMIdxVar '[' cidx ']), "' ds.SharedLocalDSMInfo, '");']);
                end
            end
        end
        
        function writeSectionCleanupRTRBody(this)
            % Temporary fix for I,R,T system until G1388896 fixes calling 
            % sequence for model blocks with I,R and T ports.
            % Redirect calls to mdlCleanUpRuntimeResources to mdlTerminate where
            % SIL cleanup code is. mdlTerminate contains protection for being 
            % called twice.
            % mdlTerminate call and checks about multiple executions can be
            % removed once the bug has been fixed
            this.writer.wLine('mdlTerminate(S);');
            writeSectionCleanupRTRBody@rtw.pil.Sfunction(this);
        end
        
        function validateInterface(this)
            % check the I/O interface
            this.validateNumIOPortsAgainstSourceComponent;

            % call super class method
            validateInterface@rtw.pil.Sfunction(this);
            
            this.validateModelArguments;
        end 
        
        function disallowSampleTimeInheritance = checkSTInheritanceRule(this, ...
                disallowSampleTimeInheritance, fixedStepSizeBasedOnHeuristic)
                        
            if ~this.SILPILWrapperUtils.isModelReferenceTarget && ...                
                (~this.codeInfoUtils.isSampleTimeIndependent && ~disallowSampleTimeInheritance)
                % This is the scenario where the model is essentially
                % sample time independent (DisallowSampleTimeInheritance is false),
                % but the solver is unconstrained and CodeInfo ends up specifying a
                % default 0.2s sample time (isSampleTimeIndependent is false).
                if this.SILPILWrapperUtils.getSILPILInterface.isTopModelXILSim
                    % For Top-Model SIL/PIL, we don't
                    % need to be concerned with different instances; the
                    % fixed sample time matches the original unconstrained
                    % context.
                    %
                    % Disallow sample time inheritance to make sure we
                    % fully honor the sample times captured in Code Descriptor,
                    % which could be internal (not on ports) - e.g.
                    % tpil_connectivity: lvlTwo_g1019858.
                    %
                    % Note: This flag directly affects the scheduling scheme
                    % used in the S-Function.
                    disallowSampleTimeInheritance = true;
                else                     
                    %
                    % If we honor the sample time then the behavior will
                    % be different to normal simulation which will treat
                    % the component as sample time independent.    If we try
                    % to ignore the sample time in CodeInfo then we may end
                    % up with edge case issues during SIL/PIL.   Instead, a robust
                    % solution is to have the user explicitly choose "Ensure
                    % sample time independent".
                    assert(fixedStepSizeBasedOnHeuristic, ...
                        'Expected fixed step size to be based on heuristic.');
                    assert(strcmp(this.SILPILWrapperUtils.getParam('SampleTimeConstraint'), 'Unconstrained'), ...
                        'Expected SampleTimeConstraint to be Unconstrained');
                    % throw exception
                    componentPath = this.SILPILWrapperUtils.getLoadableRootComponent;
                    readOnlyActionIDs = {};
                    fullAccessActionIDs = {'STIndependent', 'ExplicitST'};
                    excep = coder.connectivity.AccessDependentDiagnostic(...
                        'PIL:pil:UnconstrainedSampleTimeIndependent', ...
                        readOnlyActionIDs, ...
                        fullAccessActionIDs, ...
                        componentPath);
                    excep.throw(this.SILPILWrapperUtils.isComponentReadOnly);
                end
            end
            
            if ~this.codeInfoUtils.hasServerFcnAndNoPeriodicRate
                %Skip checks for server functions with no periodic rates.
                if ~this.codeInfoUtils.isExportFunctions
                    assert(disallowSampleTimeInheritance ~= this.codeInfoUtils.isSampleTimeIndependent, ...
                        'DisallowSampleTimeInheritance == isSampleTimeIndependent');
                end
            end
        end
        
        function writeRuntimeChecks(this)
            %write out checks to run at runtime only
            
            % Throw error if in accelerator mode and model has virtual bus
            % outports
            if this.HasVirtualBusOutputs
                this.writer.wBlockStart('if (ssGetSimMode(S) != SS_SIMMODE_NORMAL)');
                this.getPWorkValue('void', this.XILUtilsVarName, this.getXILUtilsPWorkIdx, true);
                this.writer.wBlockStart('if (%s)', this.XILUtilsVarName);
                numRHS = 3;
                this.writer.writeLine(['mxArray *rhs[' int2str(numRHS) '];']);
                this.writeCallMATLABTypeAndPathInputs( 0 );
                % rhs will get freed automatically by xilUtilsCallMATLAB
                this.writer.wLine('xilUtilsCallMATLAB(%s, 0, NULL, %d, rhs, "rtw.pil.acceleratedModeNotSupported");',this.XILUtilsVarName, numRHS);                
                this.freeSimulinkBlockPath;
                this.writer.wBlockEnd; % if (pXILUtils)
                this.writer.wBlockEnd(); % if (ssGetSimMode(S) != SS_SIMMODE_NORMAL)
            end
        end

        function commandDataToProcess = getPILIOCommandDataToProcess(this, PILCommand)
            switch PILCommand
              case 'XIL_SYSTEM_INITIALIZE_COMMAND'
                commandDataToProcess = {'FCNID'};
              otherwise
                % call super class method
                commandDataToProcess = getPILIOCommandDataToProcess@rtw.pil.Sfunction(this, PILCommand);
            end
        end

        % implementation of superclass hook method
        function mexOptions = getAdditionalMEXOptions(this)
        % call super class method
            mexOptions = getAdditionalMEXOptions@rtw.pil.Sfunction(this);
            mexOptions{end+1} = '-DMDL_REF_SIM_TGT=1';
        end

        function sfunIdx = getSfunIdxForInputDataInterface(this, dataInterfaceIdx)
        % zero-based idx
            sfunIdx = int2str(this.ModelInterfaceUtils.InputPortIndexMap(dataInterfaceIdx) - 1);
        end
        
        function writeSectionStart(this) %#ok<MANU>
            % With model block SIL/PIL mdlStart is replaced by
            % mdlSystemInitialize, so override super class method
        end

        function writeSectionOutputs(this)
            if this.NeedsPeriodicOutputUpdateFcn
                this.writeSectionPeriodicOutputUpdateFcn();
            end
            this.writer.wBlockStart('static void mdlOutputs(%s, int_T tid)',this.CodeFunctionArgDecs);
            
            if ~this.SILPILWrapperUtils.ComponentBoundaryValidation.IsValid
               % If we failed validation, then close the block and return
               % early, leaving a stub.
               this.writer.wBlockEnd();
               return;
           end
            
            resetList = repmat(struct('resetTask', [], 'resetTaskId', []), 0, 0);
            
            if this.HasModelWideEvents
                % Match up reset events with list of output
                % functions to get the correct id, and then write out
                % the output body, protecting for correct TID.
                % Include terminate call as this also needs to go through
                % the output function with a special TID
                
                allTimingObjects = this.codeInfo.TimingProperties;
                allOutputTasks = this.codeInfoUtils.getOutputTasks;
                
                bIsReset = strcmp({allTimingObjects.TimingMode}, 'RESET') | ...
                    strcmp({allTimingObjects.TimingMode}, 'RESETWITHINIT');
                lResetTimingObjects = allTimingObjects(bIsReset);
                lResetTIDs          = this.codeInfo.TimingInternalIds(bIsReset);
                
                for eIdx = 1:numel(lResetTIDs)
                    lCurrTid = lResetTIDs(eIdx);
                    lCurrTiming = lResetTimingObjects(eIdx);
                    for idx = 1:numel(allOutputTasks)
                        lOutputTaskTiming = allOutputTasks(idx).codeInfoData.Timing;
                        assert(~isempty(lOutputTaskTiming), 'Output task without a timing interface');
                        
                        % Compare objects by handle reference
                        if rtw.connectivity.CodeInfoUtils.isEquivalentTo(lOutputTaskTiming, lCurrTiming)
                            % Found the matching reset output function, so pull out it's tid.
                            resetList(end+1)  = struct('resetTask', idx, 'resetTaskId', lCurrTid); %#ok<AGROW>
                        end
                    end
                end
                
                % Get tid For Terminate/PowerDownEvent timing modes
                bIsTerminate = strcmp({allTimingObjects.TimingMode}, 'TERMINATE');
                lTerminateTIDs = this.codeInfo.TimingInternalIds(bIsTerminate);
                if ~isempty(lTerminateTIDs)
                    assert(numel(lTerminateTIDs) == 1, 'Must not be more than 1 terminate timing interface');
                    this.writer.wBlockStart('if (ssGetMexApiInt1(S) == %d)', lTerminateTIDs);
                    this.writer.wLine('mdlTerminate(S);');
                    this.writer.wLine('return;');
                    this.writer.wBlockEnd(); %if current terminate
                end
            end
            
            if this.codeInfoUtils.isExportFunctions &&...
                    this.codeInfoUtils.isSampleTimeIndependent && ...
                    ~this.HasResetFunction
                this.writer.writeLine('return;');
            else
                if this.HasResetFunction
                    % Write out the output reset body, protecting for correct TID.
                    for idx = 1:numel(resetList)
                        this.writer.wBlockStart('if (ssGetMexApiInt1(S) == %d)', resetList(idx).resetTaskId)
                        this.writeSectionOutputsBody(resetList(idx).resetTask);
                        
                        % If we've processed a reset, return.  If there are
                        % any other events to process, this function will
                        % get called again with a different value from
                        % ssGetMexApiInt1.
                        this.writer.wLine('return;');
                        this.writer.wBlockEnd(); %if current reset
                    end
                    defaultTaskIdx = 1;
                    this.writeSectionOutputsBody(defaultTaskIdx);
                else
                    defaultTaskIdx = 1;
                    this.writeSectionOutputsBody(defaultTaskIdx);
                end
            end
            this.writer.wBlockEnd(); % static void mdlOutputs(%s, int_T tid)
        end

        function writeSectionUpdate(this)
            if this.codeInfoUtils.isExportFunctions &&...
                    this.codeInfoUtils.isSampleTimeIndependent
                return;
            end

            % Generate mdlUpdate
            writeSectionUpdate@rtw.connectivity.CodeInfoSfunction(this);
        end

        function validateMethodCallForExportFunctions(~,~)
        % empty implementation
        end
        
        function writeSectionPreprocessorDefines(this)
            writeSectionPreprocessorDefines@rtw.connectivity.CodeInfoSfunction(this);
            if this.codeInfoUtils.isExportFunctions
                this.writer.writeLine('#define MODELREF_EXPORTS_FUNCTION_CALLS');
            end
        end

        function fcnName = getExportFunctionStepFcnName(~, ~, fcnName)
        % must use same formula as model reference SIM target since we
        % are using their classes to generate mdlInitializeSizes (where
        % the function call functions get registered)
            fcnName = coder.internal.modelreference.FunctionCallUtils.getFcnCallWrapperFcnName(...
                fcnName);
        end

        function fcnName = getExportFunctionEnableDisableFcnName(this, ...
                                                              sfunPortIdx, ...
                                                              fcnName, ...
                                                              typeStr)
            if this.SILPILWrapperUtils.isModelReferenceTarget
                % must use same formula as model reference SIM target since we
                % are using their classes to generate mdlInitializeSizes (where
                % the function call functions get registered)
                fcnName = coder.internal.modelreference.FunctionCallUtils.getFcnCallWrapperFcnName(...
                    fcnName);
            else
                fcnName = ['mdlExported' typeStr 'Fcn' sfunPortIdx];
            end
        end

        function writeSectionSfunctionOptions(this) %#ok<MANU>
        % this will be written by coder.connectivity.ModelBlockMdlInitializeSizesWriter
        end

        function writeSectionGlobalData(this)
        % call super class method
            writeSectionGlobalData@rtw.pil.Sfunction(this);

            % write MDL_INFO for DS registration
            dataInterfaces = ...
                this.SILPILWrapperUtils.getDataStoresToTransferInterfaces(this.codeInfoUtils.getAllSharedDataStores);
            numDS = length(dataInterfaces);
            
            lHasDWork = this.HasDWork;
            numDW = double(lHasDWork);
            totalNumDSRates = this.getTotalNumDataStoreRates(dataInterfaces);
            
            if ((numDW + numDS) > 0) && (totalNumDSRates > 0)
                % For each data store, we need to know the rates of its
                % readers and writers. This will be registered later via
                % MDL_INFO_NAME_GLOBAL_DSM. The rtMdlInfo_ array should be
                % big enough to hold these rates.
                
                this.writer.writeLine(['static RegMdlInfo rtMdlInfo_' ...
                    this.codeInfo.GraphicalPath '[' ...
                    num2str(totalNumDSRates + numDW) '] = {']);
                writtenNumberOfDSMS = totalNumDSRates + numDW;
                writtenOutDSMEntries = 0;
                this.writer.incIndent;
                                
                if lHasDWork
                    % Register MDL_INFO_NAME_MDLREF_DWORK
                    if numDS > 0
                        commaStr = ',';
                    else
                        commaStr = '';
                    end
                    lDWorkName = ['rtMdlrefDWork_', this.codeInfo.GraphicalPath];
                    this.writer.writeLine('{ "%s", MDL_INFO_NAME_MDLREF_DWORK, 0, -1, (void *) "%s" }%s', ...
                        lDWorkName, this.codeInfo.GraphicalPath, commaStr);
                    writtenOutDSMEntries = writtenOutDSMEntries + 1;
                end
                
                for dsIdx=1:numDS
                    ds = dataInterfaces(dsIdx);
                    % note that the order of DS in MDL_INFO is not significant -
                    % see DSMIdxVar for indexing
                    %
                    % note that we are only registering MDL_INFO_NAME_GLOBAL_DSM
                    % and not MDL_INFO_ID_GLOBAL_DSM - MDL_INFO_ID_GLOBAL_DSM is
                    % redundant

                    [originalModel, currentModel] = this.getGlobalDSMRegistrationModels(ds.SID);
                    registeringModelNamesString = [originalModel '|' currentModel];
                    
                    % For each data store, we need to register the sample
                    % time of its readers and writers.
                    % (sample time must be expressed in numeric form)
                    if this.codeInfoUtils.isSampleTimeIndependent
                        if dsIdx==numDS
                            % last item - no comma required
                            commaStr = '';
                        else
                            % more items to come - add comma
                            commaStr = ',';
                        end
                        nonFcnCallPartitionName = '';
                        
                        %For Sample Time independent models ensure we
                        %register Initialize and Terminate timing modes for
                        %Data Store access.
                        dsRates = this.codeInfoUtils.getRatesForDataStore(ds);
                        numDSRates = length(dsRates);
                        for rateIdx = 1:numDSRates
                            rate = dsRates(rateIdx);
                            nonFcnCallPartitionName = rate.NonFcnCallPartitionName;
                            if strcmp(rate.TimingMode, 'INITIALIZE') || strcmp(rate.TimingMode, 'TERMINATE')
                                [sampleTimeCExpr, sampleOffsetCExpr] = this.getSampleTimeCExpression(rate);
                                infoString = [registeringModelNamesString '|' ...
                                              nonFcnCallPartitionName '|' ...
                                              sampleTimeCExpr '|' sampleOffsetCExpr '|' ...
                                              ds.SharedLocalDSMInfo];
                                this.writer.writeLine('{ "%s", MDL_INFO_NAME_GLOBAL_DSM, 0, 0, (void *) "%s" },', ...
                                    ds.GraphicalName, infoString);
                                writtenOutDSMEntries = writtenOutDSMEntries + 1;
                            end
                        end
                        
                        sampleTimeCExpr = '-1';
                        sampleOffsetCExpr = '0';
                        infoString = [registeringModelNamesString '|' ...
                                      nonFcnCallPartitionName '|' ...
                                      sampleTimeCExpr '|' sampleOffsetCExpr '|' ...
                                      ds.SharedLocalDSMInfo];
                        this.writer.writeLine('{ "%s", MDL_INFO_NAME_GLOBAL_DSM, 0, 0, (void *) "%s" }%s', ...
                            ds.GraphicalName, infoString, commaStr);
                        writtenOutDSMEntries = writtenOutDSMEntries + 1;
                    else
                        % loop over the unique list of sample times of this
                        % DS readers and writers.
                        dsRates = this.codeInfoUtils.getRatesForDataStore(ds);
                        numDSRates = length(dsRates);
                        for rateIdx = 1:numDSRates
                            nonFcnCallPartitionName = dsRates(rateIdx).NonFcnCallPartitionName;

                            if ((dsIdx==numDS) && (rateIdx==numDSRates))
                                % last item - no comma required
                                commaStr = '';
                            else
                                % more items to come - add comma
                                commaStr = ',';
                            end

                            [sampleTimeCExpr, sampleOffsetCExpr] = this.getSampleTimeCExpression(dsRates(rateIdx));
                            if strcmp(sampleTimeCExpr,'mxGetInf()')
                                %Need to patch sample time for Inf to the
                                %MATLAB representation otherwise it won't
                                %process correctly in the Info String.
                                sampleTimeCExpr = 'Inf';
                            end
                            infoString = [registeringModelNamesString '|' ...
                                          nonFcnCallPartitionName '|' ...
                                          sampleTimeCExpr '|' sampleOffsetCExpr '|' ...
                                          ds.SharedLocalDSMInfo];
                            this.writer.writeLine('{ "%s", MDL_INFO_NAME_GLOBAL_DSM, 0, 0, (void *) "%s" }%s', ...
                                ds.GraphicalName, infoString, commaStr);
                            writtenOutDSMEntries = writtenOutDSMEntries + 1;
                        end
                    end
                end
                assert(writtenNumberOfDSMS >= writtenOutDSMEntries, 'Mismatch between DSM array count and DSM Elements');
                this.writer.decIndent;
                this.writer.writeLine('};');
                this.writer.writeLine('');
                % write variables to hold DSM addresses and indices
                %
                % we definitely need the indices in order to access the desired
                % DS later on!
                %
                % we don't need the addresses - this is just an optimization used
                % by the Accel S-func - but we need to define storage for them so
                % that the call to ssGetSFcnGlobalDataStoreNameAddrIdx can
                % succeed.
                if numDS > 0
                    % Must only declare these variables if numDS > 0, or
                    % the S-function won't compile on Windows.
                    [DSMVar, DSMIdxVar] = this.getGlobalDSMInfoVars;
                    this.writer.writeLine('void * %s[%d];', DSMVar, numDS);
                    this.writer.writeLine('int %s[%d];', DSMIdxVar, numDS);
                    this.writer.writeLine('');
                end
            else
                % Even if there isn't any model info to register,
                % we need to call the registration function so that
                % for code generation we import all of the exported
                % symbols from binfo.
                %
                % Although it works with gcc on Linux, on Windows/Mac, we
                % cannot create an empty variable, so create an array of
                % length 1, initialized with default data.  Later, we can tell
                % ssRegModelRefMdlInfo that there are zero additional
                % things to register though.
                this.writer.writeLine('static RegMdlInfo %s_%s[1] = { "", %s, 0, 0, NULL };', ...
                    'rtMdlInfo', ...
                    this.codeInfo.GraphicalPath, ...
                    'MDL_INFO_ID_GLOBAL_RTW_CONSTRUCT');
            end                               
            this.writer.writeLine('');
        end

        % override super class method
        function writeSectionStartSendInitializeCommand(this)
            if ~this.HasModelWideEvents
                this.writer.wLine('sendInitializeCommand(%s);', this.SimStructArgSingle);
            else
                this.writer.wComment('Initialize command issued in mdlSystemInitialize');
            end
        end
        
        % override super class method
        function writeSectionInitializeSizesBodyHeader(this)
        % call super class method
            writeSectionInitializeSizesBodyHeader@rtw.pil.Sfunction(this);

            this.writer.writeLine('initializeInfAndMinusInf();');
            this.writer.wNewLine;
            
            % model reference s-function
            this.writer.writeLine('ssSetRTWGeneratedSFcn(S, 2);');
            this.writer.newLine;
        end

        function writeSectionInitializeSampleTimes(this)

            timingInterfaceUtils = coder.internal.modelreference.TimingInterfaceUtils(...
                this.getCodeInfoUtils, this.ModelInterfaceUtils);

            initializeSampleTimesWriter = coder.internal.modelreference.MdlInitializeSampleTimesWriter(...
                this.getCodeInfoUtils, ...
                this.ModelInterfaceUtils, ...
                timingInterfaceUtils, ...
                this.writer);

            initializeSampleTimesWriter.write;
        end

        function writeSectionInitializeSizesBodyInportsAndOutports(this)
            configSet = this.SILPILWrapperUtils.getInfoStruct.configSet;
            configSetUtils = coder.internal.modelreference.ConfigSetUtils(configSet);
            timingInterfaceUtils = coder.internal.modelreference.TimingInterfaceUtils(...
                this.getCodeInfoUtils, this.ModelInterfaceUtils);

            initializeSizesWriter = coder.connectivity.ModelBlockMdlInitializeSizesWriter(...
                this.ModelInterfaceUtils, ...
                this.getCodeInfoUtils, ...
                configSetUtils, ...
                timingInterfaceUtils, ...
                this.writer, ...
                this.isSystemInitializeRequired, ...
                this.isSystemResetRequired, ...
                this.SILPILWrapperUtils.isModelReferenceTarget);
            initializeSizesWriter.write;

        end

        function validateModelArguments(this)
            % validate Model Arguments

            inTheLoopType = this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType;
            indices = unique([...
                this.codeInfoUtils.getModelArgParams, ...
                this.codeInfoUtils.getInstPArgumentIndices(inTheLoopType)]);
            codeInfoModelArgs = this.codeInfo.Parameters(indices);
            % validate codeInfoModelArgs against the model's ParameterArgumentNames
            % in case some params were unused in the generated code and
            % removed from CodeInfo
            modelArgs = this.codeInfoUtils.getModelArgumentNames;
            numModelArgs = length(modelArgs);
            numCodeInfoModelArgs = length(codeInfoModelArgs);
            % make sure codeInfo never has extra model args
            assert(numModelArgs >= numCodeInfoModelArgs, '# CodeInfo Model Args > # Model Args.');
            codeInfoModelArgsNames = this.codeInfoUtils.getParamDataInterfaceGraphicalNames(codeInfoModelArgs);
            % check each model arg
            for i=1:length(modelArgs)
                modelArg = modelArgs{i};
                if ~any(strcmp(modelArg, codeInfoModelArgsNames))
                    % TODO: workaround for g2073897
                    %
                    % a) we end up processing the instance specific parameters
                    % (twice) via the tunable model workspace parameter handling
                    % code...
                    %
                    % b) this introduces an issue if there are unused 
                    % instance specific params in the model workspace - we
                    % end up not detecting them! e.g. 
                    % test/toolbox/rtw/targets/ert/datastorage/coderdatagroups/standalone_subsystems/tStandaloneSS_InstP.m
                    if ~this.SILPILWrapperUtils.getSILPILInterface.isTopModelXILSim
                        rtw.pil.ProductInfo.error('pil', 'ModelBlockUnusedModelArgument', ...
                            this.SILPILWrapperUtils.getSILPILInterface.getRootModel, modelArg);
                    end
                end
            end
            
            % validate Grouped Model Arguments
            this.codeInfoUtils.validateGroupedModelArgs;
        end

        function writeSectionInitializeSizesGlobalDS(this) %#ok<MANU>
        % override and nullify regular global data store initialization
        %
        % Model blocks use a MDL_INFO mechanism
        end

        function writeSectionInitializeSizesBodyTrailer(this)
        % call super class method to write some common stuff
            this.writeSectionInitializeSizesBodyTrailerCommon;

            % register MDL_INFO for DS registration
            dataInterfaces = ...
                this.SILPILWrapperUtils.getDataStoresToTransferInterfaces(this.codeInfoUtils.getAllSharedDataStores);
            totalNumDSRates = this.getTotalNumDataStoreRates(dataInterfaces);

            lHasDWork = this.HasDWork;
            numDW = double(lHasDWork);
            
            % Call ssRegModelRefMdlInfo even if there is nothing to
            % register.  Calling it triggers a callback when generating
            % code that imports global symbols in from binfo.
                this.writer.writeLine('#if SS_SFCN_FOR_SIM');
                this.writer.incIndent;
                this.writer.writeLine('if (S->mdlInfo->genericFcn != (NULL) && ');
                this.writer.writeLine('      ssGetSimMode(S) != SS_SIMMODE_SIZES_CALL_ONLY) {');
                this.writer.incIndent;
                this.writer.writeLine(['ssRegModelRefMdlInfo(S, "' ...
                                    this.codeInfo.GraphicalPath '", ' ...
                                    'rtMdlInfo_' this.codeInfo.GraphicalPath ...
                ', ' num2str(totalNumDSRates+numDW) ');']);
                this.writer.decIndent;
                this.writer.writeLine('}');
                this.writer.decIndent;
                this.writer.writeLine('#endif');

            % If a referenced model permits multi-instance, then we need to
            % register a DWork.  We won't be using it in the S-function,
            % but Simulink needs to know that it exists to allow code
            % generation from the parent model.
            this.writer.writeLine('/* DWork */');
            
            % But don't register the DWork if we are running in accelerator
            % mode - we don't need it.
            this.writer.wBlockStart('if (!ssRTWGenIsAccelerator(S))');
            if numDW > 0
                assert(numDW == 1);
                this.writer.writeLine('#if SS_SFCN_FOR_SIM');
                % see SFunction.cpp, SFcnSetNumDWork() where ssSetNumDWork will return 0 for
                % model reference choices of variant subsystem that are going to
                % be removed.
                this.writer.writeLine('if (ssSetNumDWork(S, 1)) {');
                
                % Don't register the DWork type - for XIL this will be done
                % by the Model Block itself.  But we need to set the
                % width.
                this.writer.writeLine('  ssSetDWorkWidth(S, 0, 1);');
                
                this.writer.writeLine('}');
                this.writer.writeLine('#else');
                this.writer.writeLine('if ( !ssSetNumDWork(S, 1)) {return;}');
                this.writer.writeLine('#endif');
            else
                this.writer.writeLine('ssSetNumDWork(S, 0);');
            end
            this.writer.wBlockEnd('');
                
            needAbsoluteTime = this.SILPILWrapperUtils.MF0ModelInfo.needAbsoluteTimeForNonLoggingPurpose;
            if needAbsoluteTime
                this.writer.newLine;
                this.writer.writeLine('ssSetNeedAbsoluteTime(S, 1);');
            end
        end

        function writeSectionSetInputPortSampleTimeBody(this)
            this.writeSectionSetIOPortSampleTimeBody;
        end

        function writeSectionSetOutputPortSampleTimeBody(this)
            this.writeSectionSetIOPortSampleTimeBody;
        end
        
        function writeSectionSetOutputDimsRuleFcn(this)
            if this.ModelInterfaceUtils.isModelOutputSizeDependOnlyInputSize && ...
                    numel(this.codeInfo.Outports) > 0
                % We only need to define these stub functions if
                % coder.connectivity.ModelBlockMdlSetWorkWidthsWriter is
                % going to call writeRegisterSetOutputSizeRuleTermsForPort
                % for one or more output ports.  This configuration is not
                % supported for SIL simulation at the component boundary,
                % but is supported at the boundary of child models beneath
                % the immediate model under test.
                this.writer.wBlockStart('static void mdlSetOutputDimsRuleFcn(SimStruct *S, int outIdx, int ruleIdx)');
                this.writer.writeLine('/* This S-function only supports variable dimensions during code generation. */');
                this.writer.wBlockEnd;
            end
        end
        
        function writeSectionFinalizeDimsFcn(this)
            if this.ModelInterfaceUtils.isModelOutputSizeDependOnlyInputSize && ...
                    numel(this.codeInfo.Outports) > 0
                % We only need to define these stub functions if
                % coder.connectivity.ModelBlockMdlSetWorkWidthsWriter is
                % going to call writeRegisterSetOutputSizeRuleTermsForPort
                % for one or more output ports.  This configuration is not
                % supported for SIL simulation at the component boundary,
                % but is supported at the boundary of child models beneath
                % the immediate model under test.
                this.writer.wBlockStart('static void mdlFinalizeDimsFcn(SimStruct *S)');
                this.writer.writeLine('/* This S-function only supports variable dimensions during code generation. */');
                this.writer.wBlockEnd;
            end
        end
        
        function writeSectionSetWorkWidthsBody(this)
            % Register widths for variable dimensions if doing code
            % generation, so that the appropriate "setDims()" and 
            % "finalizeDims()" methods get included in the generated code.
            this.writer.wBlockStart('if(ssRTWGenIsCodeGen(S) || ssIsExternalSim(S))')
            mdlSetWorkWidthsWriter = ...
                coder.connectivity.ModelBlockMdlSetWorkWidthsWriter( ...
                    this.ModelInterfaceUtils, ...
                    this.codeInfoUtils, ...
                    this.writer);
            mdlSetWorkWidthsWriter.write();
            this.writer.wBlockEnd;
            
            % register runtime params
            this.writeSectionRegisterRuntimeParams;
        end

        function writeSectionProcessParametersHook(this)
            inportIndices = [];
            % only process params with tunable storage
            tunableParamsIndices = this.SILPILWrapperUtils.getSupportedTunableWorkspaceParams;
            % transfer the instance specific model arguments, this is
            % needed to sync parameter values after they have been changed
            % by a parameter writer block
            inTheLoopType = this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType;
            instPArgsIndices = this.codeInfoUtils.getInstPArgumentIndices(inTheLoopType);
            paramIndices = [tunableParamsIndices, instPArgsIndices];
            outportIndices = [];
            dsReadIndices = [];
            dsWriteIndices = [];
            sharedLocalDataStoreIndices = [];
            loggingIndices = [];
            inportSizesIndices = [];
            outportSizesIndices = [];
            serverFcnIndex = [];
            % process params
            %
            % tid not used
            tid = [];
            this.writeSectionPILIOCommand('XIL_PROCESS_PARAMS_COMMAND', ...
                                          inportIndices, ...
                                          inportSizesIndices, ...
                                          paramIndices, ...
                                          dsReadIndices, ...
                                          dsWriteIndices, ...
                                          sharedLocalDataStoreIndices, ...
                                          outportIndices, ...
                                          outportSizesIndices, ...
                                          loggingIndices, ...
                                          serverFcnIndex, ...
                                          tid);
        end        
        
        function writeSectionGetParameterDataInterfacePtr(this, dataInterface, sfunIdx)
            % initialize "dataInterfacePtr" for parameter data
            dataInterfaceIdx = dataInterface.getDataInterfaceIdx;
            di = this.codeInfo.Parameters(dataInterfaceIdx);
            if this.codeInfoUtils.isDirectModelArgParam(di) && ...
                                (this.SILPILWrapperUtils.isModelReferenceTarget || this.codeInfoUtils.isGroupedParameter(di))
                this.writer.wLine('if (!ssGetModelRefModelArgData(S, %s, (void **) (&dataInterfacePtr))) return%s;',...
                    sfunIdx, this.returnArgString);
            elseif this.codeInfoUtils.isPromotedModelArgParam(di) && ...
                                (this.SILPILWrapperUtils.isModelReferenceTarget || this.codeInfoUtils.isGroupedParameter(di))
                this.writer.wLine('if (!ssGetModelRefBlockArgData(S, %s, (void **) (&dataInterfacePtr))) return%s;',...
                    sfunIdx, this.returnArgString);
            elseif this.codeInfoUtils.isModelWorkspaceParam(di)                                                                            
                this.writer.writeLine(['dataInterfacePtr = (void *) ' ... ...
                                       'ssGetRunTimeParamInfo(S, ' sfunIdx ')->data;']);         
            elseif this.codeInfoUtils.isWorkspaceParam(di)
                % Need to remap this index into the one used by the MF0
                % file
                [~, mappedIdx] = ismember(di.GraphicalName, ...
                                          this.GlobalParameterNames);
                assert(~isempty(mappedIdx));
                assert(mappedIdx >= 1, 'Workspace parameter not found in GlobalParameterNames!');
                mappedIdxStr = num2str(mappedIdx -1);                
                this.writer.wLine('if (!ssGetModelRefGlobalParamData(S, %s, (void **) (&dataInterfacePtr))) return%s;',...
                    mappedIdxStr, this.returnArgString);               
            else
                assert(false, 'Parameter is not a model arg or workspace param.');
            end                                                            
        end
                
        function writeSectionEnableBody(this)
        % The export enable functions are already written
        % by writeSectionExportFunctionEnableBody, so we need to filter
        % them from here.
            if this.needToProcessEnableDisableFcn
                if ~this.SILPILWrapperUtils.ComponentBoundaryValidation.IsValid
                    % If the component boundary has not passed validation,
                    % then leave an empty body for mdlEnable, since the
                    % S-function will only be used for code generation, and
                    % not for simulation.
                    return;
                end
                isEnable = true;
                fun = this.getCodeInfoUtils.getNonExportEnableDisableFcn(isEnable);
                if ~isempty(fun)
                    this.writeSectionReturnIfCodeGen;
                    % determine if there is any I/O to transfer
                    inportIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'input');
                    paramIndices = this.getParamIndicesForFunction(fun);
                    dsReadIndices = [];
                    dsWriteIndices = [];
                    sharedLocalDataStoreIndices = [];
                    outportIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'output');
                    loggingIndices = [];
                    inportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'inputsize');
                    outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'outputsize');
                    serverFcnIndex = [];
                    % issue command
                    % this tid should not collide with the export functions
                    % enable tid (since those always start with 1)
                    tid = 0;
                    this.writeSectionPILIOCommand('XIL_ENABLE_COMMAND', ...
                                                  inportIndices, ...
                                                  inportSizesIndices, ...
                                                  paramIndices, ...
                                                  dsReadIndices, ...
                                                  dsWriteIndices, ...
                                                  sharedLocalDataStoreIndices, ...
                                                  outportIndices, ...
                                                  outportSizesIndices, ...
                                                  loggingIndices, ...
                                                  serverFcnIndex, ...
                                                  tid);
                end
            else
                % standalone code does not expose enable functions in the
                % code interface
                isEnable = true;
                this.writeSectionEmptyEnableDisableFunction(isEnable);
            end
        end

        function writeSectionDisableBody(this)
        % The export disable functions are already written
        % by writeSectionExportFunctionDisableBody, so we need to
        % filter them from here.
            if this.needToProcessEnableDisableFcn
                if ~this.SILPILWrapperUtils.ComponentBoundaryValidation.IsValid
                    % If the component boundary has not passed validation,
                    % then leave an empty body for mdlDisable, since the
                    % S-function will only be used for code generation, and
                    % not for simulation.
                    return;
                end
                isEnable = false;
                fun = this.getCodeInfoUtils.getNonExportEnableDisableFcn(isEnable);
                if ~isempty(fun)
                    this.writeSectionReturnIfCodeGen;
                    % determine if there is any I/O to transfer
                    inportIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'input');
                    paramIndices = this.getParamIndicesForFunction(fun);
                    dsReadIndices = [];
                    dsWriteIndices = [];
                    sharedLocalDataStoreIndices = [];
                    outportIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'output');
                    loggingIndices = [];
                    inportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'inputsize');
                    outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'outputsize');
                    serverFcnIndex = [];
                    % issue command
                    % this tid should not collide with the export functions
                    % disable tid (since those always start with 1)
                    tid = 0;
                    this.writeSectionPILIOCommand('XIL_DISABLE_COMMAND', ...
                                                  inportIndices, ...
                                                  inportSizesIndices, ...
                                                  paramIndices, ...
                                                  dsReadIndices, ...
                                                  dsWriteIndices, ...
                                                  sharedLocalDataStoreIndices, ...
                                                  outportIndices, ...
                                                  outportSizesIndices, ...
                                                  loggingIndices, ...
                                                  serverFcnIndex, ...
                                                  tid);
                end
            else
                % standalone code does not expose disable functions in the
                % code interface
                isEnable = false;
                this.writeSectionEmptyEnableDisableFunction(isEnable);
            end
        end

        function writeSectionExportFunctionsEnableDisableWrapper(this, ...
                                                              isEnableFcn, ...
                                                              portIdx, ...
                                                              sfunPortIdx)
            if isEnableFcn
                typeStr = 'Enable';
            else
                typeStr = 'Disable';
            end

            enableDisableFcn = this.codeInfoUtils.getExportFunctionsEnableDisableFcn(portIdx, isEnableFcn);

            % Standalone code or model reference code does not expose
            % enable/disable functions in the code interface (except for
            % old style model ref export functions with task specification
            % blocks).
            %
            % The Model block has its own runtime check for being called
            % from unsupported contexts, so there is no need to ever create
            % mdlEnable/mdlDisable just for throwing errors.
            %
            if ~isempty(enableDisableFcn)
                assert(this.SILPILWrapperUtils.isModelReferenceTarget, ...
                       'Only model reference code interface can have Enable/Disable function.');
                assert(~this.SILPILWrapperUtils.getMF0ModelInfo.configSettingsForConsistencyChecks.isExportFcnDiagram, ...
                       'Pure export function diagram does not have Enable/Disable functions.');
                this.writeSectionExportFunctionsEnableDisableFcn(isEnableFcn, ...
                                                                 sfunPortIdx, ...
                                                                 typeStr, ...
                                                                 enableDisableFcn);
            end
        end

        % write the path to the Simulink block (simulinkBlockPath)
        %
        % be sure to call freeSimulinkBlockPath when done
        %
        % Note: this could be further optimized by caching the path in a
        % block work vector rather than continually recalculating it
        function writeSimulinkBlockPath(this, errorReturn)
            narginchk(1, 2);
            if nargin < 2
                errorReturn = 'return;';
            end

            % override super class implementation
            this.writer.writeLine('char * simulinkBlockPath = getSimulinkBlockPath(S);');
            this.writer.writeLine('if (simulinkBlockPath == NULL) {');
            this.writer.incIndent;
            this.writer.writeLine(['ssSetErrorStatus(S, "ModelBlock SIL/PIL unexpected error: ' ...
                                'getSimulinkBlockPath returned NULL pointer. ' ...
                                'Check search string was found in ssGetPath.\n");']);
            this.writer.writeLine(errorReturn);
            this.writer.decIndent;
            this.writer.writeLine('}');
        end

        function writeSimulinkBlockPathFunction(this)
            this.writer.writeLine('static char * getSimulinkBlockPath(SimStruct *S) {');
            this.writer.incIndent;
            
            mf0ModelInfo = this.SILPILWrapperUtils.MF0ModelInfo;
            hasVirtualBusInports = any(mf0ModelInfo.isOrigInportVirtualBus.toArray);
            hasVirtualBusOutports = any(mf0ModelInfo.isOrigOutportVirtualBus.toArray);
            isReplacedModelBlock = this.IsFcnCallTriggered || hasVirtualBusInports || hasVirtualBusOutports;

            this.writer.writeLine('char * simulinkBlockPath = NULL;');
            % remove unwanted portion of blockPath provided by ssGetPath
            %
            % case 1: blockPath for non-function call Model Block looks like:
            %
            % model/subsys/TmpSFcnForModelReference_ModelBlockName
            %
            % case 2: blockPath for function call ModelBlock looks like:
            %
            % model/subsys/ModelBlockName/TmpSFcnForModelReference_ModelBlockName
            % orig string
            this.writer.writeLine('const char * origBlockPath = ssGetPath(S);');
            % search string
            this.writer.writeLine('const char * searchString = "TmpSFcnForModelReference_";');
            this.writer.writeLine('char * searchPtr;');
            this.writer.writeLine('size_t origLength, searchAndNameLength, copyAmount;');
            if ~isReplacedModelBlock
                this.writer.writeLine('char * secondPart;');
                this.writer.writeLine('size_t nameLength;');
            end
            % length of orig string
            this.writer.writeLine('origLength = strlen(origBlockPath);');
            % find search string within orig string
            this.writer.writeLine('searchPtr = strstr(origBlockPath, searchString);');
            % sanity check
            this.writer.writeLine('if (searchPtr == NULL) {');
            this.writer.incIndent;
            % simulinkBlockPath will be NULL
            this.writer.writeLine('return simulinkBlockPath;');
            this.writer.decIndent;
            this.writer.writeLine('}');
            % length of search string + block name
            this.writer.writeLine('searchAndNameLength = strlen(searchPtr);');
            % copy 1st section
            if isReplacedModelBlock
                % remove "/"
                this.writer.writeLine('copyAmount = origLength - searchAndNameLength - 1;');
            else
                % leave "/"
                this.writer.writeLine('copyAmount = origLength - searchAndNameLength;');
            end
            % add + 1 to allocate for null char (we won't actually need all
            % the space though because we're removing part of
            % origBlockPath)
            this.writer.writeLine('simulinkBlockPath = (char *) mxCalloc((mwSize) (origLength + 1), sizeof(char));');
            this.writer.writeLine('simulinkBlockPath = strncpy(simulinkBlockPath, origBlockPath, copyAmount);');
            % be sure null char is added
            this.writer.writeLine('simulinkBlockPath[copyAmount] = ''\0'';');
            if ~isReplacedModelBlock
                % copy 2nd section
                % length of block name
                this.writer.writeLine('nameLength = searchAndNameLength - strlen(searchString);');
                this.writer.writeLine('secondPart = &simulinkBlockPath[copyAmount];');
                this.writer.writeLine('secondPart = strncpy(secondPart, &origBlockPath[origLength - nameLength], nameLength);');
                % be sure null char is added
                this.writer.writeLine('secondPart[nameLength] = ''\0'';');
            end
            this.writer.writeLine('return simulinkBlockPath;');
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.writeLine('');
        end

        function writeSectionUserFunctionsTrailer(this)
        % write function which initializes rtInf and rtMinusInf
            this.writeSectionInitializInfAndMinusInf;

            % write SystemInitialize function
            this.writeSectionSystemInitialize;
            % write SystemReset function
            this.writeSectionSystemReset;            
            
            if this.NeedsInternalQueueFunctions
                %Temporary fix needed because these functions aren't
                %exposed publicly. Provide a for declaration for the
                %compiler.
                this.writer.wComment('Internal Queue Functions');
                this.writer.wLine('extern void slmsg_ssSetInputPortMessageCompiledID(SimStruct *S, int portIdx, int qId);');
                this.writer.wLine('extern void slmsg_ssSetMsgQueueSpecExternalInportIdx(SimStruct *S, int localIdx, int exInportIdx);');
            end

            if this.NeedsPeriodicOutputUpdateFcn
                this.writer.wLine('static void mdlPeriodicOutputUpdate(SimStruct *S, int_T tid);');
            end
        end
        
        % free any required memory associated with the Simulink block path
        function freeSimulinkBlockPath(this)
            this.writer.writeLine('mxFree((void *) simulinkBlockPath);');
        end
        function freeCodeInstrServiceData(this)
            this.writer.wLine('mxFree((void *)pCodeInstrServiceData->blockPath);');
            this.writer.wLine('mxFree(pCodeInstrServiceData);');
        end

        function writeSectionGlobalVars(this)
            this.writer.writeLine('static real_T rtInf;');
            this.writer.writeLine('static real_T rtMinusInf;');
            this.writer.wNewLine;
        end                                
        
        % return the number of parameters to register on the 
        % hidden S-Function associated with the Model block
        function numSFcnParams = getNumSFcnParams(this)
            % tunable model workspace parameters only            
            numSFcnParams = length(this.getSFunctionParamIndices);
        end
        
        function tunableModelWorkspaceIndices = getSFunctionParamIndices(this)            
            % tunable model workspace parameters only
            [~, ...
             ~, ...
             tunableModelWorkspaceIndices] ...
                = this.SILPILWrapperUtils.getSupportedTunableWorkspaceParams;
        end

        function writeSectionInitializInfAndMinusInf(this)

            this.writer.writeLine('static void initializeInfAndMinusInf(void) {');

            this.writer.wNewLine;
            this.writer.writeCellLines({'typedef struct {';
                                'struct {';
                                'uint32_T wordL;';
                                'uint32_T wordH;';
                                '} words;';
                                '} LittleEndianIEEEDouble;'});

            this.writer.writeLine('rtInf = 0.0;');
            this.writer.writeLine('rtMinusInf = 0.0;');

            % rtInf
            this.writer.wNewLine;
            this.writer.wBlockStart('');
            this.writer.writeCellLines({
                'union {';
                'LittleEndianIEEEDouble bitVal;';
                'real_T fltVal;';
                '} tmpVal1;';
                'tmpVal1.bitVal.words.wordH = 0x7FF00000U;';
                'tmpVal1.bitVal.words.wordL = 0x00000000U;';
                'rtInf = tmpVal1.fltVal;';
                   });
            this.writer.wBlockEnd;

            % rtMinusInf
            this.writer.wNewLine;
            this.writer.wBlockStart('');
            this.writer.writeCellLines({
                'union {';
                'LittleEndianIEEEDouble bitVal;';
                'real_T fltVal;';
                '} tmpVal2;';
                '    tmpVal2.bitVal.words.wordH = 0xFFF00000U;';
                '    tmpVal2.bitVal.words.wordL = 0x00000000U;';
                '    rtMinusInf = tmpVal2.fltVal;';
                   });
            this.writer.wBlockEnd;

            this.writer.writeLine('}');
        end

        % determine the number of Inports associated with this component
        function [numComponentInports, mException] = getNumComponentInports(this, componentPath)
            % handle edge Trigger/Enable ports which map to root Inports in
            % CodeInfo         
            componentInports = coder.connectivity.SimulinkInterface.findRootLevelBlocks(componentPath, 'Inport');
            
            % Handle bus element ports by organizing by port number
            portIdxs = get_param(componentInports, 'Port');
            numPorts = length(unique(portIdxs));
            handle = get_param(componentPath, 'Handle');
            if slfeature('CompositePortsAtRoot')
                intf = Simulink.internal.CompositePorts.TreeNode.getLeafDotStrsForDataInputInterface(handle);
            else
                intf = [];
            end
            
            numRootInports = 0;
            unsupportedPorts = {};
            for i = 1:numPorts
                inport = componentInports{strcmp(portIdxs, num2str(i))};
                % Handle non-bus element ports
                if isempty(intf) || isempty(intf{i})                  
                    numRootInports = numRootInports + this.countRootIOPortSignals(inport, true);
                else
                    numRootInports = numRootInports + length(intf{i});
                end
                
                if this.isRootIOBusElementPort(inport)
                    % register BusElement inports as unsupported, the
		    % error will be thrown only if there is a mismatch
		    % in the number of graphical/code inports
                    unsupportedPorts{end+1} = inport; %#ok<AGROW>
                end
            end
            
            if isempty(unsupportedPorts)
                mException = MSLException.empty;
            else
                mException = MSLException(...
                    message('PIL:pil:UnsupportedBusElementConnection', ...
                    strjoin(unsupportedPorts, ', ')));
            end
            
            if this.IsTriggered && ~this.IsFcnCallTriggered
                numComponentTrigPorts = 1;
            else
                numComponentTrigPorts = 0;
            end
            
            componentEnablePorts = coder.connectivity.SimulinkInterface.findRootLevelBlocks(componentPath, 'EnablePort');
            numEnablePorts = length(componentEnablePorts);
            
            numComponentInports = numRootInports + numComponentTrigPorts + numEnablePorts;
        end

        % determine the number of Outports associated with this component
        function [numComponentOutports, mException] = getNumComponentOutports(this, componentPath)
            componentOutports = coder.connectivity.SimulinkInterface.findRootLevelBlocks(componentPath, 'Outport');
                numComponentOutports = 0;
                unsupportedPorts = {};
                for i = 1:length(componentOutports)
                    outport = componentOutports{i};
                    [numSignalsForCurrPort, isSupported] = this.countRootIOPortSignals(outport, false);
                    numComponentOutports = numComponentOutports + numSignalsForCurrPort;
                    if ~isSupported
                        unsupportedPorts{end+1} = outport; %#ok<AGROW>
                    end
                end
                
                if isempty(unsupportedPorts)
                    mException = MSLException.empty;
                else
                    mException = MSLException(...
                        message('PIL:pil:UnsupportedVirtualBusOutportConnection', ...
                        strjoin(unsupportedPorts, ', ')));
                end
        end

        function solverType = getSolverTypeString(this)
        % SolverType 1 => variable step, 2 => fixed step (see slSolverType)
        %
        % should always be a fixed step solver for In-the-Loop
            assert(strcmp(this.SILPILWrapperUtils.getParam('SolverType'), 'Fixed-step'), ...
                   'Non-fixed step solver found');
            solverType = '2';
        end

        function sfunIdx = getSfunIdxForDataInterface(this, dataInterface)
            if isa(dataInterface, 'rtw.pil.CodeInfoData')
                % correctly calculate sfunIdx
                dataInterfaceType = dataInterface.getDataInterfaceType;
                dataInterfaceIdx = dataInterface.getDataInterfaceIdx;
                switch dataInterfaceType
                    case 'parameter'
                        di = this.codeInfo.Parameters(dataInterfaceIdx);
                        if this.codeInfoUtils.isModelArgParam(di) && ...
                                (this.SILPILWrapperUtils.isModelReferenceTarget || this.codeInfoUtils.isGroupedParameter(di))
                            
                            if this.codeInfoUtils.isDirectModelArgParam(di) 
                                % find param relative only to model args 
                                % that belong to this model                           
                                paramIndex = this.codeInfoUtils.getSfunctionIdxForDirectModelArgument(...
                                    dataInterface.getRTWDataInterface);
                            else % is a PromotedModelArgParam
                                % find param relative only to model args
                                % that are promoted from lower models
                                paramIndex = this.codeInfoUtils.getSfunctionIdxForPromotedModelArgument(...
                                    dataInterface.getRTWDataInterface);
                            end
                            
                        elseif this.codeInfoUtils.isModelWorkspaceParam(di)
                            % find param relative only to model workspace
                            % params transferred as S-Function params
                            paramIndex = find(dataInterfaceIdx == this.getSFunctionParamIndices);
                        elseif this.codeInfoUtils.isWorkspaceParam(di)
                            % find param relative only to base workspace
                            % params
                            [~, ...
                             tunableBaseWorkspaceIndices] ...
                                = this.SILPILWrapperUtils.getSupportedTunableWorkspaceParams;
                            paramIndex = find(dataInterfaceIdx == tunableBaseWorkspaceIndices);
                        else
                            assert(false, 'Parameter is not a model arg or workspace param.');
                        end
                        % 0-based index
                        sfunIdx = int2str(paramIndex - 1);
                    case {'dsread', 'dswrite', 'sharedLocalDataStore'}
                        % call super class method
                        sfunIdx = getSfunIdxForDataInterface@rtw.pil.Sfunction(this, dataInterface);
                        % lookup idx in DSMIdxVar
                        [~, DSMIdxVar] = this.getGlobalDSMInfoVars;
                        sfunIdx = [DSMIdxVar '[' sfunIdx ']'];
                    otherwise
                        % call super class method
                        sfunIdx = getSfunIdxForDataInterface@rtw.pil.Sfunction(this, dataInterface);
                end
            else
                % call super class method
                sfunIdx = getSfunIdxForDataInterface@rtw.pil.Sfunction(this, dataInterface);
            end
        end
        
        function doProcess = needToProcessEnableDisableFcn(this)
            % process Enable/Disable functions only for model reference
            % CodeInterface
            doProcess = this.SILPILWrapperUtils.isModelReferenceTarget;
        end
        
        function validateNumIOPortsAgainstSourceComponent(this)
            % if this is a protected model, we don't have the  model
            % available so we can't perform the checks in this method. In
            % any case, these checks should not be necessary in the
            % protected model case because it is not possible for the user
            % to change the model to make it out-of-sync with the
            % artifacts.
            componentPath = this.SILPILWrapperUtils.getLoadableRootComponent;
            isProtected = slInternal('getReferencedModelFileInformation', componentPath);
            if ~isProtected
                errorId = 'PIL:pil:NumIOPortMismatch';
                this.validateNumIOPortsImplementation(errorId);
            end
        end
        
        
        function validateNumIOPortsImplementation(this, errorId)
            % check the number of inports and outports in CodeInfo
            % matches up with the number of inports and outports in the
            % original model
            numCodeInfoInports = length(this.codeInfo.Inports);
            numCodeInfoOutports = length(this.codeInfo.Outports);
            numAllCodeInfoInports = numCodeInfoInports;
            if this.codeInfoUtils.isExportFunctions
                numAllCodeInfoInports = numCodeInfoInports + ...
                    this.codeInfoUtils.getNumExportFunctions;
            end
            componentPath = this.SILPILWrapperUtils.getLoadableRootComponent;
            [numComponentInports, unsupportedConnExceptInport]  = this.getNumComponentInports(componentPath);
            [numComponentOutports, unsupportedConnExceptOutport] = this.getNumComponentOutports(componentPath);

            % check inports
            if numAllCodeInfoInports ~= numComponentInports
                component = this.SILPILWrapperUtils.getComponentForDiagnostics;
                mainException = MSLException(message(errorId, ...
                                          'inports', ...
                                          numComponentInports, ...
                                          component, ...
                                          numAllCodeInfoInports));
                  if ~isempty(unsupportedConnExceptInport)
                      mainException = mainException.addCause(unsupportedConnExceptInport);
                  end
                  throw(mainException);
            end

            % check outports
            if numCodeInfoOutports ~= numComponentOutports
                component = this.SILPILWrapperUtils.getComponentForDiagnostics;
                mainException = MSLException(message(errorId, ...
                                          'outports', ...
                                          numComponentOutports, ...
                                          component, ...
                                          numCodeInfoOutports));
                if ~isempty(unsupportedConnExceptOutport)
                    mainException = mainException.addCause(unsupportedConnExceptOutport);
                end
                throw(mainException);
            end
        end
        
        function [origModel, currModel] = getGlobalDSMRegistrationModels(this, dsmSID)
            % If the SID is empty, then this model registers the DSM, 
            % otherwise use the SID as the registering model name
            currModel = this.codeInfo.GraphicalPath;
            if isempty(dsmSID)
                origModel = currModel;
            else
                origModel = dsmSID;
            end
        end
        
        % Override port based sample time setting for model reference
        % target
        function usePortBasedSampleTimes = usePortBasedSampleTimesHook(this, usePortBasedSampleTimes)
            if this.SILPILWrapperUtils.isModelReferenceTarget
                usePortBasedSampleTimes = this.SILPILWrapperUtils.MF0ModelInfo.usePortBasedSampleTime;
            end
        end
    end

    methods (Access = 'private')
        % Count the number of leaf signals in this hierarchy
        function signals = countLeafSignalInHierarchy(this, sigHier)
            if(isempty(sigHier.Children))
                % This element has no children, so it is a leaf
                signals = 1;
            else
                % This element has children, so it is not a leaf
                signals = 0;

                % Count the number of leaves in each of its children
                numChildren = length(sigHier.Children);
                for i = 1:numChildren
                    child = sigHier.Children(i);
                    signals = signals + this.countLeafSignalInHierarchy(child);
                end
            end
        end

        % Check if this root port is possibly handling a virtual bus
        function isVirtual = isRootIOPortVirtualBus(this, rootBlock, isInport)
            isVirtual = false;
            
            % All bus element ports are potentially a virtual bus
            if this.isRootIOBusElementPort(rootBlock)
                isVirtual = true;
                if ~isInport
                    this.HasVirtualBusOutputs = isVirtual;
                end
                return;
            end                
            
            % First check the datatype
            datatype = get_param(rootBlock, 'OutDataTypeStr');
            isBus = ~isempty(regexp(datatype, '^\s*Bus:', 'once'));
            if (isBus)
                isModelBlockInterface = this.SILPILWrapperUtils.isModelReferenceTarget;
                
                portVirtSetting = strcmp(get_param(rootBlock, 'BusOutputAsStruct'), 'off');
            
                if ~isInport
                    this.HasVirtualBusOutputs = portVirtSetting;
                end
                % For referenced models, check the parameter.  Doesn't
                % apply to top model
                
                isVirtual = (isModelBlockInterface && portVirtSetting);                
            end
        end        
        
        function isBusElementPort = isRootIOBusElementPort(~, rootBlock)
            if strcmp(get_param(rootBlock,'IsBusElementPort'), 'on')
                isBusElementPort = true;
            else
                isBusElementPort = false;
            end
        end
        
        function [signals, isSupportedConn] = countRootIOPortSignals(this, rootBlock, isInport)
            % Assume 1 and supported
            signals = 1;
            isSupportedConn = true;

            % Figure out of this port corresponds to an expanded virtual bus
            isVirtual = this.isRootIOPortVirtualBus(rootBlock, isInport);
            
            if isVirtual
                % This is an expanded virtual bus.  Get the SignalHierarchy
                % and count the number of leaf elements
                handles = get_param(rootBlock, 'PortHandles');
                if(isInport)
                    sigHier = get_param(handles.Outport, 'SignalHierarchy');
                else
                    sigHier = get_param(handles.Inport, 'SignalHierarchy');
                    % return early if an outport is unconnected or is 
                    % connected to a ground block, see 2051181
                    if isempty(sigHier) || ...
                            (isempty(sigHier.SignalName) && isempty(sigHier.Children))
                        isSupportedConn = false;
                        return;
                    end
                end
                
                signals = this.countLeafSignalInHierarchy(sigHier);
            end
        end

        function writeSectionSetIOPortSampleTimeBody(this)
            if this.codeInfoUtils.isSampleTimeIndependent
                this.writer.writeLine('/* inherited sample times are set by the Model Block itself */');
            else
                this.writer.writeLine('/* sample times are fully specified, not inherited */');
            end
        end

        function totalNumDSRates = getTotalNumDataStoreRates(this, dataStores)
            totalNumDSRates = 0;
            for i=1:length(dataStores)
                numRatesAccessingThisDataStore =...
                    length(this.codeInfoUtils.getRatesForDataStore(dataStores(i)));
                if (this.codeInfoUtils.isSampleTimeIndependent && ...
                    (this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType==rtw.pil.InTheLoopType.ModelBlockStandalone))
                    %For sample time independent models we add the
                    %Inherited sample time irrespective of whether there
                    %are rates associated with a DSM. So need to capture
                    %that count per DSM.
                    numRatesAccessingThisDataStore = numRatesAccessingThisDataStore + 1;
                end
                if isequal(this.getCodeInfoUtils.getRates('ONESHOT'), dataStores(i).Timing)
                    % Add DSM to count if it's timing is ONESHOT. These
                    % need to be registered and counted.
                    numRatesAccessingThisDataStore = numRatesAccessingThisDataStore + 1;
                end
                totalNumDSRates = totalNumDSRates + numRatesAccessingThisDataStore;
            end
        end

        function [DSMVar, DSMIdxVar] = getGlobalDSMInfoVars(this)
            DSMVar = [this.codeInfo.GraphicalPath '_DSM'];
            DSMIdxVar = [this.codeInfo.GraphicalPath '_DSMIdx'];
        end

        function isSystemInitializeRequired = isSystemInitializeRequired(this) %#ok<MANU>
            isSystemInitializeRequired = true;
        end

        function isSystemResetRequired = isSystemResetRequired(this)
            if this.SILPILWrapperUtils.isModelReferenceTarget
                % Need the s-function entry point even if there is no
                % function in the generated code -
                % see InitCondOptimCallbackError
                %
                % Note: standalone code has no support for conditional contexts - in
                % that case the engine code will throw an error: MdlRefXILStdAloneInsideCondExecedSubsystem
                isSystemResetRequired = true;
            else
                isSystemResetRequired = false;
                assert(isempty(this.codeInfo.SystemResetFunction), ...
                       'Did not expect a SystemResetFunction!');
            end
        end
        
        function writeSectionSystemInitialize(this)
            if this.isSystemInitializeRequired
                % this method is called when:
                % - the Init port is triggered with IRT blocks
                % - the simulation of a model block starts
                % In case of IRT model block, mdlStart is not called from the 
                % Simulink engine, so the XIL application must be started here

                this.writer.wBlockStart('static void mdlSystemInitialize(SimStruct *S)');
                if this.SILPILWrapperUtils.ComponentBoundaryValidation.IsValid
                    this.writeSectionSystemInitializeBody;
                end
                this.writer.wBlockEnd;
            end
        end

        function writeSectionSystemInitializeBody(this)
            % S-function SystemInitialize is not setup to handle exceptions
            errorReturn = 'return;';

            % Return early if mdlSystemInitialize was called during code
            % gen - we don't want to start the target application here.
            this.writeSectionReturnIfCodeGen;
            
            % start application
            this.writer.wBlockStart('');
            this.getPWorkValue('int', 'pIsXILApplicationStartedLocal', this.getIsXILApplicationStartedIdx, true);
            this.writer.wBlockStart('if (pIsXILApplicationStartedLocal == NULL || (*pIsXILApplicationStartedLocal) == 0)');
            this.writer.wLine('boolean_T applicationStarted = startAndSetupApplication(%s);',...
                this.SimStructArgSingle);
            this.writer.wBlockStart('if (!applicationStarted)');
            this.writer.wLine(errorReturn);
            this.writer.wBlockEnd();

            this.writer.wBlockEnd();
            this.writer.wLine('sendInitializeCommand(%s);', this.SimStructArgSingle);
            this.writer.wBlockEnd();
            this.writeSectionSystemInitializeCommand(errorReturn);
        end
        
        function writeSectionSystemInitializeCommand(this, errorReturn)
            if this.SILPILWrapperUtils.isModelReferenceTarget
                % there may be an SystemInitialize function to call (depending on whether
                % there is any state to reset)
                %
                % additionally, if the Model Reference timing bridge is being
                % used then xilSystemInitialize must be called to set the
                % timing bridge's "firstInitCond" to false.
                %
                % therefore we will always send the command to the target
                %
                % determine if there is any I/O to transfer
                fun = this.codeInfoUtils.getSystemInitializeFunction;
                inportIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'input');
                paramIndices = this.getParamIndicesForFunction(fun);
                % Transfer Data Store Reads to INITIALIZE call
                initTask = this.codeInfoUtils.getInitializeTasks();
                dsReadIndices = [];
                for taskIdx = 1:length(initTask)
                    dsReadIndices = [dsReadIndices ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(initTask(taskIdx), 'init')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                end
                dsReadIndices = unique(dsReadIndices,'stable');
                dsWriteIndices = [];
                for taskIdx = 1:length(initTask)
                    dsWriteIndices = [dsWriteIndices ...
                        this.codeInfoUtils.getWriterSharedDataStoresForTask(initTask(taskIdx), 'init')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                end
                dsWriteIndices = unique(dsWriteIndices,'stable');
                sharedLocalDataStoreIndices = [];
                outportIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'output');
                loggingIndices = [];
                inportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'inputsize');
                outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'outputsize');
                serverFcnIndex = [];
                % issue command
                %
                % tid not used
                tid = [];

                this.writeSectionPILIOCommand('XIL_SYSTEM_INITIALIZE_COMMAND', ...
                                              inportIndices, ...
                                              inportSizesIndices, ...
                                              paramIndices, ...
                                              dsReadIndices, ...
                                              dsWriteIndices, ...
                                              sharedLocalDataStoreIndices, ...
                                              outportIndices, ...
                                              outportSizesIndices, ...
                                              loggingIndices, ...
                                              serverFcnIndex, ...
                                              tid, ...
                                              errorReturn);
            end
        end
        
        function writeSectionSystemReset(this)
            if this.isSystemResetRequired
                this.writer.wBlockStart('static void mdlSystemReset(SimStruct *S)');
                if this.SILPILWrapperUtils.ComponentBoundaryValidation.IsValid
                    this.writeSectionSystemResetBody;
                end
                this.writer.wBlockEnd;
            end
        end

        function writeSectionSystemResetBody(this)
        % S-function SystemReset call site is not setup to handle
        % exceptions
            errorReturn = 'return;';
            % check state of "OptimizeModelRefInitCode" parameter
            %
            % this parameter removes redundant initialization to 0 of
            % states
            %
            % non-zero state initialization will not be optimized away!
            %
            optimInitCode = strcmp(this.SILPILWrapperUtils.getParam('OptimizeModelRefInitCode'), 'on');
            if optimInitCode
                % optimization is on =>
                % zero initialization in Init will have been removed =>
                % can't reset correctly
                %
                % throw error
                this.writer.wBlockStart('');
                this.getPWorkValue('void', this.XILUtilsVarName, this.getXILUtilsPWorkIdx, true);
                numRHS = 1;
                this.writer.wLine('mxArray * prhs[ %d ];', numRHS);
                % set id
                this.writer.writeLine('prhs[0] = mxCreateString("PIL:pil:InitCondOptimCallbackError");');
                % prhs[0] will get freed automatically when the scope
                % exits
                this.writer.wBlockStart('if (xilUtilsCallMATLAB(%s, 0, NULL, %d, prhs, "DAStudio.error") != XIL_UTILS_SUCCESS)', ...
                                        this.XILUtilsVarName, numRHS);
                if isempty(errorReturn)
                    this.writer.wComment('continue');
                else
                    this.writer.writeLine(errorReturn);
                end
                this.writer.wBlockEnd;
                this.writer.wBlockEnd;
            else
                fun = this.codeInfo.SystemResetFunction;
                if ~isempty(fun)
                    % determine if there is any I/O to transfer

                    inportIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'input');
                    paramIndices = this.getParamIndicesForFunction(fun);
                    dsReadIndices = [];
                    dsWriteIndices = [];
                    sharedLocalDataStoreIndices = [];
                    outportIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'output');
                    loggingIndices = [];
                    inportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'inputsize');
                    outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(fun, 'outputsize');
                    serverFcnIndex = [];
                    % issue command
                    tid = []; % unused

                    this.writeSectionPILIOCommand('XIL_SYSTEM_RESET_COMMAND', ...
                                                  inportIndices, ...
                                                  inportSizesIndices, ...
                                                  paramIndices, ...
                                                  dsReadIndices, ...
                                                  dsWriteIndices, ...
                                                  sharedLocalDataStoreIndices, ...
                                                  outportIndices, ...
                                                  outportSizesIndices, ...
                                                  loggingIndices, ...
                                                  serverFcnIndex, ...
                                                  tid, ...
                                                  errorReturn);
                end
            end
        end

        function writeSectionPeriodicOutputUpdateFcn(this)
            this.writer.wBlockStart('static void mdlPeriodicOutputUpdate(SimStruct *S, int_T tid)');
            this.writer.wComment('Periodic Output Update sample time matching');
            
            % schedule periodic output tasks
            type = 'XIL_OUTPUT_COMMAND';
            [outtasks, outtaskNames] = this.getTasksPeriodicWithName(type);
            type = 'XIL_UPDATE_COMMAND';
            [uptasks, uptaskNames] = this.getTasksPeriodicWithName(type);
            
            assert(isempty(uptasks) || numel(uptasks)==numel(outtasks), ...
                'Number of update tasks must be empty or equal output tasks');
            
            for taskIdx = 1:length(outtasks)
                task = outtasks(taskIdx);
                taskName = outtaskNames{taskIdx};
                                
                % the explicit periodic tasking code is generated in entry functions.
                % There's no need to regenerate them into step body. Thus, skip them.
                if this.skipExplicitTaskingTaskInExportFunctions(task)
                    continue;
                end
                
                this.writer.wComment(['check for sample time hit associated with task ' int2str(taskIdx)]);
                this.writer.wBlockStart('if (tid == %d)', taskIdx-1);
                this.writer.wLine('%s(%s, tid);', taskName, this.SimStructArgSingle);
                if ~isempty(uptasks)
                   this.writer.wLine('%s(%s, tid);', uptaskNames{taskIdx}, this.SimStructArgSingle); 
                end
                this.writer.wBlockEnd; % if (' sampleHitCExpr ')
            end
            this.writer.wBlockEnd();
        end       
    end
    
    methods (Static, Access = private)
        function hasDWork = initializeHasDWork(mf0, lInternalData)
            % Compute whether we need to register a DWork or not.
            %
            % Refer to ModelrefBlockNeedsDWork in modelrefsfcnlib.tlc
            % for TLC implementation.
            lSlFuncs = mf0.simulinkFunctions.compFunctions.toArray;
            if any([lSlFuncs.multiInstance])
                % If there's a multi-instance SL Function, there's a DWork
                hasDWork = true;
            else
                nModelRefsIncTop = mf0.childModelRefInfo.Size();
                lThisModel = mf0.childModelRefInfo.at(nModelRefsIncTop);
                lIsSingleInstance = lThisModel.isSingleInstance;
                if lIsSingleInstance
                    % If not a multi-instance model, then there's no DWork
                    hasDWork = false;
                else
                    hasDWork = any(arrayfun(@rtw.pil.ModelBlockSfunction.isDWork, lInternalData));
                end
            end
        end
        
        function ret = isDWork(dataInterface)
            % ISDWORK
            %   Returns true if the dataInterface matches the
            %   characteristics of a DWork.
            isDWorkForCCode = ismember(dataInterface.GraphicalName, {'localB', 'localDW', 'localZCE', 'RTModel'});
            isModelObjForCPPCode = strcmp(dataInterface.GraphicalName, '') && ...
                ~isempty(dataInterface.Implementation) && ...
                strcmp(dataInterface.Implementation.Identifier, 'ModelMDLOBJ');
            ret = isDWorkForCCode || isModelObjForCPPCode;
        end
    end
end
