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