classdef (Hidden = true) ModelBlockPIL < handle
%MODELBLOCKPIL code generation entry point into Model block PIL
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2008-2023 The MathWorks, Inc.

    
    methods (Static = true)

        function XILErrorCheckHook(iMdl, iXilMdlBlks, isXilBlkOnMultiInstPath, runChecksForProtectedModels, isUpdateDiagram)
            % Called from ModelRefCompileHierarchy.cpp to apply 
            % error checks before any SIM/RTW targets are built.

            % Check if ftrCtrl for DeriveCodeInterface is enabled
            ftrDeriveCodeInterface = ...
                matlab.internal.feature("XILDeriveCodeInterfaceWithDepType");

            if (ftrDeriveCodeInterface)
                % When deriving the Code Interface value for a Model block from the Deployment Type,
                % the referenced model must be loaded in order to access the active coder mappings.
                % Here, we do that early to guarantee all required models are loaded up front.
                % SIL/PIL has traditionally loaded the model  references under test, 
                % so the end state of loaded models remains the same.
                for i=1:length(iXilMdlBlks)
                    xilModelBlock = iXilMdlBlks(i);
                    xilModelBlockName = get_param(xilModelBlock, 'ModelName');
                    load_system(xilModelBlockName);
                end
            end
           
            % enforce that there must always be blocks to process
            assert(~isempty(iXilMdlBlks), 'No pil/sil Model blocks to process');
                   
            % validate referenced model configuration will build
            % successfully
            rtw.pil.ModelBlockPIL.RTWErrorCheckHookModelConfig(iMdl, iXilMdlBlks);
            
            % apply coverage and profiling validation checks 
            rtw.pil.ModelBlockPIL.RTWErrorCheckHookCoverage(iMdl, iXilMdlBlks, isXilBlkOnMultiInstPath);
            rtw.pil.ModelBlockPIL.RTWErrorCheckHookProfiling(iMdl, iXilMdlBlks, isXilBlkOnMultiInstPath); 
            
            % apply validation for each referenced component
            processedComponentList = {};
            processedTargetServices = {};
            
            isSubsystemWorkflow = coder.connectivity.XILSubsystemUtils.isSubsystemWorkflow(iMdl);
            for i=1:length(iXilMdlBlks)
                xilModelBlock = iXilMdlBlks(i);
                
                isProtected = rtw.pil.ModelBlockPIL.isModelProtected(xilModelBlock);
                if isProtected && ~runChecksForProtectedModels
                    % For protected models, delay running the checks until
                    % the protected models are unpacked (and thus the
                    % serialized configSet is available). These checks will
                    % run again when we call XILBuildHook from
                    % NormalModeCompileManager.cpp (at which point the
                    % protected models are unpacked).
                    continue;
                end
                
                % create configInterface object:
                % For protected models, the model is not available, so we
                % load up the serialized configSet in binfo.
                %
                % For non-protected models, the model is available, so we
                % can get its active configset.
                isModelReferenceTarget = ...
                    rtw.pil.ModelBlockPIL.isModelReferenceTarget(xilModelBlock);
                [configInterface,xilComponent] =...
                    rtw.pil.ModelBlockPIL.createConfigInterfaceModelBlock(iMdl, ...
                    xilModelBlock, isModelReferenceTarget);

                % Check the consistency between code interface block
                % parameter and deployment type. Deployment type of
                % Subcomponent requires Model Reference, and deployment
                % type of Component requires Top Model. Skip the following
                % validation for the protected model.
                if ~isProtected
                    xilModelBlockName = get_param(xilModelBlock, 'ModelName');
                    mapping = Simulink.CodeMapping.getCurrentMapping(xilModelBlockName);
                    if ~isempty(mapping) && isprop(mapping, 'DeploymentType')
                        codeInterface = get_param(xilModelBlock, 'CodeInterface');
                        deriveCodeInterface = ftrDeriveCodeInterface && strcmp(get_param(xilModelBlock, ...
                            "DeriveCodeInterface"), 'on');
                        errId = 'PIL:pil:CodeInterfaceNotSupportedForModelBlock';
                        if deriveCodeInterface
                            if ~(strcmp(mapping.DeploymentType, 'Component') || ...
                                strcmp(mapping.DeploymentType, 'Subcomponent'))
                                errId = 'PIL:pil:DeploymentTypeNotSupportedForDeriveCodeInterface';
                                error(message(errId, ...
                                xilModelBlockName, char(mapping.DeploymentType)));
                            end
                        else
                            if strcmp(mapping.DeploymentType, 'Subcomponent') && ...
                                strcmp(codeInterface, 'Top model')
                                codeInterfaceSuggestion = 'Model Reference';
                                error(message(errId, ...                                
                                xilModelBlockName, codeInterfaceSuggestion));
                            elseif strcmp(mapping.DeploymentType, 'Component') && ...
                                strcmp(codeInterface, 'Model reference')
                                codeInterfaceSuggestion = 'Top Model';
                                error(message(errId, ...          
                                xilModelBlockName, codeInterfaceSuggestion));
                            end
                        end
                    end
                end
                
                if isModelReferenceTarget
                   componentSuffix = '_mdlref'; 
                else
                   componentSuffix = '_stdalone'; 
                end                
                fullComponent = [xilComponent componentSuffix];                
                        
                isAUTOSARAdaptive = rtw.pil.ModelBlockPIL.isAUTOSARAdaptive(configInterface);

                if ~any(strcmp(processedComponentList, fullComponent))
                    rtw.pil.ModelBlockPIL.checkERTTargetOrGRTTarget(configInterface);
                    
                    if isAUTOSARAdaptive && matlab.internal.feature('SILMdlRefAUTOSARAdaptive')
                        if ~isModelReferenceTarget
                            % top model interface not supported for SIL or
                            % PIL
                            rtw.pil.ProductInfo.error('pil', ...
                                'AUTOSARAdaptiveModelBlockTopOrPIL', ...
                                getfullname(xilModelBlock), xilComponent);
                        end
                    end

                    % error checks for protected models
                    if isProtected
                        rtw.pil.ModelBlockPIL.RTWErrorCheckProtectedModel(iMdl, xilModelBlock);
                    end

                    if ~isModelReferenceTarget                                                                       

                        % apply AUTOSAR shared validation
                        coder.internal.connectivity.AutosarInterfaceStrategy.sharedValidation(configInterface);  
                       
                        % For standalone targets we need to handle logging
                        % checks in MATLAB code because the build does not 
                        % do this for us.
                        %
                        % If not protected, then by getting the config
                        % interface, we have loaded the model.
                        rtw.pil.ModelBlockPIL.standaloneDataLoggingValidation(xilComponent, isProtected);
                        
                    end                   

                    processedComponentList{end+1} = fullComponent; %#ok<AGROW>                    
                end                

                % Since the model might be protected, obtain the configset
                % with compiler information via configInterface
                cs = getConfig(configInterface);
            
                % Determine compiler information based on default mex configuration.
                % The DefaultCompInfo object must be instantiated only at the beginning
                % of the build and before any change of pwd. If default mex setup is
                % queried later in the build process after changing pwd it will not
                % pick up any mex configuration file in the original pwd.
                lDefaultMexCompInfo = coder.internal.DefaultCompInfo.createDefaultCompInfo;
                
                isSIL = rtw.pil.ModelBlockPIL.isSIL(xilModelBlock);
                
                if isAUTOSARAdaptive && matlab.internal.feature('SILMdlRefAUTOSARAdaptive') && ~isSIL
                    % error for PIL
                    rtw.pil.ProductInfo.error('pil', ...
                        'AUTOSARAdaptiveModelBlockTopOrPIL', ...
                        getfullname(xilModelBlock), xilComponent);
                end
                
                lXilCompInfo = i_getXilCompInfo(isSIL, cs, lDefaultMexCompInfo);                
                
                % Validate the connectivity configuration for the model
                % block.
                isReadOnly = rtw.pil.ModelBlockPIL.isReadOnly(isSubsystemWorkflow, isProtected);
                processedTargetServices = ...
                    rtw.pil.ModelBlockPIL.RTWErrorCheckHookTargetServices( ...
                    xilModelBlock, configInterface, ...
                    processedTargetServices, isUpdateDiagram, lXilCompInfo, isReadOnly, iMdl);
            end
        end
        
       % This function is invoked during model link to create a copy of the 
        % model block PILData which is created during compile by invoking
        % the XILBuildHook function. This copy is needed for the ReuseCompileForMultiInstanceModelRef
        % feature because we do not create a copy of a multi-instanced referenced model.
        % This means that we end up having multiple model exec blocks sharing a 
        % single comp model block. Therefore, we need to create a copy of the instance specific PIL data
        % for each exec instance.
        function pilInterface = getExecPILInterface(iMdl, pilModelBlock)            
            assert(strcmp(get_param(iMdl, 'MultipleExecBDOptimizationStatus'), 'on'));
            
            isSubsystemWorkflow = coder.connectivity.XILSubsystemUtils.isSubsystemWorkflow(iMdl);
            assert(~isSubsystemWorkflow, 'Subsystem workflow cannot have multi-instance model blocks');
            
            isModelReferenceTarget = ... 
                rtw.pil.ModelBlockPIL.isModelReferenceTarget(pilModelBlock);
            if isModelReferenceTarget
                inTheLoopType = rtw.pil.InTheLoopType.ModelBlock;
            else
                inTheLoopType = rtw.pil.InTheLoopType.ModelBlockStandalone;
            end
            
            % pilInterface is a copy of the comp PIL Data
            % setup a new target service object for the exec pil interface
            pilInterface = rtw.pil.ModelBlockPIL.getPILData(pilModelBlock, inTheLoopType);
            options = coder.connectivity.VerificationOptions(pilInterface.isSIL);
            options.SILDebuggingOverride = strcmp(get_param(iMdl, 'SILDebugging'), 'on');
            compInfo = pilInterface.getSILPILWrapperUtils().getComponentArgs().getXilCompInfo();
            targetServices = coder.connectivity.createTargetServices(getConfigInterface(getSILPILWrapperUtils(pilInterface)), ... 
                                                              compInfo, options);
            targetServices.initialize(pilInterface.getSILPILWrapperUtils().getComponentArgs());
            pilInterface.setTargetServices(targetServices);
        end        
            
        function validateConfiguration(isSIL, configInterface, xilCompInfo, isProtectedModel, isReadOnly)

            if isSIL
                rtw.pil.ModelBlockPIL.checkSilCompatibility(configInterface, isProtectedModel, isReadOnly, xilCompInfo);
            else
                rtw.pil.ModelBlockPIL.checkPilCompatibility(configInterface, isReadOnly)
            end
        end
            
        function XILBuildHook(iMdl, iXilMdlBlkPaths, isXilBlkOnMultiInstPath, buildXILApplication, varargin)
            % Called from NormalModeCompileManager.cpp to build the SIL/PIL
            % wrapper for a set of Model blocks in SIL/PIL mode.                        
            
            %
            % code below written to support multi instance
            %
            
            % Repeat the error checking, since we after multi-instance
            % normal mode has made copied models.  We may catch 
            % multi-instance errors that aren't easily caught before
            % expansion
            % 
            % The other use-case for running these checks again is
            % protected models, since at this point, the slxp files should
            % have been unpacked by the build process (and thus the
            % serialized configSet is available)
                        
            % iXilMdlBlkPaths contains the full paths (crossing model reference
            % boundaries) of each model block under XIL.
            %
            % The last element of the model block paths in iXilMdlBlkPaths
            % contains the block path of the model block under XIL from its
            % parent model. Strip the final elements from the paths to validate
            % the model blocks in XIL mode.
            
            iXilMdlBlks = cellfun(@(p) p(end), iXilMdlBlkPaths);
            
            runChecksForProtectedModels = true;
            
            % skip XILErrorCheckHook for top-model SIL/PIL because the
            % necessary checks have already been done in build_pil_target.m
            isTopModelXILSim = coder.connectivity.TopModelSILPIL.isTopModelXILHarness(iMdl);                            
            if ~isTopModelXILSim
                rtw.pil.ModelBlockPIL.XILErrorCheckHook(iMdl, iXilMdlBlks, isXilBlkOnMultiInstPath, ...
                    runChecksForProtectedModels, ~buildXILApplication);
            end
            
            p = inputParser();
            p.KeepUnmatched = true;
            p.addParameter('TopModelAccelWithTimeProfiling', false, @islogical);
            p.addParameter('TopModelAccelWithStackProfiling', false, @islogical);
            p.addParameter('CodeProfilingSaveOptionsAllDataTop', 'SummaryOnly', @ischar);
            p.parse(varargin{1:end});
            if p.Results.TopModelAccelWithTimeProfiling || p.Results.TopModelAccelWithStackProfiling
                topModelOriginalParams = containers.Map('KeyType', 'char', 'ValueType', 'any');
                if p.Results.TopModelAccelWithTimeProfiling
                    topModelOriginalParams('CodeExecutionProfiling') = 'on';
                    topModelOriginalParams('CodeProfilingSaveOptions') = p.Results.CodeProfilingSaveOptionsAllDataTop;
                end
                if p.Results.TopModelAccelWithStackProfiling
                    topModelOriginalParams('CodeStackProfiling') = 'on';
                end
            else
                topModelOriginalParams = [];
            end
            
            % Determine compiler information based on default mex configuration.
            % The DefaultCompInfo object must be instantiated only at the beginning
            % of the build and before any change of pwd. If default mex setup is
            % queried later in the build process after changing pwd it will not
            % pick up any mex configuration file in the original pwd.
            lMexCompInfoDefault = coder.internal.DefaultCompInfo.createDefaultCompInfo;
            
            % keep track of which referenced models we built PIL
            % applications for
            builtComponentList = {};
            modelWorkspaceMap = containers.Map;

            for i=1:length(iXilMdlBlkPaths)
                xilModelBlockPath = iXilMdlBlkPaths{i};
                xilModelBlock = xilModelBlockPath(end);
                isModelReferenceTarget = ...
                    rtw.pil.ModelBlockPIL.isModelReferenceTarget(xilModelBlock);
                
                % create configInterface object:
                % For protected models, the model is not available, so we
                % load up the serialized configSet in binfo.
                %
                % For non-protected models, the model is available, so we
                % can get its active configset.
                if isModelReferenceTarget
                   componentSuffix = '_mdlref'; 
                else
                   componentSuffix = '_stdalone'; 
                end
                [configInterface, xilComponent] =...
                    rtw.pil.ModelBlockPIL.createConfigInterfaceModelBlock(iMdl, ...
                    xilModelBlock, isModelReferenceTarget);        

                % Since the model might be protected, obtain the configset
                % with compiler information via configInterface
                cs = getConfig(configInterface);

                isSIL = rtw.pil.ModelBlockPIL.isSIL(xilModelBlock);
                lXilCompInfo = i_getXilCompInfo(isSIL, cs, lMexCompInfoDefault);

                fullComponent = [xilComponent componentSuffix];                  

                % retrieve the PIL interface object (cached in the Model
                % block's PILData)
                pilInterface = rtw.pil.ModelBlockPIL.getPILInterface...
                    (xilModelBlockPath, configInterface, iMdl, topModelOriginalParams, ...
                    buildXILApplication, lXilCompInfo);               

                % make sure application is up to date, but don't do this 
                % repeatedly for the same application
                if ~any(strcmp(builtComponentList, fullComponent))
                    if buildXILApplication
                        if isSIL
                            disp('### Preparing to start SIL simulation ...');
                        else
                            disp('### Preparing to start PIL simulation ...');
                        end
                    end
                    % bring the wrapper up to date
                    isPILBlockConfigure = false;
                    buildSFunctionOnly = ~buildXILApplication;
                    pilInterface.buildWrapper...
                        (isPILBlockConfigure, buildSFunctionOnly, lXilCompInfo, ...
                         lMexCompInfoDefault.DefaultMexCompilerKey);
                    
                    % bring the application up to date
                    if buildXILApplication
                        % Do deferred validation checks validation checks
                        pilInterface.getSILPILWrapperUtils.runDeferredValidation;
                        buildApplication(pilInterface, lXilCompInfo, ...
                            lMexCompInfoDefault.DefaultMexCompilerKey);
                    end
                    builtComponentList{end+1} = fullComponent; %#ok<AGROW>
                end
                % download will be performed dynamically in
                % rtw.pil.ComponentBridge on receipt of "PIL_INIT_COMMAND"

                % if needed, update list of model workspaces to clone 
                % Top-Model XIL already handles this via its wrapper model
                if ~isTopModelXILSim && pilInterface.getSILPILWrapperUtils.supportsModelWorkspaceParams()
                    supportedTunableModelWorkspaceParamsArray = ...
                        pilInterface.getSILPILWrapperUtils.SupportedTunableModelWorkspaceParamsArray;                    
                    if ~isempty(supportedTunableModelWorkspaceParamsArray)                        
                        parentModel = get_param(bdroot(xilModelBlock), 'Name');
                        if modelWorkspaceMap.isKey(parentModel)
                            workspaceData = modelWorkspaceMap(parentModel);
                        else
                            workspaceData = coder.connectivity.XILModelWksData;
                        end
                        if ~any(workspaceData.SrcModels.contains(xilComponent))
                            workspaceData.SrcModels(end+1) = xilComponent;
                            workspaceData.SrcModelParams{end+1} = supportedTunableModelWorkspaceParamsArray;
                            modelWorkspaceMap(parentModel) = workspaceData;
                        end
                    end
                end
            end
            % clone and manage model workspace parameters            
            if ~isTopModelXILSim && ~modelWorkspaceMap.isempty
                coder.connectivity.XILMdlWksHandler.initialize(iMdl, ...
                    modelWorkspaceMap);
            end
        end  
        
        function XILBuildOneStandaloneTarget(topModel, ...
                                             xilComponentModel, ...
                                             isSIL, ...
                                             pushNags, ...
                                             defaultCompInfo, ...
                                             xilCompInfo, ...
                                             topModelAccelWithTimeProfiling, ...
                                             topModelAccelWithStackProfiling, ...
                                             isXILSubsystemHiddenModelBuild)            
            % Build the standalone code generation target for one model
            % that will simulate in SIL or PIL mode

            % check external mode is not switched on, otherwise we would
            % get link errors.
            if strcmp(get_param(xilComponentModel, 'ExtMode'), 'on')
                rtw.pil.ProductInfo.error('pil', 'StandaloneExtMode', ...
                    xilComponentModel);
            end
            
            % If the top-model and the xilComponentModel are the same
            % then this is the top-model SIL/PIL scenario
            isTopModelXILSim = strcmp(topModel, xilComponentModel);
                        
            silDebugging = strcmp(get_param(topModel, 'SILDebugging'), 'on');
            lCodeExecutionProfilingTop = strcmp(get_param(topModel, 'CodeExecutionProfiling'), 'on');
            lCodeStackProfiling = strcmp(get_param(topModel, 'CodeStackProfiling'), 'on');
            lCodeProfilingWCETAnalysis = lCodeExecutionProfilingTop && ...
                strcmp(get_param(topModel, 'CodeProfilingWCETAnalysis'), 'on');
            lCodeCoverageSettings = slprivate('getCodeCoverageSettings', ...
                topModel);                                  

            % reuse top-model XIL build to make sure that
            % STANDALONE_SUPPRESS_EXE is active...
            slbuildArgs = {'StandaloneCoderTarget',...
                'TopModelXILSim', isTopModelXILSim, ...
                'TopModelAccelWithTimeProfiling', topModelAccelWithTimeProfiling,...
                'TopModelAccelWithStackProfiling', topModelAccelWithStackProfiling,...
                'CodeExecutionProfilingTop', lCodeExecutionProfilingTop, ...
                'CodeStackProfilingTop', lCodeStackProfiling, ...
                'CodeProfilingWCETAnalysis', lCodeProfilingWCETAnalysis, ...
                'CodeCoverageSettings', lCodeCoverageSettings, ...
                'InstrumentationSettingsModel', topModel, ...
                'SILDebugging', silDebugging, ...
                'TopModelSilOrPilBuild',true,...
                'TopModelIsSilMode', isSIL,...
                'OkayToPushNags', pushNags, ...
                'SlbXilCompInfo', xilCompInfo, ...
                'SlbDefaultCompInfo', defaultCompInfo, ...
                'GenerateCodeOnly', false, ...
                'IsXILSubsystemHiddenModelBuild', isXILSubsystemHiddenModelBuild};            
                                  
            % note: refModels always contains xilComponentModel
            assert(bdIsLoaded(xilComponentModel), ...
                'Expect xilComponentModel to already be loaded.');

            restoreObj = i_tagTopLevelBuildForATS(topModel, xilComponentModel); %#ok<NASGU> 

            % Avoid warning about XmlOptions not inherited from
            % architecture model when doing SIL on a component
            % block in architecture model
            %
            % TopTester: test/toolbox/rtw/targets/AUTOSAR/Architecture/Simulation/tComponentSIL.m
            if Simulink.CodeMapping.isAutosarCompliant(xilComponentModel) && ...
                    Simulink.internal.isArchitectureModel(topModel, ...
                    'AUTOSARArchitecture')
                resetCompBuildParam = onCleanup(@()set_param(xilComponentModel, ...
                    'IsComponentBuildFromComposition', 'off'));
                set_param(xilComponentModel, 'IsComponentBuildFromComposition', 'on');
            end

            % invoke double pass build to support Classic AUTOSAR and DDS
            result = coder.internal.codegenAndCompile(xilComponentModel, slbuildArgs);
            if result.SlbuildCancelled
                DAStudio.error('PIL:pil:SimCancelledByUser');
            end      
        end
        
        function XILBuildStandaloneTargets(topModel, xilMdlBlks, pushNags, ...
                topModelAccelWithTimeProfiling, topModelAccelWithStackProfiling)     
            % Called from ModelRefCompileHierarchy.cpp to
            % build standalone code generation targets for a set of Model
            % blocks in SIL/PIL mode.
                
            % Determine compiler information based on default mex configuration.
            % The DefaultCompInfo object must be instantiated only at the beginning
            % of the build and before any change of pwd. If default mex setup is
            % queried later in the build process after changing pwd it will not
            % pick up any mex configuration file in the original pwd.
            lDefaultCompInfo = coder.internal.DefaultCompInfo.createDefaultCompInfo;
            
            % In the RLS workflow the only component to build is a hidden
            % model. This build is needed to instrument the library code
            % and we can avoid packing the generated code into SLXC.
            isXILSubsystemHiddenModelBuild = coder.connectivity.XILSubsystemUtils.isRLS(topModel);
            
            % keep track of which referenced models we built XIL
            % applications for
            builtComponentList = {};
            if ishandle(topModel)
                topModel = get_param(topModel,'Name');
            end
            for i=1:length(xilMdlBlks)
                xilModelBlock = xilMdlBlks(i);
                isProtected = rtw.pil.ModelBlockPIL.isModelProtected(xilModelBlock);                  
                if isProtected
                    % this is a protected model, so code must have already
                    % been unpacked and built if necessary.
                    continue;
                end
                
                if coder.connectivity.XILSubsystemUtils.isAtomicSubsystem(topModel)
                    harnessInfo = Simulink.harness.internal.getHarnessInfoForHarnessBD(topModel);
                    xilComponentModel = harnessInfo.model;
                else
                    xilComponentModel = get_param(xilModelBlock, 'ModelName');
                end

                if Simulink.internal.isArchitectureModel(xilComponentModel, 'AUTOSARArchitecture')
                    % SIL/PIL simulations are not supported for AUTOSAR
                    % Architecture Model compositions. In this case we will
                    % error out on first detection.
                    modelBlockPath = [topModel '/' xilComponentModel];
                    mExc = MSLException('PIL:pil:AutosarArchModelBlockXILUnsupported', modelBlockPath, xilComponentModel);
                    mExc.throw();
                end

                if ~any(strcmp(builtComponentList, xilComponentModel))
                    isSIL = rtw.pil.ModelBlockPIL.isSIL(xilModelBlock);
                    lXilCompInfo = i_getXilCompInfo(isSIL, getActiveConfigSet(xilComponentModel), lDefaultCompInfo);
                    % build
                    rtw.pil.ModelBlockPIL.XILBuildOneStandaloneTarget(topModel, ...
                                                                      xilComponentModel, ...
                                                                      isSIL, ...
                                                                      pushNags, ...
                                                                      lDefaultCompInfo, ...
                                                                      lXilCompInfo, ...
                                                                      topModelAccelWithTimeProfiling, ...
                                                                      topModelAccelWithStackProfiling, ...
                                                                      isXILSubsystemHiddenModelBuild);
                    builtComponentList{end+1} = xilComponentModel; %#ok<AGROW>                                       
                end                                                
            end                                                            
        end
                                
        
        function checkPilCompatibility(configInterface, isReadOnly)
            
            % error out if some model settings are not compatible for PIL            
            rtw.pil.ModelBlockPIL.checkModelForSILPILCompatibility(configInterface, isReadOnly);
            
            % make sure word size maps to one of the integer sizes
            isPIL = true;
            rtw.pil.SlHardwareConfigHelper.checkWordSize(configInterface, isPIL, isReadOnly);
        end
        

        % Return true if the given block is a copied multi-instance normal
        % mode model, false otherwise.  This requires that the models
        % containing the blocks are all opened
        function isInCopiedModel = isBlockInCopiedModel(block)
            model = bdroot(block);
            paramVal = get_param(model, 'ModelReferenceMultiInstanceNormalModeCopy');
            isInCopiedModel = strcmp(paramVal, 'on');
        end
        
        function link = getConfigsHyperlink(configs)
            link = '';
            numConfigs = length(configs);
            for i=1:numConfigs
                config = configs(i);
                if i==numConfigs
                    spacer = '';
                else
                    spacer = ', ';
                end
                link = [link targets_hyperlink_manager('new', ...
                    config.ConfigName, ...
                    @disp, config) spacer]; %#ok<AGROW>
            end
        end
        
        function blockListStr = makeBlockListString(blockList)
            blockListStr = '';
            for i = 1:length(blockList)
                block = getfullname(blockList(i));
                blockListStr = [blockListStr block newline]; %#ok<AGROW>
            end
        end
        
        function isModelReferenceTarget = isModelReferenceTarget(modelBlock)
            % Return whether Model block in SIL or PIL mode is configured
            % for model reference code interface or top model
            % code interface
            simMode = get_param(modelBlock,'SimulationMode');
            assert(any(strcmpi({'processor-in-the-loop (pil)',...
                           'software-in-the-loop (sil)'},...
                          simMode)),'Simulation mode must be SIL or PIL');

            isModelReferenceTarget = ~strcmp(get_param(modelBlock, 'CodeInterface'), ...
                                                                   'Top model');
        end
        
        function modelInterface = getModelInterfaceForTLC(lModelBlock, isModelReferenceTarget)
            % GetModelInterfaceForTLC
            lPILData = get_param(lModelBlock, 'PILData');
            assert(~isempty(lPILData), 'PILData should not be empty for block %s', lModelBlock);
            assert(isa(lPILData, 'rtw.pil.SILPILInterface'), 'PILData in block %s should be a rtw.pil.SILPILInterface', lModelBlock);
            
            xilComponent = rtw.pil.ModelBlockPIL.getXilComponentName(lModelBlock);
            lCodeDir = lPILData.getCodeDir;
            loadConfigSet = false;
            savedInfoStruct = rtw.pil.ModelBlockPIL.getSavedInfoStruct( ...
                xilComponent, isModelReferenceTarget, lCodeDir, loadConfigSet);
            modelInterface = savedInfoStruct.modelInterface;
        end
        
        function childModelsWithProfiling = getChildModelsWithProfilingForBlock(lModelBlock)
            lPILData = get_param(lModelBlock, 'PILData');
            assert(~isempty(lPILData), 'PILData should not be empty for block %s', lModelBlock);
            assert(isa(lPILData, 'rtw.pil.SILPILInterface'), 'PILData in block %s should be a rtw.pil.SILPILInterface', lModelBlock);
            
            xilComponent = rtw.pil.ModelBlockPIL.getXilComponentName(lModelBlock);
            lCodeDir = lPILData.getCodeDir;
            
            % Get the saved infoStruct
            isModelReferenceTarget = strcmp(get_param(lModelBlock, 'CodeInterface'), ...
                'Model reference');
            loadConfigSet = false;
            savedInfoStruct = rtw.pil.ModelBlockPIL.getSavedInfoStruct( ...
                xilComponent, isModelReferenceTarget, lCodeDir, loadConfigSet);
            
            % We need to temporarily flip the cached STF in infoMATFileMgr
            lConfigInterface = lPILData.getSILPILWrapperUtils.getConfigInterface;
            lChildSystemTargetFile = lConfigInterface.getParam('SystemTargetFile');
            
            if isModelReferenceTarget
                lModelReferenceTargetType = 'RTW';
            else
                lModelReferenceTargetType = 'NONE';
            end
            [~, lGenSettings] = coder.internal.getSTFInfo...
                (xilComponent, ...
                'systemtargetfile', lChildSystemTargetFile, ...
                'noTLCSettings', true, ...
                'modelreferencetargettype', lModelReferenceTargetType);
            
            % Initialize infoMATFileMgr. The previous state of infoMATFileMgr is
            % automatically restored when the cleanup is executed.
            cleanupGenSettingsCache = coder.internal.infoMATFileMgr...
                ([], [], [], [], ...
                'InitializeGenSettings', lGenSettings);
            
            lChildModels = savedInfoStruct.modelRefsAll;
            nChildModels = numel(lChildModels);
            lKeepIdx = false(1, nChildModels);
            
            for kChildModel = 1:nChildModels
                childInfoStruct = coder.internal.infoMATFileMgr('load', ...
                    'binfo', ...
                    lChildModels{kChildModel}, ...
                    'RTW');          % Always RTW for child models.
                lCodeProfilingInstrChild = get_param...
                    (childInfoStruct.configSet, 'CodeProfilingInstrumentation');
                if ~strcmp(lCodeProfilingInstrChild, 'off')
                    lKeepIdx(kChildModel) = true;
                end
            end
            
            % Clean up change to GenSettings
            cleanupGenSettingsCache.delete;
            
            childModelsWithProfiling = lChildModels(lKeepIdx);
            lCodeProfilingInstrThis = lConfigInterface.getParam('CodeProfilingInstrumentation');
            if ~strcmp(lCodeProfilingInstrThis, 'off')
                childModelsWithProfiling = [childModelsWithProfiling, xilComponent];
            end
        end
        
        function childModelsWithProfiling = getChildModelsWithProfiling(lParentModel, lModelBlockList)
            
            assert(iscell(lModelBlockList));
            
            % This code path should only ever be used in Top Model
            % accelerator mode.  In this case, a Model Block in SIL/PIL
            % mode can only exist in the top model, since it cannot exist
            % in a model for which the SIM target is built.
            %
            % Assert that this is indeed the case.
            assert(all(startsWith(lModelBlockList, [lParentModel, '/'])));
            
            % Get a list of unique referenced models, so we don't query the
            % same model twice.
            lModelList = cellfun(@(x)rtw.pil.ModelBlockPIL.getXilComponentName(x), ...
                lModelBlockList, 'UniformOutput', false);
            [~, lUniqueIdx] = unique(lModelList, 'stable');
            lModelBlockList = lModelBlockList(lUniqueIdx);
            
            % Now find any child models with profiling
            childModelsWithProfiling = {};
            for kModelBlock = 1:numel(lModelBlockList)
                childModelsWithProfiling = [ ...
                    childModelsWithProfiling, ...
                    rtw.pil.ModelBlockPIL.getChildModelsWithProfilingForBlock(lModelBlockList{kModelBlock}), ...
                    ]; %#ok<AGROW>
            end
            childModelsWithProfiling = unique(childModelsWithProfiling, 'stable');
        end
        
       
        function prodHW = getHostProdHW()
            % select the appropriate host prod hardware
            archstr = computer('arch');
            switch archstr
                case 'win64'
                    prodHW = 'Intel->x86-64 (Windows64)';
                case 'glnxa64'
                    prodHW = 'Intel->x86-64 (Linux 64)';
                case 'maci64'
                    prodHW = 'Intel->x86-64 (Mac OS X)';
                case 'maca64'
                    prodHW = 'Apple->ARM64';
                otherwise
                    assert(false, 'Unknown arch: %s', archstr);
            end
        end
        
       function pwsEnableLongLong = getPWSEnableLongLong(componentName)
            % special handling for Windows64 which is the default
            % hardware implementation and requires long long to be
            % enabled in order for uint64 (corresponding to native
            % word size) to get typedef'd in rtwtypes.h
            if strcmp(get_param(componentName, 'ProdHWDeviceType'), ...
                    'Intel->x86-64 (Windows64)')
                pwsEnableLongLong = true;
            else
                pwsEnableLongLong = false;                
            end            
       end
        
       function checkERTTargetOrGRTTarget(configInterface)
            rtw.connectivity.Utils.validateArg(configInterface,...
                'coder.connectivity.ConfigInterface');
            xilComponent = configInterface.getCodeGenComponent;
            activeConfigSet = configInterface.getResolvedConfig;            
            if ~rtw.pil.Config.isERTTargetOrGRTTarget(activeConfigSet)
                DAStudio.error('PIL:pil:ModelNotDerivedFromERTOrGRT',...
                    xilComponent);
            end
       end

       function isAUTOSARAdaptive = isAUTOSARAdaptive(configInterface)
           % determine whether the component is configured for AUTOSAR
           % Adaptive based on the configInterface
           arguments
               configInterface (1,1) coder.connectivity.ConfigInterface
           end           
           isAUTOSARAdaptive = strcmp(get_param(configInterface, 'AutosarCompliant'), 'on') && ...
               strcmp(get_param(configInterface, 'CodeInterfacePackaging'), 'C++ class');
       end
    end
    
    methods (Access = 'private', Static = true)
        function [isProtected, modelName, modelInfo] = isModelProtected(modelBlock)
            isProtected = strcmp(get_param(modelBlock, 'ProtectedModel'), 'on');
            modelName = '';
            modelInfo = [];
            if isProtected
                modelFile = get_param(modelBlock, 'ModelFile');
                modelInfo = Simulink.ModelReference.ProtectedModel.getOptions(modelFile);
                modelName = modelInfo.modelName;
            end
        end
        
        function lXilComp = getXilComponentName(lModelBlock)
            [isProtected, lXilComp] = rtw.pil.ModelBlockPIL.isModelProtected(lModelBlock);
            if ~isProtected
                lXilComp = get_param(lModelBlock, 'ModelName');
            end
        end
        
        function [configInterface,xilComponent] = createConfigInterfaceModelBlock(...
                iMdl, xilModelBlock, isModelReferenceTarget)
            % create configInterface object:
            % For protected models, the model is not available, so we load
            % up the serialized configSet in binfo.
            %
            % For non-protected models, the model is available, so we can
            % get its active configset.
            clientInterface = coder.connectivity.SimulinkInterface;
            [isProtected, xilComponent] = rtw.pil.ModelBlockPIL.isModelProtected(xilModelBlock);            
            if isProtected
                buildDirInfo = RTW.getBuildDir(xilComponent);
                if isModelReferenceTarget
                    codeDir = fullfile(buildDirInfo.CodeGenFolder,...
                        buildDirInfo.ModelRefRelativeBuildDir);
                else
                    codeDir = buildDirInfo.BuildDirectory;
                end
                % Get the saved infoStruct
                loadConfigSet = true;
                savedInfoStruct = rtw.pil.ModelBlockPIL.getSavedInfoStruct( ...
                    xilComponent, isModelReferenceTarget, ...
                    codeDir, ...
                    loadConfigSet);
                
                % create configInterface
                configInterface = clientInterface.createConfigInterfaceFromInfoStruct(...
                    savedInfoStruct, xilComponent);
                
            else % non-protected, so load the model
                if coder.connectivity.XILSubsystemUtils.isAtomicSubsystem(iMdl)
                    % get the original model if Atomic subsystem workflow
                    harnessInfo = Simulink.harness.internal.getHarnessInfoForHarnessBD(iMdl);
                    xilComponent = harnessInfo.model;
                else
                    xilComponent = get_param(xilModelBlock, 'ModelName');
                end
                load_system(xilComponent);
                configSet = getActiveConfigSet(xilComponent);
                configInterface = clientInterface.createConfigInterface(...
                    configSet, xilComponent);
            end
        end
                
        function RTWErrorCheckProtectedModel(iMdl, xilModelBlock)
            
            % Code coverage of a protected referenced model is not
            % supported
            cov_settings = slprivate('getCodeCoverageSettings', iMdl);
            if strcmp(cov_settings.ReferencedModelCoverage, 'on')
                DAStudio.error...
                    ('Simulink:protectedModel:CodeCoverageNotSupportedForProtectedModel', ...
                    iMdl,...
                    getfullname(xilModelBlock));
            end
            
            % SIL debugging of a protected referenced model is not
            % supported
            if strcmp(get_param(iMdl, 'SILDebugging'), 'on')
                DAStudio.error...
                    ('Simulink:protectedModel:SILDebuggingNotSupportedForProtectedModel', ...
                    iMdl,...
                    getfullname(xilModelBlock));
            end
            
            % Stack profiling of a protected referenced model is not
            % supported
            if strcmp(get_param(iMdl, 'CodeStackProfiling'), 'on')
                DAStudio.error...
                    ('Simulink:protectedModel:StackProfilingNotSupportedForProtectedModel', ...
                    iMdl,...
                    getfullname(xilModelBlock));
            end
        end
        
        
        % Do error checks for multi-SIL/PIL blocks ensuring the target services
        % for each block can be run concurrently.
        function processedServices = RTWErrorCheckHookTargetServices...
            (xilModelBlock, configInterface, processedServices, isUpdateDiagram, lXilCompInfo, isReadOnly, topModel)

            rtw.connectivity.Utils.validateArg(configInterface, 'coder.connectivity.ConfigInterface');

            options = coder.connectivity.VerificationOptions(rtw.pil.ModelBlockPIL.isSIL(xilModelBlock));            
            options.SILDebuggingOverride = strcmp(get_param(topModel, 'SILDebugging'), 'on');
            
            isProtected = rtw.pil.ModelBlockPIL.isModelProtected(xilModelBlock);
            rtw.pil.ModelBlockPIL.validateConfiguration(options.IsSIL, configInterface, lXilCompInfo, isProtected, isReadOnly);                
            
            [targetServices, errorMessage] = coder.connectivity.createTargetServices(configInterface, lXilCompInfo, options);
            if ~isempty(errorMessage)                
                if (isUpdateDiagram)
                    % As we are only updating diagram we can return the SIL
                    % target service if there is an error returning the true
                    % one. We only need it to propagate information up the
                    % hierarchy. If PIL is then run this will be re-calculated.
                    options.IsSIL = true;
                    targetServices = coder.connectivity.createTargetServices(configInterface, lXilCompInfo, options);
                else
                    error(errorMessage);
                end
            end

            assert(~isempty(targetServices), 'Target services should be instantiated.');

            for i = 1:length(processedServices)
                service = processedServices{i};
                canRunWith = service.runsConcurrentlyWith(targetServices);

                if (~canRunWith)
                    rtw.pil.ProductInfo.error('pil', 'PILConfigNotMultiInstance', getfullname(xilModelBlock), targetServices.getName());
                end
            end               

            processedServices{end+1} = targetServices;
        end
                
            
        % Do error checks related to model reference configurations
        function RTWErrorCheckHookModelConfig(iMdl, iXilMdlBlks)


            % We can only validate unprotected model blocks at this point
            % as protected model blocks have not been unpacked.
            mblocks = iXilMdlBlks(~arrayfun(@rtw.pil.ModelBlockPIL.isModelProtected, ...
                                 iXilMdlBlks));

            topModelName = get_param(iMdl, 'Name');

            %We need more than one model to compare 
            numModels = length(mblocks);
            if numModels < 2
                return;
            end

            structs = cell(1, numModels);
            models = cell(1, numModels);
            modelBlockSTFs = cell(1, numModels);
            for i = 1:numModels

                % Generate the shared utils structure for the current model
                modelBlock = mblocks(i);
                modelName = get_param(modelBlock, 'ModelName');

                % we need to load the model to provide this check
                load_system(modelName);

                cs = getActiveConfigSet(modelName);
                
                % Get the shared utility checksum
                existingSharedCodeUsingClassicNonFinite = ...
                    coder.build.internal.resolveExistingSharedCodeUsingClassicNonFinite( ...
                    modelName, false);
                newStruct = coder.internal.getDefaultUtilStruct(modelName, ... 
                                                                [], ...
                                                                'RTW', ...
                                                                [], ...
                                                                existingSharedCodeUsingClassicNonFinite, ...
                                                                cs);
                                                            
                % The only checksums we care about validating against are
                % the models we have processed for the same system target
                % file. Get the models and structures to compare to
                stf = get_param(cs, 'SystemTargetFile');                
                sameSTF = strcmp(stf, modelBlockSTFs);
                structsToCompare = structs(sameSTF);
                modelsToCompare = models(sameSTF);               
                modelInstances = strcmp(modelName, models);
                
                % Compare the referenced model structure with the previous
                % models if we have a model to compare to and if we haven't
                % seen this model before
                if ~isempty(structsToCompare) && ~any(modelInstances)
                    cellfun(@(m, s) rtw.pil.ModelBlockPIL.validateSharedUtilities(topModelName, modelName, newStruct, m, s), modelsToCompare, structsToCompare);
                end
                
                modelBlockSTFs{i} = stf;
                structs{i} = newStruct;
                models{i} = modelName;
            end            
        end
        
        function validateSharedUtilities(topModel, modelA, structA, modelB, structB)
            
            % Perform the shared utils comparison, if the comparison
            % results in a shared utils conflict (i.e. the shared utilities
            % checksums are different and they have the same shared utility
            % folder.
            comp = coder.internal.SharedUtilsChecksumComparison(modelA, modelB, structA, structB);            
            if comp.SharedUtilsFolderConflict
                comp.addSolutionMessege('RTW:buildProcess:sharedUtilsInconsistentModelsXILResolve')
                comp.throwError('RTW:buildProcess:sharedUtilsInconsistentModelsXIL', ...
                    topModel);
            end
        end
        
        
        % Load infoStruct from binfo
        function savedInfoStruct = getSavedInfoStruct(xilComponent, isModelReferenceTarget, lCodeDir, loadConfigSet)
            lBinfoMATFile = coderprivate.getBinfoMATFileAndCodeName(lCodeDir);
            if isModelReferenceTarget
                tgt = 'RTW';
            else
                tgt = 'NONE';
            end
            try
                savedInfoStruct = coder.internal.infoMATFileMgr(...
                    'loadPostBuild', 'binfo', xilComponent, tgt,...
                    lBinfoMATFile, loadConfigSet);
            catch origE
                % construct standard error with origE as cause
                [newId, newMessage] = rtw.pil.ProductInfo.message(...
                    'pilverification', 'ErrorGeneratedCodeIncomplete');
                newException = MException(newId, ...
                    newMessage);
                newException = newException.addCause(origE);
                throw(newException);
            end
        end
        
        % Do error checks related to profiling
        function RTWErrorCheckHookProfiling(iMdl, iXilMdlBlks, isXilBlkOnMultiInstPath) 
            timeProfiling = strcmp(get_param(iMdl, 'CodeExecutionProfiling'), 'on');
            stackProfiling = strcmp(get_param(iMdl, 'CodeStackProfiling'), 'on');
            
            if timeProfiling || stackProfiling
                if  (length(iXilMdlBlks) > 1 || any(isXilBlkOnMultiInstPath > 0))
                    % This error message is not reachable in normal 
                    origModelBlocks = ~arrayfun(@rtw.pil.ModelBlockPIL.isBlockInCopiedModel, ...
                                               iXilMdlBlks);
                    origModelBlocks = iXilMdlBlks(origModelBlocks);

                    if(length(origModelBlocks) == 1)
                        DAStudio.error...
                            ('CoderProfile:ExecutionTime:ModelBlockNameClashOneBlock', ...
                             getfullname(origModelBlocks(1)));
                    else
                        blockListStr = rtw.pil.ModelBlockPIL.makeBlockListString...
                            (origModelBlocks);
                        if timeProfiling
                            if coder.profile.private.featureOn('MultiModelBlocks')
                                return;
                            end
                            msgId = 'CoderProfile:ExecutionTime:ModelBlockNameClash';
                        else
                            msgId = 'CoderProfile:ExecutionStack:ProfilingMultipleModelBlock';
                        end
                        DAStudio.error(msgId, blockListStr);
                    end
                end
            end
        end
            
        % Do error checks related to coverage
        function RTWErrorCheckHookCoverage(iMdl, iXilMdlBlks, isXilBlkOnMultiInstPath)
            if length(iXilMdlBlks)>1 || any(isXilBlkOnMultiInstPath >0)
                cov_settings = get_param(iMdl, 'CodeCoverageSettings');
                if (~strcmp(cov_settings.CoverageTool, 'None') && ...
                        strcmp(cov_settings.ReferencedModelCoverage, 'on'))
                    
                    % There are more than one XIL model blocks and
                    % coverage is turned on.  This is not supported.  This
                    % can happen because there is one XIL model block in
                    % the hierarchy, but in a model block that is
                    % referenced multiple times, or because there are
                    % multiple XIL model blocks in the hierarchy.  We need
                    % to report a different error for these two cases.
                    origModelBlocks = ~arrayfun(@rtw.pil.ModelBlockPIL.isBlockInCopiedModel, iXilMdlBlks);
                    origModelBlocks = iXilMdlBlks(origModelBlocks);
                    
                    % Highlight the blocks
                    for i = 1:length(origModelBlocks)
                        hilite_system(origModelBlocks(i));
                    end
                    
                    % Report the appropriate error
                    if(length(origModelBlocks) == 1)
                        blockListStr = getfullname(origModelBlocks(1));
                        rtw.pil.ProductInfo.error('pil', 'ModelBlockPILSingleInsCodeCovOneBlock', blockListStr);
                    else
                        blockListStr = rtw.pil.ModelBlockPIL.makeBlockListString(origModelBlocks);
                        rtw.pil.ProductInfo.error('pil', 'ModelBlockPILSingleInsCodeCov', blockListStr);
                    end
                end
                
                % There are more than one SIL/PIL model blocks.  If any of
                % these are PIL, error, because a PIL mode block cannot
                % be simulated with any other SIL/PIL model blocks.
                silBlocks = arrayfun(@(b) rtw.pil.ModelBlockPIL.isSIL(b), iXilMdlBlks);
                pilBlocks = ~silBlocks;
                if ~(all(silBlocks) || all(pilBlocks))
                    sillockListStr = rtw.pil.ModelBlockPIL.makeBlockListString(iXilMdlBlks(silBlocks));
                    pillockListStr = rtw.pil.ModelBlockPIL.makeBlockListString(iXilMdlBlks(pilBlocks));
                    rtw.pil.ProductInfo.error('pil', 'ModelBlockSILAndPIL', sillockListStr, pillockListStr);
                end
            end
        end     
        
        function pilData = getPILData(pilModelBlock, inTheLoopType)
            handler = coder.connectivity.PILDataHandler.createHandler(inTheLoopType);
            pilData = handler.getPILData(pilModelBlock);
        end
        
        function isSIL = isSIL(pilModelBlock)
            simMode = lower(get_param(pilModelBlock,'SimulationMode'));
            assert(any(strcmp({'processor-in-the-loop (pil)',...
                           'software-in-the-loop (sil)'},...
                          simMode)),'Simulation mode must be SIL or PIL');
            if strcmp(simMode, 'software-in-the-loop (sil)')
               isSIL = true; 
            else
               isSIL = false;
            end
        end
        
        function pilInterface = getPILInterface(pilModelBlockPath, configInterface, ...
                iMdl, topModelOriginalParams, buildingApplication, lXilCompInfo)
            % find the connectivity config that is valid for this
            % component
            %
            % Note: there is 1 PIL application per PIL component, therefore
            % all instances of the same component use the same config                            

            % reuse public static method getConnectivityConfig to
            % implement a useful hyperlink callback
            pilModelBlock = pilModelBlockPath(end);
            isRLS = coder.connectivity.XILSubsystemUtils.isRLS(iMdl);
            isAtomic = ~isRLS && coder.connectivity.XILSubsystemUtils.isAtomicSubsystem(iMdl);
            isSubsystemWorkflow = isRLS || isAtomic;
            rtw.connectivity.Utils.validateArg(configInterface, 'coder.connectivity.ConfigInterface');            
            isSIL = rtw.pil.ModelBlockPIL.isSIL(pilModelBlock);
            
            options = coder.connectivity.VerificationOptions(isSIL);            
            options.SILDebuggingOverride = strcmp(get_param(iMdl, 'SILDebugging'), 'on');
            
            [targetServices, errorMessage] = coder.connectivity.createTargetServices(configInterface, lXilCompInfo, options);
                        
            if ~isempty(errorMessage)                
                if (buildingApplication)
                    error(errorMessage);
                else                
                    % If we are not going to be building the XIL application,
                    % we can permit a default config to be used to get us the
                    % S-function for diagram update.  Everything will get
                    % re-generated if the config name changes later on.
                    options.IsSIL = true;
                    targetServices = coder.connectivity.createTargetServices(configInterface, lXilCompInfo, options);                    
                end
            end
            
            rtw.pil.ModelBlockPIL.dispPILConfigurationHyperlink(...
                ~isSIL, isSubsystemWorkflow, iMdl, configInterface, targetServices);

            codeGenComponent = configInterface.getCodeGenComponent();
            [anchorDir, codeDir, codedescriptorDir, inTheLoopType] = rtw.pil.ModelBlockPIL.getCodeDirsAndInTheLoopType( ...
                codeGenComponent, pilModelBlock, isRLS, iMdl);
            
            if isSubsystemWorkflow
                harnessInfo = Simulink.harness.internal.getHarnessInfoForHarnessBD(iMdl);
                subsystemName = harnessInfo.ownerFullPath;
                % Do not use cached PILData. For ModelBlock simulations the
                % PILData parameter is implemented as an hash table on the  
                % parent model that uses a pointer to the SLBlock 
                % representing the ModelBlock as key. For subsystem 
                % workflows the SLBlock pointer lifespan is limited to 
                % compile and execution. A new simulation will use a new 
                % SLBlock pointer and most of the times 
                % rtw.pil.ModelBlockPIL.getPILData returns empty. 
                % It may happen that the SLBlock pointer used in a previous
                % simulation is reused in the current one. In this case
                % rtw.pil.ModelBlockPIL.getPILData would return stale data.
                pilInterface = [];
            else
                subsystemName = '';
                % retrieve any existing PILData, if any
                pilInterface = rtw.pil.ModelBlockPIL.getPILData(pilModelBlock, inTheLoopType);
            end
            
            % check for existing PILData
            if isempty(pilInterface) || ~isa(pilInterface, 'rtw.pil.SILPILInterface')
                createPILInterface = true;
            else 
                createPILInterface = false;
            end                        
            
            if ~createPILInterface
               % Does PILInterface need updating?               
               if ~pilInterface.isUpToDate(codeDir, targetServices, iMdl)                   
                  createPILInterface = true; 
               end                
            end            
            
            if createPILInterface	
                % create an instance of the interface class                
                clientInterface = coder.connectivity.SimulinkInterface;                
                blockPath = getfullname(pilModelBlockPath);
                if ~iscell(blockPath)
                    blockPath = {blockPath};
                end
                pilInterface = rtw.pil.SILPILInterface(anchorDir, ...
                                                       codeDir, ...
                                                       inTheLoopType, ...
                                                       clientInterface, ...
                                                       targetServices, ...
                                                       lXilCompInfo, ...
                                                       isSIL, ...
                                                       'TopModel', iMdl,...
                                                       'TopModelOriginalParams', topModelOriginalParams, ...
                                                       'Owner', pilModelBlock, ...
                                                       'BlockPath', blockPath, ...
                                                       'CodeDescriptorDir', codedescriptorDir, ...
                                                       'SubsystemName', subsystemName, ...
                                                       'VerboseOutput', strcmp(get_param(iMdl, 'SILPILVerbosity'), 'on'));
                % set the PILData
                handler = coder.connectivity.PILDataHandler.createHandler(inTheLoopType);
                handler.setPILData(pilModelBlock, pilInterface);
            end
        end                                                       
        
        function checkIfModelIsCompatibleWithSilNoPWS...
                (configInterface, isProtectedModel, lXilCompInfo)
                  
            % assert if someone call this method when portable word sizes
            % is enabled
            rtw.connectivity.Utils.validateArg(configInterface,...
                'coder.connectivity.ConfigInterface');
            model = configInterface.getCodeGenComponent;
            isPortableWordSzEnabled =...
                strcmp(getParam(configInterface,'PortableWordSizes'),'on');
            assert(~isPortableWordSzEnabled, ...
                'expected portable word sizes for %s to be disabled', model);
            
            % construct hyperlink/text which will be used if we throw an
            % incompatible SIL config error message
            commandText = ['helpview(''ecoder'', '...
                '''validate_ert_prod_code_using_portable_word_size'')'];
            portableWordSzLink = ...
                targets_hyperlink_manager('new', ...
                'Configuring Hardware Implementation Settings for SIL', ...
                commandText);
            
            [~,portableWordSzMessage] = rtw.pil.ProductInfo.message(...
                'pil',...
                'PortableWordSizesRecommendation',...
                portableWordSzLink);
            
            % Determine if we are using toolchain or TMF approach for this
            % model.                                 
            lIsToolchainBased = lXilCompInfo.IsToolchainBased;
            
            % check that a supported host-based TMF/Toolchain is specified.
            % We only do this check if "PortableWordSizes" is not
            % enabled on the model. If "PortableWordSizes" is enabled
            % on the model, we will automatically build the generated
            % code with the appropriate host tmf/toolchain without forcing 
            % the user to change the tmf/toolchain setting on the model.
                
            if ~lIsToolchainBased
                
                % Check that we have a non-empty tmf. There is no
                % equivalent for this in the toolchain world.
                if strcmp(getParam(configInterface,'GenerateMakefile'),'on')                    
                    if isProtectedModel
                        tmf = fullfile(matlabroot, ...
                            configInterface.getInfoStruct.TemplateMakefile);
                    else
                        tmf = coder.make.internal.getTMF ...
                              (model, get_param(model,'RTWTemplateMakefile'), ...
                              lXilCompInfo.XilMexCompInfo);
                    end
                else
                    tmf = '';
                end
                
                if isempty(tmf)
                    activeConfigSet = configInterface.getResolvedConfig;
                    isGRT = activeConfigSet.getComponent('Code Generation').getComponent('Target').isGRTTarget;
                    if isGRT
                        rtw.pil.ProductInfo.error('pil', ...
                            'SILGRTNoTmf', ...
                            model);
                    else
                        rtw.pil.ProductInfo.error('pil', ...
                            'SILNoTmf', ...
                            model,...
                            portableWordSzMessage);
                    end
                end
            end

            % Check for supported TMF/Toolchain. Use the same check that 
            % we perform to determine if a compiler can be used for building a
            % simulation target, i.e. check the property SupportsBuildingMEXFuncs.
            lToolchainInfo = lXilCompInfo.ToolchainInfo;
            supportedToolchainOrTMF = ~isempty(lToolchainInfo) && ...
                lToolchainInfo.SupportsBuildingMEXFuncs;
            if ~supportedToolchainOrTMF
                if lIsToolchainBased
                    if isempty(lToolchainInfo)
                        %No toolchain from build process, use a default
                        %name.
                        rtw.pil.ProductInfo.error('pil', ...
                            'SILIncompatibleInvalidToolchain',...
                            model,...
                            portableWordSzMessage);
                    else
                        rtw.pil.ProductInfo.error('pil', ...
                            'SILIncompatibleToolchain',...
                            model,...
                            lToolchainInfo.Name, ...
                            portableWordSzMessage);
                    end
                else
                    rtw.pil.ProductInfo.error('pil', ...
                                              'SILIncompatibleTmf', ...
                                              model,...
                                              lXilCompInfo.TMFProperties.TemplateMakefile, ...
                                              portableWordSzMessage);
                end
            end                        
            
            % atomic sizes
            %
            % SIL scheduling is non-real-time => it doesn't matter what
            % atomic sizes are assumed in the generated code, it
            % won't alter results during SIL simulation.    
        end       
        
        function checkSilCompatibility(configInterface, isProtectedModel, isReadOnly, lXilCompInfo)
            
            % error out if some model settings are not compatible for
            % SIL/PIL
            rtw.connectivity.Utils.validateArg(configInterface,...
                'coder.connectivity.ConfigInterface');
            rtw.pil.ModelBlockPIL.checkModelForSILPILCompatibility(configInterface, isReadOnly);
            
            % determine if portable word sizes is enabled on the model
            isPortableWordSzEnabled =...
                strcmp(getParam(configInterface,'PortableWordSizes'),'on');
            
            if ~isPortableWordSzEnabled
                % check if the model is compatible with SIL and throw an
                % appropriate error message when it is not.
                rtw.pil.ModelBlockPIL.checkIfModelIsCompatibleWithSilNoPWS(...
                    configInterface, isProtectedModel, lXilCompInfo);
            end
            
            % Check that hardware configuration is compatible with host 
            % builds.           
            silHWImplValidator = rtw.pil.SILHWImplValidation(configInterface);
            silHWImplValidator.checkHostWordLengths;    % (Internally checks whether PWS is enabled)
            silHWImplValidator.checkHostImplProps;
            rtw.pil.SlHardwareConfigHelper.throwWarningOrErrors(silHWImplValidator, isReadOnly);
            
            % make sure word size maps to one of the integer sizes
            isPIL = false;   %   checkSilCompatibility is a SIL specific method
            rtw.pil.SlHardwareConfigHelper.checkWordSize(configInterface, isPIL, isReadOnly);
        end
                      
        function checkModelForSILPILCompatibility(configInterface, isReadOnly)
            %tmf RTW.MSVCBuild is not supported by SIL or PIL
            rtw.connectivity.Utils.validateArg(configInterface,...
                'coder.connectivity.ConfigInterface');
            xilComponent = configInterface.getCodeGenComponent;
            if strcmp(getParam(configInterface, 'TemplateMakefile'), 'RTW.MSVCBuild')
               rtw.pil.ProductInfo.error('pil', ...
                   'MSVCBuildTmfNotSupported', ...
                   xilComponent);
            end
                        
            % ert_shrlib.tlc is not supported by SIL/PIL because this
            % target builds a library and our generated makefile for SIL/PIL
            % does not support that.
            if strcmp(getParam(configInterface, 'SystemTargetFile'), 'ert_shrlib.tlc')
               rtw.pil.ProductInfo.error('pil', ...
                   'SharedLibraryTargetNotSupported', ...
                   xilComponent);
            end
            
            % autosar_adaptive.tlc (and derived targets) are not supported
            % by SIL/PIL because we do not generate all necessary artifacts
            % yet
            if ~matlab.internal.feature('SILMdlRefAUTOSARAdaptive') && ...
                    rtw.pil.ModelBlockPIL.isAUTOSARAdaptive(configInterface)                    
                rtw.pil.ProductInfo.error('pil', ...
                   'AUTOSARAdaptiveSilPilMode', ...
                   xilComponent);
            end
            
            % validate the current configuration supports Mat-file logging
            rtw.pil.ModelBlockPIL.validateMATfileLoggingSupport(configInterface, isReadOnly);
        end
        
        function standaloneDataLoggingValidation(xilComponent, isProtected)
            % With a Top Model code interface, ToWorkspace and Scope blocks
            % configured to log data can save the logged data to a MAT
            % file if MatFileLogging is enabled. If MatFileLogging is not
            % enabled, then these blocks will not log data in SIL/PIL mode.
            %
            % Verify that ToWorkspace or Scope blocks have supported
            % configurations and if an unsupported data logging combination
            % is present, issue warning or error.
            %
            % The equivalent checks get carried out during block diagram
            % compile for the model reference code interface.
            if ~isProtected
                % Default settings for find_system exclude
                % commented blocks and non-active variants.
                % That's consistent with what's needed here.
                %
                % The model will have been loaded when the config interface
                % was created.
                assert(bdIsLoaded(xilComponent), 'Model %s should be loaded', xilComponent);
                lMATFileLogging = strcmp(get_param(xilComponent, 'MatFileLogging'), 'on');
                if ~lMATFileLogging
                    if Simulink.internal.useFindSystemVariantsMatchFilter()
                        lToWorkspace = find_system(get_param(xilComponent, 'handle'), ...
                            'LookUnderMasks', 'all', ...
                            'MatchFilter', @Simulink.match.codeCompileVariants, ... % code compile variants only (post compilation)
                            'IncludeCommented', 'off', ...
                            'FirstResultOnly', 'on', ...  % stop on finding first ToWorkspace block
                            'BlockType', 'ToWorkspace');
                    else
                        lToWorkspace = find_system(get_param(xilComponent, 'handle'), ...
                            'LookUnderMasks', 'all', ...
                            'Variants', 'ActivePlusCodeVariants', ...
                            'IncludeCommented', 'off', ...
                            'BlockType', 'ToWorkspace');
                    end
                    if ~isempty(lToWorkspace)
                        rtw.pil.ModelBlockPIL.throwStandaloneDataLoggingExceptionOrWarning(xilComponent, 'ToWorkspace');
                    end
                    if Simulink.internal.useFindSystemVariantsMatchFilter()
                        lDataLoggingScopes = find_system(get_param(xilComponent, 'handle'), ...
                            'LookUnderMasks', 'all', ...
                            'MatchFilter', @Simulink.match.codeCompileVariants, ... % code compile variants only (post compilation)
                            'IncludeCommented', 'off', ...
                            'FirstResultOnly', 'on', ...  % stop on finding first ToWorkspace block
                            'BlockType', 'Scope', ...
                            'DataLogging', 'on');
                    else
                        lDataLoggingScopes = find_system(get_param(xilComponent, 'handle'), ...
                            'LookUnderMasks', 'all', ...
                            'Variants', 'ActivePlusCodeVariants', ...
                            'IncludeCommented', 'off', ...
                            'BlockType', 'Scope', ...
                            'DataLogging', 'on');
                    end
                    if ~isempty(lDataLoggingScopes)
                        rtw.pil.ModelBlockPIL.throwStandaloneDataLoggingExceptionOrWarning(xilComponent, 'Scope');
                    end
                end
            end
        end
        
        function throwStandaloneDataLoggingExceptionOrWarning(xilComponent, blockType)
            lDataLoggingDiagnosticSetting = get_param(xilComponent, 'ModelReferenceDataLoggingMessage');
            lMessageToIssue = message('PIL:pil:StandaloneDataLoggingNeedsMATFileLogging', xilComponent, blockType);
            % Need to check whether we should be
            % throwing an error.
            switch lDataLoggingDiagnosticSetting
                case 'error'
                    MException(lMessageToIssue).throw;
                case 'warning'
                    warning(lMessageToIssue);
                case 'none'
                    % Do nothing
                otherwise
                    assert(false, 'DataLoggingDiagnosticSetting must be error, warning, or none');
            end
        end
        
        function validateMATfileLoggingSupport(configInterface, isReadOnly)
            % Error out if mat-file logging and allocation function are
            % both active.
            % In this scenario data logging cannot be started because no
            % start-function description is available in codeInfo. 
            % SIL/PIL will replace mat-file logging, therefore we're not
            % going to support this combination of parameters.
            if strcmp(configInterface.getParam('MatFileLogging'), 'on') && ...
                strcmp(configInterface.getParam('GenerateAllocFcn'), 'on')
            
                readOnlyActionIDs = {};
                fullAccessActionsIds = {'MATFileLogging'};
                exception = coder.connectivity.AccessDependentDiagnostic(...
                    'PIL:pil:UnsupportedMATfileLogging', ...
                    readOnlyActionIDs, ...
                    fullAccessActionsIds,...
                    configInterface.getCodeGenComponent);
                exception.throw(isReadOnly);
            end
        end                
        
        function res = isReadOnly(isSubsystemWorkflow, isProtected)
            % return true if RLS or protected
            res = isSubsystemWorkflow || isProtected;
        end
        
        function dispPILConfigurationHyperlink(isPIL, isRLSorAtomic, topModel, configInterface, targetServices)
            if isPIL
                if isRLSorAtomic
                    harnessInfo = Simulink.harness.internal.getHarnessInfoForHarnessBD(topModel);
                    pilComponent = harnessInfo.ownerFullPath;
                else
                    pilComponent = configInterface.getCodeGenComponent;
                end
                link = targets_hyperlink_manager...
                    ('new', ...
                    targetServices.getName(), ...
                    @disp, ...
                    targetServices);
                
                disp(['### Connectivity configuration for component ' ...
                    '"' pilComponent '": ' link ' ###']);
            end
        end
        
        function [anchorDir, codeDir, codedescriptorDir, inTheLoopType] = getCodeDirsAndInTheLoopType(...
                codeGenComponent, xilModelBlock, isRLS, iMdl)

            % get the code generation folder and the InTheLoopType for the
            % current model block
            buildDirInfo = RTW.getBuildDir(codeGenComponent);
            anchorDir = buildDirInfo.CodeGenFolder;
            if isRLS % using RLS workflow
                % set codeDir to the component sub-folder (subsystem_context)
                harnessInfo = Simulink.harness.internal.getHarnessInfoForHarnessBD(iMdl);
                [codeDir, codedescriptorDir] = rtw.pil.RLSUtils.getRLSFoldersFromHarnessInfo(harnessInfo);
                inTheLoopType = rtw.pil.InTheLoopType.RLSSubsystem;
            else
                isModelReferenceTarget = ...
                    rtw.pil.ModelBlockPIL.isModelReferenceTarget(xilModelBlock);
                if isModelReferenceTarget
                    codeDir = fullfile(buildDirInfo.CodeGenFolder,...
                        buildDirInfo.ModelRefRelativeBuildDir);
                    inTheLoopType = rtw.pil.InTheLoopType.ModelBlock;
                else
                    codeDir = buildDirInfo.BuildDirectory;
                    inTheLoopType = rtw.pil.InTheLoopType.ModelBlockStandalone;
                end
                codedescriptorDir = codeDir;
            end
        end       
    end
end


function lXilCompInfo = i_getXilCompInfo(isSIL, cs, lMexCompInfoDefault)
isPws = strcmp(get_param(cs, 'PortableWordSizes'), 'on');
lIsSilAndPws = isSIL && isPws;

lXilCompInfo = coder.internal.utils.XilCompInfo.slCreateXilCompInfo...
    (cs, lMexCompInfoDefault, lIsSilAndPws);
end

function restoreObj = i_tagTopLevelBuildForATS(topModel, xilComponentModel)
    % tag models that are being build for the top-level ATS workflow and
    % return an on cleanup object
    restoreObj = [];
    if coder.connectivity.XILSubsystemUtils.isAtomicSubsystem(topModel)
        xilParamName = 'XILComponentBuildForATS';
        origTag = get_param(xilComponentModel, xilParamName);
        set_param(xilComponentModel, xilParamName, 'on');
        restoreObj = onCleanup(@() set_param(xilComponentModel, xilParamName, origTag));
    end
end

% LocalWords:  pil Configs configs cgv TMF Tmf tmf vc vcx lcc Endianess sil maca SILGRT
% LocalWords:  SILPIL TMF's
