/*
 * \file       trc_cmp_cfg_etmv4.h
 * \brief      OpenCSD : 
 * 
 * \copyright  Copyright (c) 2015, ARM Limited. All Rights Reserved.
 */


/* 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution. 
 * 
 * 3. Neither the name of the copyright holder nor the names of its contributors 
 * may be used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */ 

#ifndef ARM_TRC_CMP_CFG_ETMV4_H_INCLUDED
#define ARM_TRC_CMP_CFG_ETMV4_H_INCLUDED

#include "trc_pkt_types_etmv4.h"
#include "common/trc_cs_config.h"


/** @addtogroup ocsd_protocol_cfg
@{*/

/** @name ETMv4 configuration
@{*/

/*!
 * @class EtmV4Config   
 * @brief Interpreter class for etm v4 config structure.
 * 
 * Provides quick value interpretation methods for the ETMv4 config register values.
 * Primarily inlined for efficient code. 
 */
class EtmV4Config : public CSConfig // public ocsd_etmv4_cfg
{
public:
    EtmV4Config();  /**< Default constructor */
    EtmV4Config(const ocsd_etmv4_cfg *cfg_regs);  
    ~EtmV4Config() {}; /**< Default destructor */

// operations to convert to and from C-API structure

    //! copy assignment operator for base structure into class.
    EtmV4Config & operator=(const ocsd_etmv4_cfg *p_cfg);

    //! cast operator returning struct const reference
    operator const ocsd_etmv4_cfg &() const { return m_cfg; };
    //! cast operator returning struct const pointer
    operator const ocsd_etmv4_cfg *() const { return &m_cfg; };

    const ocsd_core_profile_t &coreProfile() const { return m_cfg.core_prof; };
    const ocsd_arch_version_t &archVersion() const { return m_cfg.arch_ver; };

    /* idr 0 */
    const bool LSasInstP0() const;
    const bool hasDataTrace() const;
    const bool hasBranchBroadcast() const;
    const bool hasCondTrace() const;
    const bool hasCycleCountI() const;
    const bool hasRetStack() const;
    const uint8_t numEvents() const;
    const bool eteHasTSMarker() const;
    
    typedef enum _condType {
        COND_PASS_FAIL,
        COND_HAS_ASPR
    } condType;

    const condType hasCondType() const;

    typedef enum _QSuppType {
        Q_NONE,
        Q_ICOUNT_ONLY,
        Q_NO_ICOUNT_ONLY,
        Q_FULL
    } QSuppType;

    const QSuppType getQSuppType();
    const bool hasQElem();
    const bool hasQFilter();

    const bool hasTrcExcpData() const;
    const uint32_t TimeStampSize() const;

    const bool commitOpt1() const;
    const bool commTransP0() const;

    /* idr 1 */
    const uint8_t MajVersion() const;
    const uint8_t MinVersion() const;
    const uint8_t FullVersion() const;
    
    /* idr 2 */
    const uint32_t iaSizeMax() const;
    const uint32_t cidSize() const;
    const uint32_t vmidSize();
    const uint32_t daSize() const;
    const uint32_t dvSize() const;
    const uint32_t ccSize() const;
    const bool vmidOpt() const;
    const bool wfiwfeBranch() const;

    /* id regs 8-13*/
    const uint32_t MaxSpecDepth() const;
    const uint32_t P0_Key_Max() const;
    const uint32_t P1_Key_Max() const;
    const uint32_t P1_Spcl_Key_Max() const;
    const uint32_t CondKeyMax() const;
    const uint32_t CondSpecKeyMax() const;
    const uint32_t CondKeyMaxIncr() const;
 
    /* trace idr */
    virtual const uint8_t getTraceID() const; //!< CoreSight Trace ID for this device.

    /* config R */
    const bool enabledDVTrace() const;
    const bool enabledDATrace() const; 
    const bool enabledDataTrace() const;

    typedef enum {
        LSP0_NONE,
        LSP0_L,
        LSP0_S,
        LSP0_LS
    } LSP0_t;

    const bool enabledLSP0Trace() const;
    const LSP0_t LSP0Type() const;
    
    const bool enabledBrBroad() const;
    const bool enabledCCI() const;
    const bool enabledCID() const;
    const bool enabledVMID() const;
    const bool enabledVMIDOpt() const;

    typedef enum {
        COND_TR_DIS,
        COND_TR_LD,
        COND_TR_ST,
        COND_TR_LDST,
        COND_TR_ALL
    } CondITrace_t;

    const CondITrace_t enabledCondITrace();

    const bool enabledTS() const;
    const bool enabledRetStack() const;

    const bool enabledQE() const;

private:
    void PrivateInit();
    void CalcQSupp();   
    void CalcVMIDSize();

    bool m_QSuppCalc;
    bool m_QSuppFilter;
    QSuppType m_QSuppType;

    bool m_VMIDSzCalc;
    uint32_t m_VMIDSize;

    bool m_condTraceCalc;
    CondITrace_t m_CondTrace;

protected:
    ocsd_etmv4_cfg m_cfg;
    uint8_t m_MajVer;
    uint8_t m_MinVer;

};

/* idr 0 */
inline const bool EtmV4Config::LSasInstP0() const
{
    return (bool)((m_cfg.reg_idr0 & 0x6) == 0x6);
}

inline const bool EtmV4Config::hasDataTrace() const
{
    return (bool)((m_cfg.reg_idr0 & 0x18) == 0x18);
}

inline const bool EtmV4Config::hasBranchBroadcast() const
{
    return (bool)((m_cfg.reg_idr0 & 0x20) == 0x20);
}

inline const bool EtmV4Config::hasCondTrace() const
{
    return (bool)((m_cfg.reg_idr0 & 0x40) == 0x40);
}

inline const bool EtmV4Config::hasCycleCountI() const
{
    return (bool)((m_cfg.reg_idr0 & 0x80) == 0x80);
}

inline const bool EtmV4Config::hasRetStack() const
{
    return (bool)((m_cfg.reg_idr0 & 0x200) == 0x200);
}

inline const uint8_t EtmV4Config::numEvents() const
{
    return ((m_cfg.reg_idr0 >> 10) & 0x3) + 1;
}

inline const EtmV4Config::condType EtmV4Config::hasCondType() const
{
    return ((m_cfg.reg_idr0 & 0x3000) == 0x1000) ? EtmV4Config::COND_HAS_ASPR : EtmV4Config::COND_PASS_FAIL;
}

inline const EtmV4Config::QSuppType EtmV4Config::getQSuppType()
{
    if(!m_QSuppCalc) CalcQSupp();
    return m_QSuppType;
}

inline const bool EtmV4Config::hasQElem()
{
    if(!m_QSuppCalc) CalcQSupp();
    return (bool)(m_QSuppType != Q_NONE);
}

inline const bool EtmV4Config::hasQFilter()
{
    if(!m_QSuppCalc) CalcQSupp();
    return m_QSuppFilter;
}

inline const bool EtmV4Config::hasTrcExcpData() const
{
    return (bool)((m_cfg.reg_idr0 & 0x20000) == 0x20000);
}

inline const bool EtmV4Config::eteHasTSMarker() const
{
    return (FullVersion() >= 0x51) && ((m_cfg.reg_idr0 & 0x800000) == 0x800000);
}

inline const uint32_t EtmV4Config::TimeStampSize() const
{
    uint32_t tsSizeF = (m_cfg.reg_idr0 >> 24) & 0x1F;
    if(tsSizeF == 0x6)
        return 48;
    if(tsSizeF == 0x8)
        return 64;
    return 0;
}

inline const bool EtmV4Config::commitOpt1() const
{
    return (bool)((m_cfg.reg_idr0 & 0x20000000) == 0x20000000) && hasCycleCountI();
}

inline const bool EtmV4Config::commTransP0() const
{
    return (bool)((m_cfg.reg_idr0 & 0x40000000) == 0x0);
}

    /* idr 1 */
inline const uint8_t EtmV4Config::MajVersion() const
{
    return m_MajVer;
}

inline const uint8_t EtmV4Config::MinVersion() const
{
    return m_MinVer;
}

inline const uint8_t EtmV4Config::FullVersion() const
{
    return (m_MajVer << 4) | m_MinVer;
}

/* idr 2 */
inline const uint32_t EtmV4Config::iaSizeMax() const
{
    return ((m_cfg.reg_idr2 & 0x1F) == 0x8) ? 64 : 32;
}

inline const uint32_t EtmV4Config::cidSize() const
{
    return (((m_cfg.reg_idr2 >> 5) & 0x1F) == 0x4) ? 32 : 0;
}

inline const uint32_t EtmV4Config::vmidSize()
{
    if(!m_VMIDSzCalc) 
    {
        CalcVMIDSize();
    }
    return m_VMIDSize;
}

inline const uint32_t EtmV4Config::daSize() const
{
    uint32_t daSizeF = ((m_cfg.reg_idr2 >> 15) & 0x1F);
    if(daSizeF)
        return (((m_cfg.reg_idr2 >> 15) & 0x1F) == 0x8) ? 64 : 32;
    return 0;
}

inline const uint32_t EtmV4Config::dvSize() const
{
    uint32_t dvSizeF = ((m_cfg.reg_idr2 >> 20) & 0x1F);
    if(dvSizeF)
        return (((m_cfg.reg_idr2 >> 20) & 0x1F) == 0x8) ? 64 : 32;
    return 0;
}

inline const uint32_t EtmV4Config::ccSize() const
{
    return ((m_cfg.reg_idr2 >> 25) & 0xF) + 12;
}

inline const bool EtmV4Config::vmidOpt() const
{
    return (bool)((m_cfg.reg_idr2 & 0x20000000) == 0x20000000) && (MinVersion() > 0);
}

inline const bool EtmV4Config::wfiwfeBranch() const
{
    return (bool)((m_cfg.reg_idr2 & 0x80000000) && (FullVersion() >= 0x43));
}


/* id regs 8-13*/

inline const uint32_t EtmV4Config::MaxSpecDepth() const
{
    return m_cfg.reg_idr8;
}

inline const uint32_t EtmV4Config::P0_Key_Max() const
{
    return (m_cfg.reg_idr9 == 0) ? 1 : m_cfg.reg_idr9;
}

inline const uint32_t EtmV4Config::P1_Key_Max() const
{
    return m_cfg.reg_idr10;
}

inline const uint32_t  EtmV4Config::P1_Spcl_Key_Max() const
{
    return m_cfg.reg_idr11;
}

inline const uint32_t EtmV4Config::CondKeyMax() const
{
    return m_cfg.reg_idr12;
}

inline const uint32_t EtmV4Config::CondSpecKeyMax() const
{
    return m_cfg.reg_idr13;
}

inline const uint32_t EtmV4Config::CondKeyMaxIncr() const
{
    return m_cfg.reg_idr12 - m_cfg.reg_idr13;
}

inline const uint8_t EtmV4Config::getTraceID() const
{
    return (uint8_t)(m_cfg.reg_traceidr & 0x7F);
}

    /* config R */
inline const bool EtmV4Config::enabledDVTrace() const
{
    return hasDataTrace() && enabledLSP0Trace() && ((m_cfg.reg_configr & (0x1 << 17)) != 0);
}

inline const bool EtmV4Config::enabledDATrace() const
{
    return hasDataTrace() && enabledLSP0Trace() && ((m_cfg.reg_configr & (0x1 << 16)) != 0);
}

inline const bool EtmV4Config::enabledDataTrace() const
{
    return enabledDATrace() || enabledDVTrace();
}

inline const bool EtmV4Config::enabledLSP0Trace() const
{
    return ((m_cfg.reg_configr & 0x6) != 0);
}

inline const EtmV4Config::LSP0_t EtmV4Config::LSP0Type() const
{
    return (LSP0_t)((m_cfg.reg_configr & 0x6) >> 1);
}
    
inline const bool EtmV4Config::enabledBrBroad() const
{
    return ((m_cfg.reg_configr & (0x1 << 3)) != 0);
}

inline const bool EtmV4Config::enabledCCI() const
{
    return ((m_cfg.reg_configr & (0x1 << 4)) != 0);
}

inline const bool EtmV4Config::enabledCID() const
{
    return ((m_cfg.reg_configr & (0x1 << 6)) != 0);
}

inline const bool EtmV4Config::enabledVMID() const
{
    return ((m_cfg.reg_configr & (0x1 << 7)) != 0);
}

inline const bool EtmV4Config::enabledVMIDOpt() const
{
    bool vmidOptVal = ((m_cfg.reg_configr & (0x1 << 15)) != 0);
    /* TRIDR2.VMIDOPT[30:29] determine value used */
    if (!vmidOpt()) { /* [29] = 1'b0 */
        vmidOptVal = false; /* res0 */
        if (FullVersion() >= 0x45) {
            /* umless version > 4.5 in which case [30] determines res val */
            vmidOptVal = ((m_cfg.reg_idr2 & (0x1 << 30)) != 0);
        }
    }
    return vmidOptVal;
}

inline const EtmV4Config::CondITrace_t EtmV4Config::enabledCondITrace()
{
    if(!m_condTraceCalc)
    {
        switch((m_cfg.reg_configr >> 8) & 0x7)
        {
        default:
        case 0: m_CondTrace = COND_TR_DIS; break;
        case 1: m_CondTrace = COND_TR_LD; break;
        case 2: m_CondTrace = COND_TR_ST; break;
        case 3: m_CondTrace = COND_TR_LDST; break;
        case 7: m_CondTrace = COND_TR_ALL; break;
        }
        m_condTraceCalc = true;
    }
    return m_CondTrace;
}

inline const bool EtmV4Config::enabledTS() const
{
    return ((m_cfg.reg_configr & (0x1 << 11)) != 0);
}

inline const bool EtmV4Config::enabledRetStack() const
{
    return ((m_cfg.reg_configr & (0x1 << 12)) != 0);
}

inline const bool EtmV4Config::enabledQE() const
{
       return ((m_cfg.reg_configr & (0x3 << 13)) != 0);
}

/** @}*/
/** @}*/

#endif // ARM_TRC_CMP_CFG_ETMV4_H_INCLUDED

/* End of File trc_cmp_cfg_etmv4.h */
