dramsim2.cc revision 11793:ef606668d247
1/*
2 * Copyright (c) 2013 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Andreas Hansson
38 */
39
40#include "mem/dramsim2.hh"
41
42#include "DRAMSim2/Callback.h"
43#include "base/callback.hh"
44#include "base/trace.hh"
45#include "debug/DRAMSim2.hh"
46#include "debug/Drain.hh"
47#include "sim/system.hh"
48
49DRAMSim2::DRAMSim2(const Params* p) :
50    AbstractMemory(p),
51    port(name() + ".port", *this),
52    wrapper(p->deviceConfigFile, p->systemConfigFile, p->filePath,
53            p->traceFile, p->range.size() / 1024 / 1024, p->enableDebug),
54    retryReq(false), retryResp(false), startTick(0),
55    nbrOutstandingReads(0), nbrOutstandingWrites(0),
56    sendResponseEvent(this), tickEvent(this)
57{
58    DPRINTF(DRAMSim2,
59            "Instantiated DRAMSim2 with clock %d ns and queue size %d\n",
60            wrapper.clockPeriod(), wrapper.queueSize());
61
62    DRAMSim::TransactionCompleteCB* read_cb =
63        new DRAMSim::Callback<DRAMSim2, void, unsigned, uint64_t, uint64_t>(
64            this, &DRAMSim2::readComplete);
65    DRAMSim::TransactionCompleteCB* write_cb =
66        new DRAMSim::Callback<DRAMSim2, void, unsigned, uint64_t, uint64_t>(
67            this, &DRAMSim2::writeComplete);
68    wrapper.setCallbacks(read_cb, write_cb);
69
70    // Register a callback to compensate for the destructor not
71    // being called. The callback prints the DRAMSim2 stats.
72    Callback* cb = new MakeCallback<DRAMSim2Wrapper,
73        &DRAMSim2Wrapper::printStats>(wrapper);
74    registerExitCallback(cb);
75}
76
77void
78DRAMSim2::init()
79{
80    AbstractMemory::init();
81
82    if (!port.isConnected()) {
83        fatal("DRAMSim2 %s is unconnected!\n", name());
84    } else {
85        port.sendRangeChange();
86    }
87
88    if (system()->cacheLineSize() != wrapper.burstSize())
89        fatal("DRAMSim2 burst size %d does not match cache line size %d\n",
90              wrapper.burstSize(), system()->cacheLineSize());
91}
92
93void
94DRAMSim2::startup()
95{
96    startTick = curTick();
97
98    // kick off the clock ticks
99    schedule(tickEvent, clockEdge());
100}
101
102void
103DRAMSim2::sendResponse()
104{
105    assert(!retryResp);
106    assert(!responseQueue.empty());
107
108    DPRINTF(DRAMSim2, "Attempting to send response\n");
109
110    bool success = port.sendTimingResp(responseQueue.front());
111    if (success) {
112        responseQueue.pop_front();
113
114        DPRINTF(DRAMSim2, "Have %d read, %d write, %d responses outstanding\n",
115                nbrOutstandingReads, nbrOutstandingWrites,
116                responseQueue.size());
117
118        if (!responseQueue.empty() && !sendResponseEvent.scheduled())
119            schedule(sendResponseEvent, curTick());
120
121        if (nbrOutstanding() == 0)
122            signalDrainDone();
123    } else {
124        retryResp = true;
125
126        DPRINTF(DRAMSim2, "Waiting for response retry\n");
127
128        assert(!sendResponseEvent.scheduled());
129    }
130}
131
132unsigned int
133DRAMSim2::nbrOutstanding() const
134{
135    return nbrOutstandingReads + nbrOutstandingWrites + responseQueue.size();
136}
137
138void
139DRAMSim2::tick()
140{
141    wrapper.tick();
142
143    // is the connected port waiting for a retry, if so check the
144    // state and send a retry if conditions have changed
145    if (retryReq && nbrOutstanding() < wrapper.queueSize()) {
146        retryReq = false;
147        port.sendRetryReq();
148    }
149
150    schedule(tickEvent, curTick() + wrapper.clockPeriod() * SimClock::Int::ns);
151}
152
153Tick
154DRAMSim2::recvAtomic(PacketPtr pkt)
155{
156    access(pkt);
157
158    // 50 ns is just an arbitrary value at this point
159    return pkt->cacheResponding() ? 0 : 50000;
160}
161
162void
163DRAMSim2::recvFunctional(PacketPtr pkt)
164{
165    pkt->pushLabel(name());
166
167    functionalAccess(pkt);
168
169    // potentially update the packets in our response queue as well
170    for (auto i = responseQueue.begin(); i != responseQueue.end(); ++i)
171        pkt->checkFunctional(*i);
172
173    pkt->popLabel();
174}
175
176bool
177DRAMSim2::recvTimingReq(PacketPtr pkt)
178{
179    // if a cache is responding, sink the packet without further action
180    if (pkt->cacheResponding()) {
181        pendingDelete.reset(pkt);
182        return true;
183    }
184
185    // we should not get a new request after committing to retry the
186    // current one, but unfortunately the CPU violates this rule, so
187    // simply ignore it for now
188    if (retryReq)
189        return false;
190
191    // if we cannot accept we need to send a retry once progress can
192    // be made
193    bool can_accept = nbrOutstanding() < wrapper.queueSize();
194
195    // keep track of the transaction
196    if (pkt->isRead()) {
197        if (can_accept) {
198            outstandingReads[pkt->getAddr()].push(pkt);
199
200            // we count a transaction as outstanding until it has left the
201            // queue in the controller, and the response has been sent
202            // back, note that this will differ for reads and writes
203            ++nbrOutstandingReads;
204        }
205    } else if (pkt->isWrite()) {
206        if (can_accept) {
207            outstandingWrites[pkt->getAddr()].push(pkt);
208
209            ++nbrOutstandingWrites;
210
211            // perform the access for writes
212            accessAndRespond(pkt);
213        }
214    } else {
215        // keep it simple and just respond if necessary
216        accessAndRespond(pkt);
217        return true;
218    }
219
220    if (can_accept) {
221        // we should never have a situation when we think there is space,
222        // and there isn't
223        assert(wrapper.canAccept());
224
225        DPRINTF(DRAMSim2, "Enqueueing address %lld\n", pkt->getAddr());
226
227        // @todo what about the granularity here, implicit assumption that
228        // a transaction matches the burst size of the memory (which we
229        // cannot determine without parsing the ini file ourselves)
230        wrapper.enqueue(pkt->isWrite(), pkt->getAddr());
231
232        return true;
233    } else {
234        retryReq = true;
235        return false;
236    }
237}
238
239void
240DRAMSim2::recvRespRetry()
241{
242    DPRINTF(DRAMSim2, "Retrying\n");
243
244    assert(retryResp);
245    retryResp = false;
246    sendResponse();
247}
248
249void
250DRAMSim2::accessAndRespond(PacketPtr pkt)
251{
252    DPRINTF(DRAMSim2, "Access for address %lld\n", pkt->getAddr());
253
254    bool needsResponse = pkt->needsResponse();
255
256    // do the actual memory access which also turns the packet into a
257    // response
258    access(pkt);
259
260    // turn packet around to go back to requester if response expected
261    if (needsResponse) {
262        // access already turned the packet into a response
263        assert(pkt->isResponse());
264        // Here we pay for xbar additional delay and to process the payload
265        // of the packet.
266        Tick time = curTick() + pkt->headerDelay + pkt->payloadDelay;
267        // Reset the timings of the packet
268        pkt->headerDelay = pkt->payloadDelay = 0;
269
270        DPRINTF(DRAMSim2, "Queuing response for address %lld\n",
271                pkt->getAddr());
272
273        // queue it to be sent back
274        responseQueue.push_back(pkt);
275
276        // if we are not already waiting for a retry, or are scheduled
277        // to send a response, schedule an event
278        if (!retryResp && !sendResponseEvent.scheduled())
279            schedule(sendResponseEvent, time);
280    } else {
281        // queue the packet for deletion
282        pendingDelete.reset(pkt);
283    }
284}
285
286void DRAMSim2::readComplete(unsigned id, uint64_t addr, uint64_t cycle)
287{
288    assert(cycle == divCeil(curTick() - startTick,
289                            wrapper.clockPeriod() * SimClock::Int::ns));
290
291    DPRINTF(DRAMSim2, "Read to address %lld complete\n", addr);
292
293    // get the outstanding reads for the address in question
294    auto p = outstandingReads.find(addr);
295    assert(p != outstandingReads.end());
296
297    // first in first out, which is not necessarily true, but it is
298    // the best we can do at this point
299    PacketPtr pkt = p->second.front();
300    p->second.pop();
301
302    if (p->second.empty())
303        outstandingReads.erase(p);
304
305    // no need to check for drain here as the next call will add a
306    // response to the response queue straight away
307    assert(nbrOutstandingReads != 0);
308    --nbrOutstandingReads;
309
310    // perform the actual memory access
311    accessAndRespond(pkt);
312}
313
314void DRAMSim2::writeComplete(unsigned id, uint64_t addr, uint64_t cycle)
315{
316    assert(cycle == divCeil(curTick() - startTick,
317                            wrapper.clockPeriod() * SimClock::Int::ns));
318
319    DPRINTF(DRAMSim2, "Write to address %lld complete\n", addr);
320
321    // get the outstanding reads for the address in question
322    auto p = outstandingWrites.find(addr);
323    assert(p != outstandingWrites.end());
324
325    // we have already responded, and this is only to keep track of
326    // what is outstanding
327    p->second.pop();
328    if (p->second.empty())
329        outstandingWrites.erase(p);
330
331    assert(nbrOutstandingWrites != 0);
332    --nbrOutstandingWrites;
333
334    if (nbrOutstanding() == 0)
335        signalDrainDone();
336}
337
338BaseSlavePort&
339DRAMSim2::getSlavePort(const std::string &if_name, PortID idx)
340{
341    if (if_name != "port") {
342        return MemObject::getSlavePort(if_name, idx);
343    } else {
344        return port;
345    }
346}
347
348DrainState
349DRAMSim2::drain()
350{
351    // check our outstanding reads and writes and if any they need to
352    // drain
353    return nbrOutstanding() != 0 ? DrainState::Draining : DrainState::Drained;
354}
355
356DRAMSim2::MemoryPort::MemoryPort(const std::string& _name,
357                                 DRAMSim2& _memory)
358    : SlavePort(_name, &_memory), memory(_memory)
359{ }
360
361AddrRangeList
362DRAMSim2::MemoryPort::getAddrRanges() const
363{
364    AddrRangeList ranges;
365    ranges.push_back(memory.getAddrRange());
366    return ranges;
367}
368
369Tick
370DRAMSim2::MemoryPort::recvAtomic(PacketPtr pkt)
371{
372    return memory.recvAtomic(pkt);
373}
374
375void
376DRAMSim2::MemoryPort::recvFunctional(PacketPtr pkt)
377{
378    memory.recvFunctional(pkt);
379}
380
381bool
382DRAMSim2::MemoryPort::recvTimingReq(PacketPtr pkt)
383{
384    // pass it to the memory controller
385    return memory.recvTimingReq(pkt);
386}
387
388void
389DRAMSim2::MemoryPort::recvRespRetry()
390{
391    memory.recvRespRetry();
392}
393
394DRAMSim2*
395DRAMSim2Params::create()
396{
397    return new DRAMSim2(this);
398}
399