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