timing.cc revision 3201:7c3b18c01b0e
1/* 2 * Copyright (c) 2002-2005 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Authors: Steve Reinhardt 29 */ 30 31#include "arch/locked_mem.hh" 32#include "arch/utility.hh" 33#include "cpu/exetrace.hh" 34#include "cpu/simple/timing.hh" 35#include "mem/packet_impl.hh" 36#include "sim/builder.hh" 37#include "sim/system.hh" 38 39using namespace std; 40using namespace TheISA; 41 42Port * 43TimingSimpleCPU::getPort(const std::string &if_name, int idx) 44{ 45 if (if_name == "dcache_port") 46 return &dcachePort; 47 else if (if_name == "icache_port") 48 return &icachePort; 49 else 50 panic("No Such Port\n"); 51} 52 53void 54TimingSimpleCPU::init() 55{ 56 BaseCPU::init(); 57#if FULL_SYSTEM 58 for (int i = 0; i < threadContexts.size(); ++i) { 59 ThreadContext *tc = threadContexts[i]; 60 61 // initialize CPU, including PC 62 TheISA::initCPU(tc, tc->readCpuId()); 63 } 64#endif 65} 66 67Tick 68TimingSimpleCPU::CpuPort::recvAtomic(Packet *pkt) 69{ 70 panic("TimingSimpleCPU doesn't expect recvAtomic callback!"); 71 return curTick; 72} 73 74void 75TimingSimpleCPU::CpuPort::recvFunctional(Packet *pkt) 76{ 77 //No internal storage to update, jusst return 78 return; 79} 80 81void 82TimingSimpleCPU::CpuPort::recvStatusChange(Status status) 83{ 84 if (status == RangeChange) 85 return; 86 87 panic("TimingSimpleCPU doesn't expect recvStatusChange callback!"); 88} 89 90 91void 92TimingSimpleCPU::CpuPort::TickEvent::schedule(Packet *_pkt, Tick t) 93{ 94 pkt = _pkt; 95 Event::schedule(t); 96} 97 98TimingSimpleCPU::TimingSimpleCPU(Params *p) 99 : BaseSimpleCPU(p), icachePort(this, p->clock), dcachePort(this, p->clock), 100 cpu_id(p->cpu_id) 101{ 102 _status = Idle; 103 ifetch_pkt = dcache_pkt = NULL; 104 drainEvent = NULL; 105 fetchEvent = NULL; 106 changeState(SimObject::Running); 107} 108 109 110TimingSimpleCPU::~TimingSimpleCPU() 111{ 112} 113 114void 115TimingSimpleCPU::serialize(ostream &os) 116{ 117 SimObject::State so_state = SimObject::getState(); 118 SERIALIZE_ENUM(so_state); 119 BaseSimpleCPU::serialize(os); 120} 121 122void 123TimingSimpleCPU::unserialize(Checkpoint *cp, const string §ion) 124{ 125 SimObject::State so_state; 126 UNSERIALIZE_ENUM(so_state); 127 BaseSimpleCPU::unserialize(cp, section); 128} 129 130unsigned int 131TimingSimpleCPU::drain(Event *drain_event) 132{ 133 // TimingSimpleCPU is ready to drain if it's not waiting for 134 // an access to complete. 135 if (status() == Idle || status() == Running || status() == SwitchedOut) { 136 changeState(SimObject::Drained); 137 return 0; 138 } else { 139 changeState(SimObject::Draining); 140 drainEvent = drain_event; 141 return 1; 142 } 143} 144 145void 146TimingSimpleCPU::resume() 147{ 148 if (_status != SwitchedOut && _status != Idle) { 149 assert(system->getMemoryMode() == System::Timing); 150 151 // Delete the old event if it existed. 152 if (fetchEvent) { 153 if (fetchEvent->scheduled()) 154 fetchEvent->deschedule(); 155 156 delete fetchEvent; 157 } 158 159 fetchEvent = 160 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false); 161 fetchEvent->schedule(curTick); 162 } 163 164 changeState(SimObject::Running); 165} 166 167void 168TimingSimpleCPU::switchOut() 169{ 170 assert(status() == Running || status() == Idle); 171 _status = SwitchedOut; 172 173 // If we've been scheduled to resume but are then told to switch out, 174 // we'll need to cancel it. 175 if (fetchEvent && fetchEvent->scheduled()) 176 fetchEvent->deschedule(); 177} 178 179 180void 181TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU) 182{ 183 BaseCPU::takeOverFrom(oldCPU); 184 185 // if any of this CPU's ThreadContexts are active, mark the CPU as 186 // running and schedule its tick event. 187 for (int i = 0; i < threadContexts.size(); ++i) { 188 ThreadContext *tc = threadContexts[i]; 189 if (tc->status() == ThreadContext::Active && _status != Running) { 190 _status = Running; 191 break; 192 } 193 } 194 195 if (_status != Running) { 196 _status = Idle; 197 } 198} 199 200 201void 202TimingSimpleCPU::activateContext(int thread_num, int delay) 203{ 204 assert(thread_num == 0); 205 assert(thread); 206 207 assert(_status == Idle); 208 209 notIdleFraction++; 210 _status = Running; 211 // kick things off by initiating the fetch of the next instruction 212 fetchEvent = 213 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false); 214 fetchEvent->schedule(curTick + cycles(delay)); 215} 216 217 218void 219TimingSimpleCPU::suspendContext(int thread_num) 220{ 221 assert(thread_num == 0); 222 assert(thread); 223 224 assert(_status == Running); 225 226 // just change status to Idle... if status != Running, 227 // completeInst() will not initiate fetch of next instruction. 228 229 notIdleFraction--; 230 _status = Idle; 231} 232 233 234template <class T> 235Fault 236TimingSimpleCPU::read(Addr addr, T &data, unsigned flags) 237{ 238 Request *req = 239 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(), 240 cpu_id, /* thread ID */ 0); 241 242 if (traceData) { 243 traceData->setAddr(req->getVaddr()); 244 } 245 246 // translate to physical address 247 Fault fault = thread->translateDataReadReq(req); 248 249 // Now do the access. 250 if (fault == NoFault) { 251 Packet *pkt = 252 new Packet(req, Packet::ReadReq, Packet::Broadcast); 253 pkt->dataDynamic<T>(new T); 254 255 if (!dcachePort.sendTiming(pkt)) { 256 _status = DcacheRetry; 257 dcache_pkt = pkt; 258 } else { 259 _status = DcacheWaitResponse; 260 // memory system takes ownership of packet 261 dcache_pkt = NULL; 262 } 263 } 264 265 // This will need a new way to tell if it has a dcache attached. 266 if (req->isUncacheable()) 267 recordEvent("Uncached Read"); 268 269 return fault; 270} 271 272#ifndef DOXYGEN_SHOULD_SKIP_THIS 273 274template 275Fault 276TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags); 277 278template 279Fault 280TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags); 281 282template 283Fault 284TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags); 285 286template 287Fault 288TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags); 289 290#endif //DOXYGEN_SHOULD_SKIP_THIS 291 292template<> 293Fault 294TimingSimpleCPU::read(Addr addr, double &data, unsigned flags) 295{ 296 return read(addr, *(uint64_t*)&data, flags); 297} 298 299template<> 300Fault 301TimingSimpleCPU::read(Addr addr, float &data, unsigned flags) 302{ 303 return read(addr, *(uint32_t*)&data, flags); 304} 305 306 307template<> 308Fault 309TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags) 310{ 311 return read(addr, (uint32_t&)data, flags); 312} 313 314 315template <class T> 316Fault 317TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) 318{ 319 Request *req = 320 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(), 321 cpu_id, /* thread ID */ 0); 322 323 // translate to physical address 324 Fault fault = thread->translateDataWriteReq(req); 325 326 // Now do the access. 327 if (fault == NoFault) { 328 assert(dcache_pkt == NULL); 329 dcache_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast); 330 dcache_pkt->allocate(); 331 dcache_pkt->set(data); 332 333 bool do_access = true; // flag to suppress cache access 334 335 if (req->isLocked()) { 336 do_access = TheISA::handleLockedWrite(thread, req); 337 } 338 339 if (do_access) { 340 if (!dcachePort.sendTiming(dcache_pkt)) { 341 _status = DcacheRetry; 342 } else { 343 _status = DcacheWaitResponse; 344 // memory system takes ownership of packet 345 dcache_pkt = NULL; 346 } 347 } 348 } 349 350 // This will need a new way to tell if it's hooked up to a cache or not. 351 if (req->isUncacheable()) 352 recordEvent("Uncached Write"); 353 354 // If the write needs to have a fault on the access, consider calling 355 // changeStatus() and changing it to "bad addr write" or something. 356 return fault; 357} 358 359 360#ifndef DOXYGEN_SHOULD_SKIP_THIS 361template 362Fault 363TimingSimpleCPU::write(uint64_t data, Addr addr, 364 unsigned flags, uint64_t *res); 365 366template 367Fault 368TimingSimpleCPU::write(uint32_t data, Addr addr, 369 unsigned flags, uint64_t *res); 370 371template 372Fault 373TimingSimpleCPU::write(uint16_t data, Addr addr, 374 unsigned flags, uint64_t *res); 375 376template 377Fault 378TimingSimpleCPU::write(uint8_t data, Addr addr, 379 unsigned flags, uint64_t *res); 380 381#endif //DOXYGEN_SHOULD_SKIP_THIS 382 383template<> 384Fault 385TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) 386{ 387 return write(*(uint64_t*)&data, addr, flags, res); 388} 389 390template<> 391Fault 392TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) 393{ 394 return write(*(uint32_t*)&data, addr, flags, res); 395} 396 397 398template<> 399Fault 400TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) 401{ 402 return write((uint32_t)data, addr, flags, res); 403} 404 405 406void 407TimingSimpleCPU::fetch() 408{ 409 checkForInterrupts(); 410 411 Request *ifetch_req = new Request(); 412 ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0); 413 Fault fault = setupFetchRequest(ifetch_req); 414 415 ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast); 416 ifetch_pkt->dataStatic(&inst); 417 418 if (fault == NoFault) { 419 if (!icachePort.sendTiming(ifetch_pkt)) { 420 // Need to wait for retry 421 _status = IcacheRetry; 422 } else { 423 // Need to wait for cache to respond 424 _status = IcacheWaitResponse; 425 // ownership of packet transferred to memory system 426 ifetch_pkt = NULL; 427 } 428 } else { 429 // fetch fault: advance directly to next instruction (fault handler) 430 advanceInst(fault); 431 } 432} 433 434 435void 436TimingSimpleCPU::advanceInst(Fault fault) 437{ 438 advancePC(fault); 439 440 if (_status == Running) { 441 // kick off fetch of next instruction... callback from icache 442 // response will cause that instruction to be executed, 443 // keeping the CPU running. 444 fetch(); 445 } 446} 447 448 449void 450TimingSimpleCPU::completeIfetch(Packet *pkt) 451{ 452 // received a response from the icache: execute the received 453 // instruction 454 assert(pkt->result == Packet::Success); 455 assert(_status == IcacheWaitResponse); 456 457 _status = Running; 458 459 delete pkt->req; 460 delete pkt; 461 462 if (getState() == SimObject::Draining) { 463 completeDrain(); 464 return; 465 } 466 467 preExecute(); 468 if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) { 469 // load or store: just send to dcache 470 Fault fault = curStaticInst->initiateAcc(this, traceData); 471 if (_status != Running) { 472 // instruction will complete in dcache response callback 473 assert(_status == DcacheWaitResponse || _status == DcacheRetry); 474 assert(fault == NoFault); 475 } else { 476 if (fault == NoFault) { 477 // early fail on store conditional: complete now 478 assert(dcache_pkt != NULL); 479 fault = curStaticInst->completeAcc(dcache_pkt, this, 480 traceData); 481 delete dcache_pkt->req; 482 delete dcache_pkt; 483 dcache_pkt = NULL; 484 } 485 postExecute(); 486 advanceInst(fault); 487 } 488 } else { 489 // non-memory instruction: execute completely now 490 Fault fault = curStaticInst->execute(this, traceData); 491 postExecute(); 492 advanceInst(fault); 493 } 494} 495 496void 497TimingSimpleCPU::IcachePort::ITickEvent::process() 498{ 499 cpu->completeIfetch(pkt); 500} 501 502bool 503TimingSimpleCPU::IcachePort::recvTiming(Packet *pkt) 504{ 505 // delay processing of returned data until next CPU clock edge 506 Tick time = pkt->req->getTime(); 507 while (time < curTick) 508 time += lat; 509 510 if (time == curTick) 511 cpu->completeIfetch(pkt); 512 else 513 tickEvent.schedule(pkt, time); 514 515 return true; 516} 517 518void 519TimingSimpleCPU::IcachePort::recvRetry() 520{ 521 // we shouldn't get a retry unless we have a packet that we're 522 // waiting to transmit 523 assert(cpu->ifetch_pkt != NULL); 524 assert(cpu->_status == IcacheRetry); 525 Packet *tmp = cpu->ifetch_pkt; 526 if (sendTiming(tmp)) { 527 cpu->_status = IcacheWaitResponse; 528 cpu->ifetch_pkt = NULL; 529 } 530} 531 532void 533TimingSimpleCPU::completeDataAccess(Packet *pkt) 534{ 535 // received a response from the dcache: complete the load or store 536 // instruction 537 assert(pkt->result == Packet::Success); 538 assert(_status == DcacheWaitResponse); 539 _status = Running; 540 541 Fault fault = curStaticInst->completeAcc(pkt, this, traceData); 542 543 if (pkt->isRead() && pkt->req->isLocked()) { 544 TheISA::handleLockedRead(thread, pkt->req); 545 } 546 547 delete pkt->req; 548 delete pkt; 549 550 if (getState() == SimObject::Draining) { 551 advancePC(fault); 552 completeDrain(); 553 554 return; 555 } 556 557 postExecute(); 558 advanceInst(fault); 559} 560 561 562void 563TimingSimpleCPU::completeDrain() 564{ 565 DPRINTF(Config, "Done draining\n"); 566 changeState(SimObject::Drained); 567 drainEvent->process(); 568} 569 570bool 571TimingSimpleCPU::DcachePort::recvTiming(Packet *pkt) 572{ 573 // delay processing of returned data until next CPU clock edge 574 Tick time = pkt->req->getTime(); 575 while (time < curTick) 576 time += lat; 577 578 if (time == curTick) 579 cpu->completeDataAccess(pkt); 580 else 581 tickEvent.schedule(pkt, time); 582 583 return true; 584} 585 586void 587TimingSimpleCPU::DcachePort::DTickEvent::process() 588{ 589 cpu->completeDataAccess(pkt); 590} 591 592void 593TimingSimpleCPU::DcachePort::recvRetry() 594{ 595 // we shouldn't get a retry unless we have a packet that we're 596 // waiting to transmit 597 assert(cpu->dcache_pkt != NULL); 598 assert(cpu->_status == DcacheRetry); 599 Packet *tmp = cpu->dcache_pkt; 600 if (sendTiming(tmp)) { 601 cpu->_status = DcacheWaitResponse; 602 // memory system takes ownership of packet 603 cpu->dcache_pkt = NULL; 604 } 605} 606 607 608//////////////////////////////////////////////////////////////////////// 609// 610// TimingSimpleCPU Simulation Object 611// 612BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU) 613 614 Param<Counter> max_insts_any_thread; 615 Param<Counter> max_insts_all_threads; 616 Param<Counter> max_loads_any_thread; 617 Param<Counter> max_loads_all_threads; 618 Param<Tick> progress_interval; 619 SimObjectParam<MemObject *> mem; 620 SimObjectParam<System *> system; 621 Param<int> cpu_id; 622 623#if FULL_SYSTEM 624 SimObjectParam<AlphaITB *> itb; 625 SimObjectParam<AlphaDTB *> dtb; 626 Param<Tick> profile; 627#else 628 SimObjectParam<Process *> workload; 629#endif // FULL_SYSTEM 630 631 Param<int> clock; 632 633 Param<bool> defer_registration; 634 Param<int> width; 635 Param<bool> function_trace; 636 Param<Tick> function_trace_start; 637 Param<bool> simulate_stalls; 638 639END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU) 640 641BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU) 642 643 INIT_PARAM(max_insts_any_thread, 644 "terminate when any thread reaches this inst count"), 645 INIT_PARAM(max_insts_all_threads, 646 "terminate when all threads have reached this inst count"), 647 INIT_PARAM(max_loads_any_thread, 648 "terminate when any thread reaches this load count"), 649 INIT_PARAM(max_loads_all_threads, 650 "terminate when all threads have reached this load count"), 651 INIT_PARAM(progress_interval, "Progress interval"), 652 INIT_PARAM(mem, "memory"), 653 INIT_PARAM(system, "system object"), 654 INIT_PARAM(cpu_id, "processor ID"), 655 656#if FULL_SYSTEM 657 INIT_PARAM(itb, "Instruction TLB"), 658 INIT_PARAM(dtb, "Data TLB"), 659 INIT_PARAM(profile, ""), 660#else 661 INIT_PARAM(workload, "processes to run"), 662#endif // FULL_SYSTEM 663 664 INIT_PARAM(clock, "clock speed"), 665 INIT_PARAM(defer_registration, "defer system registration (for sampling)"), 666 INIT_PARAM(width, "cpu width"), 667 INIT_PARAM(function_trace, "Enable function trace"), 668 INIT_PARAM(function_trace_start, "Cycle to start function trace"), 669 INIT_PARAM(simulate_stalls, "Simulate cache stall cycles") 670 671END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU) 672 673 674CREATE_SIM_OBJECT(TimingSimpleCPU) 675{ 676 TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params(); 677 params->name = getInstanceName(); 678 params->numberOfThreads = 1; 679 params->max_insts_any_thread = max_insts_any_thread; 680 params->max_insts_all_threads = max_insts_all_threads; 681 params->max_loads_any_thread = max_loads_any_thread; 682 params->max_loads_all_threads = max_loads_all_threads; 683 params->progress_interval = progress_interval; 684 params->deferRegistration = defer_registration; 685 params->clock = clock; 686 params->functionTrace = function_trace; 687 params->functionTraceStart = function_trace_start; 688 params->mem = mem; 689 params->system = system; 690 params->cpu_id = cpu_id; 691 692#if FULL_SYSTEM 693 params->itb = itb; 694 params->dtb = dtb; 695 params->profile = profile; 696#else 697 params->process = workload; 698#endif 699 700 TimingSimpleCPU *cpu = new TimingSimpleCPU(params); 701 return cpu; 702} 703 704REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU) 705 706