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