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"
77 SimpleCPU::TickEvent::TickEvent(SimpleCPU
*c
)
78 : Event(&mainEventQueue
, CPU_Tick_Pri
), cpu(c
)
83 SimpleCPU::TickEvent::process()
89 SimpleCPU::TickEvent::description()
91 return "SimpleCPU tick event";
95 SimpleCPU::CacheCompletionEvent::CacheCompletionEvent(SimpleCPU
*_cpu
)
96 : Event(&mainEventQueue
),
101 void SimpleCPU::CacheCompletionEvent::process()
103 cpu
->processCacheCompletion();
107 SimpleCPU::CacheCompletionEvent::description()
109 return "SimpleCPU cache completion event";
113 SimpleCPU::SimpleCPU(const string
&_name
,
115 Counter max_insts_any_thread
,
116 Counter max_insts_all_threads
,
117 Counter max_loads_any_thread
,
118 Counter max_loads_all_threads
,
119 AlphaITB
*itb
, AlphaDTB
*dtb
,
120 FunctionalMemory
*mem
,
121 MemInterface
*icache_interface
,
122 MemInterface
*dcache_interface
,
123 bool _def_reg
, Tick freq
)
124 : BaseCPU(_name
, /* number_of_threads */ 1,
125 max_insts_any_thread
, max_insts_all_threads
,
126 max_loads_any_thread
, max_loads_all_threads
,
129 SimpleCPU::SimpleCPU(const string
&_name
, Process
*_process
,
130 Counter max_insts_any_thread
,
131 Counter max_insts_all_threads
,
132 Counter max_loads_any_thread
,
133 Counter max_loads_all_threads
,
134 MemInterface
*icache_interface
,
135 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
), defer_registration(_def_reg
),
142 cacheCompletionEvent(this)
146 xc
= new ExecContext(this, 0, system
, itb
, dtb
, mem
);
148 // initialize CPU, including PC
149 TheISA::initCPU(&xc
->regs
);
151 xc
= new ExecContext(this, /* thread_num */ 0, _process
, /* asid */ 0);
152 #endif // !FULL_SYSTEM
154 icacheInterface
= icache_interface
;
155 dcacheInterface
= dcache_interface
;
157 memReq
= new MemReq();
160 memReq
->data
= new uint8_t[64];
169 execContexts
.push_back(xc
);
172 SimpleCPU::~SimpleCPU()
176 void SimpleCPU::init()
178 if (!defer_registration
) {
179 this->registerExecContexts();
184 SimpleCPU::switchOut()
186 _status
= SwitchedOut
;
187 if (tickEvent
.scheduled())
193 SimpleCPU::takeOverFrom(BaseCPU
*oldCPU
)
195 BaseCPU::takeOverFrom(oldCPU
);
197 assert(!tickEvent
.scheduled());
199 // if any of this CPU's ExecContexts are active, mark the CPU as
200 // running and schedule its tick event.
201 for (int i
= 0; i
< execContexts
.size(); ++i
) {
202 ExecContext
*xc
= execContexts
[i
];
203 if (xc
->status() == ExecContext::Active
&& _status
!= Running
) {
205 tickEvent
.schedule(curTick
);
214 SimpleCPU::activateContext(int thread_num
, int delay
)
216 assert(thread_num
== 0);
219 assert(_status
== Idle
);
221 scheduleTickEvent(delay
);
227 SimpleCPU::suspendContext(int thread_num
)
229 assert(thread_num
== 0);
232 assert(_status
== Running
);
234 unscheduleTickEvent();
240 SimpleCPU::deallocateContext(int thread_num
)
242 // for now, these are equivalent
243 suspendContext(thread_num
);
248 SimpleCPU::haltContext(int thread_num
)
250 // for now, these are equivalent
251 suspendContext(thread_num
);
256 SimpleCPU::regStats()
258 using namespace Stats
;
263 .name(name() + ".num_insts")
264 .desc("Number of instructions executed")
268 .name(name() + ".num_refs")
269 .desc("Number of memory references")
273 .name(name() + ".idle_fraction")
274 .desc("Percentage of idle cycles")
278 .name(name() + ".icache_stall_cycles")
279 .desc("ICache total stall cycles")
280 .prereq(icacheStallCycles
)
284 .name(name() + ".dcache_stall_cycles")
285 .desc("DCache total stall cycles")
286 .prereq(dcacheStallCycles
)
289 idleFraction
= constant(1.0) - notIdleFraction
;
293 SimpleCPU::resetStats()
295 startNumInst
= numInst
;
296 notIdleFraction
= (_status
!= Idle
);
300 SimpleCPU::serialize(ostream
&os
)
302 SERIALIZE_ENUM(_status
);
303 SERIALIZE_SCALAR(inst
);
304 nameOut(os
, csprintf("%s.xc", name()));
306 nameOut(os
, csprintf("%s.tickEvent", name()));
307 tickEvent
.serialize(os
);
308 nameOut(os
, csprintf("%s.cacheCompletionEvent", name()));
309 cacheCompletionEvent
.serialize(os
);
313 SimpleCPU::unserialize(Checkpoint
*cp
, const string
§ion
)
315 UNSERIALIZE_ENUM(_status
);
316 UNSERIALIZE_SCALAR(inst
);
317 xc
->unserialize(cp
, csprintf("%s.xc", section
));
318 tickEvent
.unserialize(cp
, csprintf("%s.tickEvent", section
));
320 .unserialize(cp
, csprintf("%s.cacheCompletionEvent", section
));
324 change_thread_state(int thread_number
, int activate
, int priority
)
329 SimpleCPU::copySrcTranslate(Addr src
)
331 memReq
->reset(src
, (dcacheInterface
) ?
332 dcacheInterface
->getBlockSize()
335 // translate to physical address
336 Fault fault
= xc
->translateDataReadReq(memReq
);
338 if (fault
== No_Fault
) {
339 xc
->copySrcAddr
= src
;
340 xc
->copySrcPhysAddr
= memReq
->paddr
;
343 xc
->copySrcPhysAddr
= 0;
349 SimpleCPU::copy(Addr dest
)
351 int blk_size
= (dcacheInterface
) ? dcacheInterface
->getBlockSize() : 64;
352 uint8_t data
[blk_size
];
353 assert(xc
->copySrcPhysAddr
);
354 memReq
->reset(dest
, blk_size
);
355 // translate to physical address
356 Fault fault
= xc
->translateDataWriteReq(memReq
);
357 if (fault
== No_Fault
) {
358 Addr dest_addr
= memReq
->paddr
;
359 // Need to read straight from memory since we have more than 8 bytes.
360 memReq
->paddr
= xc
->copySrcPhysAddr
;
361 xc
->mem
->read(memReq
, data
);
362 memReq
->paddr
= dest_addr
;
363 xc
->mem
->write(memReq
, data
);
368 // precise architected memory state accessor macros
371 SimpleCPU::read(Addr addr
, T
&data
, unsigned flags
)
373 memReq
->reset(addr
, sizeof(T
), flags
);
375 // translate to physical address
376 Fault fault
= xc
->translateDataReadReq(memReq
);
378 // do functional access
379 if (fault
== No_Fault
)
380 fault
= xc
->read(memReq
, data
);
383 traceData
->setAddr(addr
);
384 if (fault
== No_Fault
)
385 traceData
->setData(data
);
388 // if we have a cache, do cache access too
389 if (fault
== No_Fault
&& dcacheInterface
) {
391 memReq
->completionEvent
= NULL
;
392 memReq
->time
= curTick
;
393 MemAccessResult result
= dcacheInterface
->access(memReq
);
395 // Ugly hack to get an event scheduled *only* if the access is
396 // a miss. We really should add first-class support for this
398 if (result
!= MA_HIT
&& dcacheInterface
->doEvents()) {
399 memReq
->completionEvent
= &cacheCompletionEvent
;
400 lastDcacheStall
= curTick
;
401 unscheduleTickEvent();
402 _status
= DcacheMissStall
;
406 if (!dcacheInterface
&& (memReq
->flags
& UNCACHEABLE
))
407 Stats::recordEvent("Uncached Read");
412 #ifndef DOXYGEN_SHOULD_SKIP_THIS
416 SimpleCPU::read(Addr addr
, uint64_t &data
, unsigned flags
);
420 SimpleCPU::read(Addr addr
, uint32_t &data
, unsigned flags
);
424 SimpleCPU::read(Addr addr
, uint16_t &data
, unsigned flags
);
428 SimpleCPU::read(Addr addr
, uint8_t &data
, unsigned flags
);
430 #endif //DOXYGEN_SHOULD_SKIP_THIS
434 SimpleCPU::read(Addr addr
, double &data
, unsigned flags
)
436 return read(addr
, *(uint64_t*)&data
, flags
);
441 SimpleCPU::read(Addr addr
, float &data
, unsigned flags
)
443 return read(addr
, *(uint32_t*)&data
, flags
);
449 SimpleCPU::read(Addr addr
, int32_t &data
, unsigned flags
)
451 return read(addr
, (uint32_t&)data
, flags
);
457 SimpleCPU::write(T data
, Addr addr
, unsigned flags
, uint64_t *res
)
460 traceData
->setAddr(addr
);
461 traceData
->setData(data
);
464 memReq
->reset(addr
, sizeof(T
), flags
);
466 // translate to physical address
467 Fault fault
= xc
->translateDataWriteReq(memReq
);
469 // do functional access
470 if (fault
== No_Fault
)
471 fault
= xc
->write(memReq
, data
);
473 if (fault
== No_Fault
&& dcacheInterface
) {
475 memcpy(memReq
->data
,(uint8_t *)&data
,memReq
->size
);
476 memReq
->completionEvent
= NULL
;
477 memReq
->time
= curTick
;
478 MemAccessResult result
= dcacheInterface
->access(memReq
);
480 // Ugly hack to get an event scheduled *only* if the access is
481 // a miss. We really should add first-class support for this
483 if (result
!= MA_HIT
&& dcacheInterface
->doEvents()) {
484 memReq
->completionEvent
= &cacheCompletionEvent
;
485 lastDcacheStall
= curTick
;
486 unscheduleTickEvent();
487 _status
= DcacheMissStall
;
491 if (res
&& (fault
== No_Fault
))
492 *res
= memReq
->result
;
494 if (!dcacheInterface
&& (memReq
->flags
& UNCACHEABLE
))
495 Stats::recordEvent("Uncached Write");
501 #ifndef DOXYGEN_SHOULD_SKIP_THIS
504 SimpleCPU::write(uint64_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
508 SimpleCPU::write(uint32_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
512 SimpleCPU::write(uint16_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
516 SimpleCPU::write(uint8_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
518 #endif //DOXYGEN_SHOULD_SKIP_THIS
522 SimpleCPU::write(double data
, Addr addr
, unsigned flags
, uint64_t *res
)
524 return write(*(uint64_t*)&data
, addr
, flags
, res
);
529 SimpleCPU::write(float data
, Addr addr
, unsigned flags
, uint64_t *res
)
531 return write(*(uint32_t*)&data
, addr
, flags
, res
);
537 SimpleCPU::write(int32_t data
, Addr addr
, unsigned flags
, uint64_t *res
)
539 return write((uint32_t)data
, addr
, flags
, res
);
545 SimpleCPU::dbg_vtophys(Addr addr
)
547 return vtophys(xc
, addr
);
549 #endif // FULL_SYSTEM
555 SimpleCPU::processCacheCompletion()
558 case IcacheMissStall
:
559 icacheStallCycles
+= curTick
- lastIcacheStall
;
560 _status
= IcacheMissComplete
;
561 scheduleTickEvent(1);
563 case DcacheMissStall
:
564 dcacheStallCycles
+= curTick
- lastDcacheStall
;
566 scheduleTickEvent(1);
569 // If this CPU has been switched out due to sampling/warm-up,
570 // ignore any further status changes (e.g., due to cache
571 // misses outstanding at the time of the switch).
574 panic("SimpleCPU::processCacheCompletion: bad state");
581 SimpleCPU::post_interrupt(int int_num
, int index
)
583 BaseCPU::post_interrupt(int_num
, index
);
585 if (xc
->status() == ExecContext::Suspended
) {
586 DPRINTF(IPI
,"Suspended Processor awoke\n");
590 #endif // FULL_SYSTEM
592 /* start simulation, program loaded, processor precise state initialized */
600 Fault fault
= No_Fault
;
603 if (AlphaISA::check_interrupts
&&
604 xc
->cpu
->check_interrupts() &&
605 !PC_PAL(xc
->regs
.pc
) &&
606 status() != IcacheMissComplete
) {
609 AlphaISA::check_interrupts
= 0;
610 IntReg
*ipr
= xc
->regs
.ipr
;
612 if (xc
->regs
.ipr
[TheISA::IPR_SIRR
]) {
613 for (int i
= TheISA::INTLEVEL_SOFTWARE_MIN
;
614 i
< TheISA::INTLEVEL_SOFTWARE_MAX
; i
++) {
615 if (ipr
[TheISA::IPR_SIRR
] & (ULL(1) << i
)) {
616 // See table 4-19 of 21164 hardware reference
617 ipl
= (i
- TheISA::INTLEVEL_SOFTWARE_MIN
) + 1;
618 summary
|= (ULL(1) << i
);
623 uint64_t interrupts
= xc
->cpu
->intr_status();
624 for (int i
= TheISA::INTLEVEL_EXTERNAL_MIN
;
625 i
< TheISA::INTLEVEL_EXTERNAL_MAX
; i
++) {
626 if (interrupts
& (ULL(1) << i
)) {
627 // See table 4-19 of 21164 hardware reference
629 summary
|= (ULL(1) << i
);
633 if (ipr
[TheISA::IPR_ASTRR
])
634 panic("asynchronous traps not implemented\n");
636 if (ipl
&& ipl
> xc
->regs
.ipr
[TheISA::IPR_IPLR
]) {
637 ipr
[TheISA::IPR_ISR
] = summary
;
638 ipr
[TheISA::IPR_INTID
] = ipl
;
639 xc
->ev5_trap(Interrupt_Fault
);
641 DPRINTF(Flow
, "Interrupt! IPLR=%d ipl=%d summary=%x\n",
642 ipr
[TheISA::IPR_IPLR
], ipl
, summary
);
647 // maintain $r0 semantics
648 xc
->regs
.intRegFile
[ZeroReg
] = 0;
650 xc
->regs
.floatRegFile
.d
[ZeroReg
] = 0.0;
651 #endif // TARGET_ALPHA
653 if (status() == IcacheMissComplete
) {
654 // We've already fetched an instruction and were stalled on an
655 // I-cache miss. No need to fetch it again.
657 // Set status to running; tick event will get rescheduled if
658 // necessary at end of tick() function.
662 // Try to fetch an instruction
664 // set up memory request for instruction fetch
666 #define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0
668 #define IFETCH_FLAGS(pc) 0
672 memReq
->reset(xc
->regs
.pc
& ~3, sizeof(uint32_t),
673 IFETCH_FLAGS(xc
->regs
.pc
));
675 fault
= xc
->translateInstReq(memReq
);
677 if (fault
== No_Fault
)
678 fault
= xc
->mem
->read(memReq
, inst
);
680 if (icacheInterface
&& fault
== No_Fault
) {
681 memReq
->completionEvent
= NULL
;
683 memReq
->time
= curTick
;
684 MemAccessResult result
= icacheInterface
->access(memReq
);
686 // Ugly hack to get an event scheduled *only* if the access is
687 // a miss. We really should add first-class support for this
689 if (result
!= MA_HIT
&& icacheInterface
->doEvents()) {
690 memReq
->completionEvent
= &cacheCompletionEvent
;
691 lastIcacheStall
= curTick
;
692 unscheduleTickEvent();
693 _status
= IcacheMissStall
;
699 // If we've got a valid instruction (i.e., no fault on instruction
700 // fetch), then execute it.
701 if (fault
== No_Fault
) {
703 // keep an instruction count
707 // check for instruction-count-based events
708 comInstEventQueue
[0]->serviceEvents(numInst
);
710 // decode the instruction
711 StaticInstPtr
<TheISA
> si(inst
);
713 traceData
= Trace::getInstRecord(curTick
, xc
, this, si
,
718 #endif // FULL_SYSTEM
722 fault
= si
->execute(this, traceData
);
725 SWContext
*ctx
= xc
->swCtx
;
727 ctx
->process(xc
, si
.get());
730 if (si
->isMemRef()) {
736 comLoadEventQueue
[0]->serviceEvents(numLoad
);
740 traceData
->finalize();
742 } // if (fault == No_Fault)
744 if (fault
!= No_Fault
) {
747 #else // !FULL_SYSTEM
748 fatal("fault (%d) detected @ PC 0x%08p", fault
, xc
->regs
.pc
);
749 #endif // FULL_SYSTEM
752 // go to the next instruction
753 xc
->regs
.pc
= xc
->regs
.npc
;
754 xc
->regs
.npc
+= sizeof(MachInst
);
761 system
->pcEventQueue
.service(xc
);
762 } while (oldpc
!= xc
->regs
.pc
);
765 assert(status() == Running
||
767 status() == DcacheMissStall
);
769 if (status() == Running
&& !tickEvent
.scheduled())
770 tickEvent
.schedule(curTick
+ 1);
774 ////////////////////////////////////////////////////////////////////////
776 // SimpleCPU Simulation Object
778 BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU
)
780 Param
<Counter
> max_insts_any_thread
;
781 Param
<Counter
> max_insts_all_threads
;
782 Param
<Counter
> max_loads_any_thread
;
783 Param
<Counter
> max_loads_all_threads
;
786 SimObjectParam
<AlphaITB
*> itb
;
787 SimObjectParam
<AlphaDTB
*> dtb
;
788 SimObjectParam
<FunctionalMemory
*> mem
;
789 SimObjectParam
<System
*> system
;
792 SimObjectParam
<Process
*> workload
;
793 #endif // FULL_SYSTEM
795 SimObjectParam
<BaseMem
*> icache
;
796 SimObjectParam
<BaseMem
*> dcache
;
798 Param
<bool> defer_registration
;
800 END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU
)
802 BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU
)
804 INIT_PARAM_DFLT(max_insts_any_thread
,
805 "terminate when any thread reaches this inst count",
807 INIT_PARAM_DFLT(max_insts_all_threads
,
808 "terminate when all threads have reached this inst count",
810 INIT_PARAM_DFLT(max_loads_any_thread
,
811 "terminate when any thread reaches this load count",
813 INIT_PARAM_DFLT(max_loads_all_threads
,
814 "terminate when all threads have reached this load count",
818 INIT_PARAM(itb
, "Instruction TLB"),
819 INIT_PARAM(dtb
, "Data TLB"),
820 INIT_PARAM(mem
, "memory"),
821 INIT_PARAM(system
, "system object"),
822 INIT_PARAM_DFLT(mult
, "system clock multiplier", 1),
824 INIT_PARAM(workload
, "processes to run"),
825 #endif // FULL_SYSTEM
827 INIT_PARAM_DFLT(icache
, "L1 instruction cache object", NULL
),
828 INIT_PARAM_DFLT(dcache
, "L1 data cache object", NULL
),
829 INIT_PARAM_DFLT(defer_registration
, "defer registration with system "
830 "(for sampling)", false)
832 END_INIT_SIM_OBJECT_PARAMS(SimpleCPU
)
835 CREATE_SIM_OBJECT(SimpleCPU
)
840 panic("processor clock multiplier must be 1\n");
842 cpu
= new SimpleCPU(getInstanceName(), system
,
843 max_insts_any_thread
, max_insts_all_threads
,
844 max_loads_any_thread
, max_loads_all_threads
,
846 (icache
) ? icache
->getInterface() : NULL
,
847 (dcache
) ? dcache
->getInterface() : NULL
,
849 ticksPerSecond
* mult
);
852 cpu
= new SimpleCPU(getInstanceName(), workload
,
853 max_insts_any_thread
, max_insts_all_threads
,
854 max_loads_any_thread
, max_loads_all_threads
,
855 (icache
) ? icache
->getInterface() : NULL
,
856 (dcache
) ? dcache
->getInterface() : NULL
,
859 #endif // FULL_SYSTEM
864 REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU
)