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