atomic.cc revision 4192
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/mmaped_ipr.hh" 33#include "arch/utility.hh" 34#include "base/bigint.hh" 35#include "cpu/exetrace.hh" 36#include "cpu/simple/atomic.hh" 37#include "mem/packet.hh" 38#include "mem/packet_access.hh" 39#include "sim/builder.hh" 40#include "sim/system.hh" 41 42using namespace std; 43using namespace TheISA; 44 45AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c) 46 : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c) 47{ 48} 49 50 51void 52AtomicSimpleCPU::TickEvent::process() 53{ 54 cpu->tick(); 55} 56 57const char * 58AtomicSimpleCPU::TickEvent::description() 59{ 60 return "AtomicSimpleCPU tick event"; 61} 62 63Port * 64AtomicSimpleCPU::getPort(const std::string &if_name, int idx) 65{ 66 if (if_name == "dcache_port") 67 return &dcachePort; 68 else if (if_name == "icache_port") 69 return &icachePort; 70 else 71 panic("No Such Port\n"); 72} 73 74void 75AtomicSimpleCPU::init() 76{ 77 BaseCPU::init(); 78#if FULL_SYSTEM 79 for (int i = 0; i < threadContexts.size(); ++i) { 80 ThreadContext *tc = threadContexts[i]; 81 82 // initialize CPU, including PC 83 TheISA::initCPU(tc, tc->readCpuId()); 84 } 85#endif 86} 87 88bool 89AtomicSimpleCPU::CpuPort::recvTiming(PacketPtr pkt) 90{ 91 panic("AtomicSimpleCPU doesn't expect recvTiming callback!"); 92 return true; 93} 94 95Tick 96AtomicSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt) 97{ 98 //Snooping a coherence request, just return 99 return 0; 100} 101 102void 103AtomicSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt) 104{ 105 //No internal storage to update, just return 106 return; 107} 108 109void 110AtomicSimpleCPU::CpuPort::recvStatusChange(Status status) 111{ 112 if (status == RangeChange) { 113 if (!snoopRangeSent) { 114 snoopRangeSent = true; 115 sendStatusChange(Port::RangeChange); 116 } 117 return; 118 } 119 120 panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!"); 121} 122 123void 124AtomicSimpleCPU::CpuPort::recvRetry() 125{ 126 panic("AtomicSimpleCPU doesn't expect recvRetry callback!"); 127} 128 129void 130AtomicSimpleCPU::DcachePort::setPeer(Port *port) 131{ 132 Port::setPeer(port); 133 134#if FULL_SYSTEM 135 // Update the ThreadContext's memory ports (Functional/Virtual 136 // Ports) 137 cpu->tcBase()->connectMemPorts(); 138#endif 139} 140 141AtomicSimpleCPU::AtomicSimpleCPU(Params *p) 142 : BaseSimpleCPU(p), tickEvent(this), 143 width(p->width), simulate_stalls(p->simulate_stalls), 144 icachePort(name() + "-iport", this), dcachePort(name() + "-iport", this) 145{ 146 _status = Idle; 147 148 icachePort.snoopRangeSent = false; 149 dcachePort.snoopRangeSent = false; 150 151 ifetch_req = new Request(); 152 ifetch_req->setThreadContext(p->cpu_id, 0); // Add thread ID if we add MT 153 ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast); 154 ifetch_pkt->dataStatic(&inst); 155 156 data_read_req = new Request(); 157 data_read_req->setThreadContext(p->cpu_id, 0); // Add thread ID here too 158 data_read_pkt = new Packet(data_read_req, MemCmd::ReadReq, 159 Packet::Broadcast); 160 data_read_pkt->dataStatic(&dataReg); 161 162 data_write_req = new Request(); 163 data_write_req->setThreadContext(p->cpu_id, 0); // Add thread ID here too 164 data_write_pkt = new Packet(data_write_req, MemCmd::WriteReq, 165 Packet::Broadcast); 166 data_swap_pkt = new Packet(data_write_req, MemCmd::SwapReq, 167 Packet::Broadcast); 168} 169 170 171AtomicSimpleCPU::~AtomicSimpleCPU() 172{ 173} 174 175void 176AtomicSimpleCPU::serialize(ostream &os) 177{ 178 SimObject::State so_state = SimObject::getState(); 179 SERIALIZE_ENUM(so_state); 180 Status _status = status(); 181 SERIALIZE_ENUM(_status); 182 BaseSimpleCPU::serialize(os); 183 nameOut(os, csprintf("%s.tickEvent", name())); 184 tickEvent.serialize(os); 185} 186 187void 188AtomicSimpleCPU::unserialize(Checkpoint *cp, const string §ion) 189{ 190 SimObject::State so_state; 191 UNSERIALIZE_ENUM(so_state); 192 UNSERIALIZE_ENUM(_status); 193 BaseSimpleCPU::unserialize(cp, section); 194 tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); 195} 196 197void 198AtomicSimpleCPU::resume() 199{ 200 if (_status != SwitchedOut && _status != Idle) { 201 assert(system->getMemoryMode() == System::Atomic); 202 203 changeState(SimObject::Running); 204 if (thread->status() == ThreadContext::Active) { 205 if (!tickEvent.scheduled()) { 206 tickEvent.schedule(nextCycle()); 207 } 208 } 209 } 210} 211 212void 213AtomicSimpleCPU::switchOut() 214{ 215 assert(status() == Running || status() == Idle); 216 _status = SwitchedOut; 217 218 tickEvent.squash(); 219} 220 221 222void 223AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU) 224{ 225 BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort); 226 227 assert(!tickEvent.scheduled()); 228 229 // if any of this CPU's ThreadContexts are active, mark the CPU as 230 // running and schedule its tick event. 231 for (int i = 0; i < threadContexts.size(); ++i) { 232 ThreadContext *tc = threadContexts[i]; 233 if (tc->status() == ThreadContext::Active && _status != Running) { 234 _status = Running; 235 tickEvent.schedule(nextCycle()); 236 break; 237 } 238 } 239 if (_status != Running) { 240 _status = Idle; 241 } 242} 243 244 245void 246AtomicSimpleCPU::activateContext(int thread_num, int delay) 247{ 248 assert(thread_num == 0); 249 assert(thread); 250 251 assert(_status == Idle); 252 assert(!tickEvent.scheduled()); 253 254 notIdleFraction++; 255 256 //Make sure ticks are still on multiples of cycles 257 tickEvent.schedule(nextCycle(curTick + cycles(delay))); 258 _status = Running; 259} 260 261 262void 263AtomicSimpleCPU::suspendContext(int thread_num) 264{ 265 assert(thread_num == 0); 266 assert(thread); 267 268 assert(_status == Running); 269 270 // tick event may not be scheduled if this gets called from inside 271 // an instruction's execution, e.g. "quiesce" 272 if (tickEvent.scheduled()) 273 tickEvent.deschedule(); 274 275 notIdleFraction--; 276 _status = Idle; 277} 278 279 280template <class T> 281Fault 282AtomicSimpleCPU::read(Addr addr, T &data, unsigned flags) 283{ 284 // use the CPU's statically allocated read request and packet objects 285 Request *req = data_read_req; 286 PacketPtr pkt = data_read_pkt; 287 288 req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); 289 290 if (traceData) { 291 traceData->setAddr(addr); 292 } 293 294 // translate to physical address 295 Fault fault = thread->translateDataReadReq(req); 296 297 // Now do the access. 298 if (fault == NoFault) { 299 pkt->reinitFromRequest(); 300 301 if (req->isMmapedIpr()) 302 dcache_latency = TheISA::handleIprRead(thread->getTC(),pkt); 303 else 304 dcache_latency = dcachePort.sendAtomic(pkt); 305 dcache_access = true; 306#if !defined(NDEBUG) 307 if (pkt->result != Packet::Success) 308 panic("Unable to find responder for address pa = %#X va = %#X\n", 309 pkt->req->getPaddr(), pkt->req->getVaddr()); 310#endif 311 data = pkt->get<T>(); 312 313 if (req->isLocked()) { 314 TheISA::handleLockedRead(thread, req); 315 } 316 } 317 318 // This will need a new way to tell if it has a dcache attached. 319 if (req->isUncacheable()) 320 recordEvent("Uncached Read"); 321 322 return fault; 323} 324 325#ifndef DOXYGEN_SHOULD_SKIP_THIS 326 327template 328Fault 329AtomicSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags); 330 331template 332Fault 333AtomicSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags); 334 335template 336Fault 337AtomicSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags); 338 339template 340Fault 341AtomicSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags); 342 343template 344Fault 345AtomicSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags); 346 347template 348Fault 349AtomicSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags); 350 351#endif //DOXYGEN_SHOULD_SKIP_THIS 352 353template<> 354Fault 355AtomicSimpleCPU::read(Addr addr, double &data, unsigned flags) 356{ 357 return read(addr, *(uint64_t*)&data, flags); 358} 359 360template<> 361Fault 362AtomicSimpleCPU::read(Addr addr, float &data, unsigned flags) 363{ 364 return read(addr, *(uint32_t*)&data, flags); 365} 366 367 368template<> 369Fault 370AtomicSimpleCPU::read(Addr addr, int32_t &data, unsigned flags) 371{ 372 return read(addr, (uint32_t&)data, flags); 373} 374 375 376template <class T> 377Fault 378AtomicSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) 379{ 380 // use the CPU's statically allocated write request and packet objects 381 Request *req = data_write_req; 382 PacketPtr pkt; 383 384 req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); 385 386 if (req->isSwap()) 387 pkt = data_swap_pkt; 388 else 389 pkt = data_write_pkt; 390 391 if (traceData) { 392 traceData->setAddr(addr); 393 } 394 395 // translate to physical address 396 Fault fault = thread->translateDataWriteReq(req); 397 398 // Now do the access. 399 if (fault == NoFault) { 400 bool do_access = true; // flag to suppress cache access 401 402 if (req->isLocked()) { 403 do_access = TheISA::handleLockedWrite(thread, req); 404 } 405 if (req->isCondSwap()) { 406 assert(res); 407 req->setExtraData(*res); 408 } 409 410 411 if (do_access) { 412 pkt->reinitFromRequest(); 413 pkt->dataStatic(&data); 414 415 if (req->isMmapedIpr()) { 416 dcache_latency = TheISA::handleIprWrite(thread->getTC(), pkt); 417 } else { 418 data = htog(data); 419 dcache_latency = dcachePort.sendAtomic(pkt); 420 } 421 dcache_access = true; 422 423#if !defined(NDEBUG) 424 if (pkt->result != Packet::Success) 425 panic("Unable to find responder for address pa = %#X va = %#X\n", 426 pkt->req->getPaddr(), pkt->req->getVaddr()); 427#endif 428 } 429 430 if (req->isSwap()) { 431 assert(res); 432 *res = pkt->get<T>(); 433 } else if (res) { 434 *res = req->getExtraData(); 435 } 436 } 437 438 // This will need a new way to tell if it's hooked up to a cache or not. 439 if (req->isUncacheable()) 440 recordEvent("Uncached Write"); 441 442 // If the write needs to have a fault on the access, consider calling 443 // changeStatus() and changing it to "bad addr write" or something. 444 return fault; 445} 446 447 448#ifndef DOXYGEN_SHOULD_SKIP_THIS 449template 450Fault 451AtomicSimpleCPU::write(uint64_t data, Addr addr, 452 unsigned flags, uint64_t *res); 453 454template 455Fault 456AtomicSimpleCPU::write(uint32_t data, Addr addr, 457 unsigned flags, uint64_t *res); 458 459template 460Fault 461AtomicSimpleCPU::write(uint16_t data, Addr addr, 462 unsigned flags, uint64_t *res); 463 464template 465Fault 466AtomicSimpleCPU::write(uint8_t data, Addr addr, 467 unsigned flags, uint64_t *res); 468 469#endif //DOXYGEN_SHOULD_SKIP_THIS 470 471template<> 472Fault 473AtomicSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) 474{ 475 return write(*(uint64_t*)&data, addr, flags, res); 476} 477 478template<> 479Fault 480AtomicSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) 481{ 482 return write(*(uint32_t*)&data, addr, flags, res); 483} 484 485 486template<> 487Fault 488AtomicSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) 489{ 490 return write((uint32_t)data, addr, flags, res); 491} 492 493 494void 495AtomicSimpleCPU::tick() 496{ 497 Tick latency = cycles(1); // instruction takes one cycle by default 498 499 for (int i = 0; i < width; ++i) { 500 numCycles++; 501 502 if (!curStaticInst || !curStaticInst->isDelayedCommit()) 503 checkForInterrupts(); 504 505 Fault fault = setupFetchRequest(ifetch_req); 506 507 if (fault == NoFault) { 508 ifetch_pkt->reinitFromRequest(); 509 510 Tick icache_latency = icachePort.sendAtomic(ifetch_pkt); 511 // ifetch_req is initialized to read the instruction directly 512 // into the CPU object's inst field. 513 514 dcache_access = false; // assume no dcache access 515 preExecute(); 516 517 fault = curStaticInst->execute(this, traceData); 518 postExecute(); 519 520 // @todo remove me after debugging with legion done 521 if (curStaticInst && (!curStaticInst->isMicroOp() || 522 curStaticInst->isFirstMicroOp())) 523 instCnt++; 524 525 if (simulate_stalls) { 526 Tick icache_stall = icache_latency - cycles(1); 527 Tick dcache_stall = 528 dcache_access ? dcache_latency - cycles(1) : 0; 529 Tick stall_cycles = (icache_stall + dcache_stall) / cycles(1); 530 if (cycles(stall_cycles) < (icache_stall + dcache_stall)) 531 latency += cycles(stall_cycles+1); 532 else 533 latency += cycles(stall_cycles); 534 } 535 536 } 537 538 advancePC(fault); 539 } 540 541 if (_status != Idle) 542 tickEvent.schedule(curTick + latency); 543} 544 545 546//////////////////////////////////////////////////////////////////////// 547// 548// AtomicSimpleCPU Simulation Object 549// 550BEGIN_DECLARE_SIM_OBJECT_PARAMS(AtomicSimpleCPU) 551 552 Param<Counter> max_insts_any_thread; 553 Param<Counter> max_insts_all_threads; 554 Param<Counter> max_loads_any_thread; 555 Param<Counter> max_loads_all_threads; 556 Param<Tick> progress_interval; 557 SimObjectParam<System *> system; 558 Param<int> cpu_id; 559 560#if FULL_SYSTEM 561 SimObjectParam<TheISA::ITB *> itb; 562 SimObjectParam<TheISA::DTB *> dtb; 563 Param<Tick> profile; 564 565 Param<bool> do_quiesce; 566 Param<bool> do_checkpoint_insts; 567 Param<bool> do_statistics_insts; 568#else 569 SimObjectParam<Process *> workload; 570#endif // FULL_SYSTEM 571 572 Param<int> clock; 573 Param<int> phase; 574 575 Param<bool> defer_registration; 576 Param<int> width; 577 Param<bool> function_trace; 578 Param<Tick> function_trace_start; 579 Param<bool> simulate_stalls; 580 581END_DECLARE_SIM_OBJECT_PARAMS(AtomicSimpleCPU) 582 583BEGIN_INIT_SIM_OBJECT_PARAMS(AtomicSimpleCPU) 584 585 INIT_PARAM(max_insts_any_thread, 586 "terminate when any thread reaches this inst count"), 587 INIT_PARAM(max_insts_all_threads, 588 "terminate when all threads have reached this inst count"), 589 INIT_PARAM(max_loads_any_thread, 590 "terminate when any thread reaches this load count"), 591 INIT_PARAM(max_loads_all_threads, 592 "terminate when all threads have reached this load count"), 593 INIT_PARAM(progress_interval, "Progress interval"), 594 INIT_PARAM(system, "system object"), 595 INIT_PARAM(cpu_id, "processor ID"), 596 597#if FULL_SYSTEM 598 INIT_PARAM(itb, "Instruction TLB"), 599 INIT_PARAM(dtb, "Data TLB"), 600 INIT_PARAM(profile, ""), 601 INIT_PARAM(do_quiesce, ""), 602 INIT_PARAM(do_checkpoint_insts, ""), 603 INIT_PARAM(do_statistics_insts, ""), 604#else 605 INIT_PARAM(workload, "processes to run"), 606#endif // FULL_SYSTEM 607 608 INIT_PARAM(clock, "clock speed"), 609 INIT_PARAM_DFLT(phase, "clock phase", 0), 610 INIT_PARAM(defer_registration, "defer system registration (for sampling)"), 611 INIT_PARAM(width, "cpu width"), 612 INIT_PARAM(function_trace, "Enable function trace"), 613 INIT_PARAM(function_trace_start, "Cycle to start function trace"), 614 INIT_PARAM(simulate_stalls, "Simulate cache stall cycles") 615 616END_INIT_SIM_OBJECT_PARAMS(AtomicSimpleCPU) 617 618 619CREATE_SIM_OBJECT(AtomicSimpleCPU) 620{ 621 AtomicSimpleCPU::Params *params = new AtomicSimpleCPU::Params(); 622 params->name = getInstanceName(); 623 params->numberOfThreads = 1; 624 params->max_insts_any_thread = max_insts_any_thread; 625 params->max_insts_all_threads = max_insts_all_threads; 626 params->max_loads_any_thread = max_loads_any_thread; 627 params->max_loads_all_threads = max_loads_all_threads; 628 params->progress_interval = progress_interval; 629 params->deferRegistration = defer_registration; 630 params->phase = phase; 631 params->clock = clock; 632 params->functionTrace = function_trace; 633 params->functionTraceStart = function_trace_start; 634 params->width = width; 635 params->simulate_stalls = simulate_stalls; 636 params->system = system; 637 params->cpu_id = cpu_id; 638 639#if FULL_SYSTEM 640 params->itb = itb; 641 params->dtb = dtb; 642 params->profile = profile; 643 params->do_quiesce = do_quiesce; 644 params->do_checkpoint_insts = do_checkpoint_insts; 645 params->do_statistics_insts = do_statistics_insts; 646#else 647 params->process = workload; 648#endif 649 650 AtomicSimpleCPU *cpu = new AtomicSimpleCPU(params); 651 return cpu; 652} 653 654REGISTER_SIM_OBJECT("AtomicSimpleCPU", AtomicSimpleCPU) 655 656