2 * Copyright (c) 2003 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 "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"
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"
73 #include "sim/prog.hh"
78 SimpleCPU::TickEvent::TickEvent(SimpleCPU
*c
)
79 : Event(&mainEventQueue
, 100), cpu(c
)
84 SimpleCPU::TickEvent::process()
90 SimpleCPU::TickEvent::description()
92 return "SimpleCPU tick event";
96 SimpleCPU::CacheCompletionEvent::CacheCompletionEvent(SimpleCPU
*_cpu
)
97 : Event(&mainEventQueue
),
102 void SimpleCPU::CacheCompletionEvent::process()
104 cpu
->processCacheCompletion();
108 SimpleCPU::CacheCompletionEvent::description()
110 return "SimpleCPU cache completion event";
114 SimpleCPU::SimpleCPU(const string
&_name
,
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
,
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
,
130 SimpleCPU::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
),
141 tickEvent(this), xc(NULL
), cacheCompletionEvent(this)
145 xc
= new ExecContext(this, 0, system
, itb
, dtb
, mem
);
147 // initialize CPU, including PC
148 TheISA::initCPU(&xc
->regs
);
150 xc
= new ExecContext(this, /* thread_num */ 0, _process
, /* asid */ 0);
151 #endif // !FULL_SYSTEM
153 icacheInterface
= icache_interface
;
154 dcacheInterface
= dcache_interface
;
156 memReq
= new MemReq();
159 memReq
->data
= new uint8_t[64];
168 execContexts
.push_back(xc
);
171 SimpleCPU::~SimpleCPU()
176 SimpleCPU::switchOut()
178 _status
= SwitchedOut
;
179 if (tickEvent
.scheduled())
185 SimpleCPU::takeOverFrom(BaseCPU
*oldCPU
)
187 BaseCPU::takeOverFrom(oldCPU
);
189 assert(!tickEvent
.scheduled());
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
) {
197 tickEvent
.schedule(curTick
);
206 SimpleCPU::execCtxStatusChg(int thread_num
) {
207 assert(thread_num
== 0);
210 if (xc
->status() == ExecContext::Active
)
218 SimpleCPU::regStats()
220 using namespace Statistics
;
225 .name(name() + ".num_insts")
226 .desc("Number of instructions executed")
230 .name(name() + ".num_refs")
231 .desc("Number of memory references")
235 .name(name() + ".idle_fraction")
236 .desc("Percentage of idle cycles")
240 .name(name() + ".icache_stall_cycles")
241 .desc("ICache total stall cycles")
242 .prereq(icacheStallCycles
)
246 .name(name() + ".dcache_stall_cycles")
247 .desc("DCache total stall cycles")
248 .prereq(dcacheStallCycles
)
251 numInsts
= Statistics::scalar(numInst
) - Statistics::scalar(startNumInst
);
252 simInsts
+= numInsts
;
256 SimpleCPU::resetStats()
258 startNumInst
= numInst
;
262 SimpleCPU::serialize(ostream
&os
)
264 SERIALIZE_ENUM(_status
);
265 SERIALIZE_SCALAR(inst
);
266 nameOut(os
, csprintf("%s.xc", name()));
268 nameOut(os
, csprintf("%s.tickEvent", name()));
269 tickEvent
.serialize(os
);
270 nameOut(os
, csprintf("%s.cacheCompletionEvent", name()));
271 cacheCompletionEvent
.serialize(os
);
275 SimpleCPU::unserialize(Checkpoint
*cp
, const string
§ion
)
277 UNSERIALIZE_ENUM(_status
);
278 UNSERIALIZE_SCALAR(inst
);
279 xc
->unserialize(cp
, csprintf("%s.xc", section
));
280 tickEvent
.unserialize(cp
, csprintf("%s.tickEvent", section
));
282 .unserialize(cp
, csprintf("%s.cacheCompletionEvent", section
));
286 change_thread_state(int thread_number
, int activate
, int priority
)
290 // precise architected memory state accessor macros
293 SimpleCPU::read(Addr addr
, T
& data
, unsigned flags
)
295 memReq
->reset(addr
, sizeof(T
), flags
);
297 // translate to physical address
298 Fault fault
= xc
->translateDataReadReq(memReq
);
300 // do functional access
301 if (fault
== No_Fault
)
302 fault
= xc
->read(memReq
, data
);
305 traceData
->setAddr(addr
);
306 if (fault
== No_Fault
)
307 traceData
->setData(data
);
310 // if we have a cache, do cache access too
311 if (fault
== No_Fault
&& dcacheInterface
) {
313 memReq
->completionEvent
= NULL
;
314 memReq
->time
= curTick
;
315 memReq
->flags
&= ~UNCACHEABLE
;
316 MemAccessResult result
= dcacheInterface
->access(memReq
);
318 // Ugly hack to get an event scheduled *only* if the access is
319 // a miss. We really should add first-class support for this
321 if (result
!= MA_HIT
&& dcacheInterface
->doEvents
) {
322 memReq
->completionEvent
= &cacheCompletionEvent
;
323 setStatus(DcacheMissStall
);
330 #ifndef DOXYGEN_SHOULD_SKIP_THIS
334 SimpleCPU::read(Addr addr
, uint64_t& data
, unsigned flags
);
338 SimpleCPU::read(Addr addr
, uint32_t& data
, unsigned flags
);
342 SimpleCPU::read(Addr addr
, uint16_t& data
, unsigned flags
);
346 SimpleCPU::read(Addr addr
, uint8_t& data
, unsigned flags
);
348 #endif //DOXYGEN_SHOULD_SKIP_THIS
352 SimpleCPU::read(Addr addr
, double& data
, unsigned flags
)
354 return read(addr
, *(uint64_t*)&data
, flags
);
359 SimpleCPU::read(Addr addr
, float& data
, unsigned flags
)
361 return read(addr
, *(uint32_t*)&data
, flags
);
367 SimpleCPU::read(Addr addr
, int32_t& data
, unsigned flags
)
369 return read(addr
, (uint32_t&)data
, flags
);
375 SimpleCPU::write(T data
, Addr addr
, unsigned flags
, uint64_t *res
)
378 traceData
->setAddr(addr
);
379 traceData
->setData(data
);
382 memReq
->reset(addr
, sizeof(T
), flags
);
384 // translate to physical address
385 Fault fault
= xc
->translateDataWriteReq(memReq
);
387 // do functional access
388 if (fault
== No_Fault
)
389 fault
= xc
->write(memReq
, data
);
391 if (fault
== No_Fault
&& dcacheInterface
) {
393 memcpy(memReq
->data
,(uint8_t *)&data
,memReq
->size
);
394 memReq
->completionEvent
= NULL
;
395 memReq
->time
= curTick
;
396 memReq
->flags
&= ~UNCACHEABLE
;
397 MemAccessResult result
= dcacheInterface
->access(memReq
);
399 // Ugly hack to get an event scheduled *only* if the access is
400 // a miss. We really should add first-class support for this
402 if (result
!= MA_HIT
&& dcacheInterface
->doEvents
) {
403 memReq
->completionEvent
= &cacheCompletionEvent
;
404 setStatus(DcacheMissStall
);
408 if (res
&& (fault
== No_Fault
))
409 *res
= memReq
->result
;
415 #ifndef DOXYGEN_SHOULD_SKIP_THIS
418 SimpleCPU::write(uint64_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
422 SimpleCPU::write(uint32_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
426 SimpleCPU::write(uint16_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
430 SimpleCPU::write(uint8_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
432 #endif //DOXYGEN_SHOULD_SKIP_THIS
436 SimpleCPU::write(double data
, Addr addr
, unsigned flags
, uint64_t *res
)
438 return write(*(uint64_t*)&data
, addr
, flags
, res
);
443 SimpleCPU::write(float data
, Addr addr
, unsigned flags
, uint64_t *res
)
445 return write(*(uint32_t*)&data
, addr
, flags
, res
);
451 SimpleCPU::write(int32_t data
, Addr addr
, unsigned flags
, uint64_t *res
)
453 return write((uint32_t)data
, addr
, flags
, res
);
459 SimpleCPU::dbg_vtophys(Addr addr
)
461 return vtophys(xc
, addr
);
463 #endif // FULL_SYSTEM
469 SimpleCPU::processCacheCompletion()
472 case IcacheMissStall
:
473 icacheStallCycles
+= curTick
- lastIcacheStall
;
474 setStatus(IcacheMissComplete
);
476 case DcacheMissStall
:
477 dcacheStallCycles
+= curTick
- lastDcacheStall
;
481 // If this CPU has been switched out due to sampling/warm-up,
482 // ignore any further status changes (e.g., due to cache
483 // misses outstanding at the time of the switch).
486 panic("SimpleCPU::processCacheCompletion: bad state");
493 SimpleCPU::post_interrupt(int int_num
, int index
)
495 BaseCPU::post_interrupt(int_num
, index
);
497 if (xc
->status() == ExecContext::Suspended
) {
498 DPRINTF(IPI
,"Suspended Processor awoke\n");
499 xc
->setStatus(ExecContext::Active
);
500 Annotate::Resume(xc
);
503 #endif // FULL_SYSTEM
505 /* start simulation, program loaded, processor precise state initialized */
511 Fault fault
= No_Fault
;
514 if (AlphaISA::check_interrupts
&&
515 xc
->cpu
->check_interrupts() &&
516 !PC_PAL(xc
->regs
.pc
) &&
517 status() != IcacheMissComplete
) {
520 AlphaISA::check_interrupts
= 0;
521 IntReg
*ipr
= xc
->regs
.ipr
;
523 if (xc
->regs
.ipr
[TheISA::IPR_SIRR
]) {
524 for (int i
= TheISA::INTLEVEL_SOFTWARE_MIN
;
525 i
< TheISA::INTLEVEL_SOFTWARE_MAX
; i
++) {
526 if (ipr
[TheISA::IPR_SIRR
] & (ULL(1) << i
)) {
527 // See table 4-19 of 21164 hardware reference
528 ipl
= (i
- TheISA::INTLEVEL_SOFTWARE_MIN
) + 1;
529 summary
|= (ULL(1) << i
);
534 uint64_t interrupts
= xc
->cpu
->intr_status();
535 for (int i
= TheISA::INTLEVEL_EXTERNAL_MIN
;
536 i
< TheISA::INTLEVEL_EXTERNAL_MAX
; i
++) {
537 if (interrupts
& (ULL(1) << i
)) {
538 // See table 4-19 of 21164 hardware reference
540 summary
|= (ULL(1) << i
);
544 if (ipr
[TheISA::IPR_ASTRR
])
545 panic("asynchronous traps not implemented\n");
547 if (ipl
&& ipl
> xc
->regs
.ipr
[TheISA::IPR_IPLR
]) {
548 ipr
[TheISA::IPR_ISR
] = summary
;
549 ipr
[TheISA::IPR_INTID
] = ipl
;
550 xc
->ev5_trap(Interrupt_Fault
);
552 DPRINTF(Flow
, "Interrupt! IPLR=%d ipl=%d summary=%x\n",
553 ipr
[TheISA::IPR_IPLR
], ipl
, summary
);
558 // maintain $r0 semantics
559 xc
->regs
.intRegFile
[ZeroReg
] = 0;
561 xc
->regs
.floatRegFile
.d
[ZeroReg
] = 0.0;
562 #endif // TARGET_ALPHA
564 if (status() == IcacheMissComplete
) {
565 // We've already fetched an instruction and were stalled on an
566 // I-cache miss. No need to fetch it again.
571 // Try to fetch an instruction
573 // set up memory request for instruction fetch
575 #define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0
577 #define IFETCH_FLAGS(pc) 0
581 memReq
->reset(xc
->regs
.pc
& ~3, sizeof(uint32_t),
582 IFETCH_FLAGS(xc
->regs
.pc
));
584 fault
= xc
->translateInstReq(memReq
);
586 if (fault
== No_Fault
)
587 fault
= xc
->mem
->read(memReq
, inst
);
589 if (icacheInterface
&& fault
== No_Fault
) {
590 memReq
->completionEvent
= NULL
;
592 memReq
->time
= curTick
;
593 memReq
->flags
&= ~UNCACHEABLE
;
594 MemAccessResult result
= icacheInterface
->access(memReq
);
596 // Ugly hack to get an event scheduled *only* if the access is
597 // a miss. We really should add first-class support for this
599 if (result
!= MA_HIT
&& icacheInterface
->doEvents
) {
600 memReq
->completionEvent
= &cacheCompletionEvent
;
601 setStatus(IcacheMissStall
);
607 // If we've got a valid instruction (i.e., no fault on instruction
608 // fetch), then execute it.
609 if (fault
== No_Fault
) {
611 // keep an instruction count
614 // check for instruction-count-based events
615 comInsnEventQueue
[0]->serviceEvents(numInst
);
617 // decode the instruction
618 StaticInstPtr
<TheISA
> si(inst
);
620 traceData
= Trace::getInstRecord(curTick
, xc
, this, si
,
624 xc
->regs
.opcode
= (inst
>> 26) & 0x3f;
625 xc
->regs
.ra
= (inst
>> 21) & 0x1f;
626 #endif // FULL_SYSTEM
630 fault
= si
->execute(this, xc
, traceData
);
632 if (!(xc
->misspeculating()) && (xc
->system
->bin
)) {
633 SWContext
*ctx
= xc
->swCtx
;
634 if (ctx
&& !ctx
->callStack
.empty()) {
638 if (si
->isReturn()) {
639 if (ctx
->calls
== 0) {
640 fnCall
*top
= ctx
->callStack
.top();
641 DPRINTF(TCPIP
, "Removing %s from callstack.\n", top
->name
);
643 ctx
->callStack
.pop();
644 if (ctx
->callStack
.empty())
645 xc
->system
->nonPath
->activate();
647 ctx
->callStack
.top()->myBin
->activate();
649 xc
->system
->dumpState(xc
);
657 if (si
->isMemRef()) {
663 comLoadEventQueue
[0]->serviceEvents(numLoad
);
667 traceData
->finalize();
669 } // if (fault == No_Fault)
671 if (fault
!= No_Fault
) {
674 #else // !FULL_SYSTEM
675 fatal("fault (%d) detected @ PC 0x%08p", fault
, xc
->regs
.pc
);
676 #endif // FULL_SYSTEM
679 // go to the next instruction
680 xc
->regs
.pc
= xc
->regs
.npc
;
681 xc
->regs
.npc
+= sizeof(MachInst
);
688 system
->pcEventQueue
.service(xc
);
689 } while (oldpc
!= xc
->regs
.pc
);
692 assert(status() == Running
||
694 status() == DcacheMissStall
);
696 if (status() == Running
&& !tickEvent
.scheduled())
697 tickEvent
.schedule(curTick
+ 1);
701 ////////////////////////////////////////////////////////////////////////
703 // SimpleCPU Simulation Object
705 BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU
)
707 Param
<Counter
> max_insts_any_thread
;
708 Param
<Counter
> max_insts_all_threads
;
709 Param
<Counter
> max_loads_any_thread
;
710 Param
<Counter
> max_loads_all_threads
;
713 SimObjectParam
<AlphaItb
*> itb
;
714 SimObjectParam
<AlphaDtb
*> dtb
;
715 SimObjectParam
<FunctionalMemory
*> mem
;
716 SimObjectParam
<System
*> system
;
719 SimObjectParam
<Process
*> workload
;
720 #endif // FULL_SYSTEM
722 SimObjectParam
<BaseMem
*> icache
;
723 SimObjectParam
<BaseMem
*> dcache
;
725 Param
<bool> defer_registration
;
727 END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU
)
729 BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU
)
731 INIT_PARAM_DFLT(max_insts_any_thread
,
732 "terminate when any thread reaches this insn count",
734 INIT_PARAM_DFLT(max_insts_all_threads
,
735 "terminate when all threads have reached this insn count",
737 INIT_PARAM_DFLT(max_loads_any_thread
,
738 "terminate when any thread reaches this load count",
740 INIT_PARAM_DFLT(max_loads_all_threads
,
741 "terminate when all threads have reached this load count",
745 INIT_PARAM(itb
, "Instruction TLB"),
746 INIT_PARAM(dtb
, "Data TLB"),
747 INIT_PARAM(mem
, "memory"),
748 INIT_PARAM(system
, "system object"),
749 INIT_PARAM_DFLT(mult
, "system clock multiplier", 1),
751 INIT_PARAM(workload
, "processes to run"),
752 #endif // FULL_SYSTEM
754 INIT_PARAM_DFLT(icache
, "L1 instruction cache object", NULL
),
755 INIT_PARAM_DFLT(dcache
, "L1 data cache object", NULL
),
756 INIT_PARAM_DFLT(defer_registration
, "defer registration with system "
757 "(for sampling)", false)
759 END_INIT_SIM_OBJECT_PARAMS(SimpleCPU
)
762 CREATE_SIM_OBJECT(SimpleCPU
)
767 panic("processor clock multiplier must be 1\n");
769 cpu
= new SimpleCPU(getInstanceName(), system
,
770 max_insts_any_thread
, max_insts_all_threads
,
771 max_loads_any_thread
, max_loads_all_threads
,
773 (icache
) ? icache
->getInterface() : NULL
,
774 (dcache
) ? dcache
->getInterface() : NULL
,
775 ticksPerSecond
* mult
);
778 cpu
= new SimpleCPU(getInstanceName(), workload
,
779 max_insts_any_thread
, max_insts_all_threads
,
780 max_loads_any_thread
, max_loads_all_threads
,
781 (icache
) ? icache
->getInterface() : NULL
,
782 (dcache
) ? dcache
->getInterface() : NULL
);
784 #endif // FULL_SYSTEM
786 if (!defer_registration
) {
787 cpu
->registerExecContexts();
793 REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU
)