2 * Copyright (c) 2002-2005 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.
28 * Authors: Steve Reinhardt
31 #include "arch/locked_mem.hh"
32 #include "arch/mmaped_ipr.hh"
33 #include "arch/utility.hh"
34 #include "base/bigint.hh"
35 #include "cpu/exetrace.hh"
36 #include "cpu/simple/atomic.hh"
37 #include "mem/packet.hh"
38 #include "mem/packet_access.hh"
39 #include "sim/builder.hh"
40 #include "sim/system.hh"
43 using namespace TheISA
;
45 AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU
*c
)
46 : Event(&mainEventQueue
, CPU_Tick_Pri
), cpu(c
)
52 AtomicSimpleCPU::TickEvent::process()
58 AtomicSimpleCPU::TickEvent::description()
60 return "AtomicSimpleCPU tick event";
64 AtomicSimpleCPU::getPort(const std::string
&if_name
, int idx
)
66 if (if_name
== "dcache_port")
68 else if (if_name
== "icache_port")
71 panic("No Such Port\n");
75 AtomicSimpleCPU::init()
79 for (int i
= 0; i
< threadContexts
.size(); ++i
) {
80 ThreadContext
*tc
= threadContexts
[i
];
82 // initialize CPU, including PC
83 TheISA::initCPU(tc
, tc
->readCpuId());
89 AtomicSimpleCPU::CpuPort::recvTiming(PacketPtr pkt
)
91 panic("AtomicSimpleCPU doesn't expect recvTiming callback!");
96 AtomicSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt
)
98 //Snooping a coherence request, just return
103 AtomicSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt
)
105 //No internal storage to update, just return
110 AtomicSimpleCPU::CpuPort::recvStatusChange(Status status
)
112 if (status
== RangeChange
) {
113 if (!snoopRangeSent
) {
114 snoopRangeSent
= true;
115 sendStatusChange(Port::RangeChange
);
120 panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!");
124 AtomicSimpleCPU::CpuPort::recvRetry()
126 panic("AtomicSimpleCPU doesn't expect recvRetry callback!");
130 AtomicSimpleCPU::DcachePort::setPeer(Port
*port
)
135 // Update the ThreadContext's memory ports (Functional/Virtual
137 cpu
->tcBase()->connectMemPorts();
141 AtomicSimpleCPU::AtomicSimpleCPU(Params
*p
)
142 : BaseSimpleCPU(p
), tickEvent(this),
143 width(p
->width
), simulate_stalls(p
->simulate_stalls
),
144 icachePort(name() + "-iport", this), dcachePort(name() + "-iport", this)
148 icachePort
.snoopRangeSent
= false;
149 dcachePort
.snoopRangeSent
= false;
151 ifetch_req
= new Request();
152 ifetch_req
->setThreadContext(p
->cpu_id
, 0); // Add thread ID if we add MT
153 ifetch_pkt
= new Packet(ifetch_req
, MemCmd::ReadReq
, Packet::Broadcast
);
154 ifetch_pkt
->dataStatic(&inst
);
156 data_read_req
= new Request();
157 data_read_req
->setThreadContext(p
->cpu_id
, 0); // Add thread ID here too
158 data_read_pkt
= new Packet(data_read_req
, MemCmd::ReadReq
,
160 data_read_pkt
->dataStatic(&dataReg
);
162 data_write_req
= new Request();
163 data_write_req
->setThreadContext(p
->cpu_id
, 0); // Add thread ID here too
164 data_write_pkt
= new Packet(data_write_req
, MemCmd::WriteReq
,
166 data_swap_pkt
= new Packet(data_write_req
, MemCmd::SwapReq
,
171 AtomicSimpleCPU::~AtomicSimpleCPU()
176 AtomicSimpleCPU::serialize(ostream
&os
)
178 SimObject::State so_state
= SimObject::getState();
179 SERIALIZE_ENUM(so_state
);
180 Status _status
= status();
181 SERIALIZE_ENUM(_status
);
182 BaseSimpleCPU::serialize(os
);
183 nameOut(os
, csprintf("%s.tickEvent", name()));
184 tickEvent
.serialize(os
);
188 AtomicSimpleCPU::unserialize(Checkpoint
*cp
, const string
§ion
)
190 SimObject::State so_state
;
191 UNSERIALIZE_ENUM(so_state
);
192 UNSERIALIZE_ENUM(_status
);
193 BaseSimpleCPU::unserialize(cp
, section
);
194 tickEvent
.unserialize(cp
, csprintf("%s.tickEvent", section
));
198 AtomicSimpleCPU::resume()
200 if (_status
!= SwitchedOut
&& _status
!= Idle
) {
201 assert(system
->getMemoryMode() == System::Atomic
);
203 changeState(SimObject::Running
);
204 if (thread
->status() == ThreadContext::Active
) {
205 if (!tickEvent
.scheduled()) {
206 tickEvent
.schedule(nextCycle());
213 AtomicSimpleCPU::switchOut()
215 assert(status() == Running
|| status() == Idle
);
216 _status
= SwitchedOut
;
223 AtomicSimpleCPU::takeOverFrom(BaseCPU
*oldCPU
)
225 BaseCPU::takeOverFrom(oldCPU
, &icachePort
, &dcachePort
);
227 assert(!tickEvent
.scheduled());
229 // if any of this CPU's ThreadContexts are active, mark the CPU as
230 // running and schedule its tick event.
231 for (int i
= 0; i
< threadContexts
.size(); ++i
) {
232 ThreadContext
*tc
= threadContexts
[i
];
233 if (tc
->status() == ThreadContext::Active
&& _status
!= Running
) {
235 tickEvent
.schedule(nextCycle());
239 if (_status
!= Running
) {
246 AtomicSimpleCPU::activateContext(int thread_num
, int delay
)
248 assert(thread_num
== 0);
251 assert(_status
== Idle
);
252 assert(!tickEvent
.scheduled());
256 //Make sure ticks are still on multiples of cycles
257 tickEvent
.schedule(nextCycle(curTick
+ cycles(delay
)));
263 AtomicSimpleCPU::suspendContext(int thread_num
)
265 assert(thread_num
== 0);
268 assert(_status
== Running
);
270 // tick event may not be scheduled if this gets called from inside
271 // an instruction's execution, e.g. "quiesce"
272 if (tickEvent
.scheduled())
273 tickEvent
.deschedule();
282 AtomicSimpleCPU::read(Addr addr
, T
&data
, unsigned flags
)
284 // use the CPU's statically allocated read request and packet objects
285 Request
*req
= data_read_req
;
286 PacketPtr pkt
= data_read_pkt
;
288 req
->setVirt(0, addr
, sizeof(T
), flags
, thread
->readPC());
291 traceData
->setAddr(addr
);
294 // translate to physical address
295 Fault fault
= thread
->translateDataReadReq(req
);
297 // Now do the access.
298 if (fault
== NoFault
) {
299 pkt
->reinitFromRequest();
301 if (req
->isMmapedIpr())
302 dcache_latency
= TheISA::handleIprRead(thread
->getTC(),pkt
);
304 dcache_latency
= dcachePort
.sendAtomic(pkt
);
305 dcache_access
= true;
307 if (pkt
->result
!= Packet::Success
)
308 panic("Unable to find responder for address pa = %#X va = %#X\n",
309 pkt
->req
->getPaddr(), pkt
->req
->getVaddr());
311 data
= pkt
->get
<T
>();
313 if (req
->isLocked()) {
314 TheISA::handleLockedRead(thread
, req
);
318 // This will need a new way to tell if it has a dcache attached.
319 if (req
->isUncacheable())
320 recordEvent("Uncached Read");
325 #ifndef DOXYGEN_SHOULD_SKIP_THIS
329 AtomicSimpleCPU::read(Addr addr
, Twin32_t
&data
, unsigned flags
);
333 AtomicSimpleCPU::read(Addr addr
, Twin64_t
&data
, unsigned flags
);
337 AtomicSimpleCPU::read(Addr addr
, uint64_t &data
, unsigned flags
);
341 AtomicSimpleCPU::read(Addr addr
, uint32_t &data
, unsigned flags
);
345 AtomicSimpleCPU::read(Addr addr
, uint16_t &data
, unsigned flags
);
349 AtomicSimpleCPU::read(Addr addr
, uint8_t &data
, unsigned flags
);
351 #endif //DOXYGEN_SHOULD_SKIP_THIS
355 AtomicSimpleCPU::read(Addr addr
, double &data
, unsigned flags
)
357 return read(addr
, *(uint64_t*)&data
, flags
);
362 AtomicSimpleCPU::read(Addr addr
, float &data
, unsigned flags
)
364 return read(addr
, *(uint32_t*)&data
, flags
);
370 AtomicSimpleCPU::read(Addr addr
, int32_t &data
, unsigned flags
)
372 return read(addr
, (uint32_t&)data
, flags
);
378 AtomicSimpleCPU::write(T data
, Addr addr
, unsigned flags
, uint64_t *res
)
380 // use the CPU's statically allocated write request and packet objects
381 Request
*req
= data_write_req
;
384 req
->setVirt(0, addr
, sizeof(T
), flags
, thread
->readPC());
389 pkt
= data_write_pkt
;
392 traceData
->setAddr(addr
);
395 // translate to physical address
396 Fault fault
= thread
->translateDataWriteReq(req
);
398 // Now do the access.
399 if (fault
== NoFault
) {
400 bool do_access
= true; // flag to suppress cache access
402 if (req
->isLocked()) {
403 do_access
= TheISA::handleLockedWrite(thread
, req
);
405 if (req
->isCondSwap()) {
407 req
->setExtraData(*res
);
412 pkt
->reinitFromRequest();
413 pkt
->dataStatic(&data
);
415 if (req
->isMmapedIpr()) {
416 dcache_latency
= TheISA::handleIprWrite(thread
->getTC(), pkt
);
419 dcache_latency
= dcachePort
.sendAtomic(pkt
);
421 dcache_access
= true;
424 if (pkt
->result
!= Packet::Success
)
425 panic("Unable to find responder for address pa = %#X va = %#X\n",
426 pkt
->req
->getPaddr(), pkt
->req
->getVaddr());
432 *res
= pkt
->get
<T
>();
434 *res
= req
->getExtraData();
438 // This will need a new way to tell if it's hooked up to a cache or not.
439 if (req
->isUncacheable())
440 recordEvent("Uncached Write");
442 // If the write needs to have a fault on the access, consider calling
443 // changeStatus() and changing it to "bad addr write" or something.
448 #ifndef DOXYGEN_SHOULD_SKIP_THIS
452 AtomicSimpleCPU::write(Twin32_t data
, Addr addr
,
453 unsigned flags
, uint64_t *res
);
457 AtomicSimpleCPU::write(Twin64_t data
, Addr addr
,
458 unsigned flags
, uint64_t *res
);
462 AtomicSimpleCPU::write(uint64_t data
, Addr addr
,
463 unsigned flags
, uint64_t *res
);
467 AtomicSimpleCPU::write(uint32_t data
, Addr addr
,
468 unsigned flags
, uint64_t *res
);
472 AtomicSimpleCPU::write(uint16_t data
, Addr addr
,
473 unsigned flags
, uint64_t *res
);
477 AtomicSimpleCPU::write(uint8_t data
, Addr addr
,
478 unsigned flags
, uint64_t *res
);
480 #endif //DOXYGEN_SHOULD_SKIP_THIS
484 AtomicSimpleCPU::write(double data
, Addr addr
, unsigned flags
, uint64_t *res
)
486 return write(*(uint64_t*)&data
, addr
, flags
, res
);
491 AtomicSimpleCPU::write(float data
, Addr addr
, unsigned flags
, uint64_t *res
)
493 return write(*(uint32_t*)&data
, addr
, flags
, res
);
499 AtomicSimpleCPU::write(int32_t data
, Addr addr
, unsigned flags
, uint64_t *res
)
501 return write((uint32_t)data
, addr
, flags
, res
);
506 AtomicSimpleCPU::tick()
508 Tick latency
= cycles(1); // instruction takes one cycle by default
510 for (int i
= 0; i
< width
; ++i
) {
513 if (!curStaticInst
|| !curStaticInst
->isDelayedCommit())
514 checkForInterrupts();
516 Fault fault
= setupFetchRequest(ifetch_req
);
518 if (fault
== NoFault
) {
519 Tick icache_latency
= 0;
520 bool icache_access
= false;
521 dcache_access
= false; // assume no dcache access
523 //Fetch more instruction memory if necessary
524 if(predecoder
.needMoreBytes())
526 icache_access
= true;
527 ifetch_pkt
->reinitFromRequest();
529 icache_latency
= icachePort
.sendAtomic(ifetch_pkt
);
530 // ifetch_req is initialized to read the instruction directly
531 // into the CPU object's inst field.
538 fault
= curStaticInst
->execute(this, traceData
);
542 // @todo remove me after debugging with legion done
543 if (curStaticInst
&& (!curStaticInst
->isMicroOp() ||
544 curStaticInst
->isFirstMicroOp()))
547 if (simulate_stalls
) {
549 icache_access
? icache_latency
- cycles(1) : 0;
551 dcache_access
? dcache_latency
- cycles(1) : 0;
552 Tick stall_cycles
= (icache_stall
+ dcache_stall
) / cycles(1);
553 if (cycles(stall_cycles
) < (icache_stall
+ dcache_stall
))
554 latency
+= cycles(stall_cycles
+1);
556 latency
+= cycles(stall_cycles
);
560 if(predecoder
.needMoreBytes() || fault
!= NoFault
)
565 tickEvent
.schedule(curTick
+ latency
);
569 ////////////////////////////////////////////////////////////////////////
571 // AtomicSimpleCPU Simulation Object
573 BEGIN_DECLARE_SIM_OBJECT_PARAMS(AtomicSimpleCPU
)
575 Param
<Counter
> max_insts_any_thread
;
576 Param
<Counter
> max_insts_all_threads
;
577 Param
<Counter
> max_loads_any_thread
;
578 Param
<Counter
> max_loads_all_threads
;
579 Param
<Tick
> progress_interval
;
580 SimObjectParam
<System
*> system
;
584 SimObjectParam
<TheISA::ITB
*> itb
;
585 SimObjectParam
<TheISA::DTB
*> dtb
;
588 Param
<bool> do_quiesce
;
589 Param
<bool> do_checkpoint_insts
;
590 Param
<bool> do_statistics_insts
;
592 SimObjectParam
<Process
*> workload
;
593 #endif // FULL_SYSTEM
598 Param
<bool> defer_registration
;
600 Param
<bool> function_trace
;
601 Param
<Tick
> function_trace_start
;
602 Param
<bool> simulate_stalls
;
604 END_DECLARE_SIM_OBJECT_PARAMS(AtomicSimpleCPU
)
606 BEGIN_INIT_SIM_OBJECT_PARAMS(AtomicSimpleCPU
)
608 INIT_PARAM(max_insts_any_thread
,
609 "terminate when any thread reaches this inst count"),
610 INIT_PARAM(max_insts_all_threads
,
611 "terminate when all threads have reached this inst count"),
612 INIT_PARAM(max_loads_any_thread
,
613 "terminate when any thread reaches this load count"),
614 INIT_PARAM(max_loads_all_threads
,
615 "terminate when all threads have reached this load count"),
616 INIT_PARAM(progress_interval
, "Progress interval"),
617 INIT_PARAM(system
, "system object"),
618 INIT_PARAM(cpu_id
, "processor ID"),
621 INIT_PARAM(itb
, "Instruction TLB"),
622 INIT_PARAM(dtb
, "Data TLB"),
623 INIT_PARAM(profile
, ""),
624 INIT_PARAM(do_quiesce
, ""),
625 INIT_PARAM(do_checkpoint_insts
, ""),
626 INIT_PARAM(do_statistics_insts
, ""),
628 INIT_PARAM(workload
, "processes to run"),
629 #endif // FULL_SYSTEM
631 INIT_PARAM(clock
, "clock speed"),
632 INIT_PARAM_DFLT(phase
, "clock phase", 0),
633 INIT_PARAM(defer_registration
, "defer system registration (for sampling)"),
634 INIT_PARAM(width
, "cpu width"),
635 INIT_PARAM(function_trace
, "Enable function trace"),
636 INIT_PARAM(function_trace_start
, "Cycle to start function trace"),
637 INIT_PARAM(simulate_stalls
, "Simulate cache stall cycles")
639 END_INIT_SIM_OBJECT_PARAMS(AtomicSimpleCPU
)
642 CREATE_SIM_OBJECT(AtomicSimpleCPU
)
644 AtomicSimpleCPU::Params
*params
= new AtomicSimpleCPU::Params();
645 params
->name
= getInstanceName();
646 params
->numberOfThreads
= 1;
647 params
->max_insts_any_thread
= max_insts_any_thread
;
648 params
->max_insts_all_threads
= max_insts_all_threads
;
649 params
->max_loads_any_thread
= max_loads_any_thread
;
650 params
->max_loads_all_threads
= max_loads_all_threads
;
651 params
->progress_interval
= progress_interval
;
652 params
->deferRegistration
= defer_registration
;
653 params
->phase
= phase
;
654 params
->clock
= clock
;
655 params
->functionTrace
= function_trace
;
656 params
->functionTraceStart
= function_trace_start
;
657 params
->width
= width
;
658 params
->simulate_stalls
= simulate_stalls
;
659 params
->system
= system
;
660 params
->cpu_id
= cpu_id
;
665 params
->profile
= profile
;
666 params
->do_quiesce
= do_quiesce
;
667 params
->do_checkpoint_insts
= do_checkpoint_insts
;
668 params
->do_statistics_insts
= do_statistics_insts
;
670 params
->process
= workload
;
673 AtomicSimpleCPU
*cpu
= new AtomicSimpleCPU(params
);
677 REGISTER_SIM_OBJECT("AtomicSimpleCPU", AtomicSimpleCPU
)