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

%   Copyright 2009-2020 The MathWorks, Inc.

    properties (SetAccess = 'private', GetAccess = 'private')
        RunnableData; % struct array of runnable symbol / name mapping
        autosarSchemaVersion;
        ProcessedRTEDataInterfaces;
        IsMultiInstance;        
        IsCAPILoggingEnabled;
        SenderReceiverInfo;
        RTEInstActualArgName;
        RTMpointerIdentifier;
    end

    properties (Access = 'private', Constant)
        RTEInstanceArg = [AUTOSAR.CSC.getRTEInstanceType() ' ' AUTOSAR.CSC.getRTEInstanceName()];        
    end
    
    methods
        % constructor
        function this = AutosarTargetInterface(silPILWrapperUtils, ...
                targetInterfacePath)
            narginchk(2, 2);
            % call super class constructor
            this@rtw.pil.StandaloneTargetInterface(silPILWrapperUtils, ...
                targetInterfacePath);                   

            % initialize
            infoStruct = this.SILPILWrapperUtils.getInfoStruct;
            periodicRunnableData = infoStruct.GeneralDataFromTLC.AutosarPeriodicRunnables;
            if iscell(periodicRunnableData)
                periodicRunnableData = [periodicRunnableData{:}];
            end
            initRunnableData  = infoStruct.GeneralDataFromTLC.AutosarInitRunnable;
            termRunnableData = [];
            if isfield(infoStruct.GeneralDataFromTLC, 'AutosarTermRunnable')
                termRunnableData  = infoStruct.GeneralDataFromTLC.AutosarTermRunnable;
            end
            this.RunnableData = [initRunnableData, ...
                                 periodicRunnableData, ...
                                 termRunnableData];
            this.autosarSchemaVersion = silPILWrapperUtils.getParam('AutosarSchemaVersion');                       
            this.IsMultiInstance =  strcmp(this.SILPILWrapperUtils.getParam(...
                'CodeInterfacePackaging'), 'Reusable function');
            % cache a flag to check if the CAPI is enabled
            this.IsCAPILoggingEnabled = silPILWrapperUtils.getClientInterface.isSignalLoggingEnabled( ...
                silPILWrapperUtils, silPILWrapperUtils.getSILPILInterface.getInTheLoopType) && ...
                ~coder.internal.connectivity.featureOn('UseMF0CodeDescriptorLogging');
            this.SenderReceiverInfo = this.getSenderReceiverInfo;

            % cache the actual argument name of the RTE instance
            this.cacheRteInstanceActualArgName;
        end               
        
        
        function expression = getExpression(this, implementation)
            switch this.codeInfoUtils.class(implementation)
                case 'AutosarSenderReceiver'
                    rteID = this.getRteID(implementation);
                    % add xsd - XIL static data                    
                    expression = ['xsd_' rteID];
                case 'AutosarErrorStatus'       
                    rteID = this.getRteID(implementation);                    
                    % add xsd - XIL static data
                    % add err - error status
                    expression = ['xsd_' rteID '_err'];
                case 'AutosarIsUpdated'
                    rteID = this.getRteID(implementation);                    
                    % add xsd - XIL static data
                    % add updated - is updated flag
                    expression = ['xsd_' rteID '_updated'];
                case 'AutosarInterRunnable'
                    rteID = this.getRteID(implementation);
                    % add xsd - XIL static data                    
                    expression = ['xsd_' rteID];
                case 'Variable'
                    % call superclass
                    expression = getExpression@rtw.pil.StandaloneTargetInterface(this, implementation);
                case 'StructExpression'
                    [isMultiInstanceInternalPIM, storageImplementation] = ...
                        this.isMultiInstanceInternalPIM(implementation);
                    if isMultiInstanceInternalPIM
                        % special handling for e.g. rtP, which is defined
                        % in the RTE along with other PIMs
                        %
                        % construct, e.g. (Rte_Pim_Reproducer_P(self)->C)
                        % where C is a parameter stored in the rtP
                        rteID = storageImplementation.Identifier;
                        % getExpression will return e.g.
                        % "Reproducer_P.C"
                        structExpr = implementation.getExpression;
                        % strip off the rteID to get hold of the
                        % field name
                        fieldName = regexprep(structExpr, [rteID '\.'], '');
                        expression = sprintf('(Rte_Pim_%s(%s)->%s)', ...
                            rteID, ...
                            this.RTEInstActualArgName, ...
                            fieldName);
                    else
                        % call superclass
                        expression = getExpression@rtw.pil.StandaloneTargetInterface(this, implementation);   
                    end                                        
                case 'AutosarCalibration'
                    rteID = this.getRteID(implementation);
                    switch implementation.DataAccessMode
                        case 'Calibration'
                            if AR4.isCompatibleSchemaVer( ...
                                    this.autosarSchemaVersion)
                                expression = ['Rte_Prm_' implementation.Port '_' rteID '_data'];
                            else
                                expression = ['Rte_Calprm_' implementation.Port '_' rteID '_data'];        
                            end
                        case 'InternalCalPrm'
                            expression = ['Rte_CData_' rteID '_data'];
                        otherwise
                            assert(false, 'Unexpected DataAccessMode: %s', implementation.DataAccessMode);
                    end                                                            
                case 'AutosarMemoryExpression'
                    rteID = this.getRteID(implementation);
                    % go through Rte API defined in Rte_PIM.c
                    % Check if implementation is an array and if pass
                    % by pointer to base type.
                    [~, ~, isArTyped] = this.isPIMCSC(implementation);
                    if this.IsMultiInstance
                        selfArg = this.RTEInstActualArgName;
                    else
                        selfArg = '';
                    end
                    expression = sprintf('(Rte_Pim_%s(%s))', rteID, selfArg);
                    % dereference if necessary
                    if ~(isArTyped && ...
                            rtw.connectivity.CodeInfoTypeUtils.isaType(implementation.Type, 'Matrix'))
                        expression = sprintf('(*%s)', expression);
                    end
                otherwise
                    % call superclass
                    expression = getExpression@rtw.pil.StandaloneTargetInterface(this, implementation);
            end                              
        end   
    end
    
    methods (Static = true)                        
        function sharedValidation(configInterface)
            rtw.connectivity.Utils.validateArg(configInterface, 'coder.connectivity.ConfigInterface');
            % get root model
            componentPath = configInterface.getCodeGenComponent;
            rootModel = strtok(componentPath, '/');
            % if AutosarCompliant, apply various checks
            if strcmp(getParam(configInterface, 'AutosarCompliant'), 'on')
                isProtected = slInternal('getReferencedModelFileInformation', rootModel);
                if isProtected                    
                    % configInterface should be of type 
                    % coder.connectivity.SimulinkCoderConfig for protected 
                    % models.
                    rtw.connectivity.Utils.validateArg(configInterface, 'coder.connectivity.SimulinkCoderConfig');
                    % use serialized information in binfo.
                    infoStruct = configInterface.getInfoStruct;
                    isAutosarRTEHeaderFileGenerationEnabled =...
                        infoStruct.IsAutosarRTEHeaderFileGenerationEnabled;
                    configSubsystems = infoStruct.AUTOSARInvokeOperationConfigSubsystems;
                else
                    isAutosarRTEHeaderFileGenerationEnabled = true;
                    configSubsystems =...
                        rtw.pil.AutosarTargetInterface.findAUTOSARInvokeOperationConfigSubsystems(rootModel);
                end
                
                % make sure required header files will be generated
                if ~isAutosarRTEHeaderFileGenerationEnabled
                    rtw.pil.ProductInfo.error('pil', ...
                        'AutosarRTEHeaderFileGeneration', ...
                        rootModel, rootModel);
                end
                
                % check for client block configurable subsystems in
                % simulation mode                
                if ~isempty(configSubsystems)
                    rtw.pil.AutosarTargetInterface.throwAUTOSARClientBlockError(componentPath, configSubsystems);
                end
            end
        end
        
        function configSubsystems = findAUTOSARInvokeOperationConfigSubsystems(modelName)
            configSubsystems = arblk.findOperationConfigurableSubsystems(modelName);
            matches = regexp(get_param(configSubsystems, 'BlockChoice'), '\(simulation\)$', 'once', 'match');
            % translate to logical
            matchIndices = strcmp(matches, '(simulation)');
            configSubsystems = configSubsystems(matchIndices);
        end
    end
    
    methods (Static = true, Access = 'private')
        function throwAUTOSARClientBlockError(iMdl, clientBlocks)
            assert(~isempty(clientBlocks), 'clientBlocks is unexpectedly empty.');
            clientBlocksStr = '';
            for i=1:length(clientBlocks)
                clientBlocksStr = [clientBlocksStr '"' clientBlocks{i} '"' newline];  %#ok<AGROW>
            end
            rtw.pil.ProductInfo.error('pil', ...
                'AutosarClientBlocks', ...
                iMdl, ...
                clientBlocksStr);
        end
             
        function isSelf = isRteInstance(implementation)
            % return true if the implementation is the Rte instance variable
            isSelf = strcmp(implementation.Type.Identifier, AUTOSAR.CSC.getRTEInstanceType);
        end
    end
        
    methods (Access = 'protected')
        
        
        function writeSectionStorageHook(this)                                  
            % allocate internal data
            if this.IsMultiInstance && this.IsCAPILoggingEnabled
                % For multi-instance code with CAPI enabled the RTM variable 
                % is already defined in the RTE_model.c stub file.
                % We only define the RTM pointer that will be initialized 
                % later using the stub function Rte_Pim_model_M(self)
                
                allInternalData = this.codeInfo.InternalData;
                allInternalDataLength = numel(allInternalData);
                internalDataIdxToKeep = 1:allInternalDataLength;
                for idx = 1:allInternalDataLength
                    currData = allInternalData(idx);
                    % if current data is the RTM pointer
                    if strcmp('RTModel', currData.GraphicalName)
                        impl = currData.Implementation;
                        assert(this.codeInfoUtils.isa(impl, 'PointerVariable'),...
                                'RTModel should be a pointer variable');
                        % remove it from the list of data to keep
                        internalDataIdxToKeep(idx) = [];
                        % define RTM pointer, but not initialize it.
                        this.writer.writeLine('%s * %s;', ...
                            impl.Type.BaseType.Identifier, impl.Identifier);
                        % cache the identifier
                        this.RTMpointerIdentifier = impl.Identifier;
                    end
                end
                % create required data interfaces and call super class method
                dataInterfaces = rtw.pil.CodeInfoData.createDataInterfaces( ...
                    this.SILPILWrapperUtils, ...
                    internalDataIdxToKeep, 'internal');
                this.writeSectionStorageForData(dataInterfaces);
            else
                % allocate storage for all internal data
                this.writeSectionStorageInternalData;
            end
            
        end
        
        
        function  writeRTMPointerAssigmentHook(this)
            % Initialize the RTM pointer using the stub function 
            % Rte_Pim_model_M(self). This is needed because the RTM
            % instance in used to initialize the CAPI DataMapInfo structure.
            if this.IsMultiInstance && this.IsCAPILoggingEnabled
                assert(~isempty(this.RTMpointerIdentifier), 'RTM identifier must not be empty');
                this.writer.writeLine('%s = Rte_Pim_%s(%s);', ...
                    this.RTMpointerIdentifier, ...
                    this.RTMpointerIdentifier, ...
                    this.RTEInstActualArgName);
            end
        end
        
        function indices = getInportIndicesForStorage(this)
            % see g1248994 - filter SenderReceiver Inports based on 
            % DirectReads otherwise we may define data that doesn't have a 
            % typedef in Rte_Type.h
            
            % find all direct reads across all runnables
            runnables = this.getRunnables;
            directReads = [];
            for runnableIdx = 1:length(runnables)
               runnable = runnables(runnableIdx);
               directReads = ...
                   [directReads, this.codeInfoUtils.getDirectReadsForRunnable(runnable)];                               %#ok<AGROW>
            end
            % for each Inport, see if it has a direct read or is an error
            % status; don't filter error status ports
            indices = [];
            for idx = 1:length(this.codeInfo.Inports)
                inport = this.codeInfo.Inports(idx);
                if this.isMemberPresent(inport, directReads) || ...
                        ~this.codeInfoUtils.isa(inport.Implementation, 'AutosarSenderReceiver') 
                    indices(end+1) = idx; %#ok<AGROW>
                end
            end            
        end        
        
        function writeSectionUserFunctions(this) 
            % Generate RTE function definitions
            
            % assumptions about RTE generation
            %
            % AUTOSAR Target does not allow:
            %
            % - non-auto storage classes at I/O boundary        
            
            this.writer.writeLine('/* AUTOSAR RTE Implementation - see Rte_SWC.h */');          
            
            % filter duplicates
            this.ProcessedRTEDataInterfaces = [];                        
            % for each runnable, process all required RTE Functions
            runnables = this.getRunnables;
            for runnableIdx = 1:length(runnables)
               runnable = runnables(runnableIdx);
               this.writeRTEFunctions(runnable);
            end
        end
        
        function callerFunctionsInfo = getCallerFunctionsInfo(this)
            % return all the caller functions, both defined and undefined.
            %
            % For defined caller functions, SIL/PIL needs to provide the
            % implementation of the client call Rte_Call_<PortName>_<operation>
            % by simply calling the server on the target.
            %
            % For undefined caller functions, SIL/PIL needs to provide the
            % implementation by going back to Simulink and requesting the
            % service.
            callerFunctionsInfo = this.getCodeInfoUtils.getAllCallerFunctionsInfo;
        end

        function writeSectionCallerFunctionBody(this, callerFunctionInfo)
            if ~callerFunctionInfo.getIsDefined
                % server is not on the target, request service from Simulink
                writeSectionCallerFunctionBody@rtw.pil.TargetInterface(this, callerFunctionInfo);
            else
                % server is on the target, call it
                this.writer.writeLine([this.getFunctionCallExpressionForServerFunction(callerFunctionInfo) ';']);
            end
        end
             
        function writeSectionIncludes(this)
            % call super class method
            writeSectionIncludes@rtw.pil.TargetInterface(this);            
            
            % Include Software Component Header to ensure RTE_Types.h etc
            % are available (G1153091, moved here to fix G1338172)
            rteSWCName = this.getRTESWCHeader;
            
            this.writer.wLine('#include "%s"',rteSWCName);           
        end
        
        function writeSectionExternalInitialization(this) 
            % call super class method
            writeSectionExternalInitialization@rtw.pil.TargetInterface(this);
                                                    
            % additionally, process all IRV's in InternalData            
            allInternalData = this.codeInfo.InternalData;
            for i=1:length(allInternalData)
                impl = allInternalData(i).Implementation;
                if this.codeInfoUtils.isa(impl, 'AutosarInterRunnable')
                    dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                                 i, 'internal');
                    this.writer.writeLine('/* initialize IRV storage owned by XIL */');                                                                       
                    this.writeDataInterfaceDefaultInitialization(dataElements);    
                end                
            end      

            % additionally, initialize NVRAM PIM global and local data stores
            % via the X_Init_Pim_* functions generated into Rte_X_PIM.c
            dataStores = this.codeInfo.DataStores;
            rteSWCHeaderContent = [];
            for diIdx = 1:length(dataStores)
                ds = dataStores(diIdx);
                implementation = ds.Implementation;
                % limit scope to RTW.AutosarMemoryExpression
                if this.codeInfoUtils.isa(implementation, 'AutosarMemoryExpression')
                    [isPIMCSC, isNVRAM, ~] = this.isPIMCSC(implementation);
                    if isPIMCSC && isNVRAM       
                        if isempty(rteSWCHeaderContent)
                            % only read the RTE header content once
                            rteSWCName = this.getRTESWCHeader;
                            rteDir = autosar.mm.mm2rte.RTEGenerator.getRTEFilesFolder(...
                                this.SILPILWrapperUtils.getSILPILInterface.getCodeDir);
                            rteSWCHeader = fullfile(rteDir, rteSWCName);
                            rteSWCHeaderContent = fileread(rteSWCHeader);
                        end                        
                        rteID = this.getRteID(implementation);
                        initFcnName = sprintf('%s_Init_Pim_%s', ...
                             this.codeInfo.GraphicalPath, ...
                             rteID);
                        % g1865385 - check for existence of PIM
                        % initialization function in the RTE
                        if ~contains(rteSWCHeaderContent, initFcnName)
                            componentPath = this.SILPILWrapperUtils.getSILPILInterface.getFullComponentPath;
                            diString = this.codeInfoUtils.getDataInterfaceString(ds, 'datastore');
                            DAStudio.error('PIL:pil:AUTOSARMappedNVRAMPIM', ...
                                componentPath, diString);
                        end
                        
                        this.writer.writeLine('/* initialize NVRAM PIM data store */');
                        if this.IsMultiInstance
                            arg = this.RTEInstActualArgName;
                        else
                            arg = '';
                        end                        
                        
                        initFcnCall = sprintf('%s(%s);', initFcnName, arg);
                        this.writer.writeLine(initFcnCall);
                    end
                end
            end
        end                                                                                                                                              
        
        function [inputType, baseRteType] = getCallerInputArgType(this,...
                argInfo, argDataInterface, argCData) %#ok<INUSD>

            useOneDArray = true;
            [baseRteType, ~, rteType, usePointerIO] =...
                this.getRTEFunctionArgInfo(argDataInterface, useOneDArray);
            % process input arg
            if usePointerIO
                passByPtr2BaseType = this.arrayReferenceByPtr2BaseType();
                if argInfo.IsVector && passByPtr2BaseType
                    % pointer to array base type
                    inputType = [baseRteType '*'];
                else
                    % pointer to array type
                    inputType = [rteType '*'];
                end
            else
                inputType = rteType;
            end

            % add const qualifier
            if AR4.isCompatibleSchemaVer(this.autosarSchemaVersion)
                if (argInfo.IsInArg) && (argInfo.IsVector || argInfo.IsBus)
                    inputType = ['const ' inputType];
                else
                    % Autosar only supports scalar-by-pointer for Simulink Function FPC
                    % - no qualifier control
                    argImplType = argInfo.Implementation.Type;
                    if argImplType.isPointer && argImplType.BaseType.ReadOnly
                        inputType = ['const ' inputType];
                    end
                end
            end


        end
        
        function [outputType, baseRteType] = getCallerOutputArgType(this,...
                argInfo, argDataInterface, argCData) %#ok<INUSD>
            useOneDArray = true;
            [baseRteType, ~, rteType] =...
                this.getRTEFunctionArgInfo(argDataInterface, useOneDArray);
            % process output arg
            passByPtr2BaseType = this.arrayReferenceByPtr2BaseType();
            if argInfo.IsVector && passByPtr2BaseType
                % pointer to array base type
                outputType = [baseRteType '*'];                
            else
                outputType = [rteType '*'];
            end           
        end
        
        function storageSizeStr = getCallerArgStorageSizeString(this, argInfo) %#ok<INUSD>
            % return the storage size string for multidimensional signals.             
            storageSizeStr = '';
        end
        
        function returnArgType = getCallerFunctionReturnArgType(this, callerFunctionInfo)
            if callerFunctionInfo.getIsAUTOSAR
                returnArgType = 'Std_ReturnType';
            else
                % call the superclass method if the call point is in a
                % referenced model (i.e. non AUTOSAR code)
                returnArgType = getCallerFunctionReturnArgType@rtw.pil.TargetInterface( ...
                    this, callerFunctionInfo);
            end
        end
        
        function [inputAddressValue, outputAddressValue] =....
                getCallerInputArgMemcpyInAndOutAddressValues(this,...
                argInfo, argDataInterface, argCData)
            
            [inputAddressValue, outputAddressValue] = ...
                getCallerInputArgMemcpyInAndOutAddressValues@rtw.pil.StandaloneTargetInterface( ...
                    this,argInfo, argDataInterface, argCData);
                
            if(~this.arrayReferenceByPtr2BaseType())
                % vectors are passed by pointer to array in AR3.x
                if( argInfo.IsVector || this.isArrSizeOne(argInfo) )
                    % arg is a pointer to array so de-ref 
                    if(this.isArrSizeOne(argInfo))
                        derefExpr = ['(*',argInfo.ArgName,')'];
                    else
                        derefExpr = ['(*',argInfo.ArgName,')'];
                    end
                    inputAddressValue = strrep(inputAddressValue, ...
                        argInfo.ArgName, derefExpr);
                end
            end
        end
        
        function [inputAddressValue, outputAddressValue] =....
                getCallerOutputArgMemcpyInAndOutAddressValues(this,...
                argInfo, argDataInterface, argCData)
            
            [inputAddressValue, outputAddressValue] = ...
                getCallerOutputArgMemcpyInAndOutAddressValues@rtw.pil.StandaloneTargetInterface( ...
                    this,argInfo, argDataInterface, argCData);
                
            if(~this.arrayReferenceByPtr2BaseType())
                % vectors are passed by pointer to array in AR3.x
                if( argInfo.IsVector || this.isArrSizeOne(argInfo) )
                    % arg is a pointer to array so de-ref 
                    if(this.isArrSizeOne(argInfo))
                        derefExpr = ['(*',argInfo.ArgName,')'];
                    else
                        derefExpr = ['(*',argInfo.ArgName,')'];
                    end
                    inputAddressValue = strrep(inputAddressValue, ...
                        argInfo.ArgName, derefExpr);
                end
            end
        end
        
        function writeSectionCallerFunctionTrailerReturnArg(this, callerFunctionInfo)
            if callerFunctionInfo.isLegacyAUTOSARClientBlock
                % old client block always expects status return
                this.writer.wLine('return 0;');
            else % new caller block
                if callerFunctionInfo.hasReturnArg
                    % call super-class method
                    writeSectionCallerFunctionTrailerReturnArg@rtw.pil.TargetInterface(this, callerFunctionInfo);
                elseif callerFunctionInfo.getIsAUTOSAR
                    this.writer.wLine('return 0;');
                else
                    % Empty else - no return
                end
            end
        end
        
        function writeRTEFunctions(this, runnable) 
            % create RTE functions for all DirectWrites
            isRead = false;
            directWrites = this.codeInfoUtils.getDirectWritesForRunnable(runnable);
            this.writeRTEReadOrWriteFunctions(runnable, ...
                                              directWrites, ...
                                              isRead);                                              
            % create RTE functions for all DirectReads                                                      
            isRead = true;
            directReads = this.codeInfoUtils.getDirectReadsForRunnable(runnable);
            this.writeRTEReadOrWriteFunctions(runnable, ...
                                              directReads, ...
                                              isRead);            
        end
            
        function [baseRteType, ...
                  storageSize, ...
                  rteType, ...
                  usePointerIO, ...
                  dataElements] = getRTEFunctionArgInfo(this, dataInterface, useOneDArray)
            % create dataElements containing contiguous elements of the
            % current data interface
            dataElements = rtw.pil.CodeInfoData.createUnspecifiedDataElements(dataInterface, ...
                                                              this.SILPILWrapperUtils);
            % first element of has all the info we
            % need
            cData = dataElements(1);
            if cData.isStructElement
                [baseRteType, storageSize] = cData.getStructArrayTopLevelInfo;
            elseif cData.IsEnum && ...
                    ~isempty(cData.getType.StorageType)
                baseRteType = cData.HostCType;
                storageSize.Numerical = cData.getNumElements;
                storageSize.Symbolic = cData.getSymbolicNumElements;
                storageSize.SymbolicDims = cData.getSymbolicDimensions;
            else
                assert(isscalar(dataElements), 'Non-bus or struct type must have scalar contiguous data representation');
                storageSize.Numerical = cData.getNumElements;
                storageSize.Symbolic = cData.getSymbolicNumElements;
                storageSize.SymbolicDims = cData.getSymbolicDimensions;
                % body
                if storageSize.Numerical == 1 && ~this.isArrSizeOne(dataInterface.Implementation.Type)
                    baseStorageType = cData.getType;
                    baseRteType = baseStorageType.Identifier;
                else
                    % for array types, pass through any aliases to base MathWorks types
                    baseRteType = rtw.connectivity.TypeUtils.builtinToHostCType(cData.getDtypeId);
                    baseRteType = strrep(baseRteType, 'real64_T', 'real_T');
                end
            end
            
            if storageSize.Numerical == 1 && ...
                    ~dataInterface.Implementation.Type.isPointer && ... % scalar pass-by-pointer case
                    ~this.isArrSizeOne(dataInterface.Implementation.Type)
                rteType = baseRteType;
            else
                if this.arrayReferenceByPtr2BaseType
                    rteType = baseRteType;
                else
                    % array type names for pre 4.x:
                    %
                    % A future improvement would be to get the type name
                    % from the M3iModel. For now, we have 2 options: 
                    % 1) Re-calculate the type name as mm2sl.TypeBuilder 
                    %    does (maybe refactor the TypeBuilder code so we 
                    %    can call it from AutosarTargetInterface) but this 
                    %    is too difficult as TypeBuilder has states. Also,
                    %    this is not inline with our end goal by generating
                    %    the RTE files (including the implementation) from
                    %    the M3iModel mm2rte.
                    % 
                    % 2) mm2rte will continue to generate the 
                    %    Rte_rt_Array__* typedef so xil_interface.c can use
                    %    it. This option was the easiest to implement until
                    %    we can get all the RTE functions in
                    %    xil_interface.c generated by the RTE generator.       
                    numDims = length(storageSize.SymbolicDims);
                    % check for 1xA or Ax1 which collapses to A
                    isVector = numDims == 2 && ...
                            (strcmp(storageSize.SymbolicDims{1}, '1') || ...
                            strcmp(storageSize.SymbolicDims{2}, '1'));                                        
                    if useOneDArray || isVector                            
                        dimsString = storageSize.Symbolic;
                    else
                        dimsString = storageSize.SymbolicDims{1};
                        if numDims > 1
                            for dimIdx = 2:numDims
                                dimsString = sprintf('%sx%s', dimsString, storageSize.SymbolicDims{dimIdx});
                            end
                        end
                    end
                    rteType = ['Rte_rt_Array__' baseRteType '_' dimsString];
                    maxShortNameLength = this.SILPILWrapperUtils.getParam('AutosarMaxShortNameLength');
                    if length(rteType) > maxShortNameLength
                        rteType = arxml.arxml_private('p_create_aridentifier', rteType, maxShortNameLength);
                    end
                end
            end
            
            % finally, handle type replacement
            rteType = this.codeInfoUtils.getReplacedTypeIdentifier(rteType);
            baseRteType = this.codeInfoUtils.getReplacedTypeIdentifier(baseRteType);
            
            % determine whether to use pointer arguments in RTE functions
            usePointerIO = ~(storageSize.Numerical == 1 && ~cData.isStructElement) || ...
                           dataInterface.Implementation.Type.isPointer || ... % scalar pass-by-pointer case
                           this.isArrSizeOne(dataInterface.Implementation.Type);
        end
        
        function writeRTEReadOrWriteFunctions(this, ...
                                              runnable, ...
                                              dataInterfaces, ...
                                              isRead) 

            for i=1:length(dataInterfaces)
                dataInterface = dataInterfaces(i);                                                                          
                implementation = dataInterface.Implementation;
                if (isempty(implementation) && ~isempty(dataInterface.VariantInfo))
                    continue;
                end
                
                assert(implementation == rtw.connectivity.CodeInfoTypeUtils.getStorageImplementation(implementation), ...
                       'Data interface implementation must equal storage implementation');
                if rtw.pil.CodeInfoData.isInterfaceActive(dataInterface)
                    % if the interface is active
                    switch this.codeInfoUtils.class(implementation)
                        case {'AutosarSenderReceiver', 'AutosarInterRunnable'}
                            % ok
                        case 'AutosarErrorStatus'
                            % skip - processed when corresponding Receiver is
                            % processed
                            continue;  
                        case 'AutosarIsUpdated'
                            % skip - processed when corresponding Receiver is
                            % processed
                            continue;  
                        case 'AutosarCalibration'
                            % skip - RTE function defined in calib.c stub file
                            continue;
                        case 'Variable'
                            % A Non-PIM DSM can have an RTW.Variable
                            % implementation.
                            assert(this.codeInfoUtils.isDataStore(dataInterface), ...
                                   'Found DirectRead/DirectWrite RTW.Variable not associated with a Data Store');
                            continue;
                        case 'AutosarMemoryExpression'
                            % g644749: PIM DSMs are recorded in DirectReads only.
                            %          Non-PIM DSMs are not recorded in
                            %          DirectReads or DirectWrites.                        
                            assert(this.codeInfoUtils.isDataStore(dataInterface), ...
                                'Found DirectRead/DirectWrite AutosarMemoryExpression not associated with a Data Store');
                            isPIMCSC = this.isPIMCSC(implementation);
                            assert(isPIMCSC, ...
                                'Found DirectRead/DirectWrite Data Store with AutosarMemoryExpression but no PIM CSC');                        
                            % PIM Global or Local Data Store 
                            % (either NVRAM or none NVRAM)
                            %
                            % Global DS are supported for data
                            % transfer, which also initializes them.
                            %
                            % None NVRAM Local DS internally to the component
                            % are ok - non-zero initialization (including default
                            % values for enums and fixed point types with
                            % bias) will be generated into the model
                            % initialize function.
                            %
                            % NVRAM global and local datastores will be 
                            % initialized via the X_Init_Pim_* functions (see
                            % writeSectionExternalInitialization)
                            %
                            % PerInstanceMemory already defined in Rte_PIM.c
                            continue;
                        otherwise
                            assert(false, 'Unexpected implementation class: %s', class(implementation));
                    end

                    switch implementation.DataAccessMode   
                        case {'ExplicitReceive', 'QueuedExplicitReceive', ...
                              'ExplicitSend', 'ModeReceive', ...
                              'ExplicitReceiveByVal', 'ModeSend', ...
                              'QueuedExplicitSend'}
                            % one RTE function for multiple runnables (I.e. 
                            % function name does not depend on runnable) so we 
                            % must filter duplicates                         
                            if this.isMemberPresent(dataInterface, this.ProcessedRTEDataInterfaces)
                                % already processed
                                continue;
                            end  
                            % extend list of processed data interfaces
                            if isempty(this.ProcessedRTEDataInterfaces)
                                this.ProcessedRTEDataInterfaces = dataInterface;
                            else                
                                this.ProcessedRTEDataInterfaces(end+1) = dataInterface;
                            end                                                        
                        case {'ImplicitReceive', 'ImplicitSend', ...
                              'ImplicitInterRunnable', 'ExplicitInterRunnable', ...
                              'ImplicitSendByRef'}
                            % one RTE function per runnable (I.e. function name 
                            % depends on runnable) so no need to check for
                            % duplicates                         
                        otherwise
                            assert(false, 'Unexpected DataAccessMode: %s', implementation.DataAccessMode);
                    end                

                    useOneDArray = strcmp(this.SILPILWrapperUtils.getParam('AutosarMatrixIOAsArray'), 'on');
                    [baseRteType, storageSize, rteType, usePointerIO] = this.getRTEFunctionArgInfo(dataInterface, useOneDArray);

                    switch implementation.DataAccessMode                   
                        case 'ImplicitReceive'
                            this.writeSectionImplicitReceive(runnable, ...
                                                             usePointerIO, ...
                                                             storageSize, ...
                                                             rteType, ...
                                                             baseRteType, ...
                                                             implementation);                        
                        case 'ImplicitSend'
                            this.writeSectionImplicitSend(runnable, ...
                                                          usePointerIO, ...
                                                          storageSize, ...
                                                          rteType, ...
                                                          baseRteType, ...
                                                          implementation);
                        case 'ImplicitSendByRef'
                            this.writeSectionImplicitSendByRef(runnable, ...
                                                               storageSize, ...
                                                               rteType, ...
                                                               baseRteType, ...
                                                               implementation);
                        case 'ExplicitReceive'
                            this.writeSectionExplicitReceive(storageSize, ...
                                                             rteType, ...
                                                             baseRteType, ...
                                                             implementation)
                        case 'ExplicitReceiveByVal'
                            this.writeSectionExplicitReceiveByVal(usePointerIO, ...
                                                                  storageSize, ...
                                                                  rteType, ...
                                                                  baseRteType, ...
                                                                  implementation)                        
                        case 'QueuedExplicitReceive'
                            this.writeSectionQueuedExplicitReceive(...
                                                         storageSize, ...
                                                         rteType, ...
                                                         baseRteType, ...
                                                         implementation)
                        case 'ExplicitSend'
                            this.writeSectionExplicitSend(usePointerIO, ...
                                                          storageSize, ...
                                                          rteType, ...
                                                          baseRteType, ...
                                                          implementation);
                        case 'ImplicitInterRunnable'
                            % create both IRV IRead and IWrite functions
                            if isRead
                                this.writeSectionIRVRead(runnable, ...
                                                         usePointerIO, ...
                                                         storageSize, ...
                                                         rteType, ...
                                                         baseRteType, ...
                                                         implementation,...
                                                         true);
                            else                                                     
                                this.writeSectionIRVWrite(runnable, ...
                                                          usePointerIO, ...
                                                          storageSize, ...
                                                          rteType, ...
                                                          baseRteType, ...
                                                          implementation,...
                                                          true);
                            end
                        case 'ExplicitInterRunnable'
                            % create both IRV Read and Write functions
                            if isRead && ~usePointerIO
                                this.writeSectionIRVRead(runnable, ...
                                                         usePointerIO, ...
                                                         storageSize, ...
                                                         rteType, ...
                                                         baseRteType, ...
                                                         implementation,...
                                                         false);
                            elseif isRead
                                this.writeSectionIRVExplicitReadNonScalar(...
                                                         runnable, ...
                                                         storageSize, ...
                                                         rteType, ...
                                                         baseRteType, ...
                                                         implementation);
                            else                                                     
                                this.writeSectionIRVWrite(runnable, ...
                                                          usePointerIO, ...
                                                          storageSize, ...
                                                          rteType, ...
                                                          baseRteType, ...
                                                          implementation,...
                                                          false);                        
                            end
                        case 'ModeReceive'
                            this.writeSectionModeReceive(usePointerIO, ...
                                                         rteType, ...
                                                         implementation);

                        case 'ModeSend'
                            this.writeSectionModeSend(usePointerIO, ...
                                                      storageSize, ...
                                                      rteType, ...
                                                      baseRteType, ...                                                  
                                                      implementation);
                        otherwise
                            assert(false, 'Unexpected DataAccessMode: %s', implementation.DataAccessMode);
                    end
                    this.writer.newLine;
                end
            end                       
        end                
        
        % override super class method
        function hasOnlyLocalAccess = hasOnlyLocalAccess(this, dataInterface)
            switch this.codeInfoUtils.class(dataInterface.Implementation)
                case {'AutosarSenderReceiver' ,...
                        'AutosarErrorStatus' ,...
                        'AutosarIsUpdated', ...
                        'AutosarInterRunnable'}
                    % data is not directly accessed outside of
                    % xil_interface.c/cpp
                    hasOnlyLocalAccess = true;
                otherwise
                    if this.codeInfoUtils.isa(dataInterface.Implementation, 'Variable') && ...
                            this.isRteInstance(dataInterface.Implementation)
                        % the Rte instance is not directly accessed outside
                        % of xil_interface.c/cpp
                        hasOnlyLocalAccess = true;
                    else
                        % call super class method
                        hasOnlyLocalAccess = hasOnlyLocalAccess@rtw.pil.TargetInterface(this, dataInterface);  %#ok<NODEF>
                    end
            end
        end
        
        % return the "base" storage implementation for a root implementation
        function storageImplementation = getBaseStorageImplementation(this, implementation)
            implementationClass = this.codeInfoUtils.class(implementation);
            switch implementationClass               
                case {'AutosarSenderReceiver', ...
                      'AutosarErrorStatus', ...
                      'AutosarIsUpdated', ...
                      'AutosarInterRunnable', ...
                      'AutosarCalibration'}
                    storageImplementation = implementation;                 
                otherwise
                    % call super class
                    storageImplementation = getBaseStorageImplementation@rtw.pil.StandaloneTargetInterface(this, implementation);
            end                        
        end                
        
        function isDef = isDefined(this, implementation)            
            switch this.codeInfoUtils.class(implementation)                
                case {'AutosarCalibration'}
                    % Always owned by calib.c stub file                    
                    isDef = true;
                case {'AutosarSenderReceiver'}
                    owner = this.getImplementationOwner(implementation);
                    if isempty(owner)
                        isDef = false;
                    else
                        isDef = true;
                    end                                                                                           
                otherwise            
                    [isMultiInstanceInternalPIM, storageImplementation] = ...
                        this.isMultiInstanceInternalPIM(implementation);
                    if isMultiInstanceInternalPIM
                        % special handling for e.g. rtP, DWork, which are
                        % defined in the RTE along with other PIMs  
                        isDef = true;
                        this.setImplementationOwner(storageImplementation, 'RTE');
                    else
                        % call superclass
                        isDef = isDefined@rtw.pil.StandaloneTargetInterface(this, implementation);
                    end
            end
        end
        
        function setImplementationOwner(this, implementation, owner)            
            switch this.codeInfoUtils.class(implementation)
                case 'AutosarSenderReceiver'
                    dataExpression = this.getExpression(implementation);
                    dataExpressionIdx = ...
                        find(strcmp(dataExpression, this.SenderReceiverInfo.dataExpression));
                    assert(isscalar(dataExpressionIdx), ...
                        'Did not find unique data expression index.');                                       
                    this.SenderReceiverInfo.owner{dataExpressionIdx} = owner;
                otherwise            
                    % call superclass
                    setImplementationOwner@rtw.pil.StandaloneTargetInterface(this, implementation, owner);
            end
        end
        
        function owner = getImplementationOwner(this, implementation)  
            switch this.codeInfoUtils.class(implementation)
                case {'AutosarSenderReceiver'}
                    dataExpression = this.getExpression(implementation);
                    dataExpressionIdx = ...
                        find(strcmp(dataExpression, this.SenderReceiverInfo.dataExpression));
                    assert(isscalar(dataExpressionIdx), ...
                        'Did not find unique data expression index.');                                       
                    owner = this.SenderReceiverInfo.owner{dataExpressionIdx};
                otherwise            
                    % call superclass
                    owner = getImplementationOwner@rtw.pil.StandaloneTargetInterface(this, implementation);                
            end                        
        end 
                
        function writeSectionStorageForDataAddPrefix(this, dataInterface, implementation)
            switch this.codeInfoUtils.class(implementation)
                case {'AutosarSenderReceiver', ...
                      'AutosarErrorStatus', ...
                      'AutosarIsUpdated', ...
                      'AutosarInterRunnable'} 
                    % no prefix
                otherwise            
                    % call superclass
                    writeSectionStorageForDataAddPrefix@rtw.pil.StandaloneTargetInterface(this, ...
                        dataInterface, ...
                        implementation);
            end    
        end   
        
        function validateParamDataInterface(this, dataInterface)            
            implementation = dataInterface.Implementation;
            if this.codeInfoUtils.isa(implementation, 'AutosarCalibration') && ...
                    ~this.codeInfoUtils.isDataInterfaceSystemConstant(dataInterface)
                if this.codeInfoUtils.isWorkspaceParam(dataInterface) && ...
                        ~this.SILPILWrapperUtils.isSupportedTunableWorkspaceParam(dataInterface)
                    % for model workspace, mapped parameters, initial
                    % values are available so we don't error     
                    if ~(this.codeInfoUtils.isModelWorkspaceParam(dataInterface) || ...
                            this.codeInfoUtils.isDirectModelArgParam(dataInterface))
                    % we cannot support this case because Calibration
                    % Params are uninitialized in the calib.c stub file.
                    componentPath = this.SILPILWrapperUtils.getSILPILInterface.getFullComponentPath;
                        diString = this.codeInfoUtils.getDataInterfaceString(dataInterface, 'parameter');
                    DAStudio.error('PIL:pil:AutosarCalibrationParameter', ...
                                   componentPath, diString);
                end
                end
            else
                % call superclass
                validateParamDataInterface@rtw.pil.StandaloneTargetInterface(this, ...
                    dataInterface);
            end
        end
        end
    
    methods (Access = 'private') 
        
        function isPresent = isMemberPresent(~, obj, list)
            isPresent = false;
            for idx=1:numel(list)
                if isequal(obj, list(idx))
                    isPresent = true;
                    return;
                end
            end
        end
        
        function senderReceiverInfo = getSenderReceiverInfo(this)
            % keep track of the underlying data associated with sender /
            % receiver ports and whether it has already been defined or
            % not
            senderReceiverInfo.dataExpression = {};
            senderReceiverInfo.owner = {};
            % sender receiver implementations are associated only with
            % Inports and Outports
            dataInterfaces = [this.codeInfo.Inports, ...
                              this.codeInfo.Outports];                          
            for diIdx = 1:length(dataInterfaces)
               di = dataInterfaces(diIdx);
               impl = di.Implementation;
               % skip ErrorStatus
               if this.codeInfoUtils.isa(di.Implementation, 'AutosarSenderReceiver')
                  % determine dataExpression
                  dataExpression = this.getExpression(impl);
                  % add new data
                  if ~ismember(dataExpression, senderReceiverInfo.dataExpression)
                     senderReceiverInfo.dataExpression{end+1} = dataExpression;
                     senderReceiverInfo.owner{end+1} = '';
                  end
               end                
            end                                   
        end        
        
        function runnableName = getRunnableName(this, runnable)
            % get the name of the runnable
            runnableSymbol = runnable.Prototype.Name;
            % lookup the Name associated with the Symbol
            runnableName = [];            
            for idx = 1:length(this.RunnableData)
                if strcmp(runnableSymbol, this.RunnableData(idx).Symbol)
                    runnableName = this.RunnableData(idx).Name;
                    break;
                end
            end
            assert(~isempty(runnableName),...
                'Did not find runnable "%s" in RunnableData', runnableName);
        end
        
        function runnables = getRunnables(this)
            % return periodic runnables and initialize runnable
            initFcn = this.codeInfo.InitializeFunctions;
            assert( (isempty(initFcn) || (length(initFcn)==1)) ,...
                'expect to have a maximum of 1 InitializeFunctions in codeInfo for AUTOSAR.');
            runnables = initFcn;
            termFcn = this.codeInfo.TerminateFunctions;
            assert(length(termFcn) <= 1, ...
                'expect to have a maximum of 1 TerminateFunctions in codeInfo for AUTOSAR.');
            if ~isempty(termFcn)
                runnables = [runnables, termFcn];
            end
            outputTasks = this.codeInfoUtils.getOutputTasks;
            % check all tasks have not been optimized away
            % (see test/toolbox/rtw/targets/AUTOSAR/subsys/tarxml_export2
            % lvlTwo_g530955)
            if ~isempty(outputTasks)
                runnables = [runnables outputTasks.codeInfoData];
            end
        end        
        
        function isPtr2BaseType = arrayReferenceByPtr2BaseType(this) 
            if (AR4.isCompatibleSchemaVer( this.autosarSchemaVersion))
                isPtr2BaseType = true;
                return;
            else
                isPtr2BaseType = false;
                return;
            end
        end
        
        function [typeStr, paramAddrValue, implAddrValue, implICAddrValue] = ...
                getAutosarTypeAndAddrBase(this, usePointerIO, isArray, ...
                                          rteType, baseRteType, implementation, ...
                                          paramName, addConstIfNeeded)
            
            isAR4 = AR4.isCompatibleSchemaVer(this.autosarSchemaVersion);
            typeStr = ...
                autosar.mm.mm2rte.TypeBuilder.getAutosarType(...
                                isAR4, usePointerIO, isArray, false, ...
                               rteType, baseRteType , addConstIfNeeded);
                           
            rteID = this.getRteID(implementation);
            if usePointerIO
                paramAddrValue = paramName;
                if isArray && this.arrayReferenceByPtr2BaseType                    
                    implAddrValue = this.getExpression(implementation);
                    % add Rte_IC - XIL static IC data
                    implICAddrValue = ['Rte_IC_' rteID];
                else                    
                    implAddrValue = ['&(' ...
                        this.getExpression(implementation) ')'];
                    % add Rte_IC - XIL static IC data
                    implICAddrValue = ['&(' 'Rte_IC_' rteID ')'];
                end
            else
                paramAddrValue = ['&' paramName];
                implAddrValue = ['&(' ...
                    this.getExpression(implementation) ')'];
                % add Rte_IC - XIL static IC data
                implICAddrValue = ['&(' 'Rte_IC_' rteID ')'];
            end
        end
        
        function [typeStr, paramAddrValue, implAddrValue, implICAddrValue] = ...
                getAutosarInputTypeAndAddr(this, usePointerIO, isArray, ...
                                           rteType, baseRteType, implementation, ...
                                           paramName)
            % Input arguments potentially need const keyword
            [typeStr, paramAddrValue, implAddrValue, implICAddrValue] = ...
                getAutosarTypeAndAddrBase(this, usePointerIO, isArray, ...
                                          rteType, baseRteType, implementation, ...
                                          paramName, true);
        end
        
        function [typeStr, paramAddrValue, implAddrValue] = ...
                getAutosarOutputTypeAndAddr(this, isArray, ...
                                            rteType, baseRteType, implementation, ...
                                            paramName)
            % Output arguments do not need const keyword
            [typeStr, paramAddrValue, implAddrValue, ~] = ...
                getAutosarTypeAndAddrBase(this, true, isArray, ...
                                          rteType, baseRteType, implementation, ...
                                          paramName, false);
        end
        
        function [typeStr, implAddrValue] = ...
                getAutosarReturnTypeAndAddr(this, usePointerIO, isArray, ...
                                           rteType, baseRteType, implementation, addConstIfNeeded)
            % Return arguments potentially need const keyword
            [typeStr, ~, implAddrValue, ~] = ...
                getAutosarTypeAndAddrBase(this, usePointerIO, isArray, ...
                                          rteType, baseRteType, implementation, ...
                                          'ignored', addConstIfNeeded);
            % Don't take address if the return type is not pointer.
            if ~usePointerIO 
                implAddrValue = this.getExpression(implementation);
            end
            
        end
        
        function rteID = getRteID(this, ...
                                  implementation)
            switch this.codeInfoUtils.class(implementation)
                case 'AutosarSenderReceiver'
                    rteID = [implementation.Port '_' implementation.DataElement];
                case {'AutosarErrorStatus' 'AutosarIsUpdated'}
                    receiver = this.codeInfoUtils.getReceiverFromStatus(implementation);                    
                    rteID = [receiver.Port '_' receiver.DataElement];
                case 'AutosarInterRunnable'
                    rteID = implementation.VariableName;
                case 'Variable'
                    assert(false, 'Tried to get RteId from an RTW.Variable - PIMs should be of type RTW.AutosarMemoryExpression');                                        
                case 'AutosarCalibration'      
                    switch implementation.DataAccessMode
                        case 'Calibration'
                            rteID = implementation.ElementName;        
                        case 'InternalCalPrm'
                            rteID = implementation.Port;
                        otherwise
                            assert(false, 'Unexpected DataAccessMode: %s', implementation.DataAccessMode);
                    end
                case 'AutosarMemoryExpression'
                    rteID = implementation.BaseRegion.Identifier;
                otherwise
                    assert(false, 'Unexpected implementation class: %s', class(implementation));
            end                              
        end        
        
        % write RTE IRV implicit/explicit (buffered/not-buffered) read function
        function writeSectionIRVRead(this, ...
                                     runnable, ...  
                                     usePointerIO, ...
                                     storageSize, ...
                                     rteType, ...
                                     baseRteType, ...
                                     implementation,...
                                     isImplicit)
            assert(nargin == 8, ['writeSectionIRVRead ' ...
                'expecting 7 arguments, getting %d'], nargin - 1);

            [returnType, returnValue] = ...
                this.getAutosarReturnTypeAndAddr(usePointerIO, storageSize.Numerical>1, ...
                                                 rteType, baseRteType, ...
                                                 implementation, true);
            
            optI = '';
            if isImplicit
                % IRead
                optI = 'I';
            end
            
            this.writer.writeLine([returnType ' ' ...
                'Rte_Irv',optI,'Read_' ...
                this.getRunnableName(runnable) '_' ...
                this.getRteID(implementation) ...
                '(' this.getRteInstanceArg(true) ') {']);
                    
            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;

            this.writer.writeLine(['   return ' returnValue ';']);
            this.writer.writeLine('}');
            this.writer.newLine;                
        end
        
        function writeSectionIRVExplicitReadNonScalar(this, ...
                                                      runnable, ... 
                                                      storageSize, ...                                            
                                                      rteType, ...
                                                      baseRteType, ...
                                                      implementation)
            assert(nargin == 6, [... 
                'writeSectionIRVExplicitReadNonScalar ' ...
                'expecting 5 arguments, getting %d'], nargin - 1);

            [outType, ~ ,irvVar] = ...
                this.getAutosarOutputTypeAndAddr(storageSize.Numerical>1, ...
                                                 rteType, baseRteType, ... 
                                                 implementation, '');
                        
            this.writer.writeLine(['void Rte_IrvRead_' ...
                this.getRunnableName(runnable) '_' ...
                this.getRteID(implementation) '('...
                this.getRteInstanceArg(false) outType 'u) {']);
            
            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;
            sizeToCopy = this.getSizeToCopyString(rteType, storageSize);
            this.writer.writeLine(['   memcpy(u, ' irvVar ...
                ',' sizeToCopy ');']);
            this.writer.writeLine('}');
        end
        
        
        % write RTE implicit (buffered) receive function & 
        % corresponding IStatus function
        function writeSectionImplicitReceive(this, ...
                                             runnable, ...
                                             usePointerIO, ...
                                             storageSize, ...
                                             rteType, ...
                                             baseRteType, ...
                                             implementation)
            assert(nargin == 7, ['writeSectionImplicitReceive ' ...
                'expecting 6 arguments, getting %d'], nargin - 1);
                        
            isArray = rtw.pil.TargetInterface.isArrayInCode(...
                implementation.Type, storageSize.Numerical);
            
            [returnType, returnValue] = ...
                this.getAutosarReturnTypeAndAddr(usePointerIO, isArray, ...
                                                 rteType, baseRteType, ...
                                                 implementation, true);
            
            % IRead
            this.writer.writeLine([returnType ' ' ...
                'Rte_IRead_' ...
                this.getRunnableName(runnable) '_' ...
                this.getRteID(implementation) ...
                '(' this.getRteInstanceArg(true) ') {']);
            
            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;

            this.writer.writeLine(['   return ' returnValue ';']);
            this.writer.writeLine('}');
            this.writer.newLine;    
            % IStatus
            errorStatus = this.codeInfoUtils.getStatusFromReceiver(implementation, ...
                                                                   'ErrorStatus');
            if ~isempty(errorStatus)
                assert((errorStatus.Type.isScalar==1),...
                    'Expected ErrorStatus port to be scalar, but it is not!');
                returnType = 'Std_ReturnType';                               
                
                this.writer.writeLine([returnType ' Rte_IStatus_' ...
                                       this.getRunnableName(runnable) '_' ...
                                       this.getRteID(implementation) ...
                                       '(' this.getRteInstanceArg(true) ') {']);

                % For multi-instance models, % mark RTEInstance arg as unused
                this.writeUnusedParameterForRTEInstance;
            
                this.writer.writeLine(['   return ' this.getExpression(errorStatus) ';']);
                this.writer.writeLine('}');
            end                         
        end

        % Write RTE Mode receive function. There is no IStatus function 
        % for this RTE API
        function writeSectionModeReceive(this, ...
                                         usePointerIO, ...
                                         rteType, ...
                                         implementation)
            assert(nargin == 4, ['writeSectionModeReceive ' ...
                'expecting 4 arguments, getting %d'], nargin);
            
            assert(~usePointerIO, 'Mode Receive is always scalar');
            
            returnType = rteType;
            returnValue = this.getExpression(implementation);
            
            % IRead
            this.writer.writeLine([returnType ' ' ...
                'Rte_Mode_' ...
                this.getRteID(implementation) ...
                '(' this.getRteInstanceArg(true) ') {']);
            
            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;

            this.writer.writeLine(['   return ' returnValue ';']);
            this.writer.writeLine('}');
            this.writer.newLine;
        end
           
        % Write RTE Mode send function. There is no IStatus function 
        % for this RTE API
        function writeSectionModeSend(this, ...
                                      usePointerIO, ...
                                      storageSize, ...
                                      rteType, ...
                                      baseRteType, ...
                                      implementation)
            assert(nargin == 6, ['writeSectionModeSend ' ...
                'expecting 5 arguments, getting %d'], nargin);
            
            [inputType, inputAddressValue, outputAddressValue, ~] = ...
                this.getAutosarInputTypeAndAddr(usePointerIO, ...
                                                storageSize.Numerical>1, ...
                                                rteType, baseRteType, ...
                                                implementation, 'u');
                
            Rte_ID = this.getRteID(implementation);
            Rte_Inst = this.getRteInstanceArg(false);
            
            % Write
            this.writer.writeLine(...
                ['Std_ReturnType Rte_Switch_' Rte_ID '(' Rte_Inst inputType ' u) {']);
            
            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;
            
            sizeToCopy = this.getSizeToCopyString(rteType, storageSize);
            this.writer.writeLine(['   memcpy(' outputAddressValue ...
                                ', ' inputAddressValue ',' sizeToCopy ');']);
            
            this.writer.writeLine('return 0;');
            this.writer.writeLine('}');
            
            this.writer.writeLine('');

        end
        
        % write RTE IRV implicit/explicit (buffered/not-buffered) write function
        function writeSectionIRVWrite(this, ...
                                      runnable, ...
                                      usePointerIO, ...
                                      storageSize, ...
                                      rteType, ...
                                      baseRteType, ...
                                      implementation,...
                                      isImplicit)
            assert(nargin == 8, ['writeSectionIRVWrite expecting 7 ' ...
                'arguments, getting %d'], nargin - 1);

            [inputType, inputAddressValue, outputAddressValue, ~] = ...
                this.getAutosarInputTypeAndAddr(usePointerIO, storageSize.Numerical>1, ...
                                                rteType, baseRteType, ...
                                                implementation, 'u');
            
            optI = '';
            if isImplicit
                % IWrite
                optI = 'I';
            end
            
            this.writer.writeLine(['void ' ...
                'Rte_Irv',optI,'Write_' ...
                this.getRunnableName(runnable) '_' ...
                this.getRteID(implementation) ...
                '(' this.getRteInstanceArg(false) inputType ' u) {']);
            
            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;

            sizeToCopy = this.getSizeToCopyString(rteType, storageSize);
            this.writer.writeLine(['   memcpy(' outputAddressValue ', ' ...
                inputAddressValue ',' sizeToCopy ');']);
            this.writer.writeLine('}');
        end        
        
        % write RTE implicit (buffered) send function
        function writeSectionImplicitSend(this, ...
                                          runnable, ...
                                          usePointerIO, ...
                                          storageSize, ...
                                          rteType, ...
                                          baseRteType, ...
                                          implementation)
            assert(nargin == 7, ['writeSectionImplicitSend ' ...
                'expecting 6 arguments, getting %d'], nargin - 1);
            
            isArray = rtw.pil.TargetInterface.isArrayInCode(...
                implementation.Type, storageSize.Numerical);
            
            [inputType, inputAddressValue, outputAddressValue, ~] = ...
                this.getAutosarInputTypeAndAddr(usePointerIO, isArray, ...
                                                rteType, baseRteType, ...
                                                implementation, 'u');
            
            % IWrite
            this.writer.writeLine(['void ' ...
                'Rte_IWrite_' ...
                this.getRunnableName(runnable) '_' ...
                this.getRteID(implementation) ...
                '(' this.getRteInstanceArg(false) inputType ' u) {']);
            
            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;
            
            sizeToCopy = this.getSizeToCopyString(rteType, storageSize);
            this.writer.writeLine(['   memcpy(' outputAddressValue ', ' inputAddressValue ', ' sizeToCopy ');']);
            this.writer.writeLine('}');
        end
        
        function writeSectionImplicitSendByRef(this, ...
                                               runnable, ...
                                               storageSize, ...
                                               rteType, ...
                                               baseRteType, ...
                                               implementation)
            assert(nargin == 6, ['writeSectionImplicitSendByRef' ...
                                'expecting 5 arguments, getting %d'], nargin - 1);
            usePointerIO = true;
            [returnType, returnValue] = ...
                this.getAutosarReturnTypeAndAddr(usePointerIO, storageSize.Numerical>1, ...
                                                 rteType, baseRteType, ...
                                                 implementation, false);            
            % IWriteRef
            this.writer.writeLine([returnType ' ' ...
                                'Rte_IWriteRef_',...
                                this.getRunnableName(runnable) '_' ...
                                this.getRteID(implementation) ...
                                '(' this.getRteInstanceArg(true) ') {']);
            this.writer.writeLine(['    return ' returnValue ';']);
            this.writer.writeLine('}');
            this.writer.newLine;            
        end
        
        % write RTE explicit receive function
        function writeSectionExplicitReceive(this, ...
                                             storageSize, ...
                                             rteType, ...
                                             baseRteType, ...
                                             implementation)

            assert(nargin == 5, ['writeSectionExplicitReceive ' ...
                'expecting 4 arguments, getting %d'], nargin - 1);
                                                            
            [outputType, ~, inputAddressValue] = ...
                this.getAutosarOutputTypeAndAddr(storageSize.Numerical>1, ...
                                                 rteType, baseRteType, ...
                                                 implementation, '');
            % Read
            returnType = 'Std_ReturnType';
            this.writer.writeLine([returnType ' ' ...
                'Rte_Read_' ...
                this.getRteID(implementation) ...
                '(' this.getRteInstanceArg(false) outputType ' u) {']);

            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;
            
            sizeToCopy = this.getSizeToCopyString(rteType, storageSize);
            this.writer.writeLine(['   memcpy(u, ' ...
                inputAddressValue ',' sizeToCopy ');']);
            
            errorStatus = this.codeInfoUtils.getStatusFromReceiver(implementation, ...
                'ErrorStatus');
            if ~isempty(errorStatus)
                this.writer.writeLine(['   return ' this.getExpression(errorStatus) ';']);
            else
                this.writer.writeLine('   return 0;');
            end
            this.writer.writeLine('}');

            isUpdated = this.codeInfoUtils.getStatusFromReceiver(implementation, ...
                                                                 'IsUpdated');                                                                                  
            if ~isempty(isUpdated)
                assert((isUpdated.Type.isScalar==1),...
                    'Expected IsUpdated port to be scalar, but it is not!');
                returnType = this.codeInfoUtils.getReplacedTypeIdentifier(...
                    isUpdated.Type.Identifier);
                
                this.writer.writeLine([returnType ' Rte_IsUpdated_' ...
                                       this.getRteID(implementation) ...
                                       '(' this.getRteInstanceArg(true) ') {']);

                % For multi-instance models, % mark RTEInstance arg as unused
                this.writeUnusedParameterForRTEInstance;
            
                this.writer.writeLine(['   return ' this.getExpression(isUpdated) ';']);
                this.writer.writeLine('}');
            end
            
            % E2EPW_Read: generate unconditionally
            if this.IsMultiInstance
                rteActArg = [AUTOSAR.CSC.getRTEInstanceName(), ', '];
            else
                rteActArg = '';
            end
            e2eReadID = this.getRteID(implementation);
            this.writer.writeLine(['uint32 ', ..., 
                                'E2EPW_Read_', ...
                                e2eReadID, '(', ...
                                this.getRteInstanceArg(false), ...
                                outputType ' u) {']);
            callStr = ['Rte_Read_', this.getRteID(implementation), '(', ...
                      rteActArg, 'u)'];
            this.writer.writeLine(['   return ' callStr, ';']);
            this.writer.writeLine('}');
              
            this.writer.writeLine(['Std_ReturnType ', ..., 
                                'E2EPW_ReadInit_', ...
                                e2eReadID, '(', ...
                                this.getRteInstanceArg(true), ...
                                ') {']);
            this.writeUnusedParameterForRTEInstance;
            this.writer.writeLine('   return 0;');
            this.writer.writeLine('}');            
        end
        
        % write RTE explicit receive by value function
        function writeSectionExplicitReceiveByVal(this, ...
                                                  usePointerIO, ...
                                                  storageSize, ...
                                                  rteType, ...
                                                  baseRteType, ...
                                                  implementation)
            assert(nargin == 6, ['writeSectionExplicitReceiveByVal ' ...
                'expecting 5 arguments, getting %d'], nargin - 1);
            
            [returnType, returnValue] = ...
                this.getAutosarReturnTypeAndAddr(usePointerIO, ...
                                                 storageSize.Numerical>1, ...
                                                 rteType, baseRteType, ...
                                                 implementation, true);
            
            this.writer.writeLine([returnType ' ' ...
                'Rte_DRead_' ...
                this.getRteID(implementation) ...
                '(' this.getRteInstanceArg(true) ') {']);
            
            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;

            this.writer.writeLine(['   return ' returnValue ';']);
            this.writer.writeLine('}');
            this.writer.newLine;
            
            % IsUpdated
            isUpdated = this.codeInfoUtils.getStatusFromReceiver(implementation, ...
                                                                 'IsUpdated');                                                                                  
            if ~isempty(isUpdated)
                assert((isUpdated.Type.isScalar==1),...
                    'Expected IsUpdated port to be scalar, but it is not!');
                returnType = this.codeInfoUtils.getReplacedTypeIdentifier(...
                    isUpdated.Type.Identifier);
                
                this.writer.writeLine([returnType ' Rte_IsUpdated_' ...
                                       this.getRteID(implementation) ...
                                       '(' this.getRteInstanceArg(true) ') {']);

                % For multi-instance models, % mark RTEInstance arg as unused
                this.writeUnusedParameterForRTEInstance;
            
                this.writer.writeLine(['   return ' this.getExpression(isUpdated) ';']);
                this.writer.writeLine('}');
            end            
        end
                
        % write RTE queued explicit receive function
        function writeSectionQueuedExplicitReceive(this, ...
                                                   storageSize, ...
                                                   rteType, ...
                                                   baseRteType, ...
                                                   implementation)
            assert(nargin == 5, ['writeSectionQueuedExplicitReceive ' ...
                'expecting 4 arguments, getting %d'], nargin - 1);
                        
                               
            [outputType, ~, inputAddressValue] = ...
                this.getAutosarOutputTypeAndAddr(storageSize.Numerical>1, ...
                                                 rteType, baseRteType, ...
                                                 implementation, '');
            % Read
            returnType = 'Std_ReturnType';
            this.writer.writeLine([returnType ' ' ...
                'Rte_Receive_' ...
                this.getRteID(implementation) ...
                '(' this.getRteInstanceArg(false) outputType ' u) {']);

            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;

            sizeToCopy = this.getSizeToCopyString(rteType, storageSize);
            this.writer.writeLine(['   memcpy(u, ' ...
                inputAddressValue ',' sizeToCopy ');']);
            
            errorStatus = this.codeInfoUtils.getStatusFromReceiver(implementation, ...
                'ErrorStatus');
            if ~isempty(errorStatus)
                this.writer.writeLine(['   return ' this.getExpression(errorStatus) ';']);
            else
                this.writer.writeLine('return 0;');
            end
            this.writer.writeLine('}');
        end

        
        % write RTE explicit send function
        function writeSectionExplicitSend(this, ...
                                          usePointerIO, ...
                                          storageSize, ...
                                          rteType, ...
                                          baseRteType, ...
                                          implementation)
            assert(nargin == 6, ['writeSectionExplicitSend ' ...
                'expecting 5 arguments, getting %d'], nargin - 1);                        
            
            isArray = rtw.pil.TargetInterface.isArrayInCode(...
                implementation.Type, storageSize.Numerical);
            
            [inputType, inputAddressValue, outputAddressValue, implICAddrValue] = ...
                this.getAutosarInputTypeAndAddr(usePointerIO, ...
                isArray, rteType, baseRteType, implementation, 'u');
            
            Rte_ID = this.getRteID(implementation);
            Rte_Inst = this.getRteInstanceArg(false);
            
            %Determine if there is an invalidation function to plumb in.
            mf0 = this.codeInfoUtils.ConfigInterface.getMF0ModelInfo;
            %Find relevant Invalidated flag, matching Port Name and Element
            %Name
            mf0Outports = mf0.outports.toArray;
            generateInvalidateCalls = false;
            for idx=1:numel(mf0Outports)
                if ~isempty(mf0Outports(idx).autosarInfo) && ...
                        strcmp(implementation.Port, mf0Outports(idx).autosarInfo.PortName) && ...
                        strcmp(implementation.DataElement, mf0Outports(idx).autosarInfo.ElementName)
                    generateInvalidateCalls = mf0Outports(idx).autosarInfo.CanBeInvalidated;
                    break
                end
            end
            
            if generateInvalidateCalls
                %Forward declare invalidation internal function
                xilRTEInvalidateFunctionName = sprintf('%s%s', this.codeInfoUtils.InvalidatePrefix, Rte_ID);
                this.writer.writeLine('static Std_ReturnType %s(boolean_T flag);', ...
                    xilRTEInvalidateFunctionName);
            end
            this.writer.newLine;
            
            this.writer.writeLine(...
                ['Std_ReturnType Rte_Write_' Rte_ID '(' Rte_Inst inputType ' u) {']);
            
            % For multi-instance models, % mark RTEInstance arg as unused
            this.writeUnusedParameterForRTEInstance;

            sizeToCopy = this.getSizeToCopyString(rteType, storageSize);
            this.writer.writeLine(['   memcpy(' outputAddressValue ...
                ', ' inputAddressValue ',' sizeToCopy ');']);
            
            this.writer.newLine();
            if generateInvalidateCalls
                %Insert call to local function that calls back to Simulink
                this.writer.wLine('%s(0);', xilRTEInvalidateFunctionName);
            end
            this.writer.newLine();
            
            this.writer.writeLine('return 0;');
            this.writer.writeLine('}');
            
            this.writer.writeLine('');
            
            % Signal invalidation function. Generate unconditionally for
            % now; later it could be generated conditionally.
            this.writer.writeLine(['Std_ReturnType ' ...
                                'Rte_Invalidate_' Rte_ID '(' this.getRteInstanceArg(true) ') {']);
            this.writeUnusedParameterForRTEInstance;
            replacePolicyFlag = ['Rte_Replace_' Rte_ID];
            this.writer.writeLine(['#if ' replacePolicyFlag]);
            
            this.writer.writeLine(['   memcpy(' outputAddressValue ...
                                ',' implICAddrValue ',' sizeToCopy ');']);

            this.writer.writeLine('#endif');
            
            this.writer.newLine();
            
            if generateInvalidateCalls
                %Insert call to local function that calls back to Simulink
                %to invalidate function.
                this.writer.wLine('%s(1);', xilRTEInvalidateFunctionName);
                this.writer.newLine();
            end
            
            this.writer.writeLine('return 0;');
            this.writer.writeLine('}');
            
            % E2EPW_Write: generate unconditionally
            if this.IsMultiInstance
                rteActArg = [AUTOSAR.CSC.getRTEInstanceName(), ', '];
            else
                rteActArg = '';
            end
            this.writer.writeLine(...
                ['uint32 E2EPW_Write_', Rte_ID, '(', Rte_Inst, ...
                                                     inputType, ' u) {']);
            this.writer.writeLine(['return Rte_Write_', Rte_ID, ...
                                   '(', rteActArg, 'u);']);
            this.writer.writeLine('}');
              
            this.writer.writeLine(['Std_ReturnType ', ..., 
                                'E2EPW_WriteInit_', ...
                                Rte_ID, '(', ...
                                this.getRteInstanceArg(true), ...
                                ') {']);
            this.writeUnusedParameterForRTEInstance;
            this.writer.writeLine('   return 0;');
            this.writer.writeLine('}');                
        end
                
        function sizeToCopyStr = getSizeToCopyString(this, rteType, storageSize)
            if (storageSize.Numerical > 1)
                if this.arrayReferenceByPtr2BaseType
                    % rteType in this case is the base type
                    sizeToCopyStr = sprintf('(%s)*sizeof(%s)', storageSize.Symbolic, rteType);
                else
                    % rteType in this case is the array type
                    sizeToCopyStr = sprintf('sizeof(%s)', rteType);
                end
            else % non-array type
                sizeToCopyStr = sprintf('sizeof(%s)', rteType);
            end
        end
                
        % determine if implementation has an associated PIM CSC
        function [isPIMCSC, isNVRAM, isArTyped] = isPIMCSC(...
                this, implementation)
            assert(this.codeInfoUtils.isa(implementation, 'AutosarMemoryExpression'), ...
                   'Implementation must be AutosarMemoryExpression.');
            isNVRAM = implementation.NeedsNVRAMAccess;
            isPIMCSC = ismember(implementation.DataAccessMode, ...
                {'CTypedPerInstanceMemory', 'ArTypedPerInstanceMemory'});
            isArTyped = strcmp(implementation.DataAccessMode, 'ArTypedPerInstanceMemory') && ...
                this.arrayReferenceByPtr2BaseType;
        end
        
        % return the RTE instance input argument name by handling both the
        % multi-instance case and non-multi-instance case.
        function rteInstanceArg = getRteInstanceArg(this, isOnlyArgument)
            if this.IsMultiInstance                
                if isOnlyArgument
                    rteInstanceArg = rtw.pil.AutosarTargetInterface.RTEInstanceArg;
                else
                    rteInstanceArg = [rtw.pil.AutosarTargetInterface.RTEInstanceArg ', '];
                end
            else
                if isOnlyArgument
                    rteInstanceArg = 'void';
                else
                    rteInstanceArg = '';
                end
            end
        end
        
        function writeUnusedParameterForRTEInstance(this)
            if this.IsMultiInstance
                % mark RTEInstance arg as unused
                this.writer.writeLine('UNUSED_PARAMETER(%s);', AUTOSAR.CSC.getRTEInstanceName());
            end
        end
        
        function [isMultiInstanceInternalPIM, storageImplementation] = ...
                isMultiInstanceInternalPIM(this, implementation)
            % Determine if the implementation is part of InternalData 
            % like rtP and DWork that map to PIM's in the multi-instance
            % case (see also g1397482).
            isMultiInstanceInternalPIM = false;
            storageImplementation = [];
            if this.IsMultiInstance
                % check for a match with InternalData
                storageImplementation = this.getBaseStorageImplementation(implementation);                
                allInternalData = this.codeInfo.InternalData;                
                for i=1:length(allInternalData)
                    intData = allInternalData(i);
                    % Skip instance handle (it is not mapped to PIM)
                    if this.isRteInstance(intData.Implementation)
                        continue;
                    end                    
                    % Skip IRVs (they are not mapped to PIM)
                    if this.codeInfoUtils.isa(intData.Implementation, 'AutosarInterRunnable')
                        continue;
                    end                                        
                    if storageImplementation == intData.Implementation                        
                        isMultiInstanceInternalPIM = true;  
                        break;
                    end                
                end                
            end            
        end
        
        function cacheRteInstanceActualArgName(this)
            % return the actual argument name of the RteInstance,
            % return empty if there is no RteInstance
            this.RTEInstActualArgName = '';
            allInternalData = this.codeInfo.InternalData;
            for i=1:length(allInternalData)
                intData = allInternalData(i);
                if this.isRteInstance(intData.Implementation)
                    % tag the RteInstance variable
                    this.writeSectionStorageForDataAddPrefix(intData, intData.Implementation);
                    % cache the actual name
                    this.RTEInstActualArgName = intData.Implementation.Identifier;
                    break;
                end
            end
        end
        
        function rteSWCHeader = getRTESWCHeader(this)
            % codeInfo.Name is set to the AUTOSAR component name in TLC in
            % codeInfo.tlc.            
            mainInterfaceName = this.codeInfo.Name;            
            rteSWCHeader = ['Rte_', mainInterfaceName, '.h'];
        end
    end
end
