base.cc revision 237
1/* 2 * Copyright (c) 2003 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 "base/cprintf.hh" 39#include "base/inifile.hh" 40#include "base/loader/symtab.hh" 41#include "base/misc.hh" 42#include "base/pollevent.hh" 43#include "base/range.hh" 44#include "base/trace.hh" 45#include "cpu/base_cpu.hh" 46#include "cpu/exec_context.hh" 47#include "cpu/exetrace.hh" 48#include "cpu/full_cpu/smt.hh" 49#include "cpu/simple_cpu/simple_cpu.hh" 50#include "cpu/static_inst.hh" 51#include "mem/base_mem.hh" 52#include "mem/mem_interface.hh" 53#include "sim/annotation.hh" 54#include "sim/builder.hh" 55#include "sim/debug.hh" 56#include "sim/host.hh" 57#include "sim/sim_events.hh" 58#include "sim/sim_object.hh" 59#include "sim/sim_stats.hh" 60 61#ifdef FULL_SYSTEM 62#include "base/remote_gdb.hh" 63#include "dev/alpha_access.h" 64#include "dev/pciareg.h" 65#include "mem/functional_mem/memory_control.hh" 66#include "mem/functional_mem/physical_memory.hh" 67#include "sim/system.hh" 68#include "targetarch/alpha_memory.hh" 69#include "targetarch/vtophys.hh" 70#else // !FULL_SYSTEM 71#include "eio/eio.hh" 72#include "mem/functional_mem/functional_memory.hh" 73#include "sim/prog.hh" 74#endif // FULL_SYSTEM 75 76using namespace std; 77 78SimpleCPU::TickEvent::TickEvent(SimpleCPU *c) 79 : Event(&mainEventQueue, "SimpleCPU::TickEvent", 100), cpu(c) 80{ 81} 82 83void 84SimpleCPU::TickEvent::process() 85{ 86 cpu->tick(); 87} 88 89const char * 90SimpleCPU::TickEvent::description() 91{ 92 return "SimpleCPU tick event"; 93} 94 95 96SimpleCPU::CacheCompletionEvent::CacheCompletionEvent(SimpleCPU *_cpu) 97 : Event(&mainEventQueue, "SimpleCPU::CacheCompletionEvent"), 98 cpu(_cpu) 99{ 100} 101 102void SimpleCPU::CacheCompletionEvent::process() 103{ 104 cpu->processCacheCompletion(); 105} 106 107const char * 108SimpleCPU::CacheCompletionEvent::description() 109{ 110 return "SimpleCPU cache completion event"; 111} 112 113#ifdef FULL_SYSTEM 114SimpleCPU::SimpleCPU(const string &_name, 115 System *_system, 116 Counter max_insts_any_thread, 117 Counter max_insts_all_threads, 118 Counter max_loads_any_thread, 119 Counter max_loads_all_threads, 120 AlphaItb *itb, AlphaDtb *dtb, 121 FunctionalMemory *mem, 122 MemInterface *icache_interface, 123 MemInterface *dcache_interface, 124 Tick freq) 125 : BaseCPU(_name, /* number_of_threads */ 1, 126 max_insts_any_thread, max_insts_all_threads, 127 max_loads_any_thread, max_loads_all_threads, 128 _system, freq), 129#else 130SimpleCPU::SimpleCPU(const string &_name, Process *_process, 131 Counter max_insts_any_thread, 132 Counter max_insts_all_threads, 133 Counter max_loads_any_thread, 134 Counter max_loads_all_threads, 135 MemInterface *icache_interface, 136 MemInterface *dcache_interface) 137 : BaseCPU(_name, /* number_of_threads */ 1, 138 max_insts_any_thread, max_insts_all_threads, 139 max_loads_any_thread, max_loads_all_threads), 140#endif 141 tickEvent(this), xc(NULL), cacheCompletionEvent(this) 142{ 143 _status = Idle; 144#ifdef FULL_SYSTEM 145 xc = new ExecContext(this, 0, system, itb, dtb, mem); 146 147 // initialize CPU, including PC 148 TheISA::initCPU(&xc->regs); 149#else 150 xc = new ExecContext(this, /* thread_num */ 0, _process, /* asid */ 0); 151#endif // !FULL_SYSTEM 152 153 icacheInterface = icache_interface; 154 dcacheInterface = dcache_interface; 155 156 memReq = new MemReq(); 157 memReq->xc = xc; 158 memReq->asid = 0; 159 memReq->data = new uint8_t[64]; 160 161 numInst = 0; 162 numLoad = 0; 163 last_idle = 0; 164 lastIcacheStall = 0; 165 lastDcacheStall = 0; 166 167 execContexts.push_back(xc); 168} 169 170SimpleCPU::~SimpleCPU() 171{ 172} 173 174 175void 176SimpleCPU::switchOut() 177{ 178 _status = SwitchedOut; 179 if (tickEvent.scheduled()) 180 tickEvent.squash(); 181} 182 183 184void 185SimpleCPU::takeOverFrom(BaseCPU *oldCPU) 186{ 187 BaseCPU::takeOverFrom(oldCPU); 188 189 assert(!tickEvent.scheduled()); 190 191 // if any of this CPU's ExecContexts are active, mark the CPU as 192 // running and schedule its tick event. 193 for (int i = 0; i < execContexts.size(); ++i) { 194 ExecContext *xc = execContexts[i]; 195 if (xc->status() == ExecContext::Active && _status != Running) { 196 _status = Running; 197 tickEvent.schedule(curTick); 198 } 199 } 200 201 oldCPU->switchOut(); 202} 203 204 205void 206SimpleCPU::execCtxStatusChg(int thread_num) { 207 assert(thread_num == 0); 208 assert(xc); 209 210 if (xc->status() == ExecContext::Active) 211 setStatus(Running); 212 else 213 setStatus(Idle); 214} 215 216 217void 218SimpleCPU::regStats() 219{ 220 BaseCPU::regStats(); 221 222 numInsts 223 .name(name() + ".num_insts") 224 .desc("Number of instructions executed") 225 ; 226 227 numMemRefs 228 .name(name() + ".num_refs") 229 .desc("Number of memory references") 230 ; 231 232 idleCycles 233 .name(name() + ".idle_cycles") 234 .desc("Number of idle cycles") 235 ; 236 237 idleFraction 238 .name(name() + ".idle_fraction") 239 .desc("Percentage of idle cycles") 240 ; 241 242 icacheStallCycles 243 .name(name() + ".icache_stall_cycles") 244 .desc("ICache total stall cycles") 245 .prereq(icacheStallCycles) 246 ; 247 248 dcacheStallCycles 249 .name(name() + ".dcache_stall_cycles") 250 .desc("DCache total stall cycles") 251 .prereq(dcacheStallCycles) 252 ; 253 254 idleFraction = idleCycles / simTicks; 255 256 numInsts = Statistics::scalar(numInst); 257 simInsts += numInsts; 258} 259 260void 261SimpleCPU::serialize(ostream &os) 262{ 263 SERIALIZE_ENUM(_status); 264 SERIALIZE_SCALAR(inst); 265 nameOut(os, csprintf("%s.xc", name())); 266 xc->serialize(os); 267 nameOut(os, csprintf("%s.tickEvent", name())); 268 tickEvent.serialize(os); 269 nameOut(os, csprintf("%s.cacheCompletionEvent", name())); 270 cacheCompletionEvent.serialize(os); 271} 272 273void 274SimpleCPU::unserialize(Checkpoint *cp, const string §ion) 275{ 276 UNSERIALIZE_ENUM(_status); 277 UNSERIALIZE_SCALAR(inst); 278 xc->unserialize(cp, csprintf("%s.xc", section)); 279 tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); 280 cacheCompletionEvent 281 .unserialize(cp, csprintf("%s.cacheCompletionEvent", section)); 282} 283 284void 285change_thread_state(int thread_number, int activate, int priority) 286{ 287} 288 289// precise architected memory state accessor macros 290template <class T> 291Fault 292SimpleCPU::read(Addr addr, T& data, unsigned flags) 293{ 294 memReq->reset(addr, sizeof(T), flags); 295 296 // translate to physical address 297 Fault fault = xc->translateDataReadReq(memReq); 298 299 // do functional access 300 if (fault == No_Fault) 301 fault = xc->read(memReq, data); 302 303 if (traceData) { 304 traceData->setAddr(addr); 305 if (fault == No_Fault) 306 traceData->setData(data); 307 } 308 309 // if we have a cache, do cache access too 310 if (fault == No_Fault && dcacheInterface) { 311 memReq->cmd = Read; 312 memReq->completionEvent = NULL; 313 memReq->time = curTick; 314 memReq->flags &= ~UNCACHEABLE; 315 MemAccessResult result = dcacheInterface->access(memReq); 316 317 // Ugly hack to get an event scheduled *only* if the access is 318 // a miss. We really should add first-class support for this 319 // at some point. 320 if (result != MA_HIT && dcacheInterface->doEvents) { 321 memReq->completionEvent = &cacheCompletionEvent; 322 setStatus(DcacheMissStall); 323 } 324 } 325 326 return fault; 327} 328 329#ifndef DOXYGEN_SHOULD_SKIP_THIS 330 331template 332Fault 333SimpleCPU::read(Addr addr, uint64_t& data, unsigned flags); 334 335template 336Fault 337SimpleCPU::read(Addr addr, uint32_t& data, unsigned flags); 338 339template 340Fault 341SimpleCPU::read(Addr addr, uint16_t& data, unsigned flags); 342 343template 344Fault 345SimpleCPU::read(Addr addr, uint8_t& data, unsigned flags); 346 347#endif //DOXYGEN_SHOULD_SKIP_THIS 348 349template<> 350Fault 351SimpleCPU::read(Addr addr, double& data, unsigned flags) 352{ 353 return read(addr, *(uint64_t*)&data, flags); 354} 355 356template<> 357Fault 358SimpleCPU::read(Addr addr, float& data, unsigned flags) 359{ 360 return read(addr, *(uint32_t*)&data, flags); 361} 362 363 364template<> 365Fault 366SimpleCPU::read(Addr addr, int32_t& data, unsigned flags) 367{ 368 return read(addr, (uint32_t&)data, flags); 369} 370 371 372template <class T> 373Fault 374SimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) 375{ 376 if (traceData) { 377 traceData->setAddr(addr); 378 traceData->setData(data); 379 } 380 381 memReq->reset(addr, sizeof(T), flags); 382 383 // translate to physical address 384 Fault fault = xc->translateDataWriteReq(memReq); 385 386 // do functional access 387 if (fault == No_Fault) 388 fault = xc->write(memReq, data); 389 390 if (fault == No_Fault && dcacheInterface) { 391 memReq->cmd = Write; 392 memcpy(memReq->data,(uint8_t *)&data,memReq->size); 393 memReq->completionEvent = NULL; 394 memReq->time = curTick; 395 memReq->flags &= ~UNCACHEABLE; 396 MemAccessResult result = dcacheInterface->access(memReq); 397 398 // Ugly hack to get an event scheduled *only* if the access is 399 // a miss. We really should add first-class support for this 400 // at some point. 401 if (result != MA_HIT && dcacheInterface->doEvents) { 402 memReq->completionEvent = &cacheCompletionEvent; 403 setStatus(DcacheMissStall); 404 } 405 } 406 407 if (res && (fault == No_Fault)) 408 *res = memReq->result; 409 410 return fault; 411} 412 413 414#ifndef DOXYGEN_SHOULD_SKIP_THIS 415template 416Fault 417SimpleCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res); 418 419template 420Fault 421SimpleCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res); 422 423template 424Fault 425SimpleCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res); 426 427template 428Fault 429SimpleCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res); 430 431#endif //DOXYGEN_SHOULD_SKIP_THIS 432 433template<> 434Fault 435SimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) 436{ 437 return write(*(uint64_t*)&data, addr, flags, res); 438} 439 440template<> 441Fault 442SimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) 443{ 444 return write(*(uint32_t*)&data, addr, flags, res); 445} 446 447 448template<> 449Fault 450SimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) 451{ 452 return write((uint32_t)data, addr, flags, res); 453} 454 455 456#ifdef FULL_SYSTEM 457Addr 458SimpleCPU::dbg_vtophys(Addr addr) 459{ 460 return vtophys(xc, addr); 461} 462#endif // FULL_SYSTEM 463 464Tick save_cycle = 0; 465 466 467void 468SimpleCPU::processCacheCompletion() 469{ 470 switch (status()) { 471 case IcacheMissStall: 472 icacheStallCycles += curTick - lastIcacheStall; 473 setStatus(IcacheMissComplete); 474 break; 475 case DcacheMissStall: 476 dcacheStallCycles += curTick - lastDcacheStall; 477 setStatus(Running); 478 break; 479 case SwitchedOut: 480 // If this CPU has been switched out due to sampling/warm-up, 481 // ignore any further status changes (e.g., due to cache 482 // misses outstanding at the time of the switch). 483 return; 484 default: 485 panic("SimpleCPU::processCacheCompletion: bad state"); 486 break; 487 } 488} 489 490#ifdef FULL_SYSTEM 491void 492SimpleCPU::post_interrupt(int int_num, int index) 493{ 494 BaseCPU::post_interrupt(int_num, index); 495 496 if (xc->status() == ExecContext::Suspended) { 497 DPRINTF(IPI,"Suspended Processor awoke\n"); 498 xc->setStatus(ExecContext::Active); 499 Annotate::Resume(xc); 500 } 501} 502#endif // FULL_SYSTEM 503 504/* start simulation, program loaded, processor precise state initialized */ 505void 506SimpleCPU::tick() 507{ 508 traceData = NULL; 509 510 Fault fault = No_Fault; 511 512#ifdef FULL_SYSTEM 513 if (AlphaISA::check_interrupts && 514 xc->cpu->check_interrupts() && 515 !PC_PAL(xc->regs.pc) && 516 status() != IcacheMissComplete) { 517 int ipl = 0; 518 int summary = 0; 519 AlphaISA::check_interrupts = 0; 520 IntReg *ipr = xc->regs.ipr; 521 522 if (xc->regs.ipr[TheISA::IPR_SIRR]) { 523 for (int i = TheISA::INTLEVEL_SOFTWARE_MIN; 524 i < TheISA::INTLEVEL_SOFTWARE_MAX; i++) { 525 if (ipr[TheISA::IPR_SIRR] & (ULL(1) << i)) { 526 // See table 4-19 of 21164 hardware reference 527 ipl = (i - TheISA::INTLEVEL_SOFTWARE_MIN) + 1; 528 summary |= (ULL(1) << i); 529 } 530 } 531 } 532 533 uint64_t interrupts = xc->cpu->intr_status(); 534 for (int i = TheISA::INTLEVEL_EXTERNAL_MIN; 535 i < TheISA::INTLEVEL_EXTERNAL_MAX; i++) { 536 if (interrupts & (ULL(1) << i)) { 537 // See table 4-19 of 21164 hardware reference 538 ipl = i; 539 summary |= (ULL(1) << i); 540 } 541 } 542 543 if (ipr[TheISA::IPR_ASTRR]) 544 panic("asynchronous traps not implemented\n"); 545 546 if (ipl && ipl > xc->regs.ipr[TheISA::IPR_IPLR]) { 547 ipr[TheISA::IPR_ISR] = summary; 548 ipr[TheISA::IPR_INTID] = ipl; 549 xc->ev5_trap(Interrupt_Fault); 550 551 DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", 552 ipr[TheISA::IPR_IPLR], ipl, summary); 553 } 554 } 555#endif 556 557 // maintain $r0 semantics 558 xc->regs.intRegFile[ZeroReg] = 0; 559#ifdef TARGET_ALPHA 560 xc->regs.floatRegFile.d[ZeroReg] = 0.0; 561#endif // TARGET_ALPHA 562 563 if (status() == IcacheMissComplete) { 564 // We've already fetched an instruction and were stalled on an 565 // I-cache miss. No need to fetch it again. 566 567 setStatus(Running); 568 } 569 else { 570 // Try to fetch an instruction 571 572 // set up memory request for instruction fetch 573#ifdef FULL_SYSTEM 574#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0 575#else 576#define IFETCH_FLAGS(pc) 0 577#endif 578 579 memReq->cmd = Read; 580 memReq->reset(xc->regs.pc & ~3, sizeof(uint32_t), 581 IFETCH_FLAGS(xc->regs.pc)); 582 583 fault = xc->translateInstReq(memReq); 584 585 if (fault == No_Fault) 586 fault = xc->mem->read(memReq, inst); 587 588 if (icacheInterface && fault == No_Fault) { 589 memReq->completionEvent = NULL; 590 591 memReq->time = curTick; 592 memReq->flags &= ~UNCACHEABLE; 593 MemAccessResult result = icacheInterface->access(memReq); 594 595 // Ugly hack to get an event scheduled *only* if the access is 596 // a miss. We really should add first-class support for this 597 // at some point. 598 if (result != MA_HIT && icacheInterface->doEvents) { 599 memReq->completionEvent = &cacheCompletionEvent; 600 setStatus(IcacheMissStall); 601 return; 602 } 603 } 604 } 605 606 // If we've got a valid instruction (i.e., no fault on instruction 607 // fetch), then execute it. 608 if (fault == No_Fault) { 609 610 // keep an instruction count 611 numInst++; 612 613 // check for instruction-count-based events 614 comInsnEventQueue[0]->serviceEvents(numInst); 615 616 // decode the instruction 617 StaticInstPtr<TheISA> si(inst); 618 619 traceData = Trace::getInstRecord(curTick, xc, this, si, 620 xc->regs.pc); 621 622#ifdef FULL_SYSTEM 623 xc->regs.opcode = (inst >> 26) & 0x3f; 624 xc->regs.ra = (inst >> 21) & 0x1f; 625#endif // FULL_SYSTEM 626 627 xc->func_exe_insn++; 628 629 fault = si->execute(this, xc, traceData); 630 631 if (si->isMemRef()) { 632 numMemRefs++; 633 } 634 635 if (si->isLoad()) { 636 ++numLoad; 637 comLoadEventQueue[0]->serviceEvents(numLoad); 638 } 639 640 if (traceData) 641 traceData->finalize(); 642 643 } // if (fault == No_Fault) 644 645 if (fault != No_Fault) { 646#ifdef FULL_SYSTEM 647 xc->ev5_trap(fault); 648#else // !FULL_SYSTEM 649 fatal("fault (%d) detected @ PC 0x%08p", fault, xc->regs.pc); 650#endif // FULL_SYSTEM 651 } 652 else { 653 // go to the next instruction 654 xc->regs.pc = xc->regs.npc; 655 xc->regs.npc += sizeof(MachInst); 656 } 657 658#ifdef FULL_SYSTEM 659 Addr oldpc; 660 do { 661 oldpc = xc->regs.pc; 662 system->pcEventQueue.service(xc); 663 } while (oldpc != xc->regs.pc); 664#endif 665 666 assert(status() == Running || 667 status() == Idle || 668 status() == DcacheMissStall); 669 670 if (status() == Running && !tickEvent.scheduled()) 671 tickEvent.schedule(curTick + 1); 672} 673 674 675//////////////////////////////////////////////////////////////////////// 676// 677// SimpleCPU Simulation Object 678// 679BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) 680 681 Param<Counter> max_insts_any_thread; 682 Param<Counter> max_insts_all_threads; 683 Param<Counter> max_loads_any_thread; 684 Param<Counter> max_loads_all_threads; 685 686#ifdef FULL_SYSTEM 687 SimObjectParam<AlphaItb *> itb; 688 SimObjectParam<AlphaDtb *> dtb; 689 SimObjectParam<FunctionalMemory *> mem; 690 SimObjectParam<System *> system; 691 Param<int> mult; 692#else 693 SimObjectParam<Process *> workload; 694#endif // FULL_SYSTEM 695 696 SimObjectParam<BaseMem *> icache; 697 SimObjectParam<BaseMem *> dcache; 698 699 Param<bool> defer_registration; 700 701END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) 702 703BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU) 704 705 INIT_PARAM_DFLT(max_insts_any_thread, 706 "terminate when any thread reaches this insn count", 707 0), 708 INIT_PARAM_DFLT(max_insts_all_threads, 709 "terminate when all threads have reached this insn count", 710 0), 711 INIT_PARAM_DFLT(max_loads_any_thread, 712 "terminate when any thread reaches this load count", 713 0), 714 INIT_PARAM_DFLT(max_loads_all_threads, 715 "terminate when all threads have reached this load count", 716 0), 717 718#ifdef FULL_SYSTEM 719 INIT_PARAM(itb, "Instruction TLB"), 720 INIT_PARAM(dtb, "Data TLB"), 721 INIT_PARAM(mem, "memory"), 722 INIT_PARAM(system, "system object"), 723 INIT_PARAM_DFLT(mult, "system clock multiplier", 1), 724#else 725 INIT_PARAM(workload, "processes to run"), 726#endif // FULL_SYSTEM 727 728 INIT_PARAM_DFLT(icache, "L1 instruction cache object", NULL), 729 INIT_PARAM_DFLT(dcache, "L1 data cache object", NULL), 730 INIT_PARAM_DFLT(defer_registration, "defer registration with system " 731 "(for sampling)", false) 732 733END_INIT_SIM_OBJECT_PARAMS(SimpleCPU) 734 735 736CREATE_SIM_OBJECT(SimpleCPU) 737{ 738 SimpleCPU *cpu; 739#ifdef FULL_SYSTEM 740 if (mult != 1) 741 panic("processor clock multiplier must be 1\n"); 742 743 cpu = new SimpleCPU(getInstanceName(), system, 744 max_insts_any_thread, max_insts_all_threads, 745 max_loads_any_thread, max_loads_all_threads, 746 itb, dtb, mem, 747 (icache) ? icache->getInterface() : NULL, 748 (dcache) ? dcache->getInterface() : NULL, 749 ticksPerSecond * mult); 750#else 751 752 cpu = new SimpleCPU(getInstanceName(), workload, 753 max_insts_any_thread, max_insts_all_threads, 754 max_loads_any_thread, max_loads_all_threads, 755 (icache) ? icache->getInterface() : NULL, 756 (dcache) ? dcache->getInterface() : NULL); 757 758#endif // FULL_SYSTEM 759 760 if (!defer_registration) { 761 cpu->registerExecContexts(); 762 } 763 764 return cpu; 765} 766 767REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU) 768