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