dramsim2.cc revision 11793:ef606668d247
16657Snate@binkert.org/* 26657Snate@binkert.org * Copyright (c) 2013 ARM Limited 36657Snate@binkert.org * All rights reserved 46657Snate@binkert.org * 56657Snate@binkert.org * The license below extends only to copyright in the software and shall 66657Snate@binkert.org * not be construed as granting a license to any other intellectual 76657Snate@binkert.org * property including but not limited to intellectual property relating 86657Snate@binkert.org * to a hardware implementation of the functionality of the software 96657Snate@binkert.org * licensed hereunder. You may use the software subject to the license 106657Snate@binkert.org * terms below provided that you ensure that this notice is replicated 116657Snate@binkert.org * unmodified and in its entirety in all distributions of the software, 126657Snate@binkert.org * modified or unmodified, in source code or in binary form. 136657Snate@binkert.org * 146657Snate@binkert.org * Redistribution and use in source and binary forms, with or without 156657Snate@binkert.org * modification, are permitted provided that the following conditions are 166657Snate@binkert.org * met: redistributions of source code must retain the above copyright 176657Snate@binkert.org * notice, this list of conditions and the following disclaimer; 186657Snate@binkert.org * redistributions in binary form must reproduce the above copyright 196657Snate@binkert.org * notice, this list of conditions and the following disclaimer in the 206657Snate@binkert.org * documentation and/or other materials provided with the distribution; 216657Snate@binkert.org * neither the name of the copyright holders nor the names of its 226657Snate@binkert.org * contributors may be used to endorse or promote products derived from 236657Snate@binkert.org * this software without specific prior written permission. 246657Snate@binkert.org * 256657Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 266657Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 276657Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 286657Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 296657Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 306657Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 316657Snate@binkert.org * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 327007Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 337007Snate@binkert.org * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 347007Snate@binkert.org * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 357007Snate@binkert.org * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 367007Snate@binkert.org * 376657Snate@binkert.org * Authors: Andreas Hansson 386657Snate@binkert.org */ 396657Snate@binkert.org 406657Snate@binkert.org#include "mem/dramsim2.hh" 416657Snate@binkert.org 426657Snate@binkert.org#include "DRAMSim2/Callback.h" 436657Snate@binkert.org#include "base/callback.hh" 446657Snate@binkert.org#include "base/trace.hh" 456657Snate@binkert.org#include "debug/DRAMSim2.hh" 466657Snate@binkert.org#include "debug/Drain.hh" 476657Snate@binkert.org#include "sim/system.hh" 486657Snate@binkert.org 496657Snate@binkert.orgDRAMSim2::DRAMSim2(const Params* p) : 506657Snate@binkert.org AbstractMemory(p), 516657Snate@binkert.org port(name() + ".port", *this), 526657Snate@binkert.org wrapper(p->deviceConfigFile, p->systemConfigFile, p->filePath, 536657Snate@binkert.org p->traceFile, p->range.size() / 1024 / 1024, p->enableDebug), 546657Snate@binkert.org retryReq(false), retryResp(false), startTick(0), 556657Snate@binkert.org nbrOutstandingReads(0), nbrOutstandingWrites(0), 566657Snate@binkert.org sendResponseEvent(this), tickEvent(this) 576657Snate@binkert.org{ 586657Snate@binkert.org DPRINTF(DRAMSim2, 596657Snate@binkert.org "Instantiated DRAMSim2 with clock %d ns and queue size %d\n", 606657Snate@binkert.org wrapper.clockPeriod(), wrapper.queueSize()); 616657Snate@binkert.org 626657Snate@binkert.org DRAMSim::TransactionCompleteCB* read_cb = 636657Snate@binkert.org new DRAMSim::Callback<DRAMSim2, void, unsigned, uint64_t, uint64_t>( 646657Snate@binkert.org this, &DRAMSim2::readComplete); 656657Snate@binkert.org DRAMSim::TransactionCompleteCB* write_cb = 666657Snate@binkert.org new DRAMSim::Callback<DRAMSim2, void, unsigned, uint64_t, uint64_t>( 676657Snate@binkert.org this, &DRAMSim2::writeComplete); 686657Snate@binkert.org wrapper.setCallbacks(read_cb, write_cb); 696657Snate@binkert.org 706657Snate@binkert.org // Register a callback to compensate for the destructor not 716657Snate@binkert.org // being called. The callback prints the DRAMSim2 stats. 726657Snate@binkert.org Callback* cb = new MakeCallback<DRAMSim2Wrapper, 736657Snate@binkert.org &DRAMSim2Wrapper::printStats>(wrapper); 746657Snate@binkert.org registerExitCallback(cb); 756657Snate@binkert.org} 766657Snate@binkert.org 776657Snate@binkert.orgvoid 786657Snate@binkert.orgDRAMSim2::init() 796657Snate@binkert.org{ 806657Snate@binkert.org AbstractMemory::init(); 816657Snate@binkert.org 826657Snate@binkert.org if (!port.isConnected()) { 83 fatal("DRAMSim2 %s is unconnected!\n", name()); 84 } else { 85 port.sendRangeChange(); 86 } 87 88 if (system()->cacheLineSize() != wrapper.burstSize()) 89 fatal("DRAMSim2 burst size %d does not match cache line size %d\n", 90 wrapper.burstSize(), system()->cacheLineSize()); 91} 92 93void 94DRAMSim2::startup() 95{ 96 startTick = curTick(); 97 98 // kick off the clock ticks 99 schedule(tickEvent, clockEdge()); 100} 101 102void 103DRAMSim2::sendResponse() 104{ 105 assert(!retryResp); 106 assert(!responseQueue.empty()); 107 108 DPRINTF(DRAMSim2, "Attempting to send response\n"); 109 110 bool success = port.sendTimingResp(responseQueue.front()); 111 if (success) { 112 responseQueue.pop_front(); 113 114 DPRINTF(DRAMSim2, "Have %d read, %d write, %d responses outstanding\n", 115 nbrOutstandingReads, nbrOutstandingWrites, 116 responseQueue.size()); 117 118 if (!responseQueue.empty() && !sendResponseEvent.scheduled()) 119 schedule(sendResponseEvent, curTick()); 120 121 if (nbrOutstanding() == 0) 122 signalDrainDone(); 123 } else { 124 retryResp = true; 125 126 DPRINTF(DRAMSim2, "Waiting for response retry\n"); 127 128 assert(!sendResponseEvent.scheduled()); 129 } 130} 131 132unsigned int 133DRAMSim2::nbrOutstanding() const 134{ 135 return nbrOutstandingReads + nbrOutstandingWrites + responseQueue.size(); 136} 137 138void 139DRAMSim2::tick() 140{ 141 wrapper.tick(); 142 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()) { 146 retryReq = false; 147 port.sendRetryReq(); 148 } 149 150 schedule(tickEvent, curTick() + wrapper.clockPeriod() * SimClock::Int::ns); 151} 152 153Tick 154DRAMSim2::recvAtomic(PacketPtr pkt) 155{ 156 access(pkt); 157 158 // 50 ns is just an arbitrary value at this point 159 return pkt->cacheResponding() ? 0 : 50000; 160} 161 162void 163DRAMSim2::recvFunctional(PacketPtr pkt) 164{ 165 pkt->pushLabel(name()); 166 167 functionalAccess(pkt); 168 169 // potentially update the packets in our response queue as well 170 for (auto i = responseQueue.begin(); i != responseQueue.end(); ++i) 171 pkt->checkFunctional(*i); 172 173 pkt->popLabel(); 174} 175 176bool 177DRAMSim2::recvTimingReq(PacketPtr pkt) 178{ 179 // if a cache is responding, sink the packet without further action 180 if (pkt->cacheResponding()) { 181 pendingDelete.reset(pkt); 182 return true; 183 } 184 185 // we should not get a new request after committing to retry the 186 // current one, but unfortunately the CPU violates this rule, so 187 // simply ignore it for now 188 if (retryReq) 189 return false; 190 191 // if we cannot accept we need to send a retry once progress can 192 // be made 193 bool can_accept = nbrOutstanding() < wrapper.queueSize(); 194 195 // keep track of the transaction 196 if (pkt->isRead()) { 197 if (can_accept) { 198 outstandingReads[pkt->getAddr()].push(pkt); 199 200 // we count a transaction as outstanding until it has left the 201 // queue in the controller, and the response has been sent 202 // back, note that this will differ for reads and writes 203 ++nbrOutstandingReads; 204 } 205 } else if (pkt->isWrite()) { 206 if (can_accept) { 207 outstandingWrites[pkt->getAddr()].push(pkt); 208 209 ++nbrOutstandingWrites; 210 211 // perform the access for writes 212 accessAndRespond(pkt); 213 } 214 } else { 215 // keep it simple and just respond if necessary 216 accessAndRespond(pkt); 217 return true; 218 } 219 220 if (can_accept) { 221 // we should never have a situation when we think there is space, 222 // and there isn't 223 assert(wrapper.canAccept()); 224 225 DPRINTF(DRAMSim2, "Enqueueing address %lld\n", pkt->getAddr()); 226 227 // @todo what about the granularity here, implicit assumption that 228 // a transaction matches the burst size of the memory (which we 229 // cannot determine without parsing the ini file ourselves) 230 wrapper.enqueue(pkt->isWrite(), pkt->getAddr()); 231 232 return true; 233 } else { 234 retryReq = true; 235 return false; 236 } 237} 238 239void 240DRAMSim2::recvRespRetry() 241{ 242 DPRINTF(DRAMSim2, "Retrying\n"); 243 244 assert(retryResp); 245 retryResp = false; 246 sendResponse(); 247} 248 249void 250DRAMSim2::accessAndRespond(PacketPtr pkt) 251{ 252 DPRINTF(DRAMSim2, "Access for address %lld\n", pkt->getAddr()); 253 254 bool needsResponse = pkt->needsResponse(); 255 256 // do the actual memory access which also turns the packet into a 257 // response 258 access(pkt); 259 260 // turn packet around to go back to requester if response expected 261 if (needsResponse) { 262 // access already turned the packet into a response 263 assert(pkt->isResponse()); 264 // Here we pay for xbar additional delay and to process the payload 265 // of the packet. 266 Tick time = curTick() + pkt->headerDelay + pkt->payloadDelay; 267 // Reset the timings of the packet 268 pkt->headerDelay = pkt->payloadDelay = 0; 269 270 DPRINTF(DRAMSim2, "Queuing response for address %lld\n", 271 pkt->getAddr()); 272 273 // queue it to be sent back 274 responseQueue.push_back(pkt); 275 276 // if we are not already waiting for a retry, or are scheduled 277 // to send a response, schedule an event 278 if (!retryResp && !sendResponseEvent.scheduled()) 279 schedule(sendResponseEvent, time); 280 } else { 281 // queue the packet for deletion 282 pendingDelete.reset(pkt); 283 } 284} 285 286void DRAMSim2::readComplete(unsigned id, uint64_t addr, uint64_t cycle) 287{ 288 assert(cycle == divCeil(curTick() - startTick, 289 wrapper.clockPeriod() * SimClock::Int::ns)); 290 291 DPRINTF(DRAMSim2, "Read to address %lld complete\n", addr); 292 293 // get the outstanding reads for the address in question 294 auto p = outstandingReads.find(addr); 295 assert(p != outstandingReads.end()); 296 297 // first in first out, which is not necessarily true, but it is 298 // the best we can do at this point 299 PacketPtr pkt = p->second.front(); 300 p->second.pop(); 301 302 if (p->second.empty()) 303 outstandingReads.erase(p); 304 305 // no need to check for drain here as the next call will add a 306 // response to the response queue straight away 307 assert(nbrOutstandingReads != 0); 308 --nbrOutstandingReads; 309 310 // perform the actual memory access 311 accessAndRespond(pkt); 312} 313 314void DRAMSim2::writeComplete(unsigned id, uint64_t addr, uint64_t cycle) 315{ 316 assert(cycle == divCeil(curTick() - startTick, 317 wrapper.clockPeriod() * SimClock::Int::ns)); 318 319 DPRINTF(DRAMSim2, "Write to address %lld complete\n", addr); 320 321 // get the outstanding reads for the address in question 322 auto p = outstandingWrites.find(addr); 323 assert(p != outstandingWrites.end()); 324 325 // we have already responded, and this is only to keep track of 326 // what is outstanding 327 p->second.pop(); 328 if (p->second.empty()) 329 outstandingWrites.erase(p); 330 331 assert(nbrOutstandingWrites != 0); 332 --nbrOutstandingWrites; 333 334 if (nbrOutstanding() == 0) 335 signalDrainDone(); 336} 337 338BaseSlavePort& 339DRAMSim2::getSlavePort(const std::string &if_name, PortID idx) 340{ 341 if (if_name != "port") { 342 return MemObject::getSlavePort(if_name, idx); 343 } else { 344 return port; 345 } 346} 347 348DrainState 349DRAMSim2::drain() 350{ 351 // check our outstanding reads and writes and if any they need to 352 // drain 353 return nbrOutstanding() != 0 ? DrainState::Draining : DrainState::Drained; 354} 355 356DRAMSim2::MemoryPort::MemoryPort(const std::string& _name, 357 DRAMSim2& _memory) 358 : SlavePort(_name, &_memory), memory(_memory) 359{ } 360 361AddrRangeList 362DRAMSim2::MemoryPort::getAddrRanges() const 363{ 364 AddrRangeList ranges; 365 ranges.push_back(memory.getAddrRange()); 366 return ranges; 367} 368 369Tick 370DRAMSim2::MemoryPort::recvAtomic(PacketPtr pkt) 371{ 372 return memory.recvAtomic(pkt); 373} 374 375void 376DRAMSim2::MemoryPort::recvFunctional(PacketPtr pkt) 377{ 378 memory.recvFunctional(pkt); 379} 380 381bool 382DRAMSim2::MemoryPort::recvTimingReq(PacketPtr pkt) 383{ 384 // pass it to the memory controller 385 return memory.recvTimingReq(pkt); 386} 387 388void 389DRAMSim2::MemoryPort::recvRespRetry() 390{ 391 memory.recvRespRetry(); 392} 393 394DRAMSim2* 395DRAMSim2Params::create() 396{ 397 return new DRAMSim2(this); 398} 399