2 * Copyright (c) 2002-2004 The Regents of The University of Michigan
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.
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.
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 "base/stats/events.hh"
46 #include "cpu/base_cpu.hh"
47 #include "cpu/exec_context.hh"
48 #include "cpu/exetrace.hh"
49 #include "cpu/full_cpu/smt.hh"
50 #include "cpu/simple_cpu/simple_cpu.hh"
51 #include "cpu/static_inst.hh"
52 #include "mem/base_mem.hh"
53 #include "mem/mem_interface.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/stats.hh"
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"
72 #include "mem/functional_mem/functional_memory.hh"
78 SimpleCPU::TickEvent::TickEvent(SimpleCPU
*c
, int w
)
79 : Event(&mainEventQueue
, CPU_Tick_Pri
), cpu(c
), width(w
)
84 SimpleCPU::TickEvent::process()
89 } while (--count
> 0 && cpu
->status() == Running
);
93 SimpleCPU::TickEvent::description()
95 return "SimpleCPU tick event";
99 SimpleCPU::CacheCompletionEvent::CacheCompletionEvent(SimpleCPU
*_cpu
)
100 : Event(&mainEventQueue
), cpu(_cpu
)
104 void SimpleCPU::CacheCompletionEvent::process()
106 cpu
->processCacheCompletion();
110 SimpleCPU::CacheCompletionEvent::description()
112 return "SimpleCPU cache completion event";
115 SimpleCPU::SimpleCPU(Params
*p
)
116 : BaseCPU(p
), tickEvent(this, p
->width
), xc(NULL
),
117 cacheCompletionEvent(this)
121 xc
= new ExecContext(this, 0, p
->system
, p
->itb
, p
->dtb
, p
->mem
);
123 // initialize CPU, including PC
124 TheISA::initCPU(&xc
->regs
);
126 xc
= new ExecContext(this, /* thread_num */ 0, p
->process
, /* asid */ 0);
127 #endif // !FULL_SYSTEM
129 icacheInterface
= p
->icache_interface
;
130 dcacheInterface
= p
->dcache_interface
;
132 memReq
= new MemReq();
135 memReq
->data
= new uint8_t[64];
144 execContexts
.push_back(xc
);
147 SimpleCPU::~SimpleCPU()
152 SimpleCPU::switchOut()
154 _status
= SwitchedOut
;
155 if (tickEvent
.scheduled())
161 SimpleCPU::takeOverFrom(BaseCPU
*oldCPU
)
163 BaseCPU::takeOverFrom(oldCPU
);
165 assert(!tickEvent
.scheduled());
167 // if any of this CPU's ExecContexts are active, mark the CPU as
168 // running and schedule its tick event.
169 for (int i
= 0; i
< execContexts
.size(); ++i
) {
170 ExecContext
*xc
= execContexts
[i
];
171 if (xc
->status() == ExecContext::Active
&& _status
!= Running
) {
173 tickEvent
.schedule(curTick
);
182 SimpleCPU::activateContext(int thread_num
, int delay
)
184 assert(thread_num
== 0);
187 assert(_status
== Idle
);
189 scheduleTickEvent(delay
);
195 SimpleCPU::suspendContext(int thread_num
)
197 assert(thread_num
== 0);
200 assert(_status
== Running
);
202 unscheduleTickEvent();
208 SimpleCPU::deallocateContext(int thread_num
)
210 // for now, these are equivalent
211 suspendContext(thread_num
);
216 SimpleCPU::haltContext(int thread_num
)
218 // for now, these are equivalent
219 suspendContext(thread_num
);
224 SimpleCPU::regStats()
226 using namespace Stats
;
231 .name(name() + ".num_insts")
232 .desc("Number of instructions executed")
236 .name(name() + ".num_refs")
237 .desc("Number of memory references")
241 .name(name() + ".not_idle_fraction")
242 .desc("Percentage of non-idle cycles")
246 .name(name() + ".idle_fraction")
247 .desc("Percentage of idle cycles")
251 .name(name() + ".icache_stall_cycles")
252 .desc("ICache total stall cycles")
253 .prereq(icacheStallCycles
)
257 .name(name() + ".dcache_stall_cycles")
258 .desc("DCache total stall cycles")
259 .prereq(dcacheStallCycles
)
262 idleFraction
= constant(1.0) - notIdleFraction
;
266 SimpleCPU::resetStats()
268 startNumInst
= numInst
;
269 notIdleFraction
= (_status
!= Idle
);
273 SimpleCPU::serialize(ostream
&os
)
275 BaseCPU::serialize(os
);
276 SERIALIZE_ENUM(_status
);
277 SERIALIZE_SCALAR(inst
);
278 nameOut(os
, csprintf("%s.xc", name()));
280 nameOut(os
, csprintf("%s.tickEvent", name()));
281 tickEvent
.serialize(os
);
282 nameOut(os
, csprintf("%s.cacheCompletionEvent", name()));
283 cacheCompletionEvent
.serialize(os
);
287 SimpleCPU::unserialize(Checkpoint
*cp
, const string
§ion
)
289 BaseCPU::unserialize(cp
, section
);
290 UNSERIALIZE_ENUM(_status
);
291 UNSERIALIZE_SCALAR(inst
);
292 xc
->unserialize(cp
, csprintf("%s.xc", section
));
293 tickEvent
.unserialize(cp
, csprintf("%s.tickEvent", section
));
295 .unserialize(cp
, csprintf("%s.cacheCompletionEvent", section
));
299 change_thread_state(int thread_number
, int activate
, int priority
)
304 SimpleCPU::copySrcTranslate(Addr src
)
306 static bool no_warn
= true;
307 int blk_size
= (dcacheInterface
) ? dcacheInterface
->getBlockSize() : 64;
308 // Only support block sizes of 64 atm.
309 assert(blk_size
== 64);
310 int offset
= src
& (blk_size
- 1);
312 // Make sure block doesn't span page
314 (src
& TheISA::PageMask
) != ((src
+ blk_size
) & TheISA::PageMask
) &&
315 (src
>> 40) != 0xfffffc) {
316 warn("Copied block source spans pages %x.", src
);
320 memReq
->reset(src
& ~(blk_size
- 1), blk_size
);
322 // translate to physical address
323 Fault fault
= xc
->translateDataReadReq(memReq
);
325 assert(fault
!= Alignment_Fault
);
327 if (fault
== No_Fault
) {
328 xc
->copySrcAddr
= src
;
329 xc
->copySrcPhysAddr
= memReq
->paddr
+ offset
;
332 xc
->copySrcPhysAddr
= 0;
338 SimpleCPU::copy(Addr dest
)
340 static bool no_warn
= true;
341 int blk_size
= (dcacheInterface
) ? dcacheInterface
->getBlockSize() : 64;
342 // Only support block sizes of 64 atm.
343 assert(blk_size
== 64);
344 uint8_t data
[blk_size
];
345 //assert(xc->copySrcAddr);
346 int offset
= dest
& (blk_size
- 1);
348 // Make sure block doesn't span page
350 (dest
& TheISA::PageMask
) != ((dest
+ blk_size
) & TheISA::PageMask
) &&
351 (dest
>> 40) != 0xfffffc) {
353 warn("Copied block destination spans pages %x. ", dest
);
356 memReq
->reset(dest
& ~(blk_size
-1), blk_size
);
357 // translate to physical address
358 Fault fault
= xc
->translateDataWriteReq(memReq
);
360 assert(fault
!= Alignment_Fault
);
362 if (fault
== No_Fault
) {
363 Addr dest_addr
= memReq
->paddr
+ offset
;
364 // Need to read straight from memory since we have more than 8 bytes.
365 memReq
->paddr
= xc
->copySrcPhysAddr
;
366 xc
->mem
->read(memReq
, data
);
367 memReq
->paddr
= dest_addr
;
368 xc
->mem
->write(memReq
, data
);
369 if (dcacheInterface
) {
371 memReq
->completionEvent
= NULL
;
372 memReq
->paddr
= xc
->copySrcPhysAddr
;
373 memReq
->dest
= dest_addr
;
375 memReq
->time
= curTick
;
376 dcacheInterface
->access(memReq
);
382 // precise architected memory state accessor macros
385 SimpleCPU::read(Addr addr
, T
&data
, unsigned flags
)
387 memReq
->reset(addr
, sizeof(T
), flags
);
389 // translate to physical address
390 Fault fault
= xc
->translateDataReadReq(memReq
);
392 // do functional access
393 if (fault
== No_Fault
)
394 fault
= xc
->read(memReq
, data
);
397 traceData
->setAddr(addr
);
398 if (fault
== No_Fault
)
399 traceData
->setData(data
);
402 // if we have a cache, do cache access too
403 if (fault
== No_Fault
&& dcacheInterface
) {
405 memReq
->completionEvent
= NULL
;
406 memReq
->time
= curTick
;
407 MemAccessResult result
= dcacheInterface
->access(memReq
);
409 // Ugly hack to get an event scheduled *only* if the access is
410 // a miss. We really should add first-class support for this
412 if (result
!= MA_HIT
&& dcacheInterface
->doEvents()) {
413 memReq
->completionEvent
= &cacheCompletionEvent
;
414 lastDcacheStall
= curTick
;
415 unscheduleTickEvent();
416 _status
= DcacheMissStall
;
420 if (!dcacheInterface
&& (memReq
->flags
& UNCACHEABLE
))
421 recordEvent("Uncached Read");
426 #ifndef DOXYGEN_SHOULD_SKIP_THIS
430 SimpleCPU::read(Addr addr
, uint64_t &data
, unsigned flags
);
434 SimpleCPU::read(Addr addr
, uint32_t &data
, unsigned flags
);
438 SimpleCPU::read(Addr addr
, uint16_t &data
, unsigned flags
);
442 SimpleCPU::read(Addr addr
, uint8_t &data
, unsigned flags
);
444 #endif //DOXYGEN_SHOULD_SKIP_THIS
448 SimpleCPU::read(Addr addr
, double &data
, unsigned flags
)
450 return read(addr
, *(uint64_t*)&data
, flags
);
455 SimpleCPU::read(Addr addr
, float &data
, unsigned flags
)
457 return read(addr
, *(uint32_t*)&data
, flags
);
463 SimpleCPU::read(Addr addr
, int32_t &data
, unsigned flags
)
465 return read(addr
, (uint32_t&)data
, flags
);
471 SimpleCPU::write(T data
, Addr addr
, unsigned flags
, uint64_t *res
)
474 traceData
->setAddr(addr
);
475 traceData
->setData(data
);
478 memReq
->reset(addr
, sizeof(T
), flags
);
480 // translate to physical address
481 Fault fault
= xc
->translateDataWriteReq(memReq
);
483 // do functional access
484 if (fault
== No_Fault
)
485 fault
= xc
->write(memReq
, data
);
487 if (fault
== No_Fault
&& dcacheInterface
) {
489 memcpy(memReq
->data
,(uint8_t *)&data
,memReq
->size
);
490 memReq
->completionEvent
= NULL
;
491 memReq
->time
= curTick
;
492 MemAccessResult result
= dcacheInterface
->access(memReq
);
494 // Ugly hack to get an event scheduled *only* if the access is
495 // a miss. We really should add first-class support for this
497 if (result
!= MA_HIT
&& dcacheInterface
->doEvents()) {
498 memReq
->completionEvent
= &cacheCompletionEvent
;
499 lastDcacheStall
= curTick
;
500 unscheduleTickEvent();
501 _status
= DcacheMissStall
;
505 if (res
&& (fault
== No_Fault
))
506 *res
= memReq
->result
;
508 if (!dcacheInterface
&& (memReq
->flags
& UNCACHEABLE
))
509 recordEvent("Uncached Write");
515 #ifndef DOXYGEN_SHOULD_SKIP_THIS
518 SimpleCPU::write(uint64_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
522 SimpleCPU::write(uint32_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
526 SimpleCPU::write(uint16_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
530 SimpleCPU::write(uint8_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
532 #endif //DOXYGEN_SHOULD_SKIP_THIS
536 SimpleCPU::write(double data
, Addr addr
, unsigned flags
, uint64_t *res
)
538 return write(*(uint64_t*)&data
, addr
, flags
, res
);
543 SimpleCPU::write(float data
, Addr addr
, unsigned flags
, uint64_t *res
)
545 return write(*(uint32_t*)&data
, addr
, flags
, res
);
551 SimpleCPU::write(int32_t data
, Addr addr
, unsigned flags
, uint64_t *res
)
553 return write((uint32_t)data
, addr
, flags
, res
);
559 SimpleCPU::dbg_vtophys(Addr addr
)
561 return vtophys(xc
, addr
);
563 #endif // FULL_SYSTEM
569 SimpleCPU::processCacheCompletion()
572 case IcacheMissStall
:
573 icacheStallCycles
+= curTick
- lastIcacheStall
;
574 _status
= IcacheMissComplete
;
575 scheduleTickEvent(1);
577 case DcacheMissStall
:
578 dcacheStallCycles
+= curTick
- lastDcacheStall
;
580 scheduleTickEvent(1);
583 // If this CPU has been switched out due to sampling/warm-up,
584 // ignore any further status changes (e.g., due to cache
585 // misses outstanding at the time of the switch).
588 panic("SimpleCPU::processCacheCompletion: bad state");
595 SimpleCPU::post_interrupt(int int_num
, int index
)
597 BaseCPU::post_interrupt(int_num
, index
);
599 if (xc
->status() == ExecContext::Suspended
) {
600 DPRINTF(IPI
,"Suspended Processor awoke\n");
604 #endif // FULL_SYSTEM
606 /* start simulation, program loaded, processor precise state initialized */
614 Fault fault
= No_Fault
;
617 if (checkInterrupts
&& check_interrupts() && !xc
->inPalMode() &&
618 status() != IcacheMissComplete
) {
621 checkInterrupts
= false;
622 IntReg
*ipr
= xc
->regs
.ipr
;
624 if (xc
->regs
.ipr
[TheISA::IPR_SIRR
]) {
625 for (int i
= TheISA::INTLEVEL_SOFTWARE_MIN
;
626 i
< TheISA::INTLEVEL_SOFTWARE_MAX
; i
++) {
627 if (ipr
[TheISA::IPR_SIRR
] & (ULL(1) << i
)) {
628 // See table 4-19 of 21164 hardware reference
629 ipl
= (i
- TheISA::INTLEVEL_SOFTWARE_MIN
) + 1;
630 summary
|= (ULL(1) << i
);
635 uint64_t interrupts
= xc
->cpu
->intr_status();
636 for (int i
= TheISA::INTLEVEL_EXTERNAL_MIN
;
637 i
< TheISA::INTLEVEL_EXTERNAL_MAX
; i
++) {
638 if (interrupts
& (ULL(1) << i
)) {
639 // See table 4-19 of 21164 hardware reference
641 summary
|= (ULL(1) << i
);
645 if (ipr
[TheISA::IPR_ASTRR
])
646 panic("asynchronous traps not implemented\n");
648 if (ipl
&& ipl
> xc
->regs
.ipr
[TheISA::IPR_IPLR
]) {
649 ipr
[TheISA::IPR_ISR
] = summary
;
650 ipr
[TheISA::IPR_INTID
] = ipl
;
651 xc
->ev5_trap(Interrupt_Fault
);
653 DPRINTF(Flow
, "Interrupt! IPLR=%d ipl=%d summary=%x\n",
654 ipr
[TheISA::IPR_IPLR
], ipl
, summary
);
659 // maintain $r0 semantics
660 xc
->regs
.intRegFile
[ZeroReg
] = 0;
662 xc
->regs
.floatRegFile
.d
[ZeroReg
] = 0.0;
663 #endif // TARGET_ALPHA
665 if (status() == IcacheMissComplete
) {
666 // We've already fetched an instruction and were stalled on an
667 // I-cache miss. No need to fetch it again.
669 // Set status to running; tick event will get rescheduled if
670 // necessary at end of tick() function.
674 // Try to fetch an instruction
676 // set up memory request for instruction fetch
678 #define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0
680 #define IFETCH_FLAGS(pc) 0
684 memReq
->reset(xc
->regs
.pc
& ~3, sizeof(uint32_t),
685 IFETCH_FLAGS(xc
->regs
.pc
));
687 fault
= xc
->translateInstReq(memReq
);
689 if (fault
== No_Fault
)
690 fault
= xc
->mem
->read(memReq
, inst
);
692 if (icacheInterface
&& fault
== No_Fault
) {
693 memReq
->completionEvent
= NULL
;
695 memReq
->time
= curTick
;
696 MemAccessResult result
= icacheInterface
->access(memReq
);
698 // Ugly hack to get an event scheduled *only* if the access is
699 // a miss. We really should add first-class support for this
701 if (result
!= MA_HIT
&& icacheInterface
->doEvents()) {
702 memReq
->completionEvent
= &cacheCompletionEvent
;
703 lastIcacheStall
= curTick
;
704 unscheduleTickEvent();
705 _status
= IcacheMissStall
;
711 // If we've got a valid instruction (i.e., no fault on instruction
712 // fetch), then execute it.
713 if (fault
== No_Fault
) {
715 // keep an instruction count
719 // check for instruction-count-based events
720 comInstEventQueue
[0]->serviceEvents(numInst
);
722 // decode the instruction
724 StaticInstPtr
<TheISA
> si(inst
);
726 traceData
= Trace::getInstRecord(curTick
, xc
, this, si
,
731 #endif // FULL_SYSTEM
735 fault
= si
->execute(this, traceData
);
739 xc
->execute(si
.get());
742 if (si
->isMemRef()) {
748 comLoadEventQueue
[0]->serviceEvents(numLoad
);
752 traceData
->finalize();
754 traceFunctions(xc
->regs
.pc
);
756 } // if (fault == No_Fault)
758 if (fault
!= No_Fault
) {
761 #else // !FULL_SYSTEM
762 fatal("fault (%d) detected @ PC 0x%08p", fault
, xc
->regs
.pc
);
763 #endif // FULL_SYSTEM
766 // go to the next instruction
767 xc
->regs
.pc
= xc
->regs
.npc
;
768 xc
->regs
.npc
+= sizeof(MachInst
);
775 system
->pcEventQueue
.service(xc
);
776 } while (oldpc
!= xc
->regs
.pc
);
779 assert(status() == Running
||
781 status() == DcacheMissStall
);
783 if (status() == Running
&& !tickEvent
.scheduled())
784 tickEvent
.schedule(curTick
+ 1);
788 ////////////////////////////////////////////////////////////////////////
790 // SimpleCPU Simulation Object
792 BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU
)
794 Param
<Counter
> max_insts_any_thread
;
795 Param
<Counter
> max_insts_all_threads
;
796 Param
<Counter
> max_loads_any_thread
;
797 Param
<Counter
> max_loads_all_threads
;
800 SimObjectParam
<AlphaITB
*> itb
;
801 SimObjectParam
<AlphaDTB
*> dtb
;
802 SimObjectParam
<FunctionalMemory
*> mem
;
803 SimObjectParam
<System
*> system
;
806 SimObjectParam
<Process
*> workload
;
807 #endif // FULL_SYSTEM
809 SimObjectParam
<BaseMem
*> icache
;
810 SimObjectParam
<BaseMem
*> dcache
;
812 Param
<bool> defer_registration
;
814 Param
<bool> function_trace
;
815 Param
<Tick
> function_trace_start
;
817 END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU
)
819 BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU
)
821 INIT_PARAM(max_insts_any_thread
,
822 "terminate when any thread reaches this inst count"),
823 INIT_PARAM(max_insts_all_threads
,
824 "terminate when all threads have reached this inst count"),
825 INIT_PARAM(max_loads_any_thread
,
826 "terminate when any thread reaches this load count"),
827 INIT_PARAM(max_loads_all_threads
,
828 "terminate when all threads have reached this load count"),
831 INIT_PARAM(itb
, "Instruction TLB"),
832 INIT_PARAM(dtb
, "Data TLB"),
833 INIT_PARAM(mem
, "memory"),
834 INIT_PARAM(system
, "system object"),
835 INIT_PARAM(mult
, "system clock multiplier"),
837 INIT_PARAM(workload
, "processes to run"),
838 #endif // FULL_SYSTEM
840 INIT_PARAM(icache
, "L1 instruction cache object"),
841 INIT_PARAM(dcache
, "L1 data cache object"),
842 INIT_PARAM(defer_registration
, "defer system registration (for sampling)"),
843 INIT_PARAM(width
, "cpu width"),
844 INIT_PARAM(function_trace
, "Enable function trace"),
845 INIT_PARAM(function_trace_start
, "Cycle to start function trace")
847 END_INIT_SIM_OBJECT_PARAMS(SimpleCPU
)
850 CREATE_SIM_OBJECT(SimpleCPU
)
854 panic("processor clock multiplier must be 1\n");
857 SimpleCPU::Params
*params
= new SimpleCPU::Params();
858 params
->name
= getInstanceName();
859 params
->numberOfThreads
= 1;
860 params
->max_insts_any_thread
= max_insts_any_thread
;
861 params
->max_insts_all_threads
= max_insts_all_threads
;
862 params
->max_loads_any_thread
= max_loads_any_thread
;
863 params
->max_loads_all_threads
= max_loads_all_threads
;
864 params
->deferRegistration
= defer_registration
;
865 params
->freq
= ticksPerSecond
;
866 params
->functionTrace
= function_trace
;
867 params
->functionTraceStart
= function_trace_start
;
868 params
->icache_interface
= (icache
) ? icache
->getInterface() : NULL
;
869 params
->dcache_interface
= (dcache
) ? dcache
->getInterface() : NULL
;
870 params
->width
= width
;
876 params
->system
= system
;
878 params
->process
= workload
;
881 SimpleCPU
*cpu
= new SimpleCPU(params
);
885 REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU
)