1/*
2 * Copyright (c) 2017 Jason Lowe-Power
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Jason Lowe-Power
29 */
30
31#ifndef __LEARNING_GEM5_SIMPLE_CACHE_SIMPLE_CACHE_HH__
32#define __LEARNING_GEM5_SIMPLE_CACHE_SIMPLE_CACHE_HH__
33
34#include <unordered_map>
35
36#include "base/statistics.hh"
37#include "mem/port.hh"
38#include "params/SimpleCache.hh"
39#include "sim/clocked_object.hh"
40
41/**
42 * A very simple cache object. Has a fully-associative data store with random
43 * replacement.
44 * This cache is fully blocking (not non-blocking). Only a single request can
45 * be outstanding at a time.
46 * This cache is a writeback cache.
47 */
48class SimpleCache : public ClockedObject
49{
50  private:
51
52    /**
53     * Port on the CPU-side that receives requests.
54     * Mostly just forwards requests to the cache (owner)
55     */
56    class CPUSidePort : public SlavePort
57    {
58      private:
59        /// Since this is a vector port, need to know what number this one is
60        int id;
61
62        /// The object that owns this object (SimpleCache)
63        SimpleCache *owner;
64
65        /// True if the port needs to send a retry req.
66        bool needRetry;
67
68        /// If we tried to send a packet and it was blocked, store it here
69        PacketPtr blockedPacket;
70
71      public:
72        /**
73         * Constructor. Just calls the superclass constructor.
74         */
75        CPUSidePort(const std::string& name, int id, SimpleCache *owner) :
76            SlavePort(name, owner), id(id), owner(owner), needRetry(false),
77            blockedPacket(nullptr)
78        { }
79
80        /**
81         * Send a packet across this port. This is called by the owner and
82         * all of the flow control is hanled in this function.
83         * This is a convenience function for the SimpleCache to send pkts.
84         *
85         * @param packet to send.
86         */
87        void sendPacket(PacketPtr pkt);
88
89        /**
90         * Get a list of the non-overlapping address ranges the owner is
91         * responsible for. All slave ports must override this function
92         * and return a populated list with at least one item.
93         *
94         * @return a list of ranges responded to
95         */
96        AddrRangeList getAddrRanges() const override;
97
98        /**
99         * Send a retry to the peer port only if it is needed. This is called
100         * from the SimpleCache whenever it is unblocked.
101         */
102        void trySendRetry();
103
104      protected:
105        /**
106         * Receive an atomic request packet from the master port.
107         * No need to implement in this simple cache.
108         */
109        Tick recvAtomic(PacketPtr pkt) override
110        { panic("recvAtomic unimpl."); }
111
112        /**
113         * Receive a functional request packet from the master port.
114         * Performs a "debug" access updating/reading the data in place.
115         *
116         * @param packet the requestor sent.
117         */
118        void recvFunctional(PacketPtr pkt) override;
119
120        /**
121         * Receive a timing request from the master port.
122         *
123         * @param the packet that the requestor sent
124         * @return whether this object can consume to packet. If false, we
125         *         will call sendRetry() when we can try to receive this
126         *         request again.
127         */
128        bool recvTimingReq(PacketPtr pkt) override;
129
130        /**
131         * Called by the master port if sendTimingResp was called on this
132         * slave port (causing recvTimingResp to be called on the master
133         * port) and was unsuccesful.
134         */
135        void recvRespRetry() override;
136    };
137
138    /**
139     * Port on the memory-side that receives responses.
140     * Mostly just forwards requests to the cache (owner)
141     */
142    class MemSidePort : public MasterPort
143    {
144      private:
145        /// The object that owns this object (SimpleCache)
146        SimpleCache *owner;
147
148        /// If we tried to send a packet and it was blocked, store it here
149        PacketPtr blockedPacket;
150
151      public:
152        /**
153         * Constructor. Just calls the superclass constructor.
154         */
155        MemSidePort(const std::string& name, SimpleCache *owner) :
156            MasterPort(name, owner), owner(owner), blockedPacket(nullptr)
157        { }
158
159        /**
160         * Send a packet across this port. This is called by the owner and
161         * all of the flow control is hanled in this function.
162         * This is a convenience function for the SimpleCache to send pkts.
163         *
164         * @param packet to send.
165         */
166        void sendPacket(PacketPtr pkt);
167
168      protected:
169        /**
170         * Receive a timing response from the slave port.
171         */
172        bool recvTimingResp(PacketPtr pkt) override;
173
174        /**
175         * Called by the slave port if sendTimingReq was called on this
176         * master port (causing recvTimingReq to be called on the slave
177         * port) and was unsuccesful.
178         */
179        void recvReqRetry() override;
180
181        /**
182         * Called to receive an address range change from the peer slave
183         * port. The default implementation ignores the change and does
184         * nothing. Override this function in a derived class if the owner
185         * needs to be aware of the address ranges, e.g. in an
186         * interconnect component like a bus.
187         */
188        void recvRangeChange() override;
189    };
190
191    /**
192     * Handle the request from the CPU side. Called from the CPU port
193     * on a timing request.
194     *
195     * @param requesting packet
196     * @param id of the port to send the response
197     * @return true if we can handle the request this cycle, false if the
198     *         requestor needs to retry later
199     */
200    bool handleRequest(PacketPtr pkt, int port_id);
201
202    /**
203     * Handle the respone from the memory side. Called from the memory port
204     * on a timing response.
205     *
206     * @param responding packet
207     * @return true if we can handle the response this cycle, false if the
208     *         responder needs to retry later
209     */
210    bool handleResponse(PacketPtr pkt);
211
212    /**
213     * Send the packet to the CPU side.
214     * This function assumes the pkt is already a response packet and forwards
215     * it to the correct port. This function also unblocks this object and
216     * cleans up the whole request.
217     *
218     * @param the packet to send to the cpu side
219     */
220    void sendResponse(PacketPtr pkt);
221
222    /**
223     * Handle a packet functionally. Update the data on a write and get the
224     * data on a read. Called from CPU port on a recv functional.
225     *
226     * @param packet to functionally handle
227     */
228    void handleFunctional(PacketPtr pkt);
229
230    /**
231     * Access the cache for a timing access. This is called after the cache
232     * access latency has already elapsed.
233     */
234    void accessTiming(PacketPtr pkt);
235
236    /**
237     * This is where we actually update / read from the cache. This function
238     * is executed on both timing and functional accesses.
239     *
240     * @return true if a hit, false otherwise
241     */
242    bool accessFunctional(PacketPtr pkt);
243
244    /**
245     * Insert a block into the cache. If there is no room left in the cache,
246     * then this function evicts a random entry t make room for the new block.
247     *
248     * @param packet with the data (and address) to insert into the cache
249     */
250    void insert(PacketPtr pkt);
251
252    /**
253     * Return the address ranges this cache is responsible for. Just use the
254     * same as the next upper level of the hierarchy.
255     *
256     * @return the address ranges this cache is responsible for
257     */
258    AddrRangeList getAddrRanges() const;
259
260    /**
261     * Tell the CPU side to ask for our memory ranges.
262     */
263    void sendRangeChange() const;
264
265    /// Latency to check the cache. Number of cycles for both hit and miss
266    const Cycles latency;
267
268    /// The block size for the cache
269    const unsigned blockSize;
270
271    /// Number of blocks in the cache (size of cache / block size)
272    const unsigned capacity;
273
274    /// Instantiation of the CPU-side port
275    std::vector<CPUSidePort> cpuPorts;
276
277    /// Instantiation of the memory-side port
278    MemSidePort memPort;
279
280    /// True if this cache is currently blocked waiting for a response.
281    bool blocked;
282
283    /// Packet that we are currently handling. Used for upgrading to larger
284    /// cache line sizes
285    PacketPtr originalPacket;
286
287    /// The port to send the response when we recieve it back
288    int waitingPortId;
289
290    /// For tracking the miss latency
291    Tick missTime;
292
293    /// An incredibly simple cache storage. Maps block addresses to data
294    std::unordered_map<Addr, uint8_t*> cacheStore;
295
296    /// Cache statistics
297    Stats::Scalar hits;
298    Stats::Scalar misses;
299    Stats::Histogram missLatency;
300    Stats::Formula hitRatio;
301
302  public:
303
304    /** constructor
305     */
306    SimpleCache(SimpleCacheParams *params);
307
308    /**
309     * Get a port with a given name and index. This is used at
310     * binding time and returns a reference to a protocol-agnostic
311     * port.
312     *
313     * @param if_name Port name
314     * @param idx Index in the case of a VectorPort
315     *
316     * @return A reference to the given port
317     */
318    Port &getPort(const std::string &if_name,
319                  PortID idx=InvalidPortID) override;
320
321    /**
322     * Register the stats
323     */
324    void regStats() override;
325};
326
327
328#endif // __LEARNING_GEM5_SIMPLE_CACHE_SIMPLE_CACHE_HH__
329