misc: Replaced master/slave terminology
[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
38 #include "mem/dramsim2.hh"
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 "sim/system.hh"
46
47 DRAMSim2::DRAMSim2(const Params* p) :
48 AbstractMemory(p),
49 port(name() + ".port", *this),
50 wrapper(p->deviceConfigFile, p->systemConfigFile, p->filePath,
51 p->traceFile, p->range.size() / 1024 / 1024, p->enableDebug),
52 retryReq(false), retryResp(false), startTick(0),
53 nbrOutstandingReads(0), nbrOutstandingWrites(0),
54 sendResponseEvent([this]{ sendResponse(); }, name()),
55 tickEvent([this]{ tick(); }, name())
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 registerExitCallback([this]() { wrapper.printStats(); });
72 }
73
74 void
75 DRAMSim2::init()
76 {
77 AbstractMemory::init();
78
79 if (!port.isConnected()) {
80 fatal("DRAMSim2 %s is unconnected!\n", name());
81 } else {
82 port.sendRangeChange();
83 }
84
85 if (system()->cacheLineSize() != wrapper.burstSize())
86 fatal("DRAMSim2 burst size %d does not match cache line size %d\n",
87 wrapper.burstSize(), system()->cacheLineSize());
88 }
89
90 void
91 DRAMSim2::startup()
92 {
93 startTick = curTick();
94
95 // kick off the clock ticks
96 schedule(tickEvent, clockEdge());
97 }
98
99 void
100 DRAMSim2::sendResponse()
101 {
102 assert(!retryResp);
103 assert(!responseQueue.empty());
104
105 DPRINTF(DRAMSim2, "Attempting to send response\n");
106
107 bool success = port.sendTimingResp(responseQueue.front());
108 if (success) {
109 responseQueue.pop_front();
110
111 DPRINTF(DRAMSim2, "Have %d read, %d write, %d responses outstanding\n",
112 nbrOutstandingReads, nbrOutstandingWrites,
113 responseQueue.size());
114
115 if (!responseQueue.empty() && !sendResponseEvent.scheduled())
116 schedule(sendResponseEvent, curTick());
117
118 if (nbrOutstanding() == 0)
119 signalDrainDone();
120 } else {
121 retryResp = true;
122
123 DPRINTF(DRAMSim2, "Waiting for response retry\n");
124
125 assert(!sendResponseEvent.scheduled());
126 }
127 }
128
129 unsigned int
130 DRAMSim2::nbrOutstanding() const
131 {
132 return nbrOutstandingReads + nbrOutstandingWrites + responseQueue.size();
133 }
134
135 void
136 DRAMSim2::tick()
137 {
138 wrapper.tick();
139
140 // is the connected port waiting for a retry, if so check the
141 // state and send a retry if conditions have changed
142 if (retryReq && nbrOutstanding() < wrapper.queueSize()) {
143 retryReq = false;
144 port.sendRetryReq();
145 }
146
147 schedule(tickEvent, curTick() + wrapper.clockPeriod() * SimClock::Int::ns);
148 }
149
150 Tick
151 DRAMSim2::recvAtomic(PacketPtr pkt)
152 {
153 access(pkt);
154
155 // 50 ns is just an arbitrary value at this point
156 return pkt->cacheResponding() ? 0 : 50000;
157 }
158
159 void
160 DRAMSim2::recvFunctional(PacketPtr pkt)
161 {
162 pkt->pushLabel(name());
163
164 functionalAccess(pkt);
165
166 // potentially update the packets in our response queue as well
167 for (auto i = responseQueue.begin(); i != responseQueue.end(); ++i)
168 pkt->trySatisfyFunctional(*i);
169
170 pkt->popLabel();
171 }
172
173 bool
174 DRAMSim2::recvTimingReq(PacketPtr pkt)
175 {
176 // if a cache is responding, sink the packet without further action
177 if (pkt->cacheResponding()) {
178 pendingDelete.reset(pkt);
179 return true;
180 }
181
182 // we should not get a new request after committing to retry the
183 // current one, but unfortunately the CPU violates this rule, so
184 // simply ignore it for now
185 if (retryReq)
186 return false;
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
236 void
237 DRAMSim2::recvRespRetry()
238 {
239 DPRINTF(DRAMSim2, "Retrying\n");
240
241 assert(retryResp);
242 retryResp = false;
243 sendResponse();
244 }
245
246 void
247 DRAMSim2::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 requestor 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
283 void 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
311 void 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
335 Port &
336 DRAMSim2::getPort(const std::string &if_name, PortID idx)
337 {
338 if (if_name != "port") {
339 return AbstractMemory::getPort(if_name, idx);
340 } else {
341 return port;
342 }
343 }
344
345 DrainState
346 DRAMSim2::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
353 DRAMSim2::MemoryPort::MemoryPort(const std::string& _name,
354 DRAMSim2& _memory)
355 : ResponsePort(_name, &_memory), memory(_memory)
356 { }
357
358 AddrRangeList
359 DRAMSim2::MemoryPort::getAddrRanges() const
360 {
361 AddrRangeList ranges;
362 ranges.push_back(memory.getAddrRange());
363 return ranges;
364 }
365
366 Tick
367 DRAMSim2::MemoryPort::recvAtomic(PacketPtr pkt)
368 {
369 return memory.recvAtomic(pkt);
370 }
371
372 void
373 DRAMSim2::MemoryPort::recvFunctional(PacketPtr pkt)
374 {
375 memory.recvFunctional(pkt);
376 }
377
378 bool
379 DRAMSim2::MemoryPort::recvTimingReq(PacketPtr pkt)
380 {
381 // pass it to the memory controller
382 return memory.recvTimingReq(pkt);
383 }
384
385 void
386 DRAMSim2::MemoryPort::recvRespRetry()
387 {
388 memory.recvRespRetry();
389 }
390
391 DRAMSim2*
392 DRAMSim2Params::create()
393 {
394 return new DRAMSim2(this);
395 }