arch-power: Add population count instructions
[gem5.git] / src / mem / dramsim3.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/dramsim3.hh"
39
40 #include "base/callback.hh"
41 #include "base/trace.hh"
42 #include "debug/DRAMsim3.hh"
43 #include "debug/Drain.hh"
44 #include "sim/system.hh"
45
46 DRAMsim3::DRAMsim3(const Params &p) :
47 AbstractMemory(p),
48 port(name() + ".port", *this),
49 read_cb(std::bind(&DRAMsim3::readComplete,
50 this, 0, std::placeholders::_1)),
51 write_cb(std::bind(&DRAMsim3::writeComplete,
52 this, 0, std::placeholders::_1)),
53 wrapper(p.configFile, p.filePath, read_cb, write_cb),
54 retryReq(false), retryResp(false), startTick(0),
55 nbrOutstandingReads(0), nbrOutstandingWrites(0),
56 sendResponseEvent([this]{ sendResponse(); }, name()),
57 tickEvent([this]{ tick(); }, name())
58 {
59 DPRINTF(DRAMsim3,
60 "Instantiated DRAMsim3 with clock %d ns and queue size %d\n",
61 wrapper.clockPeriod(), wrapper.queueSize());
62
63 // Register a callback to compensate for the destructor not
64 // being called. The callback prints the DRAMsim3 stats.
65 registerExitCallback([this]() { wrapper.printStats(); });
66 }
67
68 void
69 DRAMsim3::init()
70 {
71 AbstractMemory::init();
72
73 if (!port.isConnected()) {
74 fatal("DRAMsim3 %s is unconnected!\n", name());
75 } else {
76 port.sendRangeChange();
77 }
78
79 if (system()->cacheLineSize() != wrapper.burstSize())
80 fatal("DRAMsim3 burst size %d does not match cache line size %d\n",
81 wrapper.burstSize(), system()->cacheLineSize());
82 }
83
84 void
85 DRAMsim3::startup()
86 {
87 startTick = curTick();
88
89 // kick off the clock ticks
90 schedule(tickEvent, clockEdge());
91 }
92
93 void
94 DRAMsim3::resetStats() {
95 wrapper.resetStats();
96 }
97
98 void
99 DRAMsim3::sendResponse()
100 {
101 assert(!retryResp);
102 assert(!responseQueue.empty());
103
104 DPRINTF(DRAMsim3, "Attempting to send response\n");
105
106 bool success = port.sendTimingResp(responseQueue.front());
107 if (success) {
108 responseQueue.pop_front();
109
110 DPRINTF(DRAMsim3, "Have %d read, %d write, %d responses outstanding\n",
111 nbrOutstandingReads, nbrOutstandingWrites,
112 responseQueue.size());
113
114 if (!responseQueue.empty() && !sendResponseEvent.scheduled())
115 schedule(sendResponseEvent, curTick());
116
117 if (nbrOutstanding() == 0)
118 signalDrainDone();
119 } else {
120 retryResp = true;
121
122 DPRINTF(DRAMsim3, "Waiting for response retry\n");
123
124 assert(!sendResponseEvent.scheduled());
125 }
126 }
127
128 unsigned int
129 DRAMsim3::nbrOutstanding() const
130 {
131 return nbrOutstandingReads + nbrOutstandingWrites + responseQueue.size();
132 }
133
134 void
135 DRAMsim3::tick()
136 {
137 // Only tick when it's timing mode
138 if (system()->isTimingMode()) {
139 wrapper.tick();
140
141 // is the connected port waiting for a retry, if so check the
142 // state and send a retry if conditions have changed
143 if (retryReq && nbrOutstanding() < wrapper.queueSize()) {
144 retryReq = false;
145 port.sendRetryReq();
146 }
147 }
148
149 schedule(tickEvent, curTick() + wrapper.clockPeriod() * SimClock::Int::ns);
150 }
151
152 Tick
153 DRAMsim3::recvAtomic(PacketPtr pkt)
154 {
155 access(pkt);
156
157 // 50 ns is just an arbitrary value at this point
158 return pkt->cacheResponding() ? 0 : 50000;
159 }
160
161 void
162 DRAMsim3::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->trySatisfyFunctional(*i);
171
172 pkt->popLabel();
173 }
174
175 bool
176 DRAMsim3::recvTimingReq(PacketPtr pkt)
177 {
178 // if a cache is responding, sink the packet without further action
179 if (pkt->cacheResponding()) {
180 pendingDelete.reset(pkt);
181 return true;
182 }
183
184 // we should not get a new request after committing to retry the
185 // current one, but unfortunately the CPU violates this rule, so
186 // simply ignore it for now
187 if (retryReq)
188 return false;
189
190 // if we cannot accept we need to send a retry once progress can
191 // be made
192 bool can_accept = nbrOutstanding() < wrapper.queueSize();
193
194 // keep track of the transaction
195 if (pkt->isRead()) {
196 if (can_accept) {
197 outstandingReads[pkt->getAddr()].push(pkt);
198
199 // we count a transaction as outstanding until it has left the
200 // queue in the controller, and the response has been sent
201 // back, note that this will differ for reads and writes
202 ++nbrOutstandingReads;
203 }
204 } else if (pkt->isWrite()) {
205 if (can_accept) {
206 outstandingWrites[pkt->getAddr()].push(pkt);
207
208 ++nbrOutstandingWrites;
209
210 // perform the access for writes
211 accessAndRespond(pkt);
212 }
213 } else {
214 // keep it simple and just respond if necessary
215 accessAndRespond(pkt);
216 return true;
217 }
218
219 if (can_accept) {
220 // we should never have a situation when we think there is space,
221 // and there isn't
222 assert(wrapper.canAccept(pkt->getAddr(), pkt->isWrite()));
223
224 DPRINTF(DRAMsim3, "Enqueueing address %lld\n", pkt->getAddr());
225
226 // @todo what about the granularity here, implicit assumption that
227 // a transaction matches the burst size of the memory (which we
228 // cannot determine without parsing the ini file ourselves)
229 wrapper.enqueue(pkt->getAddr(), pkt->isWrite());
230
231 return true;
232 } else {
233 retryReq = true;
234 return false;
235 }
236 }
237
238 void
239 DRAMsim3::recvRespRetry()
240 {
241 DPRINTF(DRAMsim3, "Retrying\n");
242
243 assert(retryResp);
244 retryResp = false;
245 sendResponse();
246 }
247
248 void
249 DRAMsim3::accessAndRespond(PacketPtr pkt)
250 {
251 DPRINTF(DRAMsim3, "Access for address %lld\n", pkt->getAddr());
252
253 bool needsResponse = pkt->needsResponse();
254
255 // do the actual memory access which also turns the packet into a
256 // response
257 access(pkt);
258
259 // turn packet around to go back to requestor if response expected
260 if (needsResponse) {
261 // access already turned the packet into a response
262 assert(pkt->isResponse());
263 // Here we pay for xbar additional delay and to process the payload
264 // of the packet.
265 Tick time = curTick() + pkt->headerDelay + pkt->payloadDelay;
266 // Reset the timings of the packet
267 pkt->headerDelay = pkt->payloadDelay = 0;
268
269 DPRINTF(DRAMsim3, "Queuing response for address %lld\n",
270 pkt->getAddr());
271
272 // queue it to be sent back
273 responseQueue.push_back(pkt);
274
275 // if we are not already waiting for a retry, or are scheduled
276 // to send a response, schedule an event
277 if (!retryResp && !sendResponseEvent.scheduled())
278 schedule(sendResponseEvent, time);
279 } else {
280 // queue the packet for deletion
281 pendingDelete.reset(pkt);
282 }
283 }
284
285 void DRAMsim3::readComplete(unsigned id, uint64_t addr)
286 {
287
288 DPRINTF(DRAMsim3, "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 DRAMsim3::writeComplete(unsigned id, uint64_t addr)
312 {
313
314 DPRINTF(DRAMsim3, "Write to address %lld complete\n", addr);
315
316 // get the outstanding reads for the address in question
317 auto p = outstandingWrites.find(addr);
318 assert(p != outstandingWrites.end());
319
320 // we have already responded, and this is only to keep track of
321 // what is outstanding
322 p->second.pop();
323 if (p->second.empty())
324 outstandingWrites.erase(p);
325
326 assert(nbrOutstandingWrites != 0);
327 --nbrOutstandingWrites;
328
329 if (nbrOutstanding() == 0)
330 signalDrainDone();
331 }
332
333 Port&
334 DRAMsim3::getPort(const std::string &if_name, PortID idx)
335 {
336 if (if_name != "port") {
337 return ClockedObject::getPort(if_name, idx);
338 } else {
339 return port;
340 }
341 }
342
343 DrainState
344 DRAMsim3::drain()
345 {
346 // check our outstanding reads and writes and if any they need to
347 // drain
348 return nbrOutstanding() != 0 ? DrainState::Draining : DrainState::Drained;
349 }
350
351 DRAMsim3::MemoryPort::MemoryPort(const std::string& _name,
352 DRAMsim3& _memory)
353 : ResponsePort(_name, &_memory), memory(_memory)
354 { }
355
356 AddrRangeList
357 DRAMsim3::MemoryPort::getAddrRanges() const
358 {
359 AddrRangeList ranges;
360 ranges.push_back(memory.getAddrRange());
361 return ranges;
362 }
363
364 Tick
365 DRAMsim3::MemoryPort::recvAtomic(PacketPtr pkt)
366 {
367 return memory.recvAtomic(pkt);
368 }
369
370 void
371 DRAMsim3::MemoryPort::recvFunctional(PacketPtr pkt)
372 {
373 memory.recvFunctional(pkt);
374 }
375
376 bool
377 DRAMsim3::MemoryPort::recvTimingReq(PacketPtr pkt)
378 {
379 // pass it to the memory controller
380 return memory.recvTimingReq(pkt);
381 }
382
383 void
384 DRAMsim3::MemoryPort::recvRespRetry()
385 {
386 memory.recvRespRetry();
387 }