| /* |
| * Copyright (c) 2020 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) 2010 Gabe Black |
| * 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. |
| */ |
| |
| #ifndef __ARCH_GENERIC_TYPES_HH__ |
| #define __ARCH_GENERIC_TYPES_HH__ |
| |
| #include <iostream> |
| #include <memory> |
| #include <type_traits> |
| |
| #include "base/compiler.hh" |
| #include "base/trace.hh" |
| #include "base/types.hh" |
| #include "sim/serialize.hh" |
| |
| namespace gem5 |
| { |
| |
| // The guaranteed interface. |
| class PCStateBase : public Serializable |
| { |
| protected: |
| Addr _pc = 0; |
| MicroPC _upc = 0; |
| |
| PCStateBase(const PCStateBase &other) : _pc(other._pc), _upc(other._upc) {} |
| PCStateBase &operator=(const PCStateBase &other) = default; |
| PCStateBase() {} |
| |
| public: |
| virtual ~PCStateBase() = default; |
| |
| template<class Target> |
| Target & |
| as() |
| { |
| return static_cast<Target &>(*this); |
| } |
| |
| template<class Target> |
| const Target & |
| as() const |
| { |
| return static_cast<const Target &>(*this); |
| } |
| |
| virtual PCStateBase *clone() const = 0; |
| virtual void |
| update(const PCStateBase &other) |
| { |
| _pc = other._pc; |
| _upc = other._upc; |
| } |
| void update(const PCStateBase *ptr) { update(*ptr); } |
| |
| virtual void output(std::ostream &os) const = 0; |
| |
| virtual bool |
| equals(const PCStateBase &other) const |
| { |
| return _pc == other._pc && _upc == other._upc; |
| } |
| |
| /** |
| * Returns the memory address of the instruction this PC points to. |
| * |
| * @return Memory address of the instruction this PC points to. |
| */ |
| Addr |
| instAddr() const |
| { |
| return _pc; |
| } |
| |
| /** |
| * Returns the current micropc. |
| * |
| * @return The current micropc. |
| */ |
| MicroPC |
| microPC() const |
| { |
| return _upc; |
| } |
| |
| virtual void |
| uReset() |
| { |
| _upc = 0; |
| } |
| |
| virtual void advance() = 0; |
| virtual bool branching() const = 0; |
| |
| void |
| serialize(CheckpointOut &cp) const override |
| { |
| SERIALIZE_SCALAR(_pc); |
| SERIALIZE_SCALAR(_upc); |
| } |
| |
| void |
| unserialize(CheckpointIn &cp) override |
| { |
| UNSERIALIZE_SCALAR(_pc); |
| UNSERIALIZE_SCALAR(_upc); |
| } |
| }; |
| |
| static inline std::ostream & |
| operator<<(std::ostream & os, const PCStateBase &pc) |
| { |
| pc.output(os); |
| return os; |
| } |
| |
| static inline bool |
| operator==(const PCStateBase &a, const PCStateBase &b) |
| { |
| return a.equals(b); |
| } |
| |
| static inline bool |
| operator!=(const PCStateBase &a, const PCStateBase &b) |
| { |
| return !a.equals(b); |
| } |
| |
| namespace |
| { |
| |
| inline void |
| set(PCStateBase *&dest, const PCStateBase *src) |
| { |
| if (GEM5_LIKELY(dest)) { |
| if (GEM5_LIKELY(src)) { |
| // Both src and dest already have storage, so just copy contents. |
| dest->update(src); |
| } else { |
| // src is empty, so clear out dest. |
| dest = nullptr; |
| } |
| } else { |
| if (GEM5_LIKELY(src)) { |
| // dest doesn't have storage, so create some as a copy of src. |
| dest = src->clone(); |
| } else { |
| // dest is already nullptr, so nothing to do. |
| } |
| } |
| } |
| |
| inline void |
| set(std::unique_ptr<PCStateBase> &dest, const PCStateBase *src) |
| { |
| PCStateBase *dest_ptr = dest.get(); |
| set(dest_ptr, src); |
| if (dest.get() != dest_ptr) |
| dest.reset(dest_ptr); |
| } |
| |
| inline void |
| set(PCStateBase *&dest, const std::unique_ptr<PCStateBase> &src) |
| { |
| const PCStateBase *src_ptr = src.get(); |
| set(dest, src_ptr); |
| } |
| |
| inline void |
| set(std::unique_ptr<PCStateBase> &dest, |
| const std::unique_ptr<PCStateBase> &src) |
| { |
| PCStateBase *dest_ptr = dest.get(); |
| const PCStateBase *src_ptr = src.get(); |
| set(dest_ptr, src_ptr); |
| if (dest.get() != dest_ptr) |
| dest.reset(dest_ptr); |
| } |
| |
| inline void |
| set(PCStateBase *&dest, const PCStateBase &src) |
| { |
| if (GEM5_LIKELY(dest)) { |
| // Update dest with the contents of src. |
| dest->update(src); |
| } else { |
| // Clone src over to dest. |
| dest = src.clone(); |
| } |
| } |
| |
| inline void |
| set(std::unique_ptr<PCStateBase> &dest, const PCStateBase &src) |
| { |
| PCStateBase *dest_ptr = dest.get(); |
| set(dest_ptr, src); |
| if (dest.get() != dest_ptr) |
| dest.reset(dest_ptr); |
| } |
| |
| inline void |
| set(PCStateBase &dest, const PCStateBase &src) |
| { |
| dest.update(src); |
| } |
| |
| } // anonymous namespace |
| |
| namespace GenericISA |
| { |
| |
| class PCStateWithNext : public PCStateBase |
| { |
| protected: |
| Addr _npc = 0; |
| |
| MicroPC _nupc = 1; |
| |
| PCStateWithNext(const PCStateWithNext &other) : PCStateBase(other), |
| _npc(other._npc), _nupc(other._nupc) |
| {} |
| PCStateWithNext &operator=(const PCStateWithNext &other) = default; |
| PCStateWithNext() {} |
| |
| public: |
| Addr pc() const { return _pc; } |
| void pc(Addr val) { _pc = val; } |
| |
| Addr npc() const { return _npc; } |
| void npc(Addr val) { _npc = val; } |
| |
| MicroPC upc() const { return _upc; } |
| void upc(MicroPC val) { _upc = val; } |
| |
| MicroPC nupc() const { return _nupc; } |
| void nupc(MicroPC val) { _nupc = val; } |
| |
| // Reset the macroop's upc without advancing the regular pc. |
| void |
| uReset() override |
| { |
| PCStateBase::uReset(); |
| _nupc = 1; |
| } |
| |
| void |
| setNPC(Addr val) |
| { |
| npc(val); |
| } |
| |
| void |
| output(std::ostream &os) const override |
| { |
| ccprintf(os, "(%#x=>%#x)", this->pc(), this->npc()); |
| } |
| |
| void |
| update(const PCStateBase &other) override |
| { |
| PCStateBase::update(other); |
| auto &pcstate = other.as<PCStateWithNext>(); |
| _npc = pcstate._npc; |
| _nupc = pcstate._nupc; |
| } |
| |
| bool |
| equals(const PCStateBase &other) const override |
| { |
| auto &ps = other.as<PCStateWithNext>(); |
| return PCStateBase::equals(other) && |
| _npc == ps._npc && _nupc == ps._nupc; |
| } |
| |
| void |
| serialize(CheckpointOut &cp) const override |
| { |
| PCStateBase::serialize(cp); |
| SERIALIZE_SCALAR(_npc); |
| SERIALIZE_SCALAR(_nupc); |
| } |
| |
| void |
| unserialize(CheckpointIn &cp) override |
| { |
| PCStateBase::unserialize(cp); |
| UNSERIALIZE_SCALAR(_npc); |
| UNSERIALIZE_SCALAR(_nupc); |
| } |
| }; |
| |
| |
| /* |
| * Different flavors of PC state. Only ISA specific code should rely on |
| * any particular type of PC state being available. All other code should |
| * use the interface above. |
| */ |
| |
| // The most basic type of PC. |
| template <int InstWidth> |
| class SimplePCState : public PCStateWithNext |
| { |
| protected: |
| typedef PCStateWithNext Base; |
| |
| public: |
| SimplePCState(const SimplePCState &other) : Base(other) {} |
| SimplePCState &operator=(const SimplePCState &other) = default; |
| SimplePCState() {} |
| explicit SimplePCState(Addr val) { set(val); } |
| |
| PCStateBase * |
| clone() const override |
| { |
| return new SimplePCState<InstWidth>(*this); |
| } |
| |
| /** |
| * Force this PC to reflect a particular value, resetting all its other |
| * fields around it. This is useful for in place (re)initialization. |
| * |
| * @param val The value to set the PC to. |
| */ |
| void |
| set(Addr val) |
| { |
| this->pc(val); |
| this->npc(val + InstWidth); |
| }; |
| |
| bool |
| branching() const override |
| { |
| return this->npc() != this->pc() + InstWidth; |
| } |
| |
| // Advance the PC. |
| void |
| advance() override |
| { |
| this->_pc = this->_npc; |
| this->_npc += InstWidth; |
| } |
| }; |
| |
| // A PC and microcode PC. |
| template <int InstWidth> |
| class UPCState : public SimplePCState<InstWidth> |
| { |
| protected: |
| typedef SimplePCState<InstWidth> Base; |
| |
| public: |
| void |
| output(std::ostream &os) const override |
| { |
| Base::output(os); |
| ccprintf(os, ".(%d=>%d)", this->upc(), this->nupc()); |
| } |
| |
| PCStateBase * |
| clone() const override |
| { |
| return new UPCState<InstWidth>(*this); |
| } |
| |
| void |
| set(Addr val) |
| { |
| Base::set(val); |
| this->upc(0); |
| this->nupc(1); |
| } |
| |
| UPCState(const UPCState &other) : Base(other) {} |
| UPCState &operator=(const UPCState &other) = default; |
| UPCState() {} |
| explicit UPCState(Addr val) { set(val); } |
| |
| bool |
| branching() const override |
| { |
| return this->npc() != this->pc() + InstWidth || |
| this->nupc() != this->upc() + 1; |
| } |
| |
| // Advance the upc within the instruction. |
| void |
| uAdvance() |
| { |
| this->upc(this->nupc()); |
| this->nupc(this->nupc() + 1); |
| } |
| |
| // End the macroop by resetting the upc and advancing the regular pc. |
| void |
| uEnd() |
| { |
| this->advance(); |
| this->upc(0); |
| this->nupc(1); |
| } |
| }; |
| |
| // A PC with a delay slot. |
| template <int InstWidth> |
| class DelaySlotPCState : public SimplePCState<InstWidth> |
| { |
| protected: |
| typedef SimplePCState<InstWidth> Base; |
| |
| Addr _nnpc; |
| |
| public: |
| void |
| output(std::ostream &os) const override |
| { |
| ccprintf(os, "(%#x=>%#x=>%#x)", this->pc(), this->npc(), nnpc()); |
| } |
| |
| PCStateBase * |
| clone() const override |
| { |
| return new DelaySlotPCState<InstWidth>(*this); |
| } |
| |
| void |
| update(const PCStateBase &other) override |
| { |
| Base::update(other); |
| auto &pcstate = other.as<DelaySlotPCState<InstWidth>>(); |
| _nnpc = pcstate._nnpc; |
| } |
| |
| Addr nnpc() const { return _nnpc; } |
| void nnpc(Addr val) { _nnpc = val; } |
| |
| void |
| set(Addr val) |
| { |
| Base::set(val); |
| nnpc(val + 2 * InstWidth); |
| } |
| |
| DelaySlotPCState(const DelaySlotPCState &other) : |
| Base(other), _nnpc(other._nnpc) |
| {} |
| DelaySlotPCState &operator=(const DelaySlotPCState &other) = default; |
| DelaySlotPCState() {} |
| explicit DelaySlotPCState(Addr val) { set(val); } |
| |
| bool |
| branching() const override |
| { |
| return !(this->nnpc() == this->npc() + InstWidth && |
| (this->npc() == this->pc() + InstWidth || |
| this->npc() == this->pc() + 2 * InstWidth)); |
| } |
| |
| // Advance the PC. |
| void |
| advance() override |
| { |
| this->_pc = this->_npc; |
| this->_npc = this->_nnpc; |
| this->_nnpc += InstWidth; |
| } |
| |
| bool |
| equals(const PCStateBase &other) const override |
| { |
| auto &ps = other.as<DelaySlotPCState<InstWidth>>(); |
| return Base::equals(other) && ps._nnpc == this->_nnpc; |
| } |
| |
| void |
| serialize(CheckpointOut &cp) const override |
| { |
| Base::serialize(cp); |
| SERIALIZE_SCALAR(_nnpc); |
| } |
| |
| void |
| unserialize(CheckpointIn &cp) override |
| { |
| Base::unserialize(cp); |
| UNSERIALIZE_SCALAR(_nnpc); |
| } |
| }; |
| |
| // A PC with a delay slot and a microcode PC. |
| template <int InstWidth> |
| class DelaySlotUPCState : public DelaySlotPCState<InstWidth> |
| { |
| protected: |
| typedef DelaySlotPCState<InstWidth> Base; |
| |
| public: |
| void |
| output(std::ostream &os) const override |
| { |
| Base::output(os); |
| ccprintf(os, ".(%d=>%d)", this->upc(), this->nupc()); |
| } |
| |
| PCStateBase * |
| clone() const override |
| { |
| return new DelaySlotUPCState<InstWidth>(*this); |
| } |
| |
| void |
| set(Addr val) |
| { |
| Base::set(val); |
| this->upc(0); |
| this->nupc(1); |
| } |
| |
| DelaySlotUPCState(const DelaySlotUPCState &other) : Base(other) {} |
| DelaySlotUPCState &operator=(const DelaySlotUPCState &other) = default; |
| DelaySlotUPCState() {} |
| explicit DelaySlotUPCState(Addr val) { set(val); } |
| |
| bool |
| branching() const override |
| { |
| return Base::branching() || this->nupc() != this->upc() + 1; |
| } |
| |
| // Advance the upc within the instruction. |
| void |
| uAdvance() |
| { |
| this->_upc = this->_nupc; |
| this->_nupc++; |
| } |
| |
| // End the macroop by resetting the upc and advancing the regular pc. |
| void |
| uEnd() |
| { |
| this->advance(); |
| this->_upc = 0; |
| this->_nupc = 1; |
| } |
| }; |
| |
| } |
| |
| } // namespace gem5 |
| |
| #endif |