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