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