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