timing.cc revision 3667:1d57100f8bf0
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.hh" 36#include "mem/packet_access.hh" 37#include "sim/builder.hh" 38#include "sim/system.hh" 39 40using namespace std; 41using namespace TheISA; 42 43Port * 44TimingSimpleCPU::getPort(const std::string &if_name, int idx) 45{ 46 if (if_name == "dcache_port") 47 return &dcachePort; 48 else if (if_name == "icache_port") 49 return &icachePort; 50 else 51 panic("No Such Port\n"); 52} 53 54void 55TimingSimpleCPU::init() 56{ 57 BaseCPU::init(); 58#if FULL_SYSTEM 59 for (int i = 0; i < threadContexts.size(); ++i) { 60 ThreadContext *tc = threadContexts[i]; 61 62 // initialize CPU, including PC 63 TheISA::initCPU(tc, tc->readCpuId()); 64 } 65#endif 66} 67 68Tick 69TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt) 70{ 71 panic("TimingSimpleCPU doesn't expect recvAtomic callback!"); 72 return curTick; 73} 74 75void 76TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt) 77{ 78 //No internal storage to update, jusst return 79 return; 80} 81 82void 83TimingSimpleCPU::CpuPort::recvStatusChange(Status status) 84{ 85 if (status == RangeChange) { 86 if (!snoopRangeSent) { 87 snoopRangeSent = true; 88 sendStatusChange(Port::RangeChange); 89 } 90 return; 91 } 92 93 panic("TimingSimpleCPU doesn't expect recvStatusChange callback!"); 94} 95 96 97void 98TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t) 99{ 100 pkt = _pkt; 101 Event::schedule(t); 102} 103 104TimingSimpleCPU::TimingSimpleCPU(Params *p) 105 : BaseSimpleCPU(p), icachePort(this, p->clock), dcachePort(this, p->clock), 106 cpu_id(p->cpu_id) 107{ 108 _status = Idle; 109 110 icachePort.snoopRangeSent = false; 111 dcachePort.snoopRangeSent = false; 112 113 ifetch_pkt = dcache_pkt = NULL; 114 drainEvent = NULL; 115 fetchEvent = NULL; 116 previousTick = 0; 117 changeState(SimObject::Running); 118} 119 120 121TimingSimpleCPU::~TimingSimpleCPU() 122{ 123} 124 125void 126TimingSimpleCPU::serialize(ostream &os) 127{ 128 SimObject::State so_state = SimObject::getState(); 129 SERIALIZE_ENUM(so_state); 130 BaseSimpleCPU::serialize(os); 131} 132 133void 134TimingSimpleCPU::unserialize(Checkpoint *cp, const string §ion) 135{ 136 SimObject::State so_state; 137 UNSERIALIZE_ENUM(so_state); 138 BaseSimpleCPU::unserialize(cp, section); 139} 140 141unsigned int 142TimingSimpleCPU::drain(Event *drain_event) 143{ 144 // TimingSimpleCPU is ready to drain if it's not waiting for 145 // an access to complete. 146 if (status() == Idle || status() == Running || status() == SwitchedOut) { 147 changeState(SimObject::Drained); 148 return 0; 149 } else { 150 changeState(SimObject::Draining); 151 drainEvent = drain_event; 152 return 1; 153 } 154} 155 156void 157TimingSimpleCPU::resume() 158{ 159 if (_status != SwitchedOut && _status != Idle) { 160 assert(system->getMemoryMode() == System::Timing); 161 162 // Delete the old event if it existed. 163 if (fetchEvent) { 164 if (fetchEvent->scheduled()) 165 fetchEvent->deschedule(); 166 167 delete fetchEvent; 168 } 169 170 fetchEvent = 171 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false); 172 fetchEvent->schedule(nextCycle()); 173 } 174 175 changeState(SimObject::Running); 176 previousTick = curTick; 177} 178 179void 180TimingSimpleCPU::switchOut() 181{ 182 assert(status() == Running || status() == Idle); 183 _status = SwitchedOut; 184 numCycles += curTick - previousTick; 185 186 // If we've been scheduled to resume but are then told to switch out, 187 // we'll need to cancel it. 188 if (fetchEvent && fetchEvent->scheduled()) 189 fetchEvent->deschedule(); 190} 191 192 193void 194TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU) 195{ 196 BaseCPU::takeOverFrom(oldCPU); 197 198 // if any of this CPU's ThreadContexts are active, mark the CPU as 199 // running and schedule its tick event. 200 for (int i = 0; i < threadContexts.size(); ++i) { 201 ThreadContext *tc = threadContexts[i]; 202 if (tc->status() == ThreadContext::Active && _status != Running) { 203 _status = Running; 204 break; 205 } 206 } 207 208 if (_status != Running) { 209 _status = Idle; 210 } 211 212 Port *peer; 213 if (icachePort.getPeer() == NULL) { 214 peer = oldCPU->getPort("icache_port")->getPeer(); 215 icachePort.setPeer(peer); 216 } else { 217 peer = icachePort.getPeer(); 218 } 219 peer->setPeer(&icachePort); 220 221 if (dcachePort.getPeer() == NULL) { 222 peer = oldCPU->getPort("dcache_port")->getPeer(); 223 dcachePort.setPeer(peer); 224 } else { 225 peer = dcachePort.getPeer(); 226 } 227 peer->setPeer(&dcachePort); 228} 229 230 231void 232TimingSimpleCPU::activateContext(int thread_num, int delay) 233{ 234 assert(thread_num == 0); 235 assert(thread); 236 237 assert(_status == Idle); 238 239 notIdleFraction++; 240 _status = Running; 241 // kick things off by initiating the fetch of the next instruction 242 fetchEvent = 243 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false); 244 fetchEvent->schedule(nextCycle(curTick + cycles(delay))); 245} 246 247 248void 249TimingSimpleCPU::suspendContext(int thread_num) 250{ 251 assert(thread_num == 0); 252 assert(thread); 253 254 assert(_status == Running); 255 256 // just change status to Idle... if status != Running, 257 // completeInst() will not initiate fetch of next instruction. 258 259 notIdleFraction--; 260 _status = Idle; 261} 262 263 264template <class T> 265Fault 266TimingSimpleCPU::read(Addr addr, T &data, unsigned flags) 267{ 268 Request *req = 269 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(), 270 cpu_id, /* thread ID */ 0); 271 272 if (traceData) { 273 traceData->setAddr(req->getVaddr()); 274 } 275 276 // translate to physical address 277 Fault fault = thread->translateDataReadReq(req); 278 279 // Now do the access. 280 if (fault == NoFault) { 281 PacketPtr pkt = 282 new Packet(req, Packet::ReadReq, Packet::Broadcast); 283 pkt->dataDynamic<T>(new T); 284 285 if (!dcachePort.sendTiming(pkt)) { 286 _status = DcacheRetry; 287 dcache_pkt = pkt; 288 } else { 289 _status = DcacheWaitResponse; 290 // memory system takes ownership of packet 291 dcache_pkt = NULL; 292 } 293 } else { 294 delete req; 295 } 296 297 // This will need a new way to tell if it has a dcache attached. 298 if (req->isUncacheable()) 299 recordEvent("Uncached Read"); 300 301 return fault; 302} 303 304#ifndef DOXYGEN_SHOULD_SKIP_THIS 305 306template 307Fault 308TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags); 309 310template 311Fault 312TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags); 313 314template 315Fault 316TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags); 317 318template 319Fault 320TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags); 321 322#endif //DOXYGEN_SHOULD_SKIP_THIS 323 324template<> 325Fault 326TimingSimpleCPU::read(Addr addr, double &data, unsigned flags) 327{ 328 return read(addr, *(uint64_t*)&data, flags); 329} 330 331template<> 332Fault 333TimingSimpleCPU::read(Addr addr, float &data, unsigned flags) 334{ 335 return read(addr, *(uint32_t*)&data, flags); 336} 337 338 339template<> 340Fault 341TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags) 342{ 343 return read(addr, (uint32_t&)data, flags); 344} 345 346 347template <class T> 348Fault 349TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) 350{ 351 Request *req = 352 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(), 353 cpu_id, /* thread ID */ 0); 354 355 // translate to physical address 356 Fault fault = thread->translateDataWriteReq(req); 357 358 // Now do the access. 359 if (fault == NoFault) { 360 assert(dcache_pkt == NULL); 361 dcache_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast); 362 dcache_pkt->allocate(); 363 dcache_pkt->set(data); 364 365 bool do_access = true; // flag to suppress cache access 366 367 if (req->isLocked()) { 368 do_access = TheISA::handleLockedWrite(thread, req); 369 } 370 371 if (do_access) { 372 if (!dcachePort.sendTiming(dcache_pkt)) { 373 _status = DcacheRetry; 374 } else { 375 _status = DcacheWaitResponse; 376 // memory system takes ownership of packet 377 dcache_pkt = NULL; 378 } 379 } 380 } else { 381 delete req; 382 } 383 384 // This will need a new way to tell if it's hooked up to a cache or not. 385 if (req->isUncacheable()) 386 recordEvent("Uncached Write"); 387 388 // If the write needs to have a fault on the access, consider calling 389 // changeStatus() and changing it to "bad addr write" or something. 390 return fault; 391} 392 393 394#ifndef DOXYGEN_SHOULD_SKIP_THIS 395template 396Fault 397TimingSimpleCPU::write(uint64_t data, Addr addr, 398 unsigned flags, uint64_t *res); 399 400template 401Fault 402TimingSimpleCPU::write(uint32_t data, Addr addr, 403 unsigned flags, uint64_t *res); 404 405template 406Fault 407TimingSimpleCPU::write(uint16_t data, Addr addr, 408 unsigned flags, uint64_t *res); 409 410template 411Fault 412TimingSimpleCPU::write(uint8_t data, Addr addr, 413 unsigned flags, uint64_t *res); 414 415#endif //DOXYGEN_SHOULD_SKIP_THIS 416 417template<> 418Fault 419TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) 420{ 421 return write(*(uint64_t*)&data, addr, flags, res); 422} 423 424template<> 425Fault 426TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) 427{ 428 return write(*(uint32_t*)&data, addr, flags, res); 429} 430 431 432template<> 433Fault 434TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) 435{ 436 return write((uint32_t)data, addr, flags, res); 437} 438 439 440void 441TimingSimpleCPU::fetch() 442{ 443 if (!curStaticInst || !curStaticInst->isDelayedCommit()) 444 checkForInterrupts(); 445 446 Request *ifetch_req = new Request(); 447 ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0); 448 Fault fault = setupFetchRequest(ifetch_req); 449 450 ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast); 451 ifetch_pkt->dataStatic(&inst); 452 453 if (fault == NoFault) { 454 if (!icachePort.sendTiming(ifetch_pkt)) { 455 // Need to wait for retry 456 _status = IcacheRetry; 457 } else { 458 // Need to wait for cache to respond 459 _status = IcacheWaitResponse; 460 // ownership of packet transferred to memory system 461 ifetch_pkt = NULL; 462 } 463 } else { 464 delete ifetch_req; 465 delete ifetch_pkt; 466 // fetch fault: advance directly to next instruction (fault handler) 467 advanceInst(fault); 468 } 469 470 numCycles += curTick - previousTick; 471 previousTick = curTick; 472} 473 474 475void 476TimingSimpleCPU::advanceInst(Fault fault) 477{ 478 advancePC(fault); 479 480 if (_status == Running) { 481 // kick off fetch of next instruction... callback from icache 482 // response will cause that instruction to be executed, 483 // keeping the CPU running. 484 fetch(); 485 } 486} 487 488 489void 490TimingSimpleCPU::completeIfetch(PacketPtr pkt) 491{ 492 // received a response from the icache: execute the received 493 // instruction 494 assert(pkt->result == Packet::Success); 495 assert(_status == IcacheWaitResponse); 496 497 _status = Running; 498 499 numCycles += curTick - previousTick; 500 previousTick = curTick; 501 502 if (getState() == SimObject::Draining) { 503 delete pkt->req; 504 delete pkt; 505 506 completeDrain(); 507 return; 508 } 509 510 preExecute(); 511 if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) { 512 // load or store: just send to dcache 513 Fault fault = curStaticInst->initiateAcc(this, traceData); 514 if (_status != Running) { 515 // instruction will complete in dcache response callback 516 assert(_status == DcacheWaitResponse || _status == DcacheRetry); 517 assert(fault == NoFault); 518 } else { 519 if (fault == NoFault) { 520 // early fail on store conditional: complete now 521 assert(dcache_pkt != NULL); 522 fault = curStaticInst->completeAcc(dcache_pkt, this, 523 traceData); 524 delete dcache_pkt->req; 525 delete dcache_pkt; 526 dcache_pkt = NULL; 527 } 528 postExecute(); 529 advanceInst(fault); 530 } 531 } else { 532 // non-memory instruction: execute completely now 533 Fault fault = curStaticInst->execute(this, traceData); 534 postExecute(); 535 advanceInst(fault); 536 } 537 538 delete pkt->req; 539 delete pkt; 540} 541 542void 543TimingSimpleCPU::IcachePort::ITickEvent::process() 544{ 545 cpu->completeIfetch(pkt); 546} 547 548bool 549TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt) 550{ 551 if (pkt->isResponse()) { 552 // delay processing of returned data until next CPU clock edge 553 Tick mem_time = pkt->req->getTime(); 554 Tick next_tick = cpu->nextCycle(mem_time); 555 556 if (next_tick == curTick) 557 cpu->completeIfetch(pkt); 558 else 559 tickEvent.schedule(pkt, next_tick); 560 561 return true; 562 } 563 else { 564 //Snooping a Coherence Request, do nothing 565 return true; 566 } 567} 568 569void 570TimingSimpleCPU::IcachePort::recvRetry() 571{ 572 // we shouldn't get a retry unless we have a packet that we're 573 // waiting to transmit 574 assert(cpu->ifetch_pkt != NULL); 575 assert(cpu->_status == IcacheRetry); 576 PacketPtr tmp = cpu->ifetch_pkt; 577 if (sendTiming(tmp)) { 578 cpu->_status = IcacheWaitResponse; 579 cpu->ifetch_pkt = NULL; 580 } 581} 582 583void 584TimingSimpleCPU::completeDataAccess(PacketPtr pkt) 585{ 586 // received a response from the dcache: complete the load or store 587 // instruction 588 assert(pkt->result == Packet::Success); 589 assert(_status == DcacheWaitResponse); 590 _status = Running; 591 592 numCycles += curTick - previousTick; 593 previousTick = curTick; 594 595 Fault fault = curStaticInst->completeAcc(pkt, this, traceData); 596 597 if (pkt->isRead() && pkt->req->isLocked()) { 598 TheISA::handleLockedRead(thread, pkt->req); 599 } 600 601 delete pkt->req; 602 delete pkt; 603 604 postExecute(); 605 606 if (getState() == SimObject::Draining) { 607 advancePC(fault); 608 completeDrain(); 609 610 return; 611 } 612 613 advanceInst(fault); 614} 615 616 617void 618TimingSimpleCPU::completeDrain() 619{ 620 DPRINTF(Config, "Done draining\n"); 621 changeState(SimObject::Drained); 622 drainEvent->process(); 623} 624 625bool 626TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt) 627{ 628 if (pkt->isResponse()) { 629 // delay processing of returned data until next CPU clock edge 630 Tick mem_time = pkt->req->getTime(); 631 Tick next_tick = cpu->nextCycle(mem_time); 632 633 if (next_tick == curTick) 634 cpu->completeDataAccess(pkt); 635 else 636 tickEvent.schedule(pkt, next_tick); 637 638 return true; 639 } 640 else { 641 //Snooping a coherence req, do nothing 642 return true; 643 } 644} 645 646void 647TimingSimpleCPU::DcachePort::DTickEvent::process() 648{ 649 cpu->completeDataAccess(pkt); 650} 651 652void 653TimingSimpleCPU::DcachePort::recvRetry() 654{ 655 // we shouldn't get a retry unless we have a packet that we're 656 // waiting to transmit 657 assert(cpu->dcache_pkt != NULL); 658 assert(cpu->_status == DcacheRetry); 659 PacketPtr tmp = cpu->dcache_pkt; 660 if (sendTiming(tmp)) { 661 cpu->_status = DcacheWaitResponse; 662 // memory system takes ownership of packet 663 cpu->dcache_pkt = NULL; 664 } 665} 666 667 668//////////////////////////////////////////////////////////////////////// 669// 670// TimingSimpleCPU Simulation Object 671// 672BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU) 673 674 Param<Counter> max_insts_any_thread; 675 Param<Counter> max_insts_all_threads; 676 Param<Counter> max_loads_any_thread; 677 Param<Counter> max_loads_all_threads; 678 Param<Tick> progress_interval; 679 SimObjectParam<System *> system; 680 Param<int> cpu_id; 681 682#if FULL_SYSTEM 683 SimObjectParam<TheISA::ITB *> itb; 684 SimObjectParam<TheISA::DTB *> dtb; 685 Param<Tick> profile; 686 687 Param<bool> do_quiesce; 688 Param<bool> do_checkpoint_insts; 689 Param<bool> do_statistics_insts; 690#else 691 SimObjectParam<Process *> workload; 692#endif // FULL_SYSTEM 693 694 Param<int> clock; 695 Param<int> phase; 696 697 Param<bool> defer_registration; 698 Param<int> width; 699 Param<bool> function_trace; 700 Param<Tick> function_trace_start; 701 Param<bool> simulate_stalls; 702 703END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU) 704 705BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU) 706 707 INIT_PARAM(max_insts_any_thread, 708 "terminate when any thread reaches this inst count"), 709 INIT_PARAM(max_insts_all_threads, 710 "terminate when all threads have reached this inst count"), 711 INIT_PARAM(max_loads_any_thread, 712 "terminate when any thread reaches this load count"), 713 INIT_PARAM(max_loads_all_threads, 714 "terminate when all threads have reached this load count"), 715 INIT_PARAM(progress_interval, "Progress interval"), 716 INIT_PARAM(system, "system object"), 717 INIT_PARAM(cpu_id, "processor ID"), 718 719#if FULL_SYSTEM 720 INIT_PARAM(itb, "Instruction TLB"), 721 INIT_PARAM(dtb, "Data TLB"), 722 INIT_PARAM(profile, ""), 723 INIT_PARAM(do_quiesce, ""), 724 INIT_PARAM(do_checkpoint_insts, ""), 725 INIT_PARAM(do_statistics_insts, ""), 726#else 727 INIT_PARAM(workload, "processes to run"), 728#endif // FULL_SYSTEM 729 730 INIT_PARAM(clock, "clock speed"), 731 INIT_PARAM_DFLT(phase, "clock phase", 0), 732 INIT_PARAM(defer_registration, "defer system registration (for sampling)"), 733 INIT_PARAM(width, "cpu width"), 734 INIT_PARAM(function_trace, "Enable function trace"), 735 INIT_PARAM(function_trace_start, "Cycle to start function trace"), 736 INIT_PARAM(simulate_stalls, "Simulate cache stall cycles") 737 738END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU) 739 740 741CREATE_SIM_OBJECT(TimingSimpleCPU) 742{ 743 TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params(); 744 params->name = getInstanceName(); 745 params->numberOfThreads = 1; 746 params->max_insts_any_thread = max_insts_any_thread; 747 params->max_insts_all_threads = max_insts_all_threads; 748 params->max_loads_any_thread = max_loads_any_thread; 749 params->max_loads_all_threads = max_loads_all_threads; 750 params->progress_interval = progress_interval; 751 params->deferRegistration = defer_registration; 752 params->clock = clock; 753 params->phase = phase; 754 params->functionTrace = function_trace; 755 params->functionTraceStart = function_trace_start; 756 params->system = system; 757 params->cpu_id = cpu_id; 758 759#if FULL_SYSTEM 760 params->itb = itb; 761 params->dtb = dtb; 762 params->profile = profile; 763 params->do_quiesce = do_quiesce; 764 params->do_checkpoint_insts = do_checkpoint_insts; 765 params->do_statistics_insts = do_statistics_insts; 766#else 767 params->process = workload; 768#endif 769 770 TimingSimpleCPU *cpu = new TimingSimpleCPU(params); 771 return cpu; 772} 773 774REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU) 775 776