2 * Copyright (c) 2013 ARM Limited
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.
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.
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.
37 * Authors: Andreas Hansson
40 #include "mem/dramsim3.hh"
42 #include "base/callback.hh"
43 #include "base/trace.hh"
44 #include "debug/DRAMsim3.hh"
45 #include "debug/Drain.hh"
46 #include "sim/system.hh"
48 DRAMsim3::DRAMsim3(const Params
&p
) :
50 port(name() + ".port", *this),
51 read_cb(std::bind(&DRAMsim3::readComplete
,
52 this, 0, std::placeholders::_1
)),
53 write_cb(std::bind(&DRAMsim3::writeComplete
,
54 this, 0, std::placeholders::_1
)),
55 wrapper(p
.configFile
, p
.filePath
, read_cb
, write_cb
),
56 retryReq(false), retryResp(false), startTick(0),
57 nbrOutstandingReads(0), nbrOutstandingWrites(0),
58 sendResponseEvent([this]{ sendResponse(); }, name()),
59 tickEvent([this]{ tick(); }, name())
62 "Instantiated DRAMsim3 with clock %d ns and queue size %d\n",
63 wrapper
.clockPeriod(), wrapper
.queueSize());
65 // Register a callback to compensate for the destructor not
66 // being called. The callback prints the DRAMsim3 stats.
67 registerExitCallback([this]() { wrapper
.printStats(); });
73 AbstractMemory::init();
75 if (!port
.isConnected()) {
76 fatal("DRAMsim3 %s is unconnected!\n", name());
78 port
.sendRangeChange();
81 if (system()->cacheLineSize() != wrapper
.burstSize())
82 fatal("DRAMsim3 burst size %d does not match cache line size %d\n",
83 wrapper
.burstSize(), system()->cacheLineSize());
89 startTick
= curTick();
91 // kick off the clock ticks
92 schedule(tickEvent
, clockEdge());
96 DRAMsim3::resetStats() {
101 DRAMsim3::sendResponse()
104 assert(!responseQueue
.empty());
106 DPRINTF(DRAMsim3
, "Attempting to send response\n");
108 bool success
= port
.sendTimingResp(responseQueue
.front());
110 responseQueue
.pop_front();
112 DPRINTF(DRAMsim3
, "Have %d read, %d write, %d responses outstanding\n",
113 nbrOutstandingReads
, nbrOutstandingWrites
,
114 responseQueue
.size());
116 if (!responseQueue
.empty() && !sendResponseEvent
.scheduled())
117 schedule(sendResponseEvent
, curTick());
119 if (nbrOutstanding() == 0)
124 DPRINTF(DRAMsim3
, "Waiting for response retry\n");
126 assert(!sendResponseEvent
.scheduled());
131 DRAMsim3::nbrOutstanding() const
133 return nbrOutstandingReads
+ nbrOutstandingWrites
+ responseQueue
.size();
139 // Only tick when it's timing mode
140 if (system()->isTimingMode()) {
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()) {
151 schedule(tickEvent
, curTick() + wrapper
.clockPeriod() * SimClock::Int::ns
);
155 DRAMsim3::recvAtomic(PacketPtr pkt
)
159 // 50 ns is just an arbitrary value at this point
160 return pkt
->cacheResponding() ? 0 : 50000;
164 DRAMsim3::recvFunctional(PacketPtr pkt
)
166 pkt
->pushLabel(name());
168 functionalAccess(pkt
);
170 // potentially update the packets in our response queue as well
171 for (auto i
= responseQueue
.begin(); i
!= responseQueue
.end(); ++i
)
172 pkt
->trySatisfyFunctional(*i
);
178 DRAMsim3::recvTimingReq(PacketPtr pkt
)
180 // if a cache is responding, sink the packet without further action
181 if (pkt
->cacheResponding()) {
182 pendingDelete
.reset(pkt
);
186 // we should not get a new request after committing to retry the
187 // current one, but unfortunately the CPU violates this rule, so
188 // simply ignore it for now
192 // if we cannot accept we need to send a retry once progress can
194 bool can_accept
= nbrOutstanding() < wrapper
.queueSize();
196 // keep track of the transaction
199 outstandingReads
[pkt
->getAddr()].push(pkt
);
201 // we count a transaction as outstanding until it has left the
202 // queue in the controller, and the response has been sent
203 // back, note that this will differ for reads and writes
204 ++nbrOutstandingReads
;
206 } else if (pkt
->isWrite()) {
208 outstandingWrites
[pkt
->getAddr()].push(pkt
);
210 ++nbrOutstandingWrites
;
212 // perform the access for writes
213 accessAndRespond(pkt
);
216 // keep it simple and just respond if necessary
217 accessAndRespond(pkt
);
222 // we should never have a situation when we think there is space,
224 assert(wrapper
.canAccept(pkt
->getAddr(), pkt
->isWrite()));
226 DPRINTF(DRAMsim3
, "Enqueueing address %lld\n", pkt
->getAddr());
228 // @todo what about the granularity here, implicit assumption that
229 // a transaction matches the burst size of the memory (which we
230 // cannot determine without parsing the ini file ourselves)
231 wrapper
.enqueue(pkt
->getAddr(), pkt
->isWrite());
241 DRAMsim3::recvRespRetry()
243 DPRINTF(DRAMsim3
, "Retrying\n");
251 DRAMsim3::accessAndRespond(PacketPtr pkt
)
253 DPRINTF(DRAMsim3
, "Access for address %lld\n", pkt
->getAddr());
255 bool needsResponse
= pkt
->needsResponse();
257 // do the actual memory access which also turns the packet into a
261 // turn packet around to go back to requestor if response expected
263 // access already turned the packet into a response
264 assert(pkt
->isResponse());
265 // Here we pay for xbar additional delay and to process the payload
267 Tick time
= curTick() + pkt
->headerDelay
+ pkt
->payloadDelay
;
268 // Reset the timings of the packet
269 pkt
->headerDelay
= pkt
->payloadDelay
= 0;
271 DPRINTF(DRAMsim3
, "Queuing response for address %lld\n",
274 // queue it to be sent back
275 responseQueue
.push_back(pkt
);
277 // if we are not already waiting for a retry, or are scheduled
278 // to send a response, schedule an event
279 if (!retryResp
&& !sendResponseEvent
.scheduled())
280 schedule(sendResponseEvent
, time
);
282 // queue the packet for deletion
283 pendingDelete
.reset(pkt
);
287 void DRAMsim3::readComplete(unsigned id
, uint64_t addr
)
290 DPRINTF(DRAMsim3
, "Read to address %lld complete\n", addr
);
292 // get the outstanding reads for the address in question
293 auto p
= outstandingReads
.find(addr
);
294 assert(p
!= outstandingReads
.end());
296 // first in first out, which is not necessarily true, but it is
297 // the best we can do at this point
298 PacketPtr pkt
= p
->second
.front();
301 if (p
->second
.empty())
302 outstandingReads
.erase(p
);
304 // no need to check for drain here as the next call will add a
305 // response to the response queue straight away
306 assert(nbrOutstandingReads
!= 0);
307 --nbrOutstandingReads
;
309 // perform the actual memory access
310 accessAndRespond(pkt
);
313 void DRAMsim3::writeComplete(unsigned id
, uint64_t addr
)
316 DPRINTF(DRAMsim3
, "Write to address %lld complete\n", addr
);
318 // get the outstanding reads for the address in question
319 auto p
= outstandingWrites
.find(addr
);
320 assert(p
!= outstandingWrites
.end());
322 // we have already responded, and this is only to keep track of
323 // what is outstanding
325 if (p
->second
.empty())
326 outstandingWrites
.erase(p
);
328 assert(nbrOutstandingWrites
!= 0);
329 --nbrOutstandingWrites
;
331 if (nbrOutstanding() == 0)
336 DRAMsim3::getPort(const std::string
&if_name
, PortID idx
)
338 if (if_name
!= "port") {
339 return ClockedObject::getPort(if_name
, idx
);
348 // check our outstanding reads and writes and if any they need to
350 return nbrOutstanding() != 0 ? DrainState::Draining
: DrainState::Drained
;
353 DRAMsim3::MemoryPort::MemoryPort(const std::string
& _name
,
355 : ResponsePort(_name
, &_memory
), memory(_memory
)
359 DRAMsim3::MemoryPort::getAddrRanges() const
361 AddrRangeList ranges
;
362 ranges
.push_back(memory
.getAddrRange());
367 DRAMsim3::MemoryPort::recvAtomic(PacketPtr pkt
)
369 return memory
.recvAtomic(pkt
);
373 DRAMsim3::MemoryPort::recvFunctional(PacketPtr pkt
)
375 memory
.recvFunctional(pkt
);
379 DRAMsim3::MemoryPort::recvTimingReq(PacketPtr pkt
)
381 // pass it to the memory controller
382 return memory
.recvTimingReq(pkt
);
386 DRAMsim3::MemoryPort::recvRespRetry()
388 memory
.recvRespRetry();