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