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