/*
 * \file       trc_pkt_elem_etmv4i.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_PKT_ELEM_ETMV4I_H_INCLUDED
#define ARM_TRC_PKT_ELEM_ETMV4I_H_INCLUDED

#include "trc_pkt_types_etmv4.h"
#include "common/trc_printable_elem.h"
#include "common/trc_pkt_elem_base.h"

/** @addtogroup trc_pkts
@{*/

/*!
* @class Etmv4PktAddrStack
* @brief ETMv4 Address packet values stack
* @ingroup trc_pkts
*
*  This class represents a stack of recent broadcast address values - 
*  used to fulfil the ExactMatch address type where no address is output.
*
*/
class Etmv4PktAddrStack
{
public:
    Etmv4PktAddrStack()
    {
        reset_stack();
    }
    ~Etmv4PktAddrStack() {};

    void push(const ocsd_pkt_vaddr vaddr, const uint8_t isa)
    {
        m_v_addr[2] = m_v_addr[1];
        m_v_addr[1] = m_v_addr[0];
        m_v_addr[0] = vaddr;
        m_v_addr_ISA[2] = m_v_addr_ISA[1];
        m_v_addr_ISA[1] = m_v_addr_ISA[0];
        m_v_addr_ISA[0] = isa;
    }

    void get_idx(const uint8_t idx, ocsd_pkt_vaddr &vaddr, uint8_t &isa)
    {
        if (idx < 3)
        {
            vaddr = m_v_addr[idx];
            isa = m_v_addr_ISA[idx];
        }
    }

    // explicit reset for TInfo.
    void reset_stack()
    {
        for (int i = 0; i < 3; i++)
        {
            m_v_addr[i].pkt_bits = 0;
            m_v_addr[i].size = OCSD_MAX_VA_BITSIZE == 64 ? VA_64BIT : VA_32BIT;
            m_v_addr[i].val = 0;
            m_v_addr[i].valid_bits = OCSD_MAX_VA_BITSIZE;
            m_v_addr_ISA[i] = 0;
        }

    }

private:
    ocsd_pkt_vaddr m_v_addr[3];         //!< most recently broadcast address packet
    uint8_t        m_v_addr_ISA[3];
};

/*!
 * @class EtmV4ITrcPacket   
 * @brief ETMv4 Instuction Trace Protocol Packet.
 * @ingroup trc_pkts
 * 
 *  This class represents a single ETMv4 data trace packet, along with intra packet state.
 * 
 */
class EtmV4ITrcPacket :  public TrcPacketBase, public ocsd_etmv4_i_pkt, public trcPrintableElem
{
public:
    EtmV4ITrcPacket();
    ~EtmV4ITrcPacket();

    EtmV4ITrcPacket &operator =(const ocsd_etmv4_i_pkt* p_pkt);

    virtual const void *c_pkt() const { return (const ocsd_etmv4_i_pkt *)this; };

    // update interface - set packet values
    void initStartState();   //!< Set to initial state - no intra packet state valid. Use on start of trace / discontinuities.
    void initNextPacket();  //!< clear any single packet only flags / state.

    void setType(const ocsd_etmv4_i_pkt_type pkt_type) { type = pkt_type; };
    void updateErrType(const ocsd_etmv4_i_pkt_type err_pkt_type, const uint8_t val = 0);

    void clearTraceInfo();  //!< clear all the trace info data prior to setting for new trace info packet.
    void setTraceInfo(const uint32_t infoVal);
    void setTraceInfoKey(const uint32_t keyVal);
    void setTraceInfoSpec(const uint32_t specVal);
    void setTraceInfoCyct(const uint32_t cyctVal);

    void setTS(const uint64_t value, const uint8_t bits);
    void setCycleCount(const uint32_t value);
    void setCommitElements(const uint32_t commit_elem);
    void setCancelElements(const uint32_t cancel_elem);
    void setAtomPacket(const ocsd_pkt_atm_type type, const uint32_t En_bits, const uint8_t num);

    void setCondIF1(uint32_t const cond_key);
    void setCondIF2(uint8_t const c_elem_idx);
    void setCondIF3(uint8_t const num_c_elem, const bool finalElem);

    void setCondRF1(const uint32_t key[2], const uint8_t res[2], const uint8_t CI[2], const bool set2Keys);
    void setCondRF2(const uint8_t key_incr, const uint8_t token);
    void setCondRF3(const uint16_t tokens);
    void setCondRF4(const uint8_t token);

    void setContextInfo(const bool update, const uint8_t EL = 0, const uint8_t NS = 0, const uint8_t SF = 0, const uint8_t NSE = 0);
    void setContextVMID(const uint32_t VMID);
    void setContextCID(const uint32_t CID);

    void setExceptionInfo(const uint16_t excep_type, const uint8_t addr_interp, const uint8_t m_fault_pending, const uint8_t m_type);
    
    void set64BitAddress(const uint64_t addr, const uint8_t IS);
    void set32BitAddress(const uint32_t addr, const uint8_t IS);
    void updateShortAddress(const uint32_t addr, const uint8_t IS, const uint8_t update_bits);
    void setAddressExactMatch(const uint8_t idx);

    void setDataSyncMarker(const uint8_t dsm_val);
    void setEvent(const uint8_t event_val);

    void setQType(const bool has_count, const uint32_t count, const bool has_addr, const bool addr_match, const uint8_t type);
    void setITE(const uint8_t el, const uint64_t value);

    // packet status interface - get packet info.
    const ocsd_etmv4_i_pkt_type getType() const { return type; };
    const ocsd_etmv4_i_pkt_type getErrType() const { return err_type; };

    //! return true if this packet has set the commit packet count.
    const bool hasCommitElementsCount() const 
    {
        return pkt_valid.bits.commit_elem_valid ? true : false;
    };
    
    // trace info
    const etmv4_trace_info_t &getTraceInfo() const { return trace_info; };    
    const uint32_t getCCThreshold() const;
    const uint32_t getP0Key() const;
    const uint32_t getCurrSpecDepth() const;

    // atom
    const ocsd_pkt_atom &getAtom() const { return atom; };
    const int getNumAtoms() const { return atom.num; };

    // context
    const etmv4_context_t &getContext() const { return context; };

    // address
    const uint8_t &getAddrMatch() const  { return addr_exact_match_idx; };
    const ocsd_vaddr_t &getAddrVal() const { return v_addr.val; };
    const uint8_t &getAddrIS() const { return v_addr_ISA; };
    const bool getAddr64Bit() const { return v_addr.size == VA_64BIT; };

    // ts
    const uint64_t getTS() const { return pkt_valid.bits.ts_valid ? ts.timestamp : 0; };

    // cc
    const uint32_t getCC() const { return pkt_valid.bits.cc_valid ? cycle_count : 0; };

    // speculation
    const int getCommitElem() const { return commit_elements; };
    const int getCancelElem() const { return cancel_elements; };

    // ITE
    const uint8_t getITE_EL() const { return ite_pkt.el; };
    const uint64_t getITE_value() const { return ite_pkt.value; };

    // packet type
    const bool isBadPacket() const;

    // printing
    virtual void toString(std::string &str) const;
    virtual void toStringFmt(const uint32_t fmtFlags, std::string &str) const;

    void setProtocolVersion(const uint8_t version) { protocol_version = version; };

private:
    const char *packetTypeName(const ocsd_etmv4_i_pkt_type type, const char **pDesc) const;
    void contextStr(std::string &ctxtStr) const;
    void atomSeq(std::string &valStr) const;
    void addrMatchIdx(std::string &valStr) const;
    void exceptionInfo(std::string &valStr) const;

    void push_vaddr();
    void pop_vaddr_idx(const uint8_t idx);

    const bool isETE() const { return (protocol_version & 0xF0) == 0x50; };

    Etmv4PktAddrStack m_addr_stack;
};

inline void  EtmV4ITrcPacket::updateErrType(const ocsd_etmv4_i_pkt_type err_pkt_type, const uint8_t err_val /* = 0 */)
{
    // set primary type to incoming error type, set packet err type to previous primary type.
    err_type = type;
    type = err_pkt_type;
    err_hdr_val = err_val;
}

inline void EtmV4ITrcPacket::clearTraceInfo()
{
    pkt_valid.bits.ts_valid = 0;
    pkt_valid.bits.trace_info_valid = 0;
    pkt_valid.bits.p0_key_valid = 0;
    pkt_valid.bits.spec_depth_valid = 0;
    pkt_valid.bits.cc_thresh_valid  = 0;

    // set these as defaults - if they don't appear in TINFO this is the state.
    setTraceInfo(0);        
    setTraceInfoSpec(0);   

    // explicitly reset the stack & zero the current address. 
    m_addr_stack.reset_stack();
    m_addr_stack.get_idx(0, v_addr, v_addr_ISA);
}

inline void EtmV4ITrcPacket::setTraceInfo(const uint32_t infoVal)
{
    trace_info.val = infoVal;
    pkt_valid.bits.trace_info_valid = 1;
}

inline void EtmV4ITrcPacket::setTraceInfoKey(const uint32_t keyVal)
{
    p0_key = keyVal;
    pkt_valid.bits.p0_key_valid = 1;
}

inline void EtmV4ITrcPacket::setTraceInfoSpec(const uint32_t specVal)
{
    curr_spec_depth = specVal;
    pkt_valid.bits.spec_depth_valid = 1;
}

inline void EtmV4ITrcPacket::setTraceInfoCyct(const uint32_t cyctVal)
{
    cc_threshold = cyctVal;
    pkt_valid.bits.cc_thresh_valid  = 1;
}

inline void EtmV4ITrcPacket::setTS(const uint64_t value, const uint8_t bits)
{
    uint64_t mask = (uint64_t)-1LL;
    if(bits < 64) mask = (1ULL << bits) - 1;
    ts.timestamp = (ts.timestamp & ~mask) | (value & mask);
    ts.bits_changed = bits;
    pkt_valid.bits.ts_valid = 1;
}

inline void EtmV4ITrcPacket::setCycleCount(const uint32_t value)
{
    pkt_valid.bits.cc_valid = 1;
    cycle_count = value;
}

inline void EtmV4ITrcPacket::setCommitElements(const uint32_t commit_elem)
{
    pkt_valid.bits.commit_elem_valid = 1;
    commit_elements = commit_elem;
}

inline const uint32_t EtmV4ITrcPacket::getCCThreshold() const
{
    if(pkt_valid.bits.cc_thresh_valid)
        return cc_threshold;
    return 0;
}

inline const uint32_t EtmV4ITrcPacket::getP0Key() const
{
    if(pkt_valid.bits.p0_key_valid)
        return p0_key;
    return 0;
}

inline const uint32_t EtmV4ITrcPacket::getCurrSpecDepth() const
{
    if(pkt_valid.bits.spec_depth_valid)
        return curr_spec_depth;
    return 0;
}

inline void EtmV4ITrcPacket::setCancelElements(const uint32_t cancel_elem)
{
    cancel_elements = cancel_elem;
}

inline void EtmV4ITrcPacket::setAtomPacket(const ocsd_pkt_atm_type type, const uint32_t En_bits, const uint8_t num)
{
    if(type == ATOM_REPEAT)
    {
        uint32_t bit_patt = En_bits & 0x1;
        if(bit_patt)
        {   
            // none zero - all 1s
            bit_patt = (bit_patt << num) - 1;
        }
        atom.En_bits = bit_patt;
    }
    else
        atom.En_bits = En_bits;
    atom.num = num;    
}

inline void EtmV4ITrcPacket::setCondIF1(const uint32_t cond_key)
{
    cond_instr.cond_key_set = 1;
    cond_instr.f3_final_elem = 0;
    cond_instr.f2_cond_incr = 0;
    cond_instr.num_c_elem = 1;
    cond_instr.cond_c_key = cond_key;
}

inline void EtmV4ITrcPacket::setCondIF2(const uint8_t c_elem_idx)
{
    cond_instr.cond_key_set = 0;
    cond_instr.f3_final_elem = 0;
    switch(c_elem_idx & 0x3)
    {
    case 0:
        cond_instr.f2_cond_incr = 1;
        cond_instr.num_c_elem = 1;
        break;

    case 1:
        cond_instr.f2_cond_incr = 0;
        cond_instr.num_c_elem = 1;
        break;

    case 2:
        cond_instr.f2_cond_incr = 1;
        cond_instr.num_c_elem = 2;
        break;
    }
}

inline void EtmV4ITrcPacket::setCondIF3(const uint8_t num_c_elem, const bool finalElem)
{
    cond_instr.cond_key_set = 0;
    cond_instr.f3_final_elem = finalElem ? 1: 0;
    cond_instr.f2_cond_incr = 0;
    cond_instr.num_c_elem = num_c_elem;
}

inline void EtmV4ITrcPacket::setCondRF1(const uint32_t key[2], const uint8_t res[2], const uint8_t CI[2],const bool set2Keys)
{
    cond_result.key_res_0_set = 1;
    cond_result.cond_r_key_0 = key[0];
    cond_result.res_0 = res[0];
    cond_result.ci_0 = CI[0];

    if(set2Keys)
    {
        cond_result.key_res_1_set = 1;
        cond_result.cond_r_key_1 = key[1];
        cond_result.res_1 = res[1];
        cond_result.ci_1 = CI[1];
    }
}


inline void EtmV4ITrcPacket::setCondRF2(const uint8_t key_incr, const uint8_t token)
{
    cond_result.key_res_0_set = 0;
    cond_result.key_res_1_set = 0;
    cond_result.f2_key_incr = key_incr;
    cond_result.f2f4_token = token;
}

inline void EtmV4ITrcPacket::setCondRF3(const uint16_t tokens)
{
    cond_result.key_res_0_set = 0;
    cond_result.key_res_1_set = 0;
    cond_result.f3_tokens = tokens;
}

inline void EtmV4ITrcPacket::setCondRF4(const uint8_t token)
{
    cond_result.key_res_0_set = 0;
    cond_result.key_res_1_set = 0;
    cond_result.f2f4_token = token;
}

inline void EtmV4ITrcPacket::setContextInfo(const bool update, const uint8_t EL, const uint8_t NS, const uint8_t SF, const uint8_t NSE)
{
    pkt_valid.bits.context_valid = 1;
    if(update)
    {
        context.updated = 1;
        context.EL = EL;
        context.NS = NS;
        context.SF = SF;
        context.NSE = NSE;
    }
}

inline void EtmV4ITrcPacket::setContextVMID(const uint32_t VMID)
{
    pkt_valid.bits.context_valid = 1;
    context.updated = 1;
    context.VMID = VMID;
    context.updated_v = 1;
}

inline void EtmV4ITrcPacket::setContextCID(const uint32_t CID)
{
    pkt_valid.bits.context_valid = 1;
    context.updated = 1;
    context.ctxtID = CID;
    context.updated_c = 1;
}

inline void EtmV4ITrcPacket::setExceptionInfo(const uint16_t excep_type, const uint8_t addr_interp, const uint8_t m_fault_pending, const uint8_t m_type)
{
    exception_info.exceptionType = excep_type;
    exception_info.addr_interp = addr_interp;
    exception_info.m_fault_pending = m_fault_pending;
    exception_info.m_type = m_type;
}

inline void EtmV4ITrcPacket::set64BitAddress(const uint64_t addr, const uint8_t IS)
{
    v_addr.pkt_bits = 64;
    v_addr.valid_bits = 64;
    v_addr.size = VA_64BIT;
    v_addr.val = addr;
    v_addr_ISA = IS;
    push_vaddr();
}

inline void EtmV4ITrcPacket::set32BitAddress(const uint32_t addr, const uint8_t IS)
{
    uint64_t mask = OCSD_BIT_MASK(32);
    v_addr.pkt_bits = 32;

	if (pkt_valid.bits.context_valid && context.SF)
	{
		v_addr.size = VA_64BIT;
		v_addr.val = (v_addr.val & ~mask) | (addr & mask);
	}
    else
    {
		v_addr.val = addr;
        v_addr.size = VA_32BIT;
	}

    if (v_addr.valid_bits < 32) // may be updating a 64 bit address so only set 32 if currently less.
        v_addr.valid_bits = 32;

    v_addr_ISA = IS;
    push_vaddr();
}

inline void EtmV4ITrcPacket::updateShortAddress(const uint32_t addr, const uint8_t IS, const uint8_t update_bits)
{
    ocsd_vaddr_t update_mask = OCSD_BIT_MASK(update_bits);
    v_addr.pkt_bits = update_bits;
    if(v_addr.valid_bits < update_bits)
        v_addr.valid_bits = update_bits;

    v_addr.val = (v_addr.val & ~update_mask) | (addr & update_mask);
    v_addr_ISA = IS;
    push_vaddr();
}

inline void EtmV4ITrcPacket::setAddressExactMatch(const uint8_t idx)
{
    addr_exact_match_idx = idx;   
    pop_vaddr_idx(idx);
    push_vaddr();
}

inline void EtmV4ITrcPacket::setDataSyncMarker(const uint8_t dsm_value)
{
    dsm_val = dsm_value;
}

inline void EtmV4ITrcPacket::setEvent(const uint8_t event_value)
{
    event_val = event_value;
}

inline void EtmV4ITrcPacket::setQType(const bool has_count, const uint32_t count, const bool has_addr, const bool addr_match, const uint8_t type)
{
    Q_pkt.q_count = count;
    Q_pkt.q_type = type;
    Q_pkt.count_present = has_count ? 1 : 0;
    Q_pkt.addr_present = has_addr ? 1: 0;
    Q_pkt.addr_match = addr_match ? 1 :0;
}

inline const bool EtmV4ITrcPacket::isBadPacket() const
{
    return (type >= ETM4_PKT_I_BAD_SEQUENCE);
}

inline void  EtmV4ITrcPacket::push_vaddr()
{
    m_addr_stack.push(v_addr, v_addr_ISA);
}

inline void EtmV4ITrcPacket::pop_vaddr_idx(const uint8_t idx)
{
    m_addr_stack.get_idx(idx, v_addr, v_addr_ISA);
}

inline void EtmV4ITrcPacket::setITE(const uint8_t el, const uint64_t value)
{
    ite_pkt.el = el;
    ite_pkt.value = value;
}

/** @}*/

#endif // ARM_TRC_PKT_ELEM_ETMV4I_H_INCLUDED

/* End of File trc_pkt_elem_etmv4i.h */
