/*
 * Copyright (c) 2010 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2007-2008 The Florida State University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * 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;
 * neither the name of the copyright holders 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
 * OWNER 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.
 *
 * Authors: Stephen Hines
 */

#ifndef __ARCH_ARM_TYPES_HH__
#define __ARCH_ARM_TYPES_HH__

#include "arch/generic/types.hh"
#include "base/bitunion.hh"
#include "base/hashmap.hh"
#include "base/misc.hh"
#include "base/types.hh"
#include "debug/Predecoder.hh"

namespace ArmISA
{
    typedef uint32_t MachInst;

    BitUnion8(ITSTATE)
        /* Note that the split (cond, mask) below is not as in ARM ARM.
         * But it is more convenient for simulation. The condition
         * is always the concatenation of the top 3 bits and the next bit,
         * which applies when one of the bottom 4 bits is set.
         * Refer to predecoder.cc for the use case.
         */
        Bitfield<7, 4> cond;
        Bitfield<3, 0> mask;
        // Bitfields for moving to/from CPSR
        Bitfield<7, 2> top6;
        Bitfield<1, 0> bottom2;
    EndBitUnion(ITSTATE)


    BitUnion64(ExtMachInst)
        // ITSTATE bits
        Bitfield<55, 48> itstate;
        Bitfield<55, 52> itstateCond;
        Bitfield<51, 48> itstateMask;

        // FPSCR fields
        Bitfield<41, 40> fpscrStride;
        Bitfield<39, 37> fpscrLen;

        // Bitfields to select mode.
        Bitfield<36>     thumb;
        Bitfield<35>     bigThumb;

        // Made up bitfields that make life easier.
        Bitfield<33>     sevenAndFour;
        Bitfield<32>     isMisc;

        uint32_t         instBits;

        // All the different types of opcode fields.
        Bitfield<27, 25> encoding;
        Bitfield<25>     useImm;
        Bitfield<24, 21> opcode;
        Bitfield<24, 20> mediaOpcode;
        Bitfield<24>     opcode24;
        Bitfield<24, 23> opcode24_23;
        Bitfield<23, 20> opcode23_20;
        Bitfield<23, 21> opcode23_21;
        Bitfield<20>     opcode20;
        Bitfield<22>     opcode22;
        Bitfield<19, 16> opcode19_16;
        Bitfield<19>     opcode19;
        Bitfield<18>     opcode18;
        Bitfield<15, 12> opcode15_12;
        Bitfield<15>     opcode15;
        Bitfield<7,  4>  miscOpcode;
        Bitfield<7,5>    opc2;
        Bitfield<7>      opcode7;
        Bitfield<6>      opcode6;
        Bitfield<4>      opcode4;

        Bitfield<31, 28> condCode;
        Bitfield<20>     sField;
        Bitfield<19, 16> rn;
        Bitfield<15, 12> rd;
        Bitfield<15, 12> rt;
        Bitfield<11, 7>  shiftSize;
        Bitfield<6,  5>  shift;
        Bitfield<3,  0>  rm;

        Bitfield<11, 8>  rs;

        SubBitUnion(puswl, 24, 20)
            Bitfield<24> prepost;
            Bitfield<23> up;
            Bitfield<22> psruser;
            Bitfield<21> writeback;
            Bitfield<20> loadOp;
        EndSubBitUnion(puswl)

        Bitfield<24, 20> pubwl;

        Bitfield<7, 0> imm;

        Bitfield<11, 8>  rotate;

        Bitfield<11, 0>  immed11_0;
        Bitfield<7,  0>  immed7_0;

        Bitfield<11, 8>  immedHi11_8;
        Bitfield<3,  0>  immedLo3_0;

        Bitfield<15, 0>  regList;
        
        Bitfield<23, 0>  offset;
        
        Bitfield<23, 0>  immed23_0;

        Bitfield<11, 8>  cpNum;
        Bitfield<18, 16> fn;
        Bitfield<14, 12> fd;
        Bitfield<3>      fpRegImm;
        Bitfield<3,  0>  fm;
        Bitfield<2,  0>  fpImm;
        Bitfield<24, 20> punwl;

        Bitfield<15,  8>  m5Func;

        // 16 bit thumb bitfields
        Bitfield<15, 13> topcode15_13;
        Bitfield<13, 11> topcode13_11;
        Bitfield<12, 11> topcode12_11;
        Bitfield<12, 10> topcode12_10;
        Bitfield<11, 9>  topcode11_9;
        Bitfield<11, 8>  topcode11_8;
        Bitfield<10, 9>  topcode10_9;
        Bitfield<10, 8>  topcode10_8;
        Bitfield<9,  6>  topcode9_6;
        Bitfield<7>      topcode7;
        Bitfield<7, 6>   topcode7_6;
        Bitfield<7, 5>   topcode7_5;
        Bitfield<7, 4>   topcode7_4;
        Bitfield<3, 0>   topcode3_0;

        // 32 bit thumb bitfields
        Bitfield<28, 27> htopcode12_11;
        Bitfield<26, 25> htopcode10_9;
        Bitfield<25>     htopcode9;
        Bitfield<25, 24> htopcode9_8;
        Bitfield<25, 21> htopcode9_5;
        Bitfield<25, 20> htopcode9_4;
        Bitfield<24>     htopcode8;
        Bitfield<24, 23> htopcode8_7;
        Bitfield<24, 22> htopcode8_6;
        Bitfield<24, 21> htopcode8_5;
        Bitfield<23>     htopcode7;
        Bitfield<23, 21> htopcode7_5;
        Bitfield<22>     htopcode6;
        Bitfield<22, 21> htopcode6_5;
        Bitfield<21, 20> htopcode5_4;
        Bitfield<20>     htopcode4;

        Bitfield<19, 16> htrn;
        Bitfield<20>     hts;

        Bitfield<15>     ltopcode15;
        Bitfield<11, 8>  ltopcode11_8;
        Bitfield<7,  6>  ltopcode7_6;
        Bitfield<7,  4>  ltopcode7_4;
        Bitfield<4>      ltopcode4;

        Bitfield<11, 8>  ltrd;
        Bitfield<11, 8>  ltcoproc;
    EndBitUnion(ExtMachInst)

    class PCState : public GenericISA::UPCState<MachInst>
    {
      protected:

        typedef GenericISA::UPCState<MachInst> Base;

        enum FlagBits {
            ThumbBit = (1 << 0),
            JazelleBit = (1 << 1)
        };
        uint8_t flags;
        uint8_t nextFlags;
        uint8_t _itstate;
        uint8_t _nextItstate;
        uint8_t _size;
      public:
        PCState() : flags(0), nextFlags(0), _itstate(0), _nextItstate(0)
        {}

        void
        set(Addr val)
        {
            Base::set(val);
            npc(val + (thumb() ? 2 : 4));
        }

        PCState(Addr val) : flags(0), nextFlags(0), _itstate(0), _nextItstate(0)
        { set(val); }

        bool
        thumb() const
        {
            return flags & ThumbBit;
        }

        void
        thumb(bool val)
        {
            if (val)
                flags |= ThumbBit;
            else
                flags &= ~ThumbBit;
        }

        bool
        nextThumb() const
        {
            return nextFlags & ThumbBit;
        }

        void
        nextThumb(bool val)
        {
            if (val)
                nextFlags |= ThumbBit;
            else
                nextFlags &= ~ThumbBit;
        }

        void size(uint8_t s) { _size = s; }
        uint8_t size() const { return _size; }

        bool
        branching() const
        {
            return ((this->pc() + this->size()) != this->npc());
        }


        bool
        jazelle() const
        {
            return flags & JazelleBit;
        }

        void
        jazelle(bool val)
        {
            if (val)
                flags |= JazelleBit;
            else
                flags &= ~JazelleBit;
        }

        bool
        nextJazelle() const
        {
            return nextFlags & JazelleBit;
        }

        void
        nextJazelle(bool val)
        {
            if (val)
                nextFlags |= JazelleBit;
            else
                nextFlags &= ~JazelleBit;
        }

        uint8_t
        itstate() const
        {
            return _itstate;
        }

        void
        itstate(uint8_t value)
        {
            _itstate = value;
        }

        uint8_t
        nextItstate() const
        {
            return _nextItstate;
        }

        void
        nextItstate(uint8_t value)
        {
            _nextItstate = value;
        }

        void
        advance()
        {
            Base::advance();
            npc(pc() + (thumb() ? 2 : 4));
            flags = nextFlags;

            if (_nextItstate) {
                _itstate = _nextItstate;
                _nextItstate = 0;
            } else if (_itstate) {
                ITSTATE it = _itstate;
                uint8_t cond_mask = it.mask;
                uint8_t thumb_cond = it.cond;
                DPRINTF(Predecoder, "Advancing ITSTATE from %#x,%#x.\n",
                        thumb_cond, cond_mask);
                cond_mask <<= 1;
                uint8_t new_bit = bits(cond_mask, 4);
                cond_mask &= mask(4);
                if (cond_mask == 0)
                    thumb_cond = 0;
                else
                    replaceBits(thumb_cond, 0, new_bit);
                DPRINTF(Predecoder, "Advancing ITSTATE to %#x,%#x.\n",
                        thumb_cond, cond_mask);
                it.mask = cond_mask;
                it.cond = thumb_cond;
                _itstate = it;
            }
        }

        void
        uEnd()
        {
            advance();
            upc(0);
            nupc(1);
        }

        Addr
        instPC() const
        {
            return pc() + (thumb() ? 4 : 8);
        }

        void
        instNPC(uint32_t val)
        {
            npc(val &~ mask(nextThumb() ? 1 : 2));
        }

        Addr
        instNPC() const
        {
            return npc();
        }

        // Perform an interworking branch.
        void
        instIWNPC(uint32_t val)
        {
            bool thumbEE = (thumb() && jazelle());

            Addr newPC = val;
            if (thumbEE) {
                if (bits(newPC, 0)) {
                    newPC = newPC & ~mask(1);
                }  // else we have a bad interworking address; do not call
                   // panic() since the instruction could be executed
                   // speculatively
            } else {
                if (bits(newPC, 0)) {
                    nextThumb(true);
                    newPC = newPC & ~mask(1);
                } else if (!bits(newPC, 1)) {
                    nextThumb(false);
                } else {
                    // This state is UNPREDICTABLE in the ARM architecture
                    // The easy thing to do is just mask off the bit and
                    // stay in the current mode, so we'll do that.
                    newPC &= ~mask(2);
                }
            }
            npc(newPC);
        }

        // Perform an interworking branch in ARM mode, a regular branch
        // otherwise.
        void
        instAIWNPC(uint32_t val)
        {
            if (!thumb() && !jazelle())
                instIWNPC(val);
            else
                instNPC(val);
        }

        bool
        operator == (const PCState &opc) const
        {
            return Base::operator == (opc) &&
                flags == opc.flags && nextFlags == opc.nextFlags &&
                _itstate == opc._itstate && _nextItstate == opc._nextItstate;
        }

        void
        serialize(std::ostream &os)
        {
            Base::serialize(os);
            SERIALIZE_SCALAR(flags);
            SERIALIZE_SCALAR(_size);
            SERIALIZE_SCALAR(nextFlags);
            SERIALIZE_SCALAR(_itstate);
            SERIALIZE_SCALAR(_nextItstate);
        }

        void
        unserialize(Checkpoint *cp, const std::string &section)
        {
            Base::unserialize(cp, section);
            UNSERIALIZE_SCALAR(flags);
            UNSERIALIZE_SCALAR(_size);
            UNSERIALIZE_SCALAR(nextFlags);
            UNSERIALIZE_SCALAR(_itstate);
            UNSERIALIZE_SCALAR(_nextItstate);
        }
    };

    // Shift types for ARM instructions
    enum ArmShiftType {
        LSL = 0,
        LSR,
        ASR,
        ROR
    };

    typedef uint64_t LargestRead;
    // Need to use 64 bits to make sure that read requests get handled properly

    typedef int RegContextParam;
    typedef int RegContextVal;

    //used in FP convert & round function
    enum ConvertType{
        SINGLE_TO_DOUBLE,
        SINGLE_TO_WORD,
        SINGLE_TO_LONG,

        DOUBLE_TO_SINGLE,
        DOUBLE_TO_WORD,
        DOUBLE_TO_LONG,

        LONG_TO_SINGLE,
        LONG_TO_DOUBLE,
        LONG_TO_WORD,
        LONG_TO_PS,

        WORD_TO_SINGLE,
        WORD_TO_DOUBLE,
        WORD_TO_LONG,
        WORD_TO_PS,

        PL_TO_SINGLE,
        PU_TO_SINGLE
    };

    //used in FP convert & round function
    enum RoundMode{
        RND_ZERO,
        RND_DOWN,
        RND_UP,
        RND_NEAREST
    };

    enum OperatingMode {
        MODE_USER = 16,
        MODE_FIQ = 17,
        MODE_IRQ = 18,
        MODE_SVC = 19,
        MODE_MON = 22,
        MODE_ABORT = 23,
        MODE_UNDEFINED = 27,
        MODE_SYSTEM = 31,
        MODE_MAXMODE = MODE_SYSTEM
    };

    static inline bool
    badMode(OperatingMode mode)
    {
        switch (mode) {
          case MODE_USER:
          case MODE_FIQ:
          case MODE_IRQ:
          case MODE_SVC:
          case MODE_MON:
          case MODE_ABORT:
          case MODE_UNDEFINED:
          case MODE_SYSTEM:
            return false;
          default:
            return true;
        }
    }

} // namespace ArmISA

namespace __hash_namespace {
    template<>
    struct hash<ArmISA::ExtMachInst> : public hash<uint32_t> {
        size_t operator()(const ArmISA::ExtMachInst &emi) const {
            return hash<uint32_t>::operator()((uint32_t)emi);
        };
    };
}

#endif
