| /* | |
| * Copyright (c) 2011-2015 Advanced Micro Devices, Inc. | |
| * 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 __GPU_TLB_HH__ | |
| #define __GPU_TLB_HH__ | |
| #include <fstream> | |
| #include <list> | |
| #include <queue> | |
| #include <string> | |
| #include <vector> | |
| #include "arch/generic/tlb.hh" | |
| #include "arch/x86/pagetable.hh" | |
| #include "arch/x86/pagetable_walker.hh" | |
| #include "arch/x86/regs/segment.hh" | |
| #include "base/callback.hh" | |
| #include "base/logging.hh" | |
| #include "base/statistics.hh" | |
| #include "base/stats/group.hh" | |
| #include "gpu-compute/compute_unit.hh" | |
| #include "mem/port.hh" | |
| #include "mem/request.hh" | |
| #include "params/X86GPUTLB.hh" | |
| #include "sim/clocked_object.hh" | |
| #include "sim/sim_object.hh" | |
| namespace gem5 | |
| { | |
| class BaseTLB; | |
| class Packet; | |
| class ThreadContext; | |
| namespace X86ISA | |
| { | |
| class GpuTLB : public ClockedObject | |
| { | |
| protected: | |
| friend class Walker; | |
| typedef std::list<TlbEntry*> EntryList; | |
| uint32_t configAddress; | |
| public: | |
| typedef X86GPUTLBParams Params; | |
| GpuTLB(const Params &p); | |
| ~GpuTLB(); | |
| typedef enum BaseMMU::Mode Mode; | |
| class Translation | |
| { | |
| public: | |
| virtual ~Translation() { } | |
| /** | |
| * Signal that the translation has been delayed due to a hw page | |
| * table walk. | |
| */ | |
| virtual void markDelayed() = 0; | |
| /** | |
| * The memory for this object may be dynamically allocated, and it | |
| * may be responsible for cleaning itslef up which will happen in | |
| * this function. Once it's called the object is no longer valid. | |
| */ | |
| virtual void finish(Fault fault, const RequestPtr &req, | |
| ThreadContext *tc, Mode mode) = 0; | |
| }; | |
| void dumpAll(); | |
| TlbEntry *lookup(Addr va, bool update_lru=true); | |
| void setConfigAddress(uint32_t addr); | |
| protected: | |
| EntryList::iterator lookupIt(Addr va, bool update_lru=true); | |
| Walker *walker; | |
| public: | |
| Walker *getWalker(); | |
| void invalidateAll(); | |
| void invalidateNonGlobal(); | |
| void demapPage(Addr va, uint64_t asn); | |
| protected: | |
| int size; | |
| int assoc; | |
| int numSets; | |
| /** | |
| * true if this is a fully-associative TLB | |
| */ | |
| bool FA; | |
| Addr setMask; | |
| /** | |
| * Allocation Policy: true if we always allocate on a hit, false | |
| * otherwise. Default is true. | |
| */ | |
| bool allocationPolicy; | |
| /** | |
| * if true, then this is not the last level TLB | |
| */ | |
| bool hasMemSidePort; | |
| /** | |
| * Print out accessDistance stats. One stat file | |
| * per TLB. | |
| */ | |
| bool accessDistance; | |
| std::vector<TlbEntry> tlb; | |
| /* | |
| * It's a per-set list. As long as we have not reached | |
| * the full capacity of the given set, grab an entry from | |
| * the freeList. | |
| */ | |
| std::vector<EntryList> freeList; | |
| /** | |
| * An entryList per set is the equivalent of an LRU stack; | |
| * it's used to guide replacement decisions. The head of the list | |
| * contains the MRU TLB entry of the given set. If the freeList | |
| * for this set is empty, the last element of the list | |
| * is evicted (i.e., dropped on the floor). | |
| */ | |
| std::vector<EntryList> entryList; | |
| Fault translateInt(bool read, const RequestPtr &req, | |
| ThreadContext *tc); | |
| Fault translate(const RequestPtr &req, ThreadContext *tc, | |
| Translation *translation, Mode mode, bool &delayedResponse, | |
| bool timing, int &latency); | |
| public: | |
| // latencies for a TLB hit, miss and page fault | |
| int hitLatency; | |
| int missLatency1; | |
| int missLatency2; | |
| void updatePageFootprint(Addr virt_page_addr); | |
| void printAccessPattern(); | |
| Fault translateAtomic(const RequestPtr &req, ThreadContext *tc, | |
| Mode mode, int &latency); | |
| void translateTiming(const RequestPtr &req, ThreadContext *tc, | |
| Translation *translation, Mode mode, | |
| int &latency); | |
| Tick doMmuRegRead(ThreadContext *tc, Packet *pkt); | |
| Tick doMmuRegWrite(ThreadContext *tc, Packet *pkt); | |
| TlbEntry *insert(Addr vpn, TlbEntry &entry); | |
| // Checkpointing | |
| virtual void serialize(CheckpointOut& cp) const override; | |
| virtual void unserialize(CheckpointIn& cp) override; | |
| void issueTranslation(); | |
| enum tlbOutcome {TLB_HIT, TLB_MISS, PAGE_WALK, MISS_RETURN}; | |
| bool tlbLookup(const RequestPtr &req, | |
| ThreadContext *tc, bool update_stats); | |
| void handleTranslationReturn(Addr addr, tlbOutcome outcome, | |
| PacketPtr pkt); | |
| void handleFuncTranslationReturn(PacketPtr pkt, tlbOutcome outcome); | |
| void pagingProtectionChecks(ThreadContext *tc, PacketPtr pkt, | |
| TlbEntry *tlb_entry, Mode mode); | |
| void updatePhysAddresses(Addr virt_page_addr, TlbEntry *tlb_entry, | |
| Addr phys_page_addr); | |
| void issueTLBLookup(PacketPtr pkt); | |
| // CpuSidePort is the TLB Port closer to the CPU/CU side | |
| class CpuSidePort : public ResponsePort | |
| { | |
| public: | |
| CpuSidePort(const std::string &_name, GpuTLB * gpu_TLB, | |
| PortID _index) | |
| : ResponsePort(_name, gpu_TLB), tlb(gpu_TLB), index(_index) { } | |
| protected: | |
| GpuTLB *tlb; | |
| int index; | |
| virtual bool recvTimingReq(PacketPtr pkt); | |
| virtual Tick recvAtomic(PacketPtr pkt) { return 0; } | |
| virtual void recvFunctional(PacketPtr pkt); | |
| virtual void recvRangeChange() { } | |
| virtual void recvReqRetry(); | |
| virtual void recvRespRetry() { panic("recvRespRetry called"); } | |
| virtual AddrRangeList getAddrRanges() const; | |
| }; | |
| /** | |
| * MemSidePort is the TLB Port closer to the memory side | |
| * If this is a last level TLB then this port will not be connected. | |
| * | |
| * Future action item: if we ever do real page walks, then this port | |
| * should be connected to a RubyPort. | |
| */ | |
| class MemSidePort : public RequestPort | |
| { | |
| public: | |
| MemSidePort(const std::string &_name, GpuTLB * gpu_TLB, | |
| PortID _index) | |
| : RequestPort(_name, gpu_TLB), tlb(gpu_TLB), index(_index) { } | |
| std::deque<PacketPtr> retries; | |
| protected: | |
| GpuTLB *tlb; | |
| int index; | |
| virtual bool recvTimingResp(PacketPtr pkt); | |
| virtual Tick recvAtomic(PacketPtr pkt) { return 0; } | |
| virtual void recvFunctional(PacketPtr pkt) { } | |
| virtual void recvRangeChange() { } | |
| virtual void recvReqRetry(); | |
| }; | |
| // TLB ports on the cpu Side | |
| std::vector<CpuSidePort*> cpuSidePort; | |
| // TLB ports on the memory side | |
| std::vector<MemSidePort*> memSidePort; | |
| Port &getPort(const std::string &if_name, | |
| PortID idx=InvalidPortID) override; | |
| // maximum number of permitted coalesced requests per cycle | |
| int maxCoalescedReqs; | |
| // Current number of outstandings coalesced requests. | |
| // Should be <= maxCoalescedReqs | |
| int outstandingReqs; | |
| /** | |
| * A TLBEvent is scheduled after the TLB lookup and helps us take the | |
| * appropriate actions: | |
| * (e.g., update TLB on a hit, | |
| * send request to lower level TLB on a miss, | |
| * or start a page walk if this was the last-level TLB). | |
| */ | |
| void translationReturn(Addr virtPageAddr, tlbOutcome outcome, | |
| PacketPtr pkt); | |
| class TLBEvent : public Event | |
| { | |
| private: | |
| GpuTLB *tlb; | |
| Addr virtPageAddr; | |
| /** | |
| * outcome can be TLB_HIT, TLB_MISS, or PAGE_WALK | |
| */ | |
| tlbOutcome outcome; | |
| PacketPtr pkt; | |
| public: | |
| TLBEvent(GpuTLB *_tlb, Addr _addr, tlbOutcome outcome, | |
| PacketPtr _pkt); | |
| void process(); | |
| const char *description() const; | |
| // updateOutcome updates the tlbOutcome of a TLBEvent | |
| void updateOutcome(tlbOutcome _outcome); | |
| Addr getTLBEventVaddr(); | |
| }; | |
| std::unordered_map<Addr, TLBEvent*> translationReturnEvent; | |
| // this FIFO queue keeps track of the virt. page addresses | |
| // that are pending cleanup | |
| std::queue<Addr> cleanupQueue; | |
| // the cleanupEvent is scheduled after a TLBEvent triggers in order to | |
| // free memory and do the required clean-up | |
| void cleanup(); | |
| EventFunctionWrapper cleanupEvent; | |
| /** | |
| * This hash map will use the virtual page address as a key | |
| * and will keep track of total number of accesses per page | |
| */ | |
| struct AccessInfo | |
| { | |
| unsigned int lastTimeAccessed; // last access to this page | |
| unsigned int accessesPerPage; | |
| // need to divide it by accessesPerPage at the end | |
| unsigned int totalReuseDistance; | |
| /** | |
| * The field below will help us compute the access distance, | |
| * that is the number of (coalesced) TLB accesses that | |
| * happened in between each access to this page | |
| * | |
| * localTLBAccesses[x] is the value of localTLBNumAccesses | |
| * when the page <Addr> was accessed for the <x>th time | |
| */ | |
| std::vector<unsigned int> localTLBAccesses; | |
| unsigned int sumDistance; | |
| unsigned int meanDistance; | |
| }; | |
| typedef std::unordered_map<Addr, AccessInfo> AccessPatternTable; | |
| AccessPatternTable TLBFootprint; | |
| // Called at the end of simulation to dump page access stats. | |
| void exitCallback(); | |
| EventFunctionWrapper exitEvent; | |
| protected: | |
| struct GpuTLBStats : public statistics::Group | |
| { | |
| GpuTLBStats(statistics::Group *parent); | |
| // local_stats are as seen from the TLB | |
| // without taking into account coalescing | |
| statistics::Scalar localNumTLBAccesses; | |
| statistics::Scalar localNumTLBHits; | |
| statistics::Scalar localNumTLBMisses; | |
| statistics::Formula localTLBMissRate; | |
| // global_stats are as seen from the | |
| // CU's perspective taking into account | |
| // all coalesced requests. | |
| statistics::Scalar globalNumTLBAccesses; | |
| statistics::Scalar globalNumTLBHits; | |
| statistics::Scalar globalNumTLBMisses; | |
| statistics::Formula globalTLBMissRate; | |
| // from the CU perspective (global) | |
| statistics::Scalar accessCycles; | |
| // from the CU perspective (global) | |
| statistics::Scalar pageTableCycles; | |
| statistics::Scalar numUniquePages; | |
| // from the perspective of this TLB | |
| statistics::Scalar localCycles; | |
| // from the perspective of this TLB | |
| statistics::Formula localLatency; | |
| // I take the avg. per page and then | |
| // the avg. over all pages. | |
| statistics::Scalar avgReuseDistance; | |
| } stats; | |
| }; | |
| } | |
| } // namespace gem5 | |
| #endif // __GPU_TLB_HH__ |