/* * Copyright (c) 2020 Inria * 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) 1999-2012 Mark D. Hill and David A. Wood * 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 __MEM_RUBY_STRUCTURES_PREFETCHER_HH__ #define __MEM_RUBY_STRUCTURES_PREFETCHER_HH__ // Implements Power 4 like prefetching #include #include "base/circular_queue.hh" #include "base/statistics.hh" #include "mem/ruby/common/Address.hh" #include "mem/ruby/network/MessageBuffer.hh" #include "mem/ruby/slicc_interface/AbstractController.hh" #include "mem/ruby/slicc_interface/RubyRequest.hh" #include "mem/ruby/system/RubySystem.hh" #include "params/RubyPrefetcher.hh" #include "sim/sim_object.hh" #define MAX_PF_INFLIGHT 8 namespace gem5 { namespace ruby { class PrefetchEntry { public: /// constructor PrefetchEntry(int block_size) { // default: 1 cache-line stride m_stride = (1 << floorLog2(block_size)); m_use_time = Cycles(0); m_is_valid = false; } //! The base address for the stream prefetch Addr m_address; //! stride distance to get next address from int m_stride; //! the last time that any prefetched request was used Cycles m_use_time; //! valid bit for each stream bool m_is_valid; //! L1D prefetches loads and stores RubyRequestType m_type; //! Bitset for tracking prefetches for which addresses have been //! issued, which ones have completed. std::bitset requestIssued; std::bitset requestCompleted; }; class RubyPrefetcher : public SimObject { public: typedef RubyPrefetcherParams Params; RubyPrefetcher(const Params &p); ~RubyPrefetcher() = default; void issueNextPrefetch(Addr address, PrefetchEntry *stream); /** * Implement the prefetch hit(miss) callback interface. * These functions are called by the cache when it hits(misses) * on a line with the line's prefetch bit set. If this address * hits in m_array we will continue prefetching the stream. */ void observePfHit(Addr address); void observePfMiss(Addr address); /** * Observe a memory miss from the cache. * * @param address The physical address that missed out of the cache. */ void observeMiss(Addr address, const RubyRequestType& type); /** * Print out some statistics */ void print(std::ostream& out) const; void setController(AbstractController *_ctrl) { m_controller = _ctrl; } private: struct UnitFilterEntry { /** Address to which this filter entry refers. */ Addr addr; /** Counter of the number of times this entry has been hit. */ uint32_t hits; UnitFilterEntry(Addr _addr = 0) : addr(_addr), hits(0) { } }; struct NonUnitFilterEntry : public UnitFilterEntry { /** Stride (in # of cache lines). */ int stride; NonUnitFilterEntry(Addr _addr = 0) : UnitFilterEntry(_addr), stride(0) { } void clear() { addr = 0; stride = 0; hits = 0; } }; /** * Returns an unused stream buffer (or if all are used, returns the * least recently used (accessed) stream buffer). * @return The index of the least recently used stream buffer. */ uint32_t getLRUindex(void); //! allocate a new stream buffer at a specific index void initializeStream(Addr address, int stride, uint32_t index, const RubyRequestType& type); //! get pointer to the matching stream entry, returns NULL if not found //! index holds the multiple of the stride this address is. PrefetchEntry* getPrefetchEntry(Addr address, uint32_t &index); /** * Access a unit stride filter to determine if there is a hit, and * update it otherwise. * * @param filter Unit filter being accessed. * @param line_addr Address being accessed, block aligned. * @param stride The stride value. * @param type Type of the request that generated the access. * @return True if a corresponding entry was found. */ bool accessUnitFilter(CircularQueue* const filter, Addr line_addr, int stride, const RubyRequestType& type); /** * Access a non-unit stride filter to determine if there is a hit, and * update it otherwise. * * @param line_addr Address being accessed, block aligned. * @param type Type of the request that generated the access. * @return True if a corresponding entry was found and its stride is * not zero. */ bool accessNonunitFilter(Addr line_addr, const RubyRequestType& type); /// determine the page aligned address Addr pageAddress(Addr addr) const; //! number of prefetch streams available uint32_t m_num_streams; //! an array of the active prefetch streams std::vector m_array; //! number of misses I must see before allocating a stream uint32_t m_train_misses; //! number of initial prefetches to startup a stream uint32_t m_num_startup_pfs; /** * A unit stride filter array: helps reduce BW requirement * of prefetching. */ CircularQueue unitFilter; /** * A negative unit stride filter array: helps reduce BW requirement * of prefetching. */ CircularQueue negativeFilter; /** * A non-unit stride filter array: helps reduce BW requirement of * prefetching. */ CircularQueue nonUnitFilter; /// Used for allowing prefetches across pages. bool m_prefetch_cross_pages; AbstractController *m_controller; const unsigned pageShift; int m_block_size_bits = 0; int m_block_size_bytes = 0; Addr makeNextStrideAddress(Addr addr, int stride) const { return ruby::makeNextStrideAddress(addr, stride, m_block_size_bytes); } struct RubyPrefetcherStats : public statistics::Group { RubyPrefetcherStats(statistics::Group *parent); //! Count of accesses to the prefetcher statistics::Scalar numMissObserved; //! Count of prefetch streams allocated statistics::Scalar numAllocatedStreams; //! Count of prefetch requests made statistics::Scalar numPrefetchRequested; //! Count of successful prefetches statistics::Scalar numHits; //! Count of partial successful prefetches statistics::Scalar numPartialHits; //! Count of pages crossed statistics::Scalar numPagesCrossed; //! Count of misses incurred for blocks that were prefetched statistics::Scalar numMissedPrefetchedBlocks; } rubyPrefetcherStats; }; } // namespace ruby } // namespace gem5 #endif // __MEM_RUBY_STRUCTURES_PREFETCHER_HH__