base.cc revision 2439
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 <cmath> 30#include <cstdio> 31#include <cstdlib> 32#include <iostream> 33#include <iomanip> 34#include <list> 35#include <sstream> 36#include <string> 37 38#include "arch/utility.hh" 39#include "base/cprintf.hh" 40#include "base/inifile.hh" 41#include "base/loader/symtab.hh" 42#include "base/misc.hh" 43#include "base/pollevent.hh" 44#include "base/range.hh" 45#include "base/stats/events.hh" 46#include "base/trace.hh" 47#include "cpu/base.hh" 48#include "cpu/cpu_exec_context.hh" 49#include "cpu/exec_context.hh" 50#include "cpu/exetrace.hh" 51#include "cpu/profile.hh" 52#include "cpu/sampler/sampler.hh" 53#include "cpu/simple/cpu.hh" 54#include "cpu/smt.hh" 55#include "cpu/static_inst.hh" 56#include "kern/kernel_stats.hh" 57#include "sim/byteswap.hh" 58#include "sim/builder.hh" 59#include "sim/debug.hh" 60#include "sim/host.hh" 61#include "sim/sim_events.hh" 62#include "sim/sim_object.hh" 63#include "sim/stats.hh" 64 65#if FULL_SYSTEM 66#include "base/remote_gdb.hh" 67#include "mem/functional/memory_control.hh" 68#include "mem/functional/physical.hh" 69#include "sim/system.hh" 70#include "arch/tlb.hh" 71#include "arch/stacktrace.hh" 72#include "arch/vtophys.hh" 73#else // !FULL_SYSTEM 74#include "mem/memory.hh" 75#endif // FULL_SYSTEM 76 77using namespace std; 78//The SimpleCPU does alpha only 79using namespace AlphaISA; 80 81 82SimpleCPU::TickEvent::TickEvent(SimpleCPU *c, int w) 83 : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c), width(w) 84{ 85} 86 87 88void 89SimpleCPU::init() 90{ 91 BaseCPU::init(); 92#if FULL_SYSTEM 93 for (int i = 0; i < execContexts.size(); ++i) { 94 ExecContext *xc = execContexts[i]; 95 96 // initialize CPU, including PC 97 TheISA::initCPU(xc, xc->readCpuId()); 98 } 99#endif 100} 101 102void 103SimpleCPU::TickEvent::process() 104{ 105 int count = width; 106 do { 107 cpu->tick(); 108 } while (--count > 0 && cpu->status() == Running); 109} 110 111const char * 112SimpleCPU::TickEvent::description() 113{ 114 return "SimpleCPU tick event"; 115} 116 117 118bool 119SimpleCPU::CpuPort::recvTiming(Packet &pkt) 120{ 121 cpu->processResponse(pkt); 122 return true; 123} 124 125Tick 126SimpleCPU::CpuPort::recvAtomic(Packet &pkt) 127{ 128 panic("CPU doesn't expect callback!"); 129 return curTick; 130} 131 132void 133SimpleCPU::CpuPort::recvFunctional(Packet &pkt) 134{ 135 panic("CPU doesn't expect callback!"); 136} 137 138void 139SimpleCPU::CpuPort::recvStatusChange(Status status) 140{ 141 cpu->recvStatusChange(status); 142} 143 144Packet * 145SimpleCPU::CpuPort::recvRetry() 146{ 147 return cpu->processRetry(); 148} 149 150SimpleCPU::SimpleCPU(Params *p) 151 : BaseCPU(p), icachePort(this), 152 dcachePort(this), tickEvent(this, p->width), cpuXC(NULL) 153{ 154 _status = Idle; 155 156 //Create Memory Ports (conect them up) 157 p->mem->addPort("DCACHE"); 158 dcachePort.setPeer(p->mem->getPort("DCACHE")); 159 (p->mem->getPort("DCACHE"))->setPeer(&dcachePort); 160 161 p->mem->addPort("ICACHE"); 162 icachePort.setPeer(p->mem->getPort("ICACHE")); 163 (p->mem->getPort("ICACHE"))->setPeer(&icachePort); 164 165#if FULL_SYSTEM 166 cpuXC = new CPUExecContext(this, 0, p->system, p->itb, p->dtb, p->mem); 167#else 168 cpuXC = new CPUExecContext(this, /* thread_num */ 0, p->process, /* asid */ 0, 169 &dcachePort); 170#endif // !FULL_SYSTEM 171 172 xcProxy = cpuXC->getProxy(); 173 174#if SIMPLE_CPU_MEM_ATOMIC || SIMPLE_CPU_MEM_IMMEDIATE 175 ifetch_req = new CpuRequest; 176 ifetch_req->asid = 0; 177 ifetch_req->size = sizeof(MachInst); 178 ifetch_pkt = new Packet; 179 ifetch_pkt->cmd = Read; 180 ifetch_pkt->data = (uint8_t *)&inst; 181 ifetch_pkt->req = ifetch_req; 182 ifetch_pkt->size = sizeof(MachInst); 183 184 data_read_req = new CpuRequest; 185 data_read_req->asid = 0; 186 data_read_pkt = new Packet; 187 data_read_pkt->cmd = Read; 188 data_read_pkt->data = new uint8_t[8]; 189 data_read_pkt->req = data_read_req; 190 191 data_write_req = new CpuRequest; 192 data_write_req->asid = 0; 193 data_write_pkt = new Packet; 194 data_write_pkt->cmd = Write; 195 data_write_pkt->req = data_write_req; 196#endif 197 198 numInst = 0; 199 startNumInst = 0; 200 numLoad = 0; 201 startNumLoad = 0; 202 lastIcacheStall = 0; 203 lastDcacheStall = 0; 204 205 execContexts.push_back(xcProxy); 206} 207 208SimpleCPU::~SimpleCPU() 209{ 210} 211 212void 213SimpleCPU::switchOut(Sampler *s) 214{ 215 sampler = s; 216 if (status() == DcacheWaitResponse) { 217 DPRINTF(Sampler,"Outstanding dcache access, waiting for completion\n"); 218 _status = DcacheWaitSwitch; 219 } 220 else { 221 _status = SwitchedOut; 222 223 if (tickEvent.scheduled()) 224 tickEvent.squash(); 225 226 sampler->signalSwitched(); 227 } 228} 229 230 231void 232SimpleCPU::takeOverFrom(BaseCPU *oldCPU) 233{ 234 BaseCPU::takeOverFrom(oldCPU); 235 236 assert(!tickEvent.scheduled()); 237 238 // if any of this CPU's ExecContexts are active, mark the CPU as 239 // running and schedule its tick event. 240 for (int i = 0; i < execContexts.size(); ++i) { 241 ExecContext *xc = execContexts[i]; 242 if (xc->status() == ExecContext::Active && _status != Running) { 243 _status = Running; 244 tickEvent.schedule(curTick); 245 } 246 } 247} 248 249 250void 251SimpleCPU::activateContext(int thread_num, int delay) 252{ 253 assert(thread_num == 0); 254 assert(cpuXC); 255 256 assert(_status == Idle); 257 notIdleFraction++; 258 scheduleTickEvent(delay); 259 _status = Running; 260} 261 262 263void 264SimpleCPU::suspendContext(int thread_num) 265{ 266 assert(thread_num == 0); 267 assert(cpuXC); 268 269 assert(_status == Running); 270 notIdleFraction--; 271 unscheduleTickEvent(); 272 _status = Idle; 273} 274 275 276void 277SimpleCPU::deallocateContext(int thread_num) 278{ 279 // for now, these are equivalent 280 suspendContext(thread_num); 281} 282 283 284void 285SimpleCPU::haltContext(int thread_num) 286{ 287 // for now, these are equivalent 288 suspendContext(thread_num); 289} 290 291 292void 293SimpleCPU::regStats() 294{ 295 using namespace Stats; 296 297 BaseCPU::regStats(); 298 299 numInsts 300 .name(name() + ".num_insts") 301 .desc("Number of instructions executed") 302 ; 303 304 numMemRefs 305 .name(name() + ".num_refs") 306 .desc("Number of memory references") 307 ; 308 309 notIdleFraction 310 .name(name() + ".not_idle_fraction") 311 .desc("Percentage of non-idle cycles") 312 ; 313 314 idleFraction 315 .name(name() + ".idle_fraction") 316 .desc("Percentage of idle cycles") 317 ; 318 319 icacheStallCycles 320 .name(name() + ".icache_stall_cycles") 321 .desc("ICache total stall cycles") 322 .prereq(icacheStallCycles) 323 ; 324 325 dcacheStallCycles 326 .name(name() + ".dcache_stall_cycles") 327 .desc("DCache total stall cycles") 328 .prereq(dcacheStallCycles) 329 ; 330 331 icacheRetryCycles 332 .name(name() + ".icache_retry_cycles") 333 .desc("ICache total retry cycles") 334 .prereq(icacheRetryCycles) 335 ; 336 337 dcacheRetryCycles 338 .name(name() + ".dcache_retry_cycles") 339 .desc("DCache total retry cycles") 340 .prereq(dcacheRetryCycles) 341 ; 342 343 idleFraction = constant(1.0) - notIdleFraction; 344} 345 346void 347SimpleCPU::resetStats() 348{ 349 startNumInst = numInst; 350 notIdleFraction = (_status != Idle); 351} 352 353void 354SimpleCPU::serialize(ostream &os) 355{ 356 BaseCPU::serialize(os); 357 SERIALIZE_ENUM(_status); 358 SERIALIZE_SCALAR(inst); 359 nameOut(os, csprintf("%s.xc", name())); 360 cpuXC->serialize(os); 361 nameOut(os, csprintf("%s.tickEvent", name())); 362 tickEvent.serialize(os); 363 nameOut(os, csprintf("%s.cacheCompletionEvent", name())); 364} 365 366void 367SimpleCPU::unserialize(Checkpoint *cp, const string §ion) 368{ 369 BaseCPU::unserialize(cp, section); 370 UNSERIALIZE_ENUM(_status); 371 UNSERIALIZE_SCALAR(inst); 372 cpuXC->unserialize(cp, csprintf("%s.xc", section)); 373 tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); 374} 375 376void 377change_thread_state(int thread_number, int activate, int priority) 378{ 379} 380 381Fault 382SimpleCPU::copySrcTranslate(Addr src) 383{ 384#if 0 385 static bool no_warn = true; 386 int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; 387 // Only support block sizes of 64 atm. 388 assert(blk_size == 64); 389 int offset = src & (blk_size - 1); 390 391 // Make sure block doesn't span page 392 if (no_warn && 393 (src & PageMask) != ((src + blk_size) & PageMask) && 394 (src >> 40) != 0xfffffc) { 395 warn("Copied block source spans pages %x.", src); 396 no_warn = false; 397 } 398 399 memReq->reset(src & ~(blk_size - 1), blk_size); 400 401 // translate to physical address Fault fault = cpuXC->translateDataReadReq(req); 402 403 if (fault == NoFault) { 404 cpuXC->copySrcAddr = src; 405 cpuXC->copySrcPhysAddr = memReq->paddr + offset; 406 } else { 407 assert(!fault->isAlignmentFault()); 408 409 cpuXC->copySrcAddr = 0; 410 cpuXC->copySrcPhysAddr = 0; 411 } 412 return fault; 413#else 414 return NoFault; 415#endif 416} 417 418Fault 419SimpleCPU::copy(Addr dest) 420{ 421#if 0 422 static bool no_warn = true; 423 int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; 424 // Only support block sizes of 64 atm. 425 assert(blk_size == 64); 426 uint8_t data[blk_size]; 427 //assert(cpuXC->copySrcAddr); 428 int offset = dest & (blk_size - 1); 429 430 // Make sure block doesn't span page 431 if (no_warn && 432 (dest & PageMask) != ((dest + blk_size) & PageMask) && 433 (dest >> 40) != 0xfffffc) { 434 no_warn = false; 435 warn("Copied block destination spans pages %x. ", dest); 436 } 437 438 memReq->reset(dest & ~(blk_size -1), blk_size); 439 // translate to physical address 440 Fault fault = cpuXC->translateDataWriteReq(req); 441 442 if (fault == NoFault) { 443 Addr dest_addr = memReq->paddr + offset; 444 // Need to read straight from memory since we have more than 8 bytes. 445 memReq->paddr = cpuXC->copySrcPhysAddr; 446 cpuXC->mem->read(memReq, data); 447 memReq->paddr = dest_addr; 448 cpuXC->mem->write(memReq, data); 449 if (dcacheInterface) { 450 memReq->cmd = Copy; 451 memReq->completionEvent = NULL; 452 memReq->paddr = cpuXC->copySrcPhysAddr; 453 memReq->dest = dest_addr; 454 memReq->size = 64; 455 memReq->time = curTick; 456 memReq->flags &= ~INST_READ; 457 dcacheInterface->access(memReq); 458 } 459 } 460 else 461 assert(!fault->isAlignmentFault()); 462 463 return fault; 464#else 465 panic("copy not implemented"); 466 return NoFault; 467#endif 468} 469 470// precise architected memory state accessor macros 471template <class T> 472Fault 473SimpleCPU::read(Addr addr, T &data, unsigned flags) 474{ 475 if (status() == DcacheWaitResponse || status() == DcacheWaitSwitch) { 476// Fault fault = xc->read(memReq,data); 477 // Not sure what to check for no fault... 478 if (data_read_pkt->result == Success) { 479 memcpy(&data, data_read_pkt->data, sizeof(T)); 480 } 481 482 if (traceData) { 483 traceData->setAddr(addr); 484 } 485 486 // @todo: Figure out a way to create a Fault from the packet result. 487 return NoFault; 488 } 489 490// memReq->reset(addr, sizeof(T), flags); 491 492#if SIMPLE_CPU_MEM_TIMING 493 CpuRequest *data_read_req = new CpuRequest; 494#endif 495 496 data_read_req->vaddr = addr; 497 data_read_req->size = sizeof(T); 498 data_read_req->flags = flags; 499 data_read_req->time = curTick; 500 501 // translate to physical address 502 Fault fault = cpuXC->translateDataReadReq(data_read_req); 503 504 // Now do the access. 505 if (fault == NoFault) { 506#if SIMPLE_CPU_MEM_TIMING 507 data_read_pkt = new Packet; 508 data_read_pkt->cmd = Read; 509 data_read_pkt->req = data_read_req; 510 data_read_pkt->data = new uint8_t[8]; 511#endif 512 data_read_pkt->addr = data_read_req->paddr; 513 data_read_pkt->size = sizeof(T); 514 515 sendDcacheRequest(data_read_pkt); 516 517#if SIMPLE_CPU_MEM_IMMEDIATE 518 // Need to find a way to not duplicate code above. 519 520 if (data_read_pkt->result == Success) { 521 memcpy(&data, data_read_pkt->data, sizeof(T)); 522 } 523 524 if (traceData) { 525 traceData->setAddr(addr); 526 } 527 528 // @todo: Figure out a way to create a Fault from the packet result. 529 return NoFault; 530#endif 531 } 532/* 533 memReq->cmd = Read; 534 memReq->completionEvent = NULL; 535 memReq->time = curTick; 536 memReq->flags &= ~INST_READ; 537 MemAccessResult result = dcacheInterface->access(memReq); 538 539 // Ugly hack to get an event scheduled *only* if the access is 540 // a miss. We really should add first-class support for this 541 // at some point. 542 if (result != MA_HIT && dcacheInterface->doEvents()) { 543 memReq->completionEvent = &cacheCompletionEvent; 544 lastDcacheStall = curTick; 545 unscheduleTickEvent(); 546 _status = DcacheMissStall; 547 } else { 548 // do functional access 549 fault = cpuXC->read(memReq, data); 550 551 } 552 } else if(fault == NoFault) { 553 // do functional access 554 fault = cpuXC->read(memReq, data); 555 556 } 557*/ 558 // This will need a new way to tell if it has a dcache attached. 559 if (data_read_req->flags & UNCACHEABLE) 560 recordEvent("Uncached Read"); 561 562 return fault; 563} 564 565#ifndef DOXYGEN_SHOULD_SKIP_THIS 566 567template 568Fault 569SimpleCPU::read(Addr addr, uint64_t &data, unsigned flags); 570 571template 572Fault 573SimpleCPU::read(Addr addr, uint32_t &data, unsigned flags); 574 575template 576Fault 577SimpleCPU::read(Addr addr, uint16_t &data, unsigned flags); 578 579template 580Fault 581SimpleCPU::read(Addr addr, uint8_t &data, unsigned flags); 582 583#endif //DOXYGEN_SHOULD_SKIP_THIS 584 585template<> 586Fault 587SimpleCPU::read(Addr addr, double &data, unsigned flags) 588{ 589 return read(addr, *(uint64_t*)&data, flags); 590} 591 592template<> 593Fault 594SimpleCPU::read(Addr addr, float &data, unsigned flags) 595{ 596 return read(addr, *(uint32_t*)&data, flags); 597} 598 599 600template<> 601Fault 602SimpleCPU::read(Addr addr, int32_t &data, unsigned flags) 603{ 604 return read(addr, (uint32_t&)data, flags); 605} 606 607 608template <class T> 609Fault 610SimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) 611{ 612 data_write_req->vaddr = addr; 613 data_write_req->time = curTick; 614 data_write_req->size = sizeof(T); 615 data_write_req->flags = flags; 616 617 // translate to physical address 618 Fault fault = cpuXC->translateDataWriteReq(data_write_req); 619 // Now do the access. 620 if (fault == NoFault) { 621#if SIMPLE_CPU_MEM_TIMING 622 data_write_pkt = new Packet; 623 data_write_pkt->cmd = Write; 624 data_write_pkt->req = data_write_req; 625 data_write_pkt->data = new uint8_t[64]; 626 memcpy(data_write_pkt->data, &data, sizeof(T)); 627#else 628 data_write_pkt->data = (uint8_t *)&data; 629#endif 630 data_write_pkt->addr = data_write_req->paddr; 631 data_write_pkt->size = sizeof(T); 632 633 sendDcacheRequest(data_write_pkt); 634 } 635 636/* 637 // do functional access 638 if (fault == NoFault) 639 fault = cpuXC->write(memReq, data); 640 641 if (fault == NoFault && dcacheInterface) { 642 memReq->cmd = Write; 643 memcpy(memReq->data,(uint8_t *)&data,memReq->size); 644 memReq->completionEvent = NULL; 645 memReq->time = curTick; 646 memReq->flags &= ~INST_READ; 647 MemAccessResult result = dcacheInterface->access(memReq); 648 649 // Ugly hack to get an event scheduled *only* if the access is 650 // a miss. We really should add first-class support for this 651 // at some point. 652 if (result != MA_HIT && dcacheInterface->doEvents()) { 653 memReq->completionEvent = &cacheCompletionEvent; 654 lastDcacheStall = curTick; 655 unscheduleTickEvent(); 656 _status = DcacheMissStall; 657 } 658 } 659*/ 660 if (res && (fault == NoFault)) 661 *res = data_write_pkt->result; 662 663 // This will need a new way to tell if it's hooked up to a cache or not. 664 if (data_write_req->flags & UNCACHEABLE) 665 recordEvent("Uncached Write"); 666 667 // If the write needs to have a fault on the access, consider calling 668 // changeStatus() and changing it to "bad addr write" or something. 669 return fault; 670} 671 672 673#ifndef DOXYGEN_SHOULD_SKIP_THIS 674template 675Fault 676SimpleCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res); 677 678template 679Fault 680SimpleCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res); 681 682template 683Fault 684SimpleCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res); 685 686template 687Fault 688SimpleCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res); 689 690#endif //DOXYGEN_SHOULD_SKIP_THIS 691 692template<> 693Fault 694SimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) 695{ 696 return write(*(uint64_t*)&data, addr, flags, res); 697} 698 699template<> 700Fault 701SimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) 702{ 703 return write(*(uint32_t*)&data, addr, flags, res); 704} 705 706 707template<> 708Fault 709SimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) 710{ 711 return write((uint32_t)data, addr, flags, res); 712} 713 714 715#if FULL_SYSTEM 716Addr 717SimpleCPU::dbg_vtophys(Addr addr) 718{ 719 return vtophys(xcProxy, addr); 720} 721#endif // FULL_SYSTEM 722 723void 724SimpleCPU::sendIcacheRequest(Packet *pkt) 725{ 726 assert(!tickEvent.scheduled()); 727#if SIMPLE_CPU_MEM_TIMING 728 retry_pkt = pkt; 729 bool success = icachePort.sendTiming(*pkt); 730 731 unscheduleTickEvent(); 732 733 lastIcacheStall = curTick; 734 735 if (!success) { 736 // Need to wait for retry 737 _status = IcacheRetry; 738 } else { 739 // Need to wait for cache to respond 740 _status = IcacheWaitResponse; 741 } 742#elif SIMPLE_CPU_MEM_ATOMIC 743 Tick latency = icachePort.sendAtomic(*pkt); 744 745 unscheduleTickEvent(); 746 scheduleTickEvent(latency); 747 748 // Note that Icache miss cycles will be incorrect. Unless 749 // we check the status of the packet sent (is this valid?), 750 // we won't know if the latency is a hit or a miss. 751 icacheStallCycles += latency; 752 753 _status = IcacheAccessComplete; 754#elif SIMPLE_CPU_MEM_IMMEDIATE 755 icachePort.sendAtomic(*pkt); 756#else 757#error "SimpleCPU has no mem model set" 758#endif 759} 760 761void 762SimpleCPU::sendDcacheRequest(Packet *pkt) 763{ 764 assert(!tickEvent.scheduled()); 765#if SIMPLE_CPU_MEM_TIMING 766 unscheduleTickEvent(); 767 768 retry_pkt = pkt; 769 bool success = dcachePort.sendTiming(*pkt); 770 771 lastDcacheStall = curTick; 772 773 if (!success) { 774 _status = DcacheRetry; 775 } else { 776 _status = DcacheWaitResponse; 777 } 778#elif SIMPLE_CPU_MEM_ATOMIC 779 unscheduleTickEvent(); 780 781 Tick latency = dcachePort.sendAtomic(*pkt); 782 783 scheduleTickEvent(latency); 784 785 // Note that Dcache miss cycles will be incorrect. Unless 786 // we check the status of the packet sent (is this valid?), 787 // we won't know if the latency is a hit or a miss. 788 dcacheStallCycles += latency; 789#elif SIMPLE_CPU_MEM_IMMEDIATE 790 dcachePort.sendAtomic(*pkt); 791#else 792#error "SimpleCPU has no mem model set" 793#endif 794} 795 796void 797SimpleCPU::processResponse(Packet &response) 798{ 799 assert(SIMPLE_CPU_MEM_TIMING); 800 801 // For what things is the CPU the consumer of the packet it sent 802 // out? This may create a memory leak if that's the case and it's 803 // expected of the SimpleCPU to delete its own packet. 804 Packet *pkt = &response; 805 806 switch (status()) { 807 case IcacheWaitResponse: 808 icacheStallCycles += curTick - lastIcacheStall; 809 810 _status = IcacheAccessComplete; 811 scheduleTickEvent(1); 812 813 // Copy the icache data into the instruction itself. 814 memcpy(&inst, pkt->data, sizeof(inst)); 815 816 delete pkt; 817 break; 818 case DcacheWaitResponse: 819 if (pkt->cmd == Read) { 820 curStaticInst->execute(this,traceData); 821 if (traceData) 822 traceData->finalize(); 823 } 824 825 delete pkt; 826 827 dcacheStallCycles += curTick - lastDcacheStall; 828 _status = Running; 829 scheduleTickEvent(1); 830 break; 831 case DcacheWaitSwitch: 832 if (pkt->cmd == Read) { 833 curStaticInst->execute(this,traceData); 834 if (traceData) 835 traceData->finalize(); 836 } 837 838 delete pkt; 839 840 _status = SwitchedOut; 841 sampler->signalSwitched(); 842 case SwitchedOut: 843 // If this CPU has been switched out due to sampling/warm-up, 844 // ignore any further status changes (e.g., due to cache 845 // misses outstanding at the time of the switch). 846 delete pkt; 847 848 return; 849 default: 850 panic("SimpleCPU::processCacheCompletion: bad state"); 851 break; 852 } 853} 854 855Packet * 856SimpleCPU::processRetry() 857{ 858#if SIMPLE_CPU_MEM_TIMING 859 switch(status()) { 860 case IcacheRetry: 861 icacheRetryCycles += curTick - lastIcacheStall; 862 return retry_pkt; 863 break; 864 case DcacheRetry: 865 dcacheRetryCycles += curTick - lastDcacheStall; 866 return retry_pkt; 867 break; 868 default: 869 panic("SimpleCPU::processRetry: bad state"); 870 break; 871 } 872#else 873 panic("shouldn't be here"); 874#endif 875} 876 877#if FULL_SYSTEM 878void 879SimpleCPU::post_interrupt(int int_num, int index) 880{ 881 BaseCPU::post_interrupt(int_num, index); 882 883 if (cpuXC->status() == ExecContext::Suspended) { 884 DPRINTF(IPI,"Suspended Processor awoke\n"); 885 cpuXC->activate(); 886 } 887} 888#endif // FULL_SYSTEM 889 890/* start simulation, program loaded, processor precise state initialized */ 891void 892SimpleCPU::tick() 893{ 894 numCycles++; 895 896 traceData = NULL; 897 898 Fault fault = NoFault; 899 900#if FULL_SYSTEM 901 if (checkInterrupts && check_interrupts() && !cpuXC->inPalMode() && 902 status() != IcacheMissComplete) { 903 int ipl = 0; 904 int summary = 0; 905 checkInterrupts = false; 906 907 if (cpuXC->readMiscReg(IPR_SIRR)) { 908 for (int i = INTLEVEL_SOFTWARE_MIN; 909 i < INTLEVEL_SOFTWARE_MAX; i++) { 910 if (cpuXC->readMiscReg(IPR_SIRR) & (ULL(1) << i)) { 911 // See table 4-19 of 21164 hardware reference 912 ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; 913 summary |= (ULL(1) << i); 914 } 915 } 916 } 917 918 uint64_t interrupts = cpuXC->cpu->intr_status(); 919 for (int i = INTLEVEL_EXTERNAL_MIN; 920 i < INTLEVEL_EXTERNAL_MAX; i++) { 921 if (interrupts & (ULL(1) << i)) { 922 // See table 4-19 of 21164 hardware reference 923 ipl = i; 924 summary |= (ULL(1) << i); 925 } 926 } 927 928 if (cpuXC->readMiscReg(IPR_ASTRR)) 929 panic("asynchronous traps not implemented\n"); 930 931 if (ipl && ipl > cpuXC->readMiscReg(IPR_IPLR)) { 932 cpuXC->setMiscReg(IPR_ISR, summary); 933 cpuXC->setMiscReg(IPR_INTID, ipl); 934 935 Fault(new InterruptFault)->invoke(xcProxy); 936 937 DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", 938 cpuXC->readMiscReg(IPR_IPLR), ipl, summary); 939 } 940 } 941#endif 942 943 // maintain $r0 semantics 944 cpuXC->setIntReg(ZeroReg, 0); 945#if THE_ISA == ALPHA_ISA 946 cpuXC->setFloatRegDouble(ZeroReg, 0.0); 947#endif // ALPHA_ISA 948 949 if (status() == IcacheAccessComplete) { 950 // We've already fetched an instruction and were stalled on an 951 // I-cache miss. No need to fetch it again. 952 953 // Set status to running; tick event will get rescheduled if 954 // necessary at end of tick() function. 955 _status = Running; 956 } else { 957 // Try to fetch an instruction 958 959 // set up memory request for instruction fetch 960#if FULL_SYSTEM 961#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0 962#else 963#define IFETCH_FLAGS(pc) 0 964#endif 965 966#if SIMPLE_CPU_MEM_TIMING 967 CpuRequest *ifetch_req = new CpuRequest(); 968 ifetch_req->size = sizeof(MachInst); 969#endif 970 971 ifetch_req->vaddr = cpuXC->readPC() & ~3; 972 ifetch_req->time = curTick; 973 974/* memReq->reset(xc->regs.pc & ~3, sizeof(uint32_t), 975 IFETCH_FLAGS(xc->regs.pc)); 976*/ 977 978 fault = cpuXC->translateInstReq(ifetch_req); 979 980 if (fault == NoFault) { 981#if SIMPLE_CPU_MEM_TIMING 982 Packet *ifetch_pkt = new Packet; 983 ifetch_pkt->cmd = Read; 984 ifetch_pkt->data = (uint8_t *)&inst; 985 ifetch_pkt->req = ifetch_req; 986 ifetch_pkt->size = sizeof(MachInst); 987#endif 988 ifetch_pkt->addr = ifetch_req->paddr; 989 990 sendIcacheRequest(ifetch_pkt); 991#if SIMPLE_CPU_MEM_TIMING || SIMPLE_CPU_MEM_ATOMIC 992 return; 993#endif 994/* 995 if (icacheInterface && fault == NoFault) { 996 memReq->completionEvent = NULL; 997 998 memReq->time = curTick; 999 memReq->flags |= INST_READ; 1000 MemAccessResult result = icacheInterface->access(memReq); 1001 1002 // Ugly hack to get an event scheduled *only* if the access is 1003 // a miss. We really should add first-class support for this 1004 // at some point. 1005 if (result != MA_HIT && icacheInterface->doEvents()) { 1006 memReq->completionEvent = &cacheCompletionEvent; 1007 lastIcacheStall = curTick; 1008 unscheduleTickEvent(); 1009 _status = IcacheMissStall; 1010 return; 1011 } 1012 } 1013*/ 1014 } 1015 } 1016 1017 // If we've got a valid instruction (i.e., no fault on instruction 1018 // fetch), then execute it. 1019 if (fault == NoFault) { 1020 1021 // keep an instruction count 1022 numInst++; 1023 numInsts++; 1024 1025 // check for instruction-count-based events 1026 comInstEventQueue[0]->serviceEvents(numInst); 1027 1028 // decode the instruction 1029 inst = gtoh(inst); 1030 curStaticInst = StaticInst::decode(makeExtMI(inst, cpuXC->readPC())); 1031 1032 traceData = Trace::getInstRecord(curTick, xcProxy, this, curStaticInst, 1033 cpuXC->readPC()); 1034 1035#if FULL_SYSTEM 1036 cpuXC->setInst(inst); 1037#endif // FULL_SYSTEM 1038 1039 cpuXC->func_exe_inst++; 1040 1041 fault = curStaticInst->execute(this, traceData); 1042 1043#if FULL_SYSTEM 1044 if (system->kernelBinning->fnbin) { 1045 assert(kernelStats); 1046 system->kernelBinning->execute(xcProxy, inst); 1047 } 1048 1049 if (cpuXC->profile) { 1050 bool usermode = 1051 (cpuXC->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; 1052 cpuXC->profilePC = usermode ? 1 : cpuXC->readPC(); 1053 ProfileNode *node = cpuXC->profile->consume(xcProxy, inst); 1054 if (node) 1055 cpuXC->profileNode = node; 1056 } 1057#endif 1058 1059 if (curStaticInst->isMemRef()) { 1060 numMemRefs++; 1061 } 1062 1063 if (curStaticInst->isLoad()) { 1064 ++numLoad; 1065 comLoadEventQueue[0]->serviceEvents(numLoad); 1066 } 1067 1068 // If we have a dcache miss, then we can't finialize the instruction 1069 // trace yet because we want to populate it with the data later 1070 if (traceData && (status() != DcacheWaitResponse)) { 1071 traceData->finalize(); 1072 } 1073 1074 traceFunctions(cpuXC->readPC()); 1075 1076 } // if (fault == NoFault) 1077 1078 if (fault != NoFault) { 1079#if FULL_SYSTEM 1080 fault->invoke(xcProxy); 1081#else // !FULL_SYSTEM 1082 fatal("fault (%d) detected @ PC 0x%08p", fault, cpuXC->readPC()); 1083#endif // FULL_SYSTEM 1084 } 1085 else { 1086#if THE_ISA != MIPS_ISA 1087 // go to the next instruction 1088 cpuXC->setPC(cpuXC->readNextPC()); 1089 cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst)); 1090#else 1091 // go to the next instruction 1092 cpuXC->setPC(cpuXC->readNextPC()); 1093 cpuXC->setNextPC(cpuXC->readNextNPC()); 1094 cpuXC->setNextNPC(cpuXC->readNextNPC() + sizeof(MachInst)); 1095#endif 1096 1097 } 1098 1099#if FULL_SYSTEM 1100 Addr oldpc; 1101 do { 1102 oldpc = cpuXC->readPC(); 1103 system->pcEventQueue.service(xcProxy); 1104 } while (oldpc != cpuXC->readPC()); 1105#endif 1106 1107 assert(status() == Running || 1108 status() == Idle || 1109 status() == DcacheWaitResponse); 1110 1111 if (status() == Running && !tickEvent.scheduled()) 1112 tickEvent.schedule(curTick + cycles(1)); 1113} 1114 1115//////////////////////////////////////////////////////////////////////// 1116// 1117// SimpleCPU Simulation Object 1118// 1119BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) 1120 1121 Param<Counter> max_insts_any_thread; 1122 Param<Counter> max_insts_all_threads; 1123 Param<Counter> max_loads_any_thread; 1124 Param<Counter> max_loads_all_threads; 1125 1126#if FULL_SYSTEM 1127 SimObjectParam<AlphaITB *> itb; 1128 SimObjectParam<AlphaDTB *> dtb; 1129 SimObjectParam<System *> system; 1130 Param<int> cpu_id; 1131 Param<Tick> profile; 1132#else 1133 SimObjectParam<Memory *> mem; 1134 SimObjectParam<Process *> workload; 1135#endif // FULL_SYSTEM 1136 1137 Param<int> clock; 1138 1139 Param<bool> defer_registration; 1140 Param<int> width; 1141 Param<bool> function_trace; 1142 Param<Tick> function_trace_start; 1143 1144END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) 1145 1146BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU) 1147 1148 INIT_PARAM(max_insts_any_thread, 1149 "terminate when any thread reaches this inst count"), 1150 INIT_PARAM(max_insts_all_threads, 1151 "terminate when all threads have reached this inst count"), 1152 INIT_PARAM(max_loads_any_thread, 1153 "terminate when any thread reaches this load count"), 1154 INIT_PARAM(max_loads_all_threads, 1155 "terminate when all threads have reached this load count"), 1156 1157#if FULL_SYSTEM 1158 INIT_PARAM(itb, "Instruction TLB"), 1159 INIT_PARAM(dtb, "Data TLB"), 1160 INIT_PARAM(system, "system object"), 1161 INIT_PARAM(cpu_id, "processor ID"), 1162 INIT_PARAM(profile, ""), 1163#else 1164 INIT_PARAM(mem, "memory"), 1165 INIT_PARAM(workload, "processes to run"), 1166#endif // FULL_SYSTEM 1167 1168 INIT_PARAM(clock, "clock speed"), 1169 INIT_PARAM(defer_registration, "defer system registration (for sampling)"), 1170 INIT_PARAM(width, "cpu width"), 1171 INIT_PARAM(function_trace, "Enable function trace"), 1172 INIT_PARAM(function_trace_start, "Cycle to start function trace") 1173 1174END_INIT_SIM_OBJECT_PARAMS(SimpleCPU) 1175 1176 1177CREATE_SIM_OBJECT(SimpleCPU) 1178{ 1179 SimpleCPU::Params *params = new SimpleCPU::Params(); 1180 params->name = getInstanceName(); 1181 params->numberOfThreads = 1; 1182 params->max_insts_any_thread = max_insts_any_thread; 1183 params->max_insts_all_threads = max_insts_all_threads; 1184 params->max_loads_any_thread = max_loads_any_thread; 1185 params->max_loads_all_threads = max_loads_all_threads; 1186 params->deferRegistration = defer_registration; 1187 params->clock = clock; 1188 params->functionTrace = function_trace; 1189 params->functionTraceStart = function_trace_start; 1190 params->width = width; 1191 1192#if FULL_SYSTEM 1193 params->itb = itb; 1194 params->dtb = dtb; 1195 params->system = system; 1196 params->cpu_id = cpu_id; 1197 params->profile = profile; 1198#else 1199 params->mem = mem; 1200 params->process = workload; 1201#endif 1202 1203 SimpleCPU *cpu = new SimpleCPU(params); 1204 return cpu; 1205} 1206 1207REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU) 1208 1209