timing.cc revision 5018:21795007349e
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 "base/bigint.hh" 34#include "cpu/exetrace.hh" 35#include "cpu/simple/timing.hh" 36#include "mem/packet.hh" 37#include "mem/packet_access.hh" 38#include "params/TimingSimpleCPU.hh" 39#include "sim/system.hh" 40 41using namespace std; 42using namespace TheISA; 43 44Port * 45TimingSimpleCPU::getPort(const std::string &if_name, int idx) 46{ 47 if (if_name == "dcache_port") 48 return &dcachePort; 49 else if (if_name == "icache_port") 50 return &icachePort; 51 else 52 panic("No Such Port\n"); 53} 54 55void 56TimingSimpleCPU::init() 57{ 58 BaseCPU::init(); 59#if FULL_SYSTEM 60 for (int i = 0; i < threadContexts.size(); ++i) { 61 ThreadContext *tc = threadContexts[i]; 62 63 // initialize CPU, including PC 64 TheISA::initCPU(tc, tc->readCpuId()); 65 } 66#endif 67} 68 69Tick 70TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt) 71{ 72 panic("TimingSimpleCPU doesn't expect recvAtomic callback!"); 73 return curTick; 74} 75 76void 77TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt) 78{ 79 //No internal storage to update, jusst return 80 return; 81} 82 83void 84TimingSimpleCPU::CpuPort::recvStatusChange(Status status) 85{ 86 if (status == RangeChange) { 87 if (!snoopRangeSent) { 88 snoopRangeSent = true; 89 sendStatusChange(Port::RangeChange); 90 } 91 return; 92 } 93 94 panic("TimingSimpleCPU doesn't expect recvStatusChange callback!"); 95} 96 97 98void 99TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t) 100{ 101 pkt = _pkt; 102 Event::schedule(t); 103} 104 105TimingSimpleCPU::TimingSimpleCPU(Params *p) 106 : BaseSimpleCPU(p), icachePort(this, p->clock), dcachePort(this, p->clock), 107 cpu_id(p->cpu_id) 108{ 109 _status = Idle; 110 111 icachePort.snoopRangeSent = false; 112 dcachePort.snoopRangeSent = false; 113 114 ifetch_pkt = dcache_pkt = NULL; 115 drainEvent = NULL; 116 fetchEvent = NULL; 117 previousTick = 0; 118 changeState(SimObject::Running); 119} 120 121 122TimingSimpleCPU::~TimingSimpleCPU() 123{ 124} 125 126void 127TimingSimpleCPU::serialize(ostream &os) 128{ 129 SimObject::State so_state = SimObject::getState(); 130 SERIALIZE_ENUM(so_state); 131 BaseSimpleCPU::serialize(os); 132} 133 134void 135TimingSimpleCPU::unserialize(Checkpoint *cp, const string §ion) 136{ 137 SimObject::State so_state; 138 UNSERIALIZE_ENUM(so_state); 139 BaseSimpleCPU::unserialize(cp, section); 140} 141 142unsigned int 143TimingSimpleCPU::drain(Event *drain_event) 144{ 145 // TimingSimpleCPU is ready to drain if it's not waiting for 146 // an access to complete. 147 if (status() == Idle || status() == Running || status() == SwitchedOut) { 148 changeState(SimObject::Drained); 149 return 0; 150 } else { 151 changeState(SimObject::Draining); 152 drainEvent = drain_event; 153 return 1; 154 } 155} 156 157void 158TimingSimpleCPU::resume() 159{ 160 if (_status != SwitchedOut && _status != Idle) { 161 assert(system->getMemoryMode() == Enums::timing); 162 163 // Delete the old event if it existed. 164 if (fetchEvent) { 165 if (fetchEvent->scheduled()) 166 fetchEvent->deschedule(); 167 168 delete fetchEvent; 169 } 170 171 fetchEvent = new FetchEvent(this, nextCycle()); 172 } 173 174 changeState(SimObject::Running); 175 previousTick = curTick; 176} 177 178void 179TimingSimpleCPU::switchOut() 180{ 181 assert(status() == Running || status() == Idle); 182 _status = SwitchedOut; 183 numCycles += curTick - previousTick; 184 185 // If we've been scheduled to resume but are then told to switch out, 186 // we'll need to cancel it. 187 if (fetchEvent && fetchEvent->scheduled()) 188 fetchEvent->deschedule(); 189} 190 191 192void 193TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU) 194{ 195 BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort); 196 197 // if any of this CPU's ThreadContexts are active, mark the CPU as 198 // running and schedule its tick event. 199 for (int i = 0; i < threadContexts.size(); ++i) { 200 ThreadContext *tc = threadContexts[i]; 201 if (tc->status() == ThreadContext::Active && _status != Running) { 202 _status = Running; 203 break; 204 } 205 } 206 207 if (_status != Running) { 208 _status = Idle; 209 } 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 224 // kick things off by initiating the fetch of the next instruction 225 fetchEvent = new FetchEvent(this, nextCycle(curTick + cycles(delay))); 226} 227 228 229void 230TimingSimpleCPU::suspendContext(int thread_num) 231{ 232 assert(thread_num == 0); 233 assert(thread); 234 235 assert(_status == Running); 236 237 // just change status to Idle... if status != Running, 238 // completeInst() will not initiate fetch of next instruction. 239 240 notIdleFraction--; 241 _status = Idle; 242} 243 244 245template <class T> 246Fault 247TimingSimpleCPU::read(Addr addr, T &data, unsigned flags) 248{ 249 Request *req = 250 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(), 251 cpu_id, /* thread ID */ 0); 252 253 if (traceData) { 254 traceData->setAddr(req->getVaddr()); 255 } 256 257 // translate to physical address 258 Fault fault = thread->translateDataReadReq(req); 259 260 // Now do the access. 261 if (fault == NoFault) { 262 PacketPtr pkt = 263 new Packet(req, 264 (req->isLocked() ? 265 MemCmd::LoadLockedReq : MemCmd::ReadReq), 266 Packet::Broadcast); 267 pkt->dataDynamic<T>(new T); 268 269 if (!dcachePort.sendTiming(pkt)) { 270 _status = DcacheRetry; 271 dcache_pkt = pkt; 272 } else { 273 _status = DcacheWaitResponse; 274 // memory system takes ownership of packet 275 dcache_pkt = NULL; 276 } 277 278 // This will need a new way to tell if it has a dcache attached. 279 if (req->isUncacheable()) 280 recordEvent("Uncached Read"); 281 } else { 282 delete req; 283 } 284 285 return fault; 286} 287 288#ifndef DOXYGEN_SHOULD_SKIP_THIS 289 290template 291Fault 292TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags); 293 294template 295Fault 296TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags); 297 298template 299Fault 300TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags); 301 302template 303Fault 304TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags); 305 306template 307Fault 308TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags); 309 310template 311Fault 312TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags); 313 314#endif //DOXYGEN_SHOULD_SKIP_THIS 315 316template<> 317Fault 318TimingSimpleCPU::read(Addr addr, double &data, unsigned flags) 319{ 320 return read(addr, *(uint64_t*)&data, flags); 321} 322 323template<> 324Fault 325TimingSimpleCPU::read(Addr addr, float &data, unsigned flags) 326{ 327 return read(addr, *(uint32_t*)&data, flags); 328} 329 330 331template<> 332Fault 333TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags) 334{ 335 return read(addr, (uint32_t&)data, flags); 336} 337 338 339template <class T> 340Fault 341TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) 342{ 343 Request *req = 344 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(), 345 cpu_id, /* thread ID */ 0); 346 347 if (traceData) { 348 traceData->setAddr(req->getVaddr()); 349 } 350 351 // translate to physical address 352 Fault fault = thread->translateDataWriteReq(req); 353 354 // Now do the access. 355 if (fault == NoFault) { 356 MemCmd cmd = MemCmd::WriteReq; // default 357 bool do_access = true; // flag to suppress cache access 358 359 if (req->isLocked()) { 360 cmd = MemCmd::StoreCondReq; 361 do_access = TheISA::handleLockedWrite(thread, req); 362 } else if (req->isSwap()) { 363 cmd = MemCmd::SwapReq; 364 if (req->isCondSwap()) { 365 assert(res); 366 req->setExtraData(*res); 367 } 368 } 369 370 // Note: need to allocate dcache_pkt even if do_access is 371 // false, as it's used unconditionally to call completeAcc(). 372 assert(dcache_pkt == NULL); 373 dcache_pkt = new Packet(req, cmd, Packet::Broadcast); 374 dcache_pkt->allocate(); 375 dcache_pkt->set(data); 376 377 if (do_access) { 378 if (!dcachePort.sendTiming(dcache_pkt)) { 379 _status = DcacheRetry; 380 } else { 381 _status = DcacheWaitResponse; 382 // memory system takes ownership of packet 383 dcache_pkt = NULL; 384 } 385 } 386 // This will need a new way to tell if it's hooked up to a cache or not. 387 if (req->isUncacheable()) 388 recordEvent("Uncached Write"); 389 } else { 390 delete req; 391 } 392 393 394 // If the write needs to have a fault on the access, consider calling 395 // changeStatus() and changing it to "bad addr write" or something. 396 return fault; 397} 398 399 400#ifndef DOXYGEN_SHOULD_SKIP_THIS 401template 402Fault 403TimingSimpleCPU::write(Twin32_t data, Addr addr, 404 unsigned flags, uint64_t *res); 405 406template 407Fault 408TimingSimpleCPU::write(Twin64_t data, Addr addr, 409 unsigned flags, uint64_t *res); 410 411template 412Fault 413TimingSimpleCPU::write(uint64_t data, Addr addr, 414 unsigned flags, uint64_t *res); 415 416template 417Fault 418TimingSimpleCPU::write(uint32_t data, Addr addr, 419 unsigned flags, uint64_t *res); 420 421template 422Fault 423TimingSimpleCPU::write(uint16_t data, Addr addr, 424 unsigned flags, uint64_t *res); 425 426template 427Fault 428TimingSimpleCPU::write(uint8_t data, Addr addr, 429 unsigned flags, uint64_t *res); 430 431#endif //DOXYGEN_SHOULD_SKIP_THIS 432 433template<> 434Fault 435TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) 436{ 437 return write(*(uint64_t*)&data, addr, flags, res); 438} 439 440template<> 441Fault 442TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) 443{ 444 return write(*(uint32_t*)&data, addr, flags, res); 445} 446 447 448template<> 449Fault 450TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) 451{ 452 return write((uint32_t)data, addr, flags, res); 453} 454 455 456void 457TimingSimpleCPU::fetch() 458{ 459 if (!curStaticInst || !curStaticInst->isDelayedCommit()) 460 checkForInterrupts(); 461 462 Request *ifetch_req = new Request(); 463 ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0); 464 Fault fault = setupFetchRequest(ifetch_req); 465 466 ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast); 467 ifetch_pkt->dataStatic(&inst); 468 469 if (fault == NoFault) { 470 if (!icachePort.sendTiming(ifetch_pkt)) { 471 // Need to wait for retry 472 _status = IcacheRetry; 473 } else { 474 // Need to wait for cache to respond 475 _status = IcacheWaitResponse; 476 // ownership of packet transferred to memory system 477 ifetch_pkt = NULL; 478 } 479 } else { 480 delete ifetch_req; 481 delete ifetch_pkt; 482 // fetch fault: advance directly to next instruction (fault handler) 483 advanceInst(fault); 484 } 485 486 numCycles += curTick - previousTick; 487 previousTick = curTick; 488} 489 490 491void 492TimingSimpleCPU::advanceInst(Fault fault) 493{ 494 advancePC(fault); 495 496 if (_status == Running) { 497 // kick off fetch of next instruction... callback from icache 498 // response will cause that instruction to be executed, 499 // keeping the CPU running. 500 fetch(); 501 } 502} 503 504 505void 506TimingSimpleCPU::completeIfetch(PacketPtr pkt) 507{ 508 // received a response from the icache: execute the received 509 // instruction 510 assert(!pkt->isError()); 511 assert(_status == IcacheWaitResponse); 512 513 _status = Running; 514 515 numCycles += curTick - previousTick; 516 previousTick = curTick; 517 518 if (getState() == SimObject::Draining) { 519 delete pkt->req; 520 delete pkt; 521 522 completeDrain(); 523 return; 524 } 525 526 preExecute(); 527 if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) { 528 // load or store: just send to dcache 529 Fault fault = curStaticInst->initiateAcc(this, traceData); 530 if (_status != Running) { 531 // instruction will complete in dcache response callback 532 assert(_status == DcacheWaitResponse || _status == DcacheRetry); 533 assert(fault == NoFault); 534 } else { 535 if (fault == NoFault) { 536 // early fail on store conditional: complete now 537 assert(dcache_pkt != NULL); 538 fault = curStaticInst->completeAcc(dcache_pkt, this, 539 traceData); 540 delete dcache_pkt->req; 541 delete dcache_pkt; 542 dcache_pkt = NULL; 543 544 // keep an instruction count 545 if (fault == NoFault) 546 countInst(); 547 } else if (traceData) { 548 // If there was a fault, we shouldn't trace this instruction. 549 delete traceData; 550 traceData = NULL; 551 } 552 553 postExecute(); 554 advanceInst(fault); 555 } 556 } else { 557 // non-memory instruction: execute completely now 558 Fault fault = curStaticInst->execute(this, traceData); 559 560 // keep an instruction count 561 if (fault == NoFault) 562 countInst(); 563 else if (traceData) { 564 // If there was a fault, we shouldn't trace this instruction. 565 delete traceData; 566 traceData = NULL; 567 } 568 569 postExecute(); 570 advanceInst(fault); 571 } 572 573 delete pkt->req; 574 delete pkt; 575} 576 577void 578TimingSimpleCPU::IcachePort::ITickEvent::process() 579{ 580 cpu->completeIfetch(pkt); 581} 582 583bool 584TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt) 585{ 586 if (pkt->isResponse() && !pkt->wasNacked()) { 587 // delay processing of returned data until next CPU clock edge 588 Tick next_tick = cpu->nextCycle(curTick); 589 590 if (next_tick == curTick) 591 cpu->completeIfetch(pkt); 592 else 593 tickEvent.schedule(pkt, next_tick); 594 595 return true; 596 } 597 else if (pkt->wasNacked()) { 598 assert(cpu->_status == IcacheWaitResponse); 599 pkt->reinitNacked(); 600 if (!sendTiming(pkt)) { 601 cpu->_status = IcacheRetry; 602 cpu->ifetch_pkt = pkt; 603 } 604 } 605 //Snooping a Coherence Request, do nothing 606 return true; 607} 608 609void 610TimingSimpleCPU::IcachePort::recvRetry() 611{ 612 // we shouldn't get a retry unless we have a packet that we're 613 // waiting to transmit 614 assert(cpu->ifetch_pkt != NULL); 615 assert(cpu->_status == IcacheRetry); 616 PacketPtr tmp = cpu->ifetch_pkt; 617 if (sendTiming(tmp)) { 618 cpu->_status = IcacheWaitResponse; 619 cpu->ifetch_pkt = NULL; 620 } 621} 622 623void 624TimingSimpleCPU::completeDataAccess(PacketPtr pkt) 625{ 626 // received a response from the dcache: complete the load or store 627 // instruction 628 assert(!pkt->isError()); 629 assert(_status == DcacheWaitResponse); 630 _status = Running; 631 632 numCycles += curTick - previousTick; 633 previousTick = curTick; 634 635 Fault fault = curStaticInst->completeAcc(pkt, this, traceData); 636 637 // keep an instruction count 638 if (fault == NoFault) 639 countInst(); 640 else if (traceData) { 641 // If there was a fault, we shouldn't trace this instruction. 642 delete traceData; 643 traceData = NULL; 644 } 645 646 if (pkt->isRead() && pkt->isLocked()) { 647 TheISA::handleLockedRead(thread, pkt->req); 648 } 649 650 delete pkt->req; 651 delete pkt; 652 653 postExecute(); 654 655 if (getState() == SimObject::Draining) { 656 advancePC(fault); 657 completeDrain(); 658 659 return; 660 } 661 662 advanceInst(fault); 663} 664 665 666void 667TimingSimpleCPU::completeDrain() 668{ 669 DPRINTF(Config, "Done draining\n"); 670 changeState(SimObject::Drained); 671 drainEvent->process(); 672} 673 674void 675TimingSimpleCPU::DcachePort::setPeer(Port *port) 676{ 677 Port::setPeer(port); 678 679#if FULL_SYSTEM 680 // Update the ThreadContext's memory ports (Functional/Virtual 681 // Ports) 682 cpu->tcBase()->connectMemPorts(); 683#endif 684} 685 686bool 687TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt) 688{ 689 if (pkt->isResponse() && !pkt->wasNacked()) { 690 // delay processing of returned data until next CPU clock edge 691 Tick next_tick = cpu->nextCycle(curTick); 692 693 if (next_tick == curTick) 694 cpu->completeDataAccess(pkt); 695 else 696 tickEvent.schedule(pkt, next_tick); 697 698 return true; 699 } 700 else if (pkt->wasNacked()) { 701 assert(cpu->_status == DcacheWaitResponse); 702 pkt->reinitNacked(); 703 if (!sendTiming(pkt)) { 704 cpu->_status = DcacheRetry; 705 cpu->dcache_pkt = pkt; 706 } 707 } 708 //Snooping a Coherence Request, do nothing 709 return true; 710} 711 712void 713TimingSimpleCPU::DcachePort::DTickEvent::process() 714{ 715 cpu->completeDataAccess(pkt); 716} 717 718void 719TimingSimpleCPU::DcachePort::recvRetry() 720{ 721 // we shouldn't get a retry unless we have a packet that we're 722 // waiting to transmit 723 assert(cpu->dcache_pkt != NULL); 724 assert(cpu->_status == DcacheRetry); 725 PacketPtr tmp = cpu->dcache_pkt; 726 if (sendTiming(tmp)) { 727 cpu->_status = DcacheWaitResponse; 728 // memory system takes ownership of packet 729 cpu->dcache_pkt = NULL; 730 } 731} 732 733 734//////////////////////////////////////////////////////////////////////// 735// 736// TimingSimpleCPU Simulation Object 737// 738TimingSimpleCPU * 739TimingSimpleCPUParams::create() 740{ 741 TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params(); 742 params->name = name; 743 params->numberOfThreads = 1; 744 params->max_insts_any_thread = max_insts_any_thread; 745 params->max_insts_all_threads = max_insts_all_threads; 746 params->max_loads_any_thread = max_loads_any_thread; 747 params->max_loads_all_threads = max_loads_all_threads; 748 params->progress_interval = progress_interval; 749 params->deferRegistration = defer_registration; 750 params->clock = clock; 751 params->phase = phase; 752 params->functionTrace = function_trace; 753 params->functionTraceStart = function_trace_start; 754 params->system = system; 755 params->cpu_id = cpu_id; 756 params->tracer = tracer; 757 758 params->itb = itb; 759 params->dtb = dtb; 760#if FULL_SYSTEM 761 params->profile = profile; 762 params->do_quiesce = do_quiesce; 763 params->do_checkpoint_insts = do_checkpoint_insts; 764 params->do_statistics_insts = do_statistics_insts; 765#else 766 if (workload.size() != 1) 767 panic("only one workload allowed"); 768 params->process = workload[0]; 769#endif 770 771 TimingSimpleCPU *cpu = new TimingSimpleCPU(params); 772 return cpu; 773} 774