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 "params/AtomicSimpleCPU.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";
64 AtomicSimpleCPU::getPort(const std::string
&if_name
, int idx
)
66 if (if_name
== "dcache_port")
68 else if (if_name
== "icache_port")
70 else if (if_name
== "physmem_port") {
71 hasPhysMemPort
= true;
75 panic("No Such Port\n");
79 AtomicSimpleCPU::init()
83 for (int i
= 0; i
< threadContexts
.size(); ++i
) {
84 ThreadContext
*tc
= threadContexts
[i
];
86 // initialize CPU, including PC
87 TheISA::initCPU(tc
, tc
->readCpuId());
92 AddrRangeList pmAddrList
;
93 physmemPort
.getPeerAddressRanges(pmAddrList
, snoop
);
94 physMemAddr
= *pmAddrList
.begin();
99 AtomicSimpleCPU::CpuPort::recvTiming(PacketPtr pkt
)
101 panic("AtomicSimpleCPU doesn't expect recvTiming callback!");
106 AtomicSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt
)
108 //Snooping a coherence request, just return
113 AtomicSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt
)
115 //No internal storage to update, just return
120 AtomicSimpleCPU::CpuPort::recvStatusChange(Status status
)
122 if (status
== RangeChange
) {
123 if (!snoopRangeSent
) {
124 snoopRangeSent
= true;
125 sendStatusChange(Port::RangeChange
);
130 panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!");
134 AtomicSimpleCPU::CpuPort::recvRetry()
136 panic("AtomicSimpleCPU doesn't expect recvRetry callback!");
140 AtomicSimpleCPU::DcachePort::setPeer(Port
*port
)
145 // Update the ThreadContext's memory ports (Functional/Virtual
147 cpu
->tcBase()->connectMemPorts();
151 AtomicSimpleCPU::AtomicSimpleCPU(Params
*p
)
152 : BaseSimpleCPU(p
), tickEvent(this),
153 width(p
->width
), simulate_stalls(p
->simulate_stalls
),
154 icachePort(name() + "-iport", this), dcachePort(name() + "-iport", this),
155 physmemPort(name() + "-iport", this), hasPhysMemPort(false)
159 icachePort
.snoopRangeSent
= false;
160 dcachePort
.snoopRangeSent
= false;
162 ifetch_req
.setThreadContext(p
->cpu_id
, 0); // Add thread ID if we add MT
163 data_read_req
.setThreadContext(p
->cpu_id
, 0); // Add thread ID here too
164 data_write_req
.setThreadContext(p
->cpu_id
, 0); // Add thread ID here too
168 AtomicSimpleCPU::~AtomicSimpleCPU()
173 AtomicSimpleCPU::serialize(ostream
&os
)
175 SimObject::State so_state
= SimObject::getState();
176 SERIALIZE_ENUM(so_state
);
177 Status _status
= status();
178 SERIALIZE_ENUM(_status
);
179 BaseSimpleCPU::serialize(os
);
180 nameOut(os
, csprintf("%s.tickEvent", name()));
181 tickEvent
.serialize(os
);
185 AtomicSimpleCPU::unserialize(Checkpoint
*cp
, const string
§ion
)
187 SimObject::State so_state
;
188 UNSERIALIZE_ENUM(so_state
);
189 UNSERIALIZE_ENUM(_status
);
190 BaseSimpleCPU::unserialize(cp
, section
);
191 tickEvent
.unserialize(cp
, csprintf("%s.tickEvent", section
));
195 AtomicSimpleCPU::resume()
197 DPRINTF(SimpleCPU
, "Resume\n");
198 if (_status
!= SwitchedOut
&& _status
!= Idle
) {
199 assert(system
->getMemoryMode() == Enums::atomic
);
201 changeState(SimObject::Running
);
202 if (thread
->status() == ThreadContext::Active
) {
203 if (!tickEvent
.scheduled()) {
204 tickEvent
.schedule(nextCycle());
211 AtomicSimpleCPU::switchOut()
213 assert(status() == Running
|| status() == Idle
);
214 _status
= SwitchedOut
;
221 AtomicSimpleCPU::takeOverFrom(BaseCPU
*oldCPU
)
223 BaseCPU::takeOverFrom(oldCPU
, &icachePort
, &dcachePort
);
225 assert(!tickEvent
.scheduled());
227 // if any of this CPU's ThreadContexts are active, mark the CPU as
228 // running and schedule its tick event.
229 for (int i
= 0; i
< threadContexts
.size(); ++i
) {
230 ThreadContext
*tc
= threadContexts
[i
];
231 if (tc
->status() == ThreadContext::Active
&& _status
!= Running
) {
233 tickEvent
.schedule(nextCycle());
237 if (_status
!= Running
) {
244 AtomicSimpleCPU::activateContext(int thread_num
, int delay
)
246 DPRINTF(SimpleCPU
, "ActivateContext %d (%d cycles)\n", thread_num
, 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 DPRINTF(SimpleCPU
, "SuspendContext %d\n", thread_num
);
267 assert(thread_num
== 0);
270 assert(_status
== Running
);
272 // tick event may not be scheduled if this gets called from inside
273 // an instruction's execution, e.g. "quiesce"
274 if (tickEvent
.scheduled())
275 tickEvent
.deschedule();
284 AtomicSimpleCPU::read(Addr addr
, T
&data
, unsigned flags
)
286 // use the CPU's statically allocated read request and packet objects
287 Request
*req
= &data_read_req
;
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
) {
301 req
->isLocked() ? MemCmd::LoadLockedReq
: MemCmd::ReadReq
,
303 pkt
.dataStatic(&data
);
305 if (req
->isMmapedIpr())
306 dcache_latency
= TheISA::handleIprRead(thread
->getTC(), &pkt
);
308 if (hasPhysMemPort
&& pkt
.getAddr() == physMemAddr
)
309 dcache_latency
= physmemPort
.sendAtomic(&pkt
);
311 dcache_latency
= dcachePort
.sendAtomic(&pkt
);
313 dcache_access
= true;
314 assert(!pkt
.isError());
318 if (req
->isLocked()) {
319 TheISA::handleLockedRead(thread
, req
);
323 // This will need a new way to tell if it has a dcache attached.
324 if (req
->isUncacheable())
325 recordEvent("Uncached Read");
330 #ifndef DOXYGEN_SHOULD_SKIP_THIS
334 AtomicSimpleCPU::read(Addr addr
, Twin32_t
&data
, unsigned flags
);
338 AtomicSimpleCPU::read(Addr addr
, Twin64_t
&data
, unsigned flags
);
342 AtomicSimpleCPU::read(Addr addr
, uint64_t &data
, unsigned flags
);
346 AtomicSimpleCPU::read(Addr addr
, uint32_t &data
, unsigned flags
);
350 AtomicSimpleCPU::read(Addr addr
, uint16_t &data
, unsigned flags
);
354 AtomicSimpleCPU::read(Addr addr
, uint8_t &data
, unsigned flags
);
356 #endif //DOXYGEN_SHOULD_SKIP_THIS
360 AtomicSimpleCPU::read(Addr addr
, double &data
, unsigned flags
)
362 return read(addr
, *(uint64_t*)&data
, flags
);
367 AtomicSimpleCPU::read(Addr addr
, float &data
, unsigned flags
)
369 return read(addr
, *(uint32_t*)&data
, flags
);
375 AtomicSimpleCPU::read(Addr addr
, int32_t &data
, unsigned flags
)
377 return read(addr
, (uint32_t&)data
, flags
);
383 AtomicSimpleCPU::write(T data
, Addr addr
, unsigned flags
, uint64_t *res
)
385 // use the CPU's statically allocated write request and packet objects
386 Request
*req
= &data_write_req
;
387 req
->setVirt(0, addr
, sizeof(T
), flags
, thread
->readPC());
390 traceData
->setAddr(addr
);
393 // translate to physical address
394 Fault fault
= thread
->translateDataWriteReq(req
);
396 // Now do the access.
397 if (fault
== NoFault
) {
398 MemCmd cmd
= MemCmd::WriteReq
; // default
399 bool do_access
= true; // flag to suppress cache access
401 if (req
->isLocked()) {
402 cmd
= MemCmd::StoreCondReq
;
403 do_access
= TheISA::handleLockedWrite(thread
, req
);
404 } else if (req
->isSwap()) {
405 cmd
= MemCmd::SwapReq
;
406 if (req
->isCondSwap()) {
408 req
->setExtraData(*res
);
413 Packet pkt
= Packet(req
, cmd
, Packet::Broadcast
);
414 pkt
.dataStatic(&data
);
416 if (req
->isMmapedIpr()) {
417 dcache_latency
= TheISA::handleIprWrite(thread
->getTC(), &pkt
);
420 if (hasPhysMemPort
&& pkt
.getAddr() == physMemAddr
)
421 dcache_latency
= physmemPort
.sendAtomic(&pkt
);
423 dcache_latency
= dcachePort
.sendAtomic(&pkt
);
425 dcache_access
= true;
426 assert(!pkt
.isError());
434 if (res
&& !req
->isSwap()) {
435 *res
= req
->getExtraData();
439 // This will need a new way to tell if it's hooked up to a cache or not.
440 if (req
->isUncacheable())
441 recordEvent("Uncached Write");
443 // If the write needs to have a fault on the access, consider calling
444 // changeStatus() and changing it to "bad addr write" or something.
449 #ifndef DOXYGEN_SHOULD_SKIP_THIS
453 AtomicSimpleCPU::write(Twin32_t data
, Addr addr
,
454 unsigned flags
, uint64_t *res
);
458 AtomicSimpleCPU::write(Twin64_t data
, Addr addr
,
459 unsigned flags
, uint64_t *res
);
463 AtomicSimpleCPU::write(uint64_t data
, Addr addr
,
464 unsigned flags
, uint64_t *res
);
468 AtomicSimpleCPU::write(uint32_t data
, Addr addr
,
469 unsigned flags
, uint64_t *res
);
473 AtomicSimpleCPU::write(uint16_t data
, Addr addr
,
474 unsigned flags
, uint64_t *res
);
478 AtomicSimpleCPU::write(uint8_t data
, Addr addr
,
479 unsigned flags
, uint64_t *res
);
481 #endif //DOXYGEN_SHOULD_SKIP_THIS
485 AtomicSimpleCPU::write(double data
, Addr addr
, unsigned flags
, uint64_t *res
)
487 return write(*(uint64_t*)&data
, addr
, flags
, res
);
492 AtomicSimpleCPU::write(float data
, Addr addr
, unsigned flags
, uint64_t *res
)
494 return write(*(uint32_t*)&data
, addr
, flags
, res
);
500 AtomicSimpleCPU::write(int32_t data
, Addr addr
, unsigned flags
, uint64_t *res
)
502 return write((uint32_t)data
, addr
, flags
, res
);
507 AtomicSimpleCPU::tick()
509 DPRINTF(SimpleCPU
, "Tick\n");
511 Tick latency
= cycles(1); // instruction takes one cycle by default
513 for (int i
= 0; i
< width
; ++i
) {
516 if (!curStaticInst
|| !curStaticInst
->isDelayedCommit())
517 checkForInterrupts();
519 Fault fault
= setupFetchRequest(&ifetch_req
);
521 if (fault
== NoFault
) {
522 Tick icache_latency
= 0;
523 bool icache_access
= false;
524 dcache_access
= false; // assume no dcache access
526 //Fetch more instruction memory if necessary
527 //if(predecoder.needMoreBytes())
529 icache_access
= true;
530 Packet ifetch_pkt
= Packet(&ifetch_req
, MemCmd::ReadReq
,
532 ifetch_pkt
.dataStatic(&inst
);
534 if (hasPhysMemPort
&& ifetch_pkt
.getAddr() == physMemAddr
)
535 icache_latency
= physmemPort
.sendAtomic(&ifetch_pkt
);
537 icache_latency
= icachePort
.sendAtomic(&ifetch_pkt
);
540 // ifetch_req is initialized to read the instruction directly
541 // into the CPU object's inst field.
548 fault
= curStaticInst
->execute(this, traceData
);
552 // @todo remove me after debugging with legion done
553 if (curStaticInst
&& (!curStaticInst
->isMicroop() ||
554 curStaticInst
->isFirstMicroop()))
557 if (simulate_stalls
) {
559 icache_access
? icache_latency
- cycles(1) : 0;
561 dcache_access
? dcache_latency
- cycles(1) : 0;
562 Tick stall_cycles
= (icache_stall
+ dcache_stall
) / cycles(1);
563 if (cycles(stall_cycles
) < (icache_stall
+ dcache_stall
))
564 latency
+= cycles(stall_cycles
+1);
566 latency
+= cycles(stall_cycles
);
570 if(fault
!= NoFault
|| !stayAtPC
)
575 tickEvent
.schedule(curTick
+ latency
);
579 ////////////////////////////////////////////////////////////////////////
581 // AtomicSimpleCPU Simulation Object
584 AtomicSimpleCPUParams::create()
586 AtomicSimpleCPU::Params
*params
= new AtomicSimpleCPU::Params();
588 params
->numberOfThreads
= 1;
589 params
->max_insts_any_thread
= max_insts_any_thread
;
590 params
->max_insts_all_threads
= max_insts_all_threads
;
591 params
->max_loads_any_thread
= max_loads_any_thread
;
592 params
->max_loads_all_threads
= max_loads_all_threads
;
593 params
->progress_interval
= progress_interval
;
594 params
->deferRegistration
= defer_registration
;
595 params
->phase
= phase
;
596 params
->clock
= clock
;
597 params
->functionTrace
= function_trace
;
598 params
->functionTraceStart
= function_trace_start
;
599 params
->width
= width
;
600 params
->simulate_stalls
= simulate_stalls
;
601 params
->system
= system
;
602 params
->cpu_id
= cpu_id
;
603 params
->tracer
= tracer
;
608 params
->profile
= profile
;
609 params
->do_quiesce
= do_quiesce
;
610 params
->do_checkpoint_insts
= do_checkpoint_insts
;
611 params
->do_statistics_insts
= do_statistics_insts
;
613 if (workload
.size() != 1)
614 panic("only one workload allowed");
615 params
->process
= workload
[0];
618 AtomicSimpleCPU
*cpu
= new AtomicSimpleCPU(params
);