atomic.cc revision 4539
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 449 450template 451Fault 452AtomicSimpleCPU::write(Twin32_t data, Addr addr, 453 unsigned flags, uint64_t *res); 454 455template 456Fault 457AtomicSimpleCPU::write(Twin64_t data, Addr addr, 458 unsigned flags, uint64_t *res); 459 460template 461Fault 462AtomicSimpleCPU::write(uint64_t data, Addr addr, 463 unsigned flags, uint64_t *res); 464 465template 466Fault 467AtomicSimpleCPU::write(uint32_t data, Addr addr, 468 unsigned flags, uint64_t *res); 469 470template 471Fault 472AtomicSimpleCPU::write(uint16_t data, Addr addr, 473 unsigned flags, uint64_t *res); 474 475template 476Fault 477AtomicSimpleCPU::write(uint8_t data, Addr addr, 478 unsigned flags, uint64_t *res); 479 480#endif //DOXYGEN_SHOULD_SKIP_THIS 481 482template<> 483Fault 484AtomicSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) 485{ 486 return write(*(uint64_t*)&data, addr, flags, res); 487} 488 489template<> 490Fault 491AtomicSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) 492{ 493 return write(*(uint32_t*)&data, addr, flags, res); 494} 495 496 497template<> 498Fault 499AtomicSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) 500{ 501 return write((uint32_t)data, addr, flags, res); 502} 503 504 505void 506AtomicSimpleCPU::tick() 507{ 508 Tick latency = cycles(1); // instruction takes one cycle by default 509 510 for (int i = 0; i < width; ++i) { 511 numCycles++; 512 513 if (!curStaticInst || !curStaticInst->isDelayedCommit()) 514 checkForInterrupts(); 515 516 Fault fault = setupFetchRequest(ifetch_req); 517 518 if (fault == NoFault) { 519 Tick icache_latency = 0; 520 bool icache_access = false; 521 dcache_access = false; // assume no dcache access 522 523 //Fetch more instruction memory if necessary 524 if(predecoder.needMoreBytes()) 525 { 526 icache_access = true; 527 ifetch_pkt->reinitFromRequest(); 528 529 icache_latency = icachePort.sendAtomic(ifetch_pkt); 530 // ifetch_req is initialized to read the instruction directly 531 // into the CPU object's inst field. 532 } 533 534 preExecute(); 535 536 if(curStaticInst) 537 { 538 fault = curStaticInst->execute(this, traceData); 539 postExecute(); 540 } 541 542 // @todo remove me after debugging with legion done 543 if (curStaticInst && (!curStaticInst->isMicroop() || 544 curStaticInst->isFirstMicroop())) 545 instCnt++; 546 547 if (simulate_stalls) { 548 Tick icache_stall = 549 icache_access ? icache_latency - cycles(1) : 0; 550 Tick dcache_stall = 551 dcache_access ? dcache_latency - cycles(1) : 0; 552 Tick stall_cycles = (icache_stall + dcache_stall) / cycles(1); 553 if (cycles(stall_cycles) < (icache_stall + dcache_stall)) 554 latency += cycles(stall_cycles+1); 555 else 556 latency += cycles(stall_cycles); 557 } 558 559 } 560 if(fault != NoFault || !stayAtPC) 561 advancePC(fault); 562 } 563 564 if (_status != Idle) 565 tickEvent.schedule(curTick + latency); 566} 567 568 569//////////////////////////////////////////////////////////////////////// 570// 571// AtomicSimpleCPU Simulation Object 572// 573BEGIN_DECLARE_SIM_OBJECT_PARAMS(AtomicSimpleCPU) 574 575 Param<Counter> max_insts_any_thread; 576 Param<Counter> max_insts_all_threads; 577 Param<Counter> max_loads_any_thread; 578 Param<Counter> max_loads_all_threads; 579 Param<Tick> progress_interval; 580 SimObjectParam<System *> system; 581 Param<int> cpu_id; 582 583#if FULL_SYSTEM 584 SimObjectParam<TheISA::ITB *> itb; 585 SimObjectParam<TheISA::DTB *> dtb; 586 Param<Tick> profile; 587 588 Param<bool> do_quiesce; 589 Param<bool> do_checkpoint_insts; 590 Param<bool> do_statistics_insts; 591#else 592 SimObjectParam<Process *> workload; 593#endif // FULL_SYSTEM 594 595 Param<int> clock; 596 Param<int> phase; 597 598 Param<bool> defer_registration; 599 Param<int> width; 600 Param<bool> function_trace; 601 Param<Tick> function_trace_start; 602 Param<bool> simulate_stalls; 603 604END_DECLARE_SIM_OBJECT_PARAMS(AtomicSimpleCPU) 605 606BEGIN_INIT_SIM_OBJECT_PARAMS(AtomicSimpleCPU) 607 608 INIT_PARAM(max_insts_any_thread, 609 "terminate when any thread reaches this inst count"), 610 INIT_PARAM(max_insts_all_threads, 611 "terminate when all threads have reached this inst count"), 612 INIT_PARAM(max_loads_any_thread, 613 "terminate when any thread reaches this load count"), 614 INIT_PARAM(max_loads_all_threads, 615 "terminate when all threads have reached this load count"), 616 INIT_PARAM(progress_interval, "Progress interval"), 617 INIT_PARAM(system, "system object"), 618 INIT_PARAM(cpu_id, "processor ID"), 619 620#if FULL_SYSTEM 621 INIT_PARAM(itb, "Instruction TLB"), 622 INIT_PARAM(dtb, "Data TLB"), 623 INIT_PARAM(profile, ""), 624 INIT_PARAM(do_quiesce, ""), 625 INIT_PARAM(do_checkpoint_insts, ""), 626 INIT_PARAM(do_statistics_insts, ""), 627#else 628 INIT_PARAM(workload, "processes to run"), 629#endif // FULL_SYSTEM 630 631 INIT_PARAM(clock, "clock speed"), 632 INIT_PARAM_DFLT(phase, "clock phase", 0), 633 INIT_PARAM(defer_registration, "defer system registration (for sampling)"), 634 INIT_PARAM(width, "cpu width"), 635 INIT_PARAM(function_trace, "Enable function trace"), 636 INIT_PARAM(function_trace_start, "Cycle to start function trace"), 637 INIT_PARAM(simulate_stalls, "Simulate cache stall cycles") 638 639END_INIT_SIM_OBJECT_PARAMS(AtomicSimpleCPU) 640 641 642CREATE_SIM_OBJECT(AtomicSimpleCPU) 643{ 644 AtomicSimpleCPU::Params *params = new AtomicSimpleCPU::Params(); 645 params->name = getInstanceName(); 646 params->numberOfThreads = 1; 647 params->max_insts_any_thread = max_insts_any_thread; 648 params->max_insts_all_threads = max_insts_all_threads; 649 params->max_loads_any_thread = max_loads_any_thread; 650 params->max_loads_all_threads = max_loads_all_threads; 651 params->progress_interval = progress_interval; 652 params->deferRegistration = defer_registration; 653 params->phase = phase; 654 params->clock = clock; 655 params->functionTrace = function_trace; 656 params->functionTraceStart = function_trace_start; 657 params->width = width; 658 params->simulate_stalls = simulate_stalls; 659 params->system = system; 660 params->cpu_id = cpu_id; 661 662#if FULL_SYSTEM 663 params->itb = itb; 664 params->dtb = dtb; 665 params->profile = profile; 666 params->do_quiesce = do_quiesce; 667 params->do_checkpoint_insts = do_checkpoint_insts; 668 params->do_statistics_insts = do_statistics_insts; 669#else 670 params->process = workload; 671#endif 672 673 AtomicSimpleCPU *cpu = new AtomicSimpleCPU(params); 674 return cpu; 675} 676 677REGISTER_SIM_OBJECT("AtomicSimpleCPU", AtomicSimpleCPU) 678 679