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.
39 #include "sim/host.hh"
40 #include "base/cprintf.hh"
41 #include "base/misc.hh"
42 #include "cpu/full_cpu/smt.hh"
44 #include "sim/annotation.hh"
45 #include "cpu/exec_context.hh"
46 #include "cpu/base_cpu.hh"
47 #include "sim/debug.hh"
48 #include "cpu/simple_cpu/simple_cpu.hh"
49 #include "base/inifile.hh"
50 #include "mem/mem_interface.hh"
51 #include "mem/base_mem.hh"
52 #include "cpu/static_inst.hh"
55 #include "mem/functional_mem/memory_control.hh"
56 #include "mem/functional_mem/physical_memory.hh"
57 #include "targetarch/alpha_memory.hh"
58 #include "sim/system.hh"
60 #include "mem/functional_mem/functional_memory.hh"
61 #include "sim/prog.hh"
65 #include "cpu/exetrace.hh"
66 #include "base/trace.hh"
67 #include "sim/sim_events.hh"
68 #include "base/pollevent.hh"
69 #include "sim/sim_object.hh"
70 #include "sim/sim_stats.hh"
72 #include "base/range.hh"
73 #include "base/loader/symtab.hh"
76 #include "targetarch/vtophys.hh"
77 #include "dev/pciareg.h"
78 #include "base/remote_gdb.hh"
79 #include "dev/alpha_access.h"
85 SimpleCPU::CacheCompletionEvent::CacheCompletionEvent(SimpleCPU
*_cpu
)
86 : Event(&mainEventQueue
),
91 void SimpleCPU::CacheCompletionEvent::process()
93 cpu
->processCacheCompletion();
97 SimpleCPU::CacheCompletionEvent::description()
99 return "cache completion event";
103 SimpleCPU::SimpleCPU(const string
&_name
,
105 Counter max_insts_any_thread
,
106 Counter max_insts_all_threads
,
107 Counter max_loads_any_thread
,
108 Counter max_loads_all_threads
,
109 AlphaItb
*itb
, AlphaDtb
*dtb
,
110 FunctionalMemory
*mem
,
111 MemInterface
*icache_interface
,
112 MemInterface
*dcache_interface
,
113 int cpu_id
, Tick freq
)
114 : BaseCPU(_name
, /* number_of_threads */ 1,
115 max_insts_any_thread
, max_insts_all_threads
,
116 max_loads_any_thread
, max_loads_all_threads
,
117 _system
, cpu_id
, freq
),
119 SimpleCPU::SimpleCPU(const string
&_name
, Process
*_process
,
120 Counter max_insts_any_thread
,
121 Counter max_insts_all_threads
,
122 Counter max_loads_any_thread
,
123 Counter max_loads_all_threads
,
124 MemInterface
*icache_interface
,
125 MemInterface
*dcache_interface
)
126 : BaseCPU(_name
, /* number_of_threads */ 1,
127 max_insts_any_thread
, max_insts_all_threads
,
128 max_loads_any_thread
, max_loads_all_threads
),
130 tickEvent(this), xc(NULL
), cacheCompletionEvent(this)
133 xc
= new ExecContext(this, 0, system
, itb
, dtb
, mem
, cpu_id
);
138 xc
->setStatus(ExecContext::Unallocated
);
140 //Open a GDB debug session on port (7000 + the cpu_id)
141 (new GDBListener(new RemoteGDB(system
, xc
), 7000 + cpu_id
))->listen();
143 AlphaISA::init(system
->physmem
, &xc
->regs
);
147 IntReg
*ipr
= xc
->regs
.ipr
;
148 ipr
[TheISA::IPR_MCSR
] = 0x6;
150 AlphaISA::swap_palshadow(&xc
->regs
, true);
153 ipr
[TheISA::IPR_PAL_BASE
] + AlphaISA::fault_addr
[fault
];
154 xc
->regs
.npc
= xc
->regs
.pc
+ sizeof(MachInst
);
163 AlphaISA::init(system
->physmem
, &xc
->regs
);
167 IntReg
*ipr
= xc
->regs
.ipr
;
168 ipr
[TheISA::IPR_MCSR
] = 0x6;
170 AlphaISA::swap_palshadow(&xc
->regs
, true);
172 xc
->regs
.pc
= ipr
[TheISA::IPR_PAL_BASE
] + AlphaISA::fault_addr
[fault
];
173 xc
->regs
.npc
= xc
->regs
.pc
+ sizeof(MachInst
);
176 tickEvent
.schedule(0);
180 xc
= new ExecContext(this, /* thread_num */ 0, _process
, /* asid */ 0);
182 if (xc
->status() == ExecContext::Active
) {
184 tickEvent
.schedule(0);
187 #endif // !FULL_SYSTEM
189 icacheInterface
= icache_interface
;
190 dcacheInterface
= dcache_interface
;
192 memReq
= new MemReq();
201 contexts
.push_back(xc
);
204 SimpleCPU::~SimpleCPU()
209 SimpleCPU::regStats()
214 .name(name() + ".num_insts")
215 .desc("Number of instructions executed")
219 .name(name() + ".num_refs")
220 .desc("Number of memory references")
224 .name(name() + ".idle_cycles")
225 .desc("Number of idle cycles")
229 .name(name() + ".idle_fraction")
230 .desc("Percentage of idle cycles")
234 .name(name() + ".icache_stall_cycles")
235 .desc("ICache total stall cycles")
236 .prereq(icacheStallCycles
)
240 .name(name() + ".dcache_stall_cycles")
241 .desc("DCache total stall cycles")
242 .prereq(dcacheStallCycles
)
245 idleFraction
= idleCycles
/ simTicks
;
247 numInsts
= Statistics::scalar(numInst
);
248 simInsts
+= numInsts
;
252 SimpleCPU::serialize()
258 // do we need this anymore?? egh
259 childOut("itb", xc
->itb
);
260 childOut("dtb", xc
->dtb
);
261 childOut("physmem", physmem
);
265 for (int i
= 0; i
< NumIntRegs
; i
++) {
267 ccprintf(buf
, "R%02d", i
);
268 paramOut(buf
.str(), xc
->regs
.intRegFile
[i
]);
270 for (int i
= 0; i
< NumFloatRegs
; i
++) {
272 ccprintf(buf
, "F%02d", i
);
273 paramOut(buf
.str(), xc
->regs
.floatRegFile
.d
[i
]);
275 // CPUTraitsType::serializeSpecialRegs(getProxy(), xc->regs);
279 SimpleCPU::unserialize(IniFile
&db
, const string
&category
, ConfigNode
*node
)
283 for (int i
= 0; i
< NumIntRegs
; i
++) {
285 ccprintf(buf
, "R%02d", i
);
286 db
.findDefault(category
, buf
.str(), data
);
287 to_number(data
,xc
->regs
.intRegFile
[i
]);
289 for (int i
= 0; i
< NumFloatRegs
; i
++) {
291 ccprintf(buf
, "F%02d", i
);
292 db
.findDefault(category
, buf
.str(), data
);
293 xc
->regs
.floatRegFile
.d
[i
] = strtod(data
.c_str(),NULL
);
296 // Read in Special registers
298 // CPUTraitsType::unserializeSpecialRegs(db,category,node,xc->regs);
302 change_thread_state(int thread_number
, int activate
, int priority
)
306 // precise architected memory state accessor macros
309 SimpleCPU::read(Addr addr
, T
& data
, unsigned flags
)
311 memReq
->reset(addr
, sizeof(T
), flags
);
313 // translate to physical address
314 Fault fault
= xc
->translateDataReadReq(memReq
);
316 // do functional access
317 if (fault
== No_Fault
)
318 fault
= xc
->read(memReq
, data
);
321 traceData
->setAddr(addr
);
322 if (fault
== No_Fault
)
323 traceData
->setData(data
);
326 // if we have a cache, do cache access too
327 if (fault
== No_Fault
&& dcacheInterface
) {
329 memReq
->completionEvent
= NULL
;
330 memReq
->time
= curTick
;
331 memReq
->flags
&= ~UNCACHEABLE
;
332 MemAccessResult result
= dcacheInterface
->access(memReq
);
334 // Ugly hack to get an event scheduled *only* if the access is
335 // a miss. We really should add first-class support for this
337 if (result
!= MA_HIT
&& dcacheInterface
->doEvents
) {
338 memReq
->completionEvent
= &cacheCompletionEvent
;
339 setStatus(DcacheMissStall
);
346 #ifndef DOXYGEN_SHOULD_SKIP_THIS
350 SimpleCPU::read(Addr addr
, uint64_t& data
, unsigned flags
);
354 SimpleCPU::read(Addr addr
, uint32_t& data
, unsigned flags
);
358 SimpleCPU::read(Addr addr
, uint16_t& data
, unsigned flags
);
362 SimpleCPU::read(Addr addr
, uint8_t& data
, unsigned flags
);
364 #endif //DOXYGEN_SHOULD_SKIP_THIS
368 SimpleCPU::read(Addr addr
, double& data
, unsigned flags
)
370 return read(addr
, *(uint64_t*)&data
, flags
);
375 SimpleCPU::read(Addr addr
, float& data
, unsigned flags
)
377 return read(addr
, *(uint32_t*)&data
, flags
);
383 SimpleCPU::read(Addr addr
, int32_t& data
, unsigned flags
)
385 return read(addr
, (uint32_t&)data
, flags
);
391 SimpleCPU::write(T data
, Addr addr
, unsigned flags
, uint64_t *res
)
394 traceData
->setAddr(addr
);
395 traceData
->setData(data
);
398 memReq
->reset(addr
, sizeof(T
), flags
);
400 // translate to physical address
401 Fault fault
= xc
->translateDataWriteReq(memReq
);
403 // do functional access
404 if (fault
== No_Fault
)
405 fault
= xc
->write(memReq
, data
);
407 if (fault
== No_Fault
&& dcacheInterface
) {
409 memReq
->data
= (uint8_t *)&data
;
410 memReq
->completionEvent
= NULL
;
411 memReq
->time
= curTick
;
412 memReq
->flags
&= ~UNCACHEABLE
;
413 MemAccessResult result
= dcacheInterface
->access(memReq
);
415 // Ugly hack to get an event scheduled *only* if the access is
416 // a miss. We really should add first-class support for this
418 if (result
!= MA_HIT
&& dcacheInterface
->doEvents
) {
419 memReq
->completionEvent
= &cacheCompletionEvent
;
420 setStatus(DcacheMissStall
);
424 if (res
&& (fault
== No_Fault
))
425 *res
= memReq
->result
;
431 #ifndef DOXYGEN_SHOULD_SKIP_THIS
434 SimpleCPU::write(uint64_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
438 SimpleCPU::write(uint32_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
442 SimpleCPU::write(uint16_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
446 SimpleCPU::write(uint8_t data
, Addr addr
, unsigned flags
, uint64_t *res
);
448 #endif //DOXYGEN_SHOULD_SKIP_THIS
452 SimpleCPU::write(double data
, Addr addr
, unsigned flags
, uint64_t *res
)
454 return write(*(uint64_t*)&data
, addr
, flags
, res
);
459 SimpleCPU::write(float data
, Addr addr
, unsigned flags
, uint64_t *res
)
461 return write(*(uint32_t*)&data
, addr
, flags
, res
);
467 SimpleCPU::write(int32_t data
, Addr addr
, unsigned flags
, uint64_t *res
)
469 return write((uint32_t)data
, addr
, flags
, res
);
475 SimpleCPU::dbg_vtophys(Addr addr
)
477 return vtophys(xc
, addr
);
479 #endif // FULL_SYSTEM
485 SimpleCPU::processCacheCompletion()
488 case IcacheMissStall
:
489 icacheStallCycles
+= curTick
- lastIcacheStall
;
490 setStatus(IcacheMissComplete
);
492 case DcacheMissStall
:
493 dcacheStallCycles
+= curTick
- lastDcacheStall
;
497 panic("SimpleCPU::processCacheCompletion: bad state");
504 SimpleCPU::post_interrupt(int int_num
, int index
)
506 BaseCPU::post_interrupt(int_num
, index
);
508 if (xc
->status() == ExecContext::Suspended
) {
509 DPRINTF(IPI
,"Suspended Processor awoke\n");
510 xc
->setStatus(ExecContext::Active
);
511 Annotate::Resume(xc
);
514 #endif // FULL_SYSTEM
516 /* start simulation, program loaded, processor precise state initialized */
523 if (fault
== No_Fault
&& AlphaISA::check_interrupts
&&
524 xc
->cpu
->check_interrupts() &&
525 !PC_PAL(xc
->regs
.pc
) &&
526 status() != IcacheMissComplete
) {
529 AlphaISA::check_interrupts
= 0;
530 IntReg
*ipr
= xc
->regs
.ipr
;
532 if (xc
->regs
.ipr
[TheISA::IPR_SIRR
]) {
533 for (int i
= TheISA::INTLEVEL_SOFTWARE_MIN
;
534 i
< TheISA::INTLEVEL_SOFTWARE_MAX
; i
++) {
535 if (ipr
[TheISA::IPR_SIRR
] & (ULL(1) << i
)) {
536 // See table 4-19 of 21164 hardware reference
537 ipl
= (i
- TheISA::INTLEVEL_SOFTWARE_MIN
) + 1;
538 summary
|= (ULL(1) << i
);
543 uint64_t interrupts
= xc
->cpu
->intr_status();
544 for (int i
= TheISA::INTLEVEL_EXTERNAL_MIN
;
545 i
< TheISA::INTLEVEL_EXTERNAL_MAX
; i
++) {
546 if (interrupts
& (ULL(1) << i
)) {
547 // See table 4-19 of 21164 hardware reference
549 summary
|= (ULL(1) << i
);
553 if (ipr
[TheISA::IPR_ASTRR
])
554 panic("asynchronous traps not implemented\n");
556 if (ipl
&& ipl
> xc
->regs
.ipr
[TheISA::IPR_IPLR
]) {
557 ipr
[TheISA::IPR_ISR
] = summary
;
558 ipr
[TheISA::IPR_INTID
] = ipl
;
559 xc
->ev5_trap(Interrupt_Fault
);
561 DPRINTF(Flow
, "Interrupt! IPLR=%d ipl=%d summary=%x\n",
562 ipr
[TheISA::IPR_IPLR
], ipl
, summary
);
567 // maintain $r0 semantics
568 xc
->regs
.intRegFile
[ZeroReg
] = 0;
570 xc
->regs
.floatRegFile
.d
[ZeroReg
] = 0.0;
571 #endif // TARGET_ALPHA
573 if (status() == IcacheMissComplete
) {
574 // We've already fetched an instruction and were stalled on an
575 // I-cache miss. No need to fetch it again.
580 // Try to fetch an instruction
582 // set up memory request for instruction fetch
584 #define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0
586 #define IFETCH_FLAGS(pc) 0
590 memReq
->reset(xc
->regs
.pc
& ~3, sizeof(uint32_t),
591 IFETCH_FLAGS(xc
->regs
.pc
));
593 fault
= xc
->translateInstReq(memReq
);
595 if (fault
== No_Fault
)
596 fault
= xc
->mem
->read(memReq
, inst
);
598 if (icacheInterface
&& fault
== No_Fault
) {
599 memReq
->completionEvent
= NULL
;
601 memReq
->time
= curTick
;
602 memReq
->flags
&= ~UNCACHEABLE
;
603 MemAccessResult result
= icacheInterface
->access(memReq
);
605 // Ugly hack to get an event scheduled *only* if the access is
606 // a miss. We really should add first-class support for this
608 if (result
!= MA_HIT
&& icacheInterface
->doEvents
) {
609 memReq
->completionEvent
= &cacheCompletionEvent
;
610 setStatus(IcacheMissStall
);
616 // If we've got a valid instruction (i.e., no fault on instruction
617 // fetch), then execute it.
618 if (fault
== No_Fault
) {
620 // keep an instruction count
623 // check for instruction-count-based events
624 comInsnEventQueue
[0]->serviceEvents(numInst
);
626 // decode the instruction
627 StaticInstPtr
<TheISA
> si(inst
);
629 traceData
= Trace::getInstRecord(curTick
, xc
, this, si
,
633 xc
->regs
.opcode
= (inst
>> 26) & 0x3f;
634 xc
->regs
.ra
= (inst
>> 21) & 0x1f;
635 #endif // FULL_SYSTEM
639 fault
= si
->execute(this, xc
, traceData
);
641 if (si
->isMemRef()) {
647 comLoadEventQueue
[0]->serviceEvents(numLoad
);
651 traceData
->finalize();
653 } // if (fault == No_Fault)
655 if (fault
!= No_Fault
) {
658 #else // !FULL_SYSTEM
659 fatal("fault (%d) detected @ PC 0x%08p", fault
, xc
->regs
.pc
);
660 #endif // FULL_SYSTEM
663 // go to the next instruction
664 xc
->regs
.pc
= xc
->regs
.npc
;
665 xc
->regs
.npc
+= sizeof(MachInst
);
672 system
->pcEventQueue
.service(xc
);
673 } while (oldpc
!= xc
->regs
.pc
);
676 assert(status() == Running
||
678 status() == DcacheMissStall
);
680 if (status() == Running
&& !tickEvent
.scheduled())
681 tickEvent
.schedule(curTick
+ 1);
685 ////////////////////////////////////////////////////////////////////////
687 // SimpleCPU Simulation Object
689 BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU
)
691 Param
<Counter
> max_insts_any_thread
;
692 Param
<Counter
> max_insts_all_threads
;
693 Param
<Counter
> max_loads_any_thread
;
694 Param
<Counter
> max_loads_all_threads
;
697 SimObjectParam
<AlphaItb
*> itb
;
698 SimObjectParam
<AlphaDtb
*> dtb
;
699 SimObjectParam
<FunctionalMemory
*> mem
;
700 SimObjectParam
<System
*> system
;
704 SimObjectParam
<Process
*> workload
;
705 #endif // FULL_SYSTEM
707 SimObjectParam
<BaseMem
*> icache
;
708 SimObjectParam
<BaseMem
*> dcache
;
710 END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU
)
712 BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU
)
714 INIT_PARAM_DFLT(max_insts_any_thread
,
715 "terminate when any thread reaches this insn count",
717 INIT_PARAM_DFLT(max_insts_all_threads
,
718 "terminate when all threads have reached this insn count",
720 INIT_PARAM_DFLT(max_loads_any_thread
,
721 "terminate when any thread reaches this load count",
723 INIT_PARAM_DFLT(max_loads_all_threads
,
724 "terminate when all threads have reached this load count",
728 INIT_PARAM(itb
, "Instruction TLB"),
729 INIT_PARAM(dtb
, "Data TLB"),
730 INIT_PARAM(mem
, "memory"),
731 INIT_PARAM(system
, "system object"),
732 INIT_PARAM_DFLT(cpu_id
, "CPU identification number", 0),
733 INIT_PARAM_DFLT(mult
, "system clock multiplier", 1),
735 INIT_PARAM(workload
, "processes to run"),
736 #endif // FULL_SYSTEM
738 INIT_PARAM_DFLT(icache
, "L1 instruction cache object", NULL
),
739 INIT_PARAM_DFLT(dcache
, "L1 data cache object", NULL
)
741 END_INIT_SIM_OBJECT_PARAMS(SimpleCPU
)
744 CREATE_SIM_OBJECT(SimpleCPU
)
748 panic("processor clock multiplier must be 1\n");
750 return new SimpleCPU(getInstanceName(), system
,
751 max_insts_any_thread
, max_insts_all_threads
,
752 max_loads_any_thread
, max_loads_all_threads
,
754 (icache
) ? icache
->getInterface() : NULL
,
755 (dcache
) ? dcache
->getInterface() : NULL
,
756 cpu_id
, ticksPerSecond
* mult
);
759 return new SimpleCPU(getInstanceName(), workload
,
760 max_insts_any_thread
, max_insts_all_threads
,
761 max_loads_any_thread
, max_loads_all_threads
,
762 icache
->getInterface(), dcache
->getInterface());
764 #endif // FULL_SYSTEM
767 REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU
)