mem: Remove DRAMSim2 DDR3 configuration
[gem5.git] / src / mem / dramsim2.cc
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
48 DRAMSim2::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
77 void
78 DRAMSim2::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
91 void
92 DRAMSim2::startup()
93 {
94 startTick = curTick();
95
96 // kick off the clock ticks
97 schedule(tickEvent, clockEdge());
98 }
99
100 void
101 DRAMSim2::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
133 unsigned int
134 DRAMSim2::nbrOutstanding() const
135 {
136 return nbrOutstandingReads + nbrOutstandingWrites + responseQueue.size();
137 }
138
139 void
140 DRAMSim2::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
154 Tick
155 DRAMSim2::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
163 void
164 DRAMSim2::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
177 bool
178 DRAMSim2::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
244 void
245 DRAMSim2::recvRetry()
246 {
247 DPRINTF(DRAMSim2, "Retrying\n");
248
249 assert(retryResp);
250 retryResp = false;
251 sendResponse();
252 }
253
254 void
255 DRAMSim2::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
290 void 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
318 void 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
345 BaseSlavePort&
346 DRAMSim2::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
355 unsigned int
356 DRAMSim2::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
370 DRAMSim2::MemoryPort::MemoryPort(const std::string& _name,
371 DRAMSim2& _memory)
372 : SlavePort(_name, &_memory), memory(_memory)
373 { }
374
375 AddrRangeList
376 DRAMSim2::MemoryPort::getAddrRanges() const
377 {
378 AddrRangeList ranges;
379 ranges.push_back(memory.getAddrRange());
380 return ranges;
381 }
382
383 Tick
384 DRAMSim2::MemoryPort::recvAtomic(PacketPtr pkt)
385 {
386 return memory.recvAtomic(pkt);
387 }
388
389 void
390 DRAMSim2::MemoryPort::recvFunctional(PacketPtr pkt)
391 {
392 memory.recvFunctional(pkt);
393 }
394
395 bool
396 DRAMSim2::MemoryPort::recvTimingReq(PacketPtr pkt)
397 {
398 // pass it to the memory controller
399 return memory.recvTimingReq(pkt);
400 }
401
402 void
403 DRAMSim2::MemoryPort::recvRetry()
404 {
405 memory.recvRetry();
406 }
407
408 DRAMSim2*
409 DRAMSim2Params::create()
410 {
411 return new DRAMSim2(this);
412 }