dramsim2.cc revision 10405:7a618c07e663
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 "DRAMSim2/Callback.h"
41#include "base/callback.hh"
42#include "base/trace.hh"
43#include "debug/DRAMSim2.hh"
44#include "debug/Drain.hh"
45#include "mem/dramsim2.hh"
46#include "sim/system.hh"
47
48DRAMSim2::DRAMSim2(const Params* p) :
49    AbstractMemory(p),
50    port(name() + ".port", *this),
51    wrapper(p->deviceConfigFile, p->systemConfigFile, p->filePath,
52            p->traceFile, p->range.size() / 1024 / 1024, p->enableDebug),
53    retryReq(false), retryResp(false), startTick(0),
54    nbrOutstandingReads(0), nbrOutstandingWrites(0),
55    drainManager(NULL),
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    if (!port.isConnected()) {
81        fatal("DRAMSim2 %s is unconnected!\n", name());
82    } else {
83        port.sendRangeChange();
84    }
85
86    if (system()->cacheLineSize() != wrapper.burstSize())
87        fatal("DRAMSim2 burst size %d does not match cache line size %d\n",
88              wrapper.burstSize(), system()->cacheLineSize());
89}
90
91void
92DRAMSim2::startup()
93{
94    startTick = curTick();
95
96    // kick off the clock ticks
97    schedule(tickEvent, clockEdge());
98}
99
100void
101DRAMSim2::sendResponse()
102{
103    assert(!retryResp);
104    assert(!responseQueue.empty());
105
106    DPRINTF(DRAMSim2, "Attempting to send response\n");
107
108    bool success = port.sendTimingResp(responseQueue.front());
109    if (success) {
110        responseQueue.pop_front();
111
112        DPRINTF(DRAMSim2, "Have %d read, %d write, %d responses outstanding\n",
113                nbrOutstandingReads, nbrOutstandingWrites,
114                responseQueue.size());
115
116        if (!responseQueue.empty() && !sendResponseEvent.scheduled())
117            schedule(sendResponseEvent, curTick());
118
119        // check if we were asked to drain and if we are now done
120        if (drainManager && nbrOutstanding() == 0) {
121            drainManager->signalDrainDone();
122            drainManager = NULL;
123        }
124    } else {
125        retryResp = true;
126
127        DPRINTF(DRAMSim2, "Waiting for response retry\n");
128
129        assert(!sendResponseEvent.scheduled());
130    }
131}
132
133unsigned int
134DRAMSim2::nbrOutstanding() const
135{
136    return nbrOutstandingReads + nbrOutstandingWrites + responseQueue.size();
137}
138
139void
140DRAMSim2::tick()
141{
142    wrapper.tick();
143
144    // is the connected port waiting for a retry, if so check the
145    // state and send a retry if conditions have changed
146    if (retryReq && nbrOutstanding() < wrapper.queueSize()) {
147        retryReq = false;
148        port.sendRetry();
149    }
150
151    schedule(tickEvent, curTick() + wrapper.clockPeriod() * SimClock::Int::ns);
152}
153
154Tick
155DRAMSim2::recvAtomic(PacketPtr pkt)
156{
157    access(pkt);
158
159    // 50 ns is just an arbitrary value at this point
160    return pkt->memInhibitAsserted() ? 0 : 50000;
161}
162
163void
164DRAMSim2::recvFunctional(PacketPtr pkt)
165{
166    pkt->pushLabel(name());
167
168    functionalAccess(pkt);
169
170    // potentially update the packets in our response queue as well
171    for (auto i = responseQueue.begin(); i != responseQueue.end(); ++i)
172        pkt->checkFunctional(*i);
173
174    pkt->popLabel();
175}
176
177bool
178DRAMSim2::recvTimingReq(PacketPtr pkt)
179{
180    // we should never see a new request while in retry
181    assert(!retryReq);
182
183    // @todo temporary hack to deal with memory corruption issues until
184    // 4-phase transactions are complete
185    for (int x = 0; x < pendingDelete.size(); x++)
186        delete pendingDelete[x];
187    pendingDelete.clear();
188
189    if (pkt->memInhibitAsserted()) {
190        // snooper will supply based on copy of packet
191        // still target's responsibility to delete packet
192        pendingDelete.push_back(pkt);
193        return true;
194    }
195
196    // if we cannot accept we need to send a retry once progress can
197    // be made
198    bool can_accept = nbrOutstanding() < wrapper.queueSize();
199
200    // keep track of the transaction
201    if (pkt->isRead()) {
202        if (can_accept) {
203            outstandingReads[pkt->getAddr()].push(pkt);
204
205            // we count a transaction as outstanding until it has left the
206            // queue in the controller, and the response has been sent
207            // back, note that this will differ for reads and writes
208            ++nbrOutstandingReads;
209        }
210    } else if (pkt->isWrite()) {
211        if (can_accept) {
212            outstandingWrites[pkt->getAddr()].push(pkt);
213
214            ++nbrOutstandingWrites;
215
216            // perform the access for writes
217            accessAndRespond(pkt);
218        }
219    } else {
220        // keep it simple and just respond if necessary
221        accessAndRespond(pkt);
222        return true;
223    }
224
225    if (can_accept) {
226        // we should never have a situation when we think there is space,
227        // and there isn't
228        assert(wrapper.canAccept());
229
230        DPRINTF(DRAMSim2, "Enqueueing address %lld\n", pkt->getAddr());
231
232        // @todo what about the granularity here, implicit assumption that
233        // a transaction matches the burst size of the memory (which we
234        // cannot determine without parsing the ini file ourselves)
235        wrapper.enqueue(pkt->isWrite(), pkt->getAddr());
236
237        return true;
238    } else {
239        retryReq = true;
240        return false;
241    }
242}
243
244void
245DRAMSim2::recvRetry()
246{
247    DPRINTF(DRAMSim2, "Retrying\n");
248
249    assert(retryResp);
250    retryResp = false;
251    sendResponse();
252}
253
254void
255DRAMSim2::accessAndRespond(PacketPtr pkt)
256{
257    DPRINTF(DRAMSim2, "Access for address %lld\n", pkt->getAddr());
258
259    bool needsResponse = pkt->needsResponse();
260
261    // do the actual memory access which also turns the packet into a
262    // response
263    access(pkt);
264
265    // turn packet around to go back to requester if response expected
266    if (needsResponse) {
267        // access already turned the packet into a response
268        assert(pkt->isResponse());
269
270        // @todo someone should pay for this
271        pkt->firstWordDelay = pkt->lastWordDelay = 0;
272
273        DPRINTF(DRAMSim2, "Queuing response for address %lld\n",
274                pkt->getAddr());
275
276        // queue it to be sent back
277        responseQueue.push_back(pkt);
278
279        // if we are not already waiting for a retry, or are scheduled
280        // to send a response, schedule an event
281        if (!retryResp && !sendResponseEvent.scheduled())
282            schedule(sendResponseEvent, curTick());
283    } else {
284        // @todo the packet is going to be deleted, and the DRAMPacket
285        // is still having a pointer to it
286        pendingDelete.push_back(pkt);
287    }
288}
289
290void DRAMSim2::readComplete(unsigned id, uint64_t addr, uint64_t cycle)
291{
292    assert(cycle == divCeil(curTick() - startTick,
293                            wrapper.clockPeriod() * SimClock::Int::ns));
294
295    DPRINTF(DRAMSim2, "Read to address %lld complete\n", addr);
296
297    // get the outstanding reads for the address in question
298    auto p = outstandingReads.find(addr);
299    assert(p != outstandingReads.end());
300
301    // first in first out, which is not necessarily true, but it is
302    // the best we can do at this point
303    PacketPtr pkt = p->second.front();
304    p->second.pop();
305
306    if (p->second.empty())
307        outstandingReads.erase(p);
308
309    // no need to check for drain here as the next call will add a
310    // response to the response queue straight away
311    assert(nbrOutstandingReads != 0);
312    --nbrOutstandingReads;
313
314    // perform the actual memory access
315    accessAndRespond(pkt);
316}
317
318void DRAMSim2::writeComplete(unsigned id, uint64_t addr, uint64_t cycle)
319{
320    assert(cycle == divCeil(curTick() - startTick,
321                            wrapper.clockPeriod() * SimClock::Int::ns));
322
323    DPRINTF(DRAMSim2, "Write to address %lld complete\n", addr);
324
325    // get the outstanding reads for the address in question
326    auto p = outstandingWrites.find(addr);
327    assert(p != outstandingWrites.end());
328
329    // we have already responded, and this is only to keep track of
330    // what is outstanding
331    p->second.pop();
332    if (p->second.empty())
333        outstandingWrites.erase(p);
334
335    assert(nbrOutstandingWrites != 0);
336    --nbrOutstandingWrites;
337
338    // check if we were asked to drain and if we are now done
339    if (drainManager && nbrOutstanding() == 0) {
340        drainManager->signalDrainDone();
341        drainManager = NULL;
342    }
343}
344
345BaseSlavePort&
346DRAMSim2::getSlavePort(const std::string &if_name, PortID idx)
347{
348    if (if_name != "port") {
349        return MemObject::getSlavePort(if_name, idx);
350    } else {
351        return port;
352    }
353}
354
355unsigned int
356DRAMSim2::drain(DrainManager* dm)
357{
358    // check our outstanding reads and writes and if any they need to
359    // drain
360    if (nbrOutstanding() != 0) {
361        setDrainState(Drainable::Draining);
362        drainManager = dm;
363        return 1;
364    } else {
365        setDrainState(Drainable::Drained);
366        return 0;
367    }
368}
369
370DRAMSim2::MemoryPort::MemoryPort(const std::string& _name,
371                                 DRAMSim2& _memory)
372    : SlavePort(_name, &_memory), memory(_memory)
373{ }
374
375AddrRangeList
376DRAMSim2::MemoryPort::getAddrRanges() const
377{
378    AddrRangeList ranges;
379    ranges.push_back(memory.getAddrRange());
380    return ranges;
381}
382
383Tick
384DRAMSim2::MemoryPort::recvAtomic(PacketPtr pkt)
385{
386    return memory.recvAtomic(pkt);
387}
388
389void
390DRAMSim2::MemoryPort::recvFunctional(PacketPtr pkt)
391{
392    memory.recvFunctional(pkt);
393}
394
395bool
396DRAMSim2::MemoryPort::recvTimingReq(PacketPtr pkt)
397{
398    // pass it to the memory controller
399    return memory.recvTimingReq(pkt);
400}
401
402void
403DRAMSim2::MemoryPort::recvRetry()
404{
405    memory.recvRetry();
406}
407
408DRAMSim2*
409DRAMSim2Params::create()
410{
411    return new DRAMSim2(this);
412}
413