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