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