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