classdef (Hidden = true) ExtendedHardwareConfig < handle
%EXTENDEDHARDWARECONFIG defines extended hardware characteristics
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2007-2019 The MathWorks, Inc.

    properties (SetAccess = 'private', GetAccess = 'private')
        TypeInfo;
    end

    methods
        % constructor
        function this = ExtendedHardwareConfig
            narginchk(0, 0);            
        end
        
        % allow setting of ComponentArgs
        function setComponentArgs(this, componentArgs)
            % determine the native data types            
           targetBitSizes = [componentArgs.getParam('TargetBitPerChar') ...
                             componentArgs.getParam('TargetBitPerShort') ...
                             componentArgs.getParam('TargetBitPerInt') ...
                             componentArgs.getParam('TargetBitPerLong')];
           % conditional support for "long long"
           if strcmp(componentArgs.getParam('TargetLongLongMode'), 'on')
              targetBitSizes(end+1) = componentArgs.getParam('TargetBitPerLongLong');
           end
           targetWordSize = componentArgs.getParam('TargetWordSize');
           bitPerFloat = componentArgs.getParam('TargetBitPerFloat');
           bitPerDouble = componentArgs.getParam('TargetBitPerDouble');
           this.TypeInfo = this.buildTypeInfo(targetBitSizes, ...
               targetWordSize, ...
               bitPerFloat, ...
               bitPerDouble);
        end
        
        % ISBYTEADDRESSABLE: Method to indicate whether the
        %                    target is byte addressable or not.
        %
        % byteAddressable = isByteAddressable
        %
        % byteAddressable: Boolean indicating whether the target is byte addressable
        % or not.
        function byteAddressable = isByteAddressable(this)
            narginchk(1, 1);            
            typeInfo = this.TypeInfo;
            byteAddressable = this.staticIsByteAddressable(typeInfo);
        end

        % GETDATATYPEWORDORDER: Method to return the target word order for
        %                       multi-word Simulink datatypes.
        %
        % wordOrder = getDatatypeWordOrder(simulinkType, byteOrder)
        %
        % simulinkType: A Simulink datatype name.
        % byteOrder: The byte order of the target.
        %
        % wordOrder: The corresponding target word order - "LittleEndian" or "BigEndian"
        function wordOrder = getDatatypeWordOrder(this, simulinkType, byteOrder) %#ok<INUSL>
            narginchk(3, 3);            
            % default implementation 
            wordOrder = byteOrder;
        end
                        
        % GETDATATYPESIZE: Method to return the target datatype size for
        %                  standard Simulink datatypes.
        %
        % size = getDatatypeSize(simulinkType)
        %
        % simulinkType: A Simulink datatype name.
        %
        % size: The corresponding target datatype size in 8-bit bytes
        function size = getDatatypeSize(this, simulinkType)
            narginchk(2, 2);
            % define default datatype sizes                        
            typeInfo = this.TypeInfo;
            switch simulinkType
                case 'double'
                    size = typeInfo.bitPerDouble / 8;                    
                case 'single'        
                    size = typeInfo.bitPerFloat / 8;      
                case {'int64' 'uint64'}
                    if this.isTypeSizeSupported(64)
                       size = 8; 
                    else
                       % unsupported 
                       size = 0;
                    end
                case {'int32' 'uint32'}
                    if this.isTypeSizeSupported(32)
                       size = 4; 
                    else
                       % unsupported 
                       size = 0;
                    end
                case {'int16' 'uint16' 'half'}                                                            
                    if this.isTypeSizeSupported(16)                        
                        size = 2;
                    else
                        % unsupported
                        size = 0;
                    end
                case {'int8' 'uint8' 'boolean' 'char'}   
                    if this.isTypeSizeSupported(8)                        
                        size = 1;                    
                    else
                       % unsupported
                       size = 0;
                    end
                otherwise
                    rtw.pil.ProductInfo.error('pilverification', 'UnknownDataType', simulinkType);
            end                                                 
        end
    end
    
    methods (Static)
       function checkWordSize(configInterface, ...
                              isPIL, ...
                              isReadOnly, ...
                              hwSettingsActions, ...
                              hwSettingsMsgString, ...
                              enableLongLongActions, ...
                              enableLongLongMsgString, ...
                              noWordSizeErrorId, ...
                              getActionArgs)
            % XIL and execution time profiling need an integer type to 
            % exist for the native word size, which may not be true.
            %
            % Note that for SIL, the execution time profiling will create
            % its own 64-bit data type even if LongLong mode is not
            % enabled.  For PIL we cannot guarantee this due to the
            % diversity of platforms.
            %                        
            prodEqTarget = strcmp(getParam(configInterface, 'ProdEqTarget'), 'on');
            if (prodEqTarget)
                prefix = 'Prod';
            else
                prefix = 'Target';
            end            
            charParam = [prefix, 'BitPerChar'];
            shortParam = [prefix, 'BitPerShort'];
            intParam = [prefix, 'BitPerInt'];
            longParam = [prefix, 'BitPerLong'];
            longLongParam = [prefix, 'BitPerLongLong'];
            longLongModeParam = [prefix, 'LongLongMode'];
            wordSizeParam = [prefix, 'WordSize'];
            
            % determine the native data types            
            baseBitSizes = [getParam(configInterface, charParam) ...
                getParam(configInterface, shortParam) ...
                getParam(configInterface, intParam) ...
                getParam(configInterface, longParam)];
            
            % conditional support for "long long"
            longLongSize = getParam(configInterface, longLongParam);            
            if strcmp(getParam(configInterface, longLongModeParam), 'on')                
                % conditionally add longLongSize
                bitSizes = [baseBitSizes longLongSize];
                longLongEnabled = true;
                longLongDisabledStr = '';
            else
                bitSizes = baseBitSizes;
                longLongEnabled = false;
                longLongDisabledMsg = message('PIL:pil:LongLongDisabled');
                longLongDisabledStr = sprintf(' %s', longLongDisabledMsg.getString);                               
            end
            wordSize = getParam(configInterface, wordSizeParam);
            
            % For SIL, we do not need wordSize to be a member of bitSizes,
            % except in PWS mode when the production hardware is not going
            % to be byte addressable since:
            %   (a) we only support x86-64 host arch and host_timer_x86.h
            % used for profiling SIL code creates its own typedefs 
            % independently of the [Prod/Target]LongLongMode setting used
            % for the design under test, by using its own preprocessor 
            % macros that detect the specific compiler.
            %   (b) On Linux/Windows/Mac, if LongLong mode is not enabled,
            %   pointer size is still 64-bit, which is sufficient for rtwgen.
            %   If pointer size is not equal to word size and no other data
            %   type is (e.g., 64 bit word size, 32 bit pointer, 32 bit long,
            %   128 bit long long), then a downstream error will be thrown by rtwgen.
            typeInfo = rtw.connectivity.ExtendedHardwareConfig.buildTypeInfo(bitSizes, ...
                wordSize, ...
                0, ...  % Float information not used here
                0);     % Double information not used here.
            isByteAddressable = rtw.connectivity.ExtendedHardwareConfig.staticIsByteAddressable(typeInfo);
            if (isPIL || ~isByteAddressable) && ~ismember(wordSize, bitSizes)
                % always include longLongSize
                infoBitSizes = [baseBitSizes longLongSize];
                paramNames = {charParam, ...
                              shortParam, ...
                              intParam, ...
                              longParam, ...
                              longLongParam};
                infoStr = [];
                for i=1:length(infoBitSizes)
                    disableStr = '';
                    if length(infoBitSizes) == i
                        disableStr = longLongDisabledStr;
                    end                    
                    % align the values based on the longest parameter name
                    padLength = length(longLongParam) + 1 - length(paramNames{i});
                    padding = repmat(' ', padLength, 1);
                    infoStr = sprintf('%s- %s%s%d%s\n', ...
                        infoStr, ...
                        paramNames{i}, ...     
                        padding, ...
                        infoBitSizes(i), ...
                        disableStr);
                end
                                
                if ~longLongEnabled && (wordSize == longLongSize)
                    % suggest to enable long long
                    fullAccessActionsIds = [enableLongLongActions, hwSettingsActions];
                    additionalMsg = enableLongLongMsgString;
                else
                    % suggest to adjust hw settings
                    fullAccessActionsIds = hwSettingsActions;
                    additionalMsg = hwSettingsMsgString;
                end
                
                [actionArg0, actionArg1] = getActionArgs();
                
                % throw
                readOnlyActionIDs = {};
                exception = coder.connectivity.AccessDependentDiagnostic(...
                    noWordSizeErrorId, ...
                    readOnlyActionIDs, ...   
                    fullAccessActionsIds,...    
                    wordSizeParam, ...
                    wordSize, ...
                    infoStr, ...
                    additionalMsg, ...
                    actionArg0, ...
                    actionArg1);
                exception.throw(isReadOnly);
            end
       end
       
       function typeInfo = buildTypeInfo(targetBitSizes, targetWordSize, bitPerFloat, bitPerDouble)
           % BUILDTYPEINFO
           %    Creates the typeInfo struct
           typeInfo.wordSize = targetWordSize;

           if ismember(8, targetBitSizes)
              typeInfo.native8 = true;
           else
              typeInfo.native8 = false;              
           end
           if ismember(16, targetBitSizes)
              typeInfo.native16 = true; 
           else
              typeInfo.native16 = false;              
           end
           if ismember(32, targetBitSizes)
              typeInfo.native32 = true; 
           else
              typeInfo.native32 = false;             
           end
           if ismember(64, targetBitSizes)
              typeInfo.native64 = true; 
           else
              typeInfo.native64 = false;             
           end
           typeInfo.bitPerFloat = bitPerFloat;
           typeInfo.bitPerDouble = bitPerDouble;
       end
       
       
       function byteAddressable = staticIsByteAddressable(typeInfo)
           % STATICISBYTEADDRESSABLE
           if typeInfo.native8
                byteAddressable = true;
            else        
                byteAddressable = false;                
            end
       end
    end
    
    methods (Access = 'private')
        % return whether a given typeSize (e.g. 8, 16, 32) has a native data type
        function hasNativeType = hasNativeType(this, typeSize)
            typeInfo = this.TypeInfo;
            switch typeSize
                case 8
                    hasNativeType = typeInfo.native8;
                case 16
                    hasNativeType = typeInfo.native16;
                case 32
                    hasNativeType = typeInfo.native32;
                case 64
                    hasNativeType = typeInfo.native64;
                otherwise
                    assert(false, 'Unsupported typeSize: %d', typeSize);
            end
        end
        
        % return whether a given typeSize (e.g. 8, 16, 32) is supported
        function isTypeSizeSupported = isTypeSizeSupported(this, typeSize)
            % default to false
            isTypeSizeSupported = false;
            isByteAddressable = this.isByteAddressable;
            wordSize = this.TypeInfo.wordSize;
            if isByteAddressable
                if this.hasNativeType(typeSize)
                    % byte addressable + native type
                    isTypeSizeSupported = true;
                end
            else
                % only 16 & 32-bit word addressable architectures are
                % supported
                if typeSize >= wordSize
                    if this.hasNativeType(typeSize)
                        % word addressable + native type >= word size
                        isTypeSizeSupported = true;
                    end
                else
                    % up cast is supported if there is no native type before
                    % we hit the word size
                    switch typeSize
                        case 8
                            switch wordSize
                                case 16
                                    if ~this.hasNativeType(8)
                                        isTypeSizeSupported = true;
                                    end
                                case 32
                                    if ~this.hasNativeType(8) && ~this.hasNativeType(16)
                                        isTypeSizeSupported = true;
                                    end                                
                                otherwise
                                    assert(false, 'Unexpected wordSize: %d', wordSize);
                            end
                        case 16
                            switch wordSize
                                case 32
                                    if ~this.hasNativeType(16)
                                        isTypeSizeSupported = true;
                                    end                                
                                otherwise
                                    assert(false, 'Unexpected wordSize: %d', wordSize);
                            end                        
                        otherwise
                            assert(false, 'Unexpected typeSize: %d', typeSize);
                    end
                end
            end
        end
    end
end
