2 * Copyright (c) 2012 ARM Limited
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
14 * Copyright (c) 2002-2005 The Regents of The University of Michigan
15 * All rights reserved.
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 * Authors: Steve Reinhardt
43 #include "arch/locked_mem.hh"
44 #include "arch/mmapped_ipr.hh"
45 #include "arch/utility.hh"
46 #include "base/bigint.hh"
47 #include "config/the_isa.hh"
48 #include "cpu/simple/atomic.hh"
49 #include "cpu/exetrace.hh"
50 #include "debug/Drain.hh"
51 #include "debug/ExecFaulting.hh"
52 #include "debug/SimpleCPU.hh"
53 #include "mem/packet.hh"
54 #include "mem/packet_access.hh"
55 #include "mem/physical.hh"
56 #include "params/AtomicSimpleCPU.hh"
57 #include "sim/faults.hh"
58 #include "sim/system.hh"
59 #include "sim/full_system.hh"
62 using namespace TheISA
;
64 AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU
*c
)
65 : Event(CPU_Tick_Pri
), cpu(c
)
71 AtomicSimpleCPU::TickEvent::process()
77 AtomicSimpleCPU::TickEvent::description() const
79 return "AtomicSimpleCPU tick";
83 AtomicSimpleCPU::init()
87 // Initialise the ThreadContext's memory proxies
88 tcBase()->initMemProxies(tcBase());
90 if (FullSystem
&& !params()->switched_out
) {
91 ThreadID size
= threadContexts
.size();
92 for (ThreadID i
= 0; i
< size
; ++i
) {
93 ThreadContext
*tc
= threadContexts
[i
];
94 // initialize CPU, including PC
95 TheISA::initCPU(tc
, tc
->contextId());
99 // Atomic doesn't do MT right now, so contextId == threadId
100 ifetch_req
.setThreadContext(_cpuId
, 0); // Add thread ID if we add MT
101 data_read_req
.setThreadContext(_cpuId
, 0); // Add thread ID here too
102 data_write_req
.setThreadContext(_cpuId
, 0); // Add thread ID here too
105 AtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams
*p
)
106 : BaseSimpleCPU(p
), tickEvent(this), width(p
->width
), locked(false),
107 simulate_data_stalls(p
->simulate_data_stalls
),
108 simulate_inst_stalls(p
->simulate_inst_stalls
),
110 icachePort(name() + ".icache_port", this),
111 dcachePort(name() + ".dcache_port", this),
118 AtomicSimpleCPU::~AtomicSimpleCPU()
120 if (tickEvent
.scheduled()) {
121 deschedule(tickEvent
);
126 AtomicSimpleCPU::drain(DrainManager
*dm
)
128 assert(!drain_manager
);
133 DPRINTF(Drain
, "Requesting drain: %s\n", pcState());
137 if (tickEvent
.scheduled())
138 deschedule(tickEvent
);
140 DPRINTF(Drain
, "Not executing microcode, no need to drain.\n");
146 AtomicSimpleCPU::drainResume()
148 assert(!tickEvent
.scheduled());
149 assert(!drain_manager
);
153 DPRINTF(SimpleCPU
, "Resume\n");
156 assert(!threadContexts
.empty());
157 if (threadContexts
.size() > 1)
158 fatal("The atomic CPU only supports one thread.\n");
160 if (thread
->status() == ThreadContext::Active
) {
161 schedule(tickEvent
, nextCycle());
162 _status
= BaseSimpleCPU::Running
;
164 _status
= BaseSimpleCPU::Idle
;
167 system
->totalNumInsts
= 0;
171 AtomicSimpleCPU::tryCompleteDrain()
176 DPRINTF(Drain
, "tryCompleteDrain: %s\n", pcState());
180 DPRINTF(Drain
, "CPU done draining, processing drain event\n");
181 drain_manager
->signalDrainDone();
182 drain_manager
= NULL
;
189 AtomicSimpleCPU::switchOut()
191 BaseSimpleCPU::switchOut();
193 assert(!tickEvent
.scheduled());
194 assert(_status
== BaseSimpleCPU::Running
|| _status
== Idle
);
200 AtomicSimpleCPU::takeOverFrom(BaseCPU
*oldCPU
)
202 BaseSimpleCPU::takeOverFrom(oldCPU
);
204 // The tick event should have been descheduled by drain()
205 assert(!tickEvent
.scheduled());
207 ifetch_req
.setThreadContext(_cpuId
, 0); // Add thread ID if we add MT
208 data_read_req
.setThreadContext(_cpuId
, 0); // Add thread ID here too
209 data_write_req
.setThreadContext(_cpuId
, 0); // Add thread ID here too
213 AtomicSimpleCPU::verifyMemoryMode() const
215 if (!system
->isAtomicMode()) {
216 fatal("The atomic CPU requires the memory system to be in "
222 AtomicSimpleCPU::activateContext(ThreadID thread_num
, Cycles delay
)
224 DPRINTF(SimpleCPU
, "ActivateContext %d (%d cycles)\n", thread_num
, delay
);
226 assert(thread_num
== 0);
229 assert(_status
== Idle
);
230 assert(!tickEvent
.scheduled());
233 numCycles
+= ticksToCycles(thread
->lastActivate
- thread
->lastSuspend
);
235 //Make sure ticks are still on multiples of cycles
236 schedule(tickEvent
, clockEdge(delay
));
237 _status
= BaseSimpleCPU::Running
;
242 AtomicSimpleCPU::suspendContext(ThreadID thread_num
)
244 DPRINTF(SimpleCPU
, "SuspendContext %d\n", thread_num
);
246 assert(thread_num
== 0);
252 assert(_status
== BaseSimpleCPU::Running
);
254 // tick event may not be scheduled if this gets called from inside
255 // an instruction's execution, e.g. "quiesce"
256 if (tickEvent
.scheduled())
257 deschedule(tickEvent
);
265 AtomicSimpleCPU::readMem(Addr addr
, uint8_t * data
,
266 unsigned size
, unsigned flags
)
268 // use the CPU's statically allocated read request and packet objects
269 Request
*req
= &data_read_req
;
272 traceData
->setAddr(addr
);
275 //The block size of our peer.
276 unsigned blockSize
= dcachePort
.peerBlockSize();
277 //The size of the data we're trying to read.
280 //The address of the second part of this access if it needs to be split
281 //across a cache line boundary.
282 Addr secondAddr
= roundDown(addr
+ size
- 1, blockSize
);
284 if (secondAddr
> addr
)
285 size
= secondAddr
- addr
;
290 req
->setVirt(0, addr
, size
, flags
, dataMasterId(), thread
->pcState().instAddr());
292 // translate to physical address
293 Fault fault
= thread
->dtb
->translateAtomic(req
, tc
, BaseTLB::Read
);
295 // Now do the access.
296 if (fault
== NoFault
&& !req
->getFlags().isSet(Request::NO_ACCESS
)) {
297 Packet pkt
= Packet(req
,
298 req
->isLLSC() ? MemCmd::LoadLockedReq
:
300 pkt
.dataStatic(data
);
302 if (req
->isMmappedIpr())
303 dcache_latency
+= TheISA::handleIprRead(thread
->getTC(), &pkt
);
305 if (fastmem
&& system
->isMemAddr(pkt
.getAddr()))
306 system
->getPhysMem().access(&pkt
);
308 dcache_latency
+= dcachePort
.sendAtomic(&pkt
);
310 dcache_access
= true;
312 assert(!pkt
.isError());
315 TheISA::handleLockedRead(thread
, req
);
319 //If there's a fault, return it
320 if (fault
!= NoFault
) {
321 if (req
->isPrefetch()) {
328 //If we don't need to access a second cache line, stop now.
329 if (secondAddr
<= addr
)
331 if (req
->isLocked() && fault
== NoFault
) {
339 * Set up for accessing the second cache line.
342 //Move the pointer we're reading into to the correct location.
344 //Adjust the size to get the remaining bytes.
345 size
= addr
+ fullSize
- secondAddr
;
346 //And access the right address.
353 AtomicSimpleCPU::writeMem(uint8_t *data
, unsigned size
,
354 Addr addr
, unsigned flags
, uint64_t *res
)
356 // use the CPU's statically allocated write request and packet objects
357 Request
*req
= &data_write_req
;
360 traceData
->setAddr(addr
);
363 //The block size of our peer.
364 unsigned blockSize
= dcachePort
.peerBlockSize();
365 //The size of the data we're trying to read.
368 //The address of the second part of this access if it needs to be split
369 //across a cache line boundary.
370 Addr secondAddr
= roundDown(addr
+ size
- 1, blockSize
);
372 if(secondAddr
> addr
)
373 size
= secondAddr
- addr
;
378 req
->setVirt(0, addr
, size
, flags
, dataMasterId(), thread
->pcState().instAddr());
380 // translate to physical address
381 Fault fault
= thread
->dtb
->translateAtomic(req
, tc
, BaseTLB::Write
);
383 // Now do the access.
384 if (fault
== NoFault
) {
385 MemCmd cmd
= MemCmd::WriteReq
; // default
386 bool do_access
= true; // flag to suppress cache access
389 cmd
= MemCmd::StoreCondReq
;
390 do_access
= TheISA::handleLockedWrite(thread
, req
);
391 } else if (req
->isSwap()) {
392 cmd
= MemCmd::SwapReq
;
393 if (req
->isCondSwap()) {
395 req
->setExtraData(*res
);
399 if (do_access
&& !req
->getFlags().isSet(Request::NO_ACCESS
)) {
400 Packet pkt
= Packet(req
, cmd
);
401 pkt
.dataStatic(data
);
403 if (req
->isMmappedIpr()) {
405 TheISA::handleIprWrite(thread
->getTC(), &pkt
);
407 if (fastmem
&& system
->isMemAddr(pkt
.getAddr()))
408 system
->getPhysMem().access(&pkt
);
410 dcache_latency
+= dcachePort
.sendAtomic(&pkt
);
412 dcache_access
= true;
413 assert(!pkt
.isError());
417 memcpy(res
, pkt
.getPtr
<uint8_t>(), fullSize
);
421 if (res
&& !req
->isSwap()) {
422 *res
= req
->getExtraData();
426 //If there's a fault or we don't need to access a second cache line,
428 if (fault
!= NoFault
|| secondAddr
<= addr
)
430 if (req
->isLocked() && fault
== NoFault
) {
434 if (fault
!= NoFault
&& req
->isPrefetch()) {
442 * Set up for accessing the second cache line.
445 //Move the pointer we're reading into to the correct location.
447 //Adjust the size to get the remaining bytes.
448 size
= addr
+ fullSize
- secondAddr
;
449 //And access the right address.
456 AtomicSimpleCPU::tick()
458 DPRINTF(SimpleCPU
, "Tick\n");
462 for (int i
= 0; i
< width
|| locked
; ++i
) {
465 if (!curStaticInst
|| !curStaticInst
->isDelayedCommit())
466 checkForInterrupts();
469 // We must have just got suspended by a PC event
470 if (_status
== Idle
) {
475 Fault fault
= NoFault
;
477 TheISA::PCState pcState
= thread
->pcState();
479 bool needToFetch
= !isRomMicroPC(pcState
.microPC()) &&
482 setupFetchRequest(&ifetch_req
);
483 fault
= thread
->itb
->translateAtomic(&ifetch_req
, tc
,
487 if (fault
== NoFault
) {
488 Tick icache_latency
= 0;
489 bool icache_access
= false;
490 dcache_access
= false; // assume no dcache access
493 // This is commented out because the decoder would act like
494 // a tiny cache otherwise. It wouldn't be flushed when needed
495 // like the I cache. It should be flushed, and when that works
496 // this code should be uncommented.
497 //Fetch more instruction memory if necessary
498 //if(decoder.needMoreBytes())
500 icache_access
= true;
501 Packet ifetch_pkt
= Packet(&ifetch_req
, MemCmd::ReadReq
);
502 ifetch_pkt
.dataStatic(&inst
);
504 if (fastmem
&& system
->isMemAddr(ifetch_pkt
.getAddr()))
505 system
->getPhysMem().access(&ifetch_pkt
);
507 icache_latency
= icachePort
.sendAtomic(&ifetch_pkt
);
509 assert(!ifetch_pkt
.isError());
511 // ifetch_req is initialized to read the instruction directly
512 // into the CPU object's inst field.
519 fault
= curStaticInst
->execute(this, traceData
);
521 // keep an instruction count
522 if (fault
== NoFault
)
524 else if (traceData
&& !DTRACE(ExecFaulting
)) {
532 // @todo remove me after debugging with legion done
533 if (curStaticInst
&& (!curStaticInst
->isMicroop() ||
534 curStaticInst
->isFirstMicroop()))
537 Tick stall_ticks
= 0;
538 if (simulate_inst_stalls
&& icache_access
)
539 stall_ticks
+= icache_latency
;
541 if (simulate_data_stalls
&& dcache_access
)
542 stall_ticks
+= dcache_latency
;
545 // the atomic cpu does its accounting in ticks, so
546 // keep counting in ticks but round to the clock
548 latency
+= divCeil(stall_ticks
, clockPeriod()) *
553 if(fault
!= NoFault
|| !stayAtPC
)
557 if (tryCompleteDrain())
560 // instruction takes at least one cycle
561 if (latency
< clockPeriod())
562 latency
= clockPeriod();
565 schedule(tickEvent
, curTick() + latency
);
570 AtomicSimpleCPU::printAddr(Addr a
)
572 dcachePort
.printAddr(a
);
576 ////////////////////////////////////////////////////////////////////////
578 // AtomicSimpleCPU Simulation Object
581 AtomicSimpleCPUParams::create()
584 if (!FullSystem
&& workload
.size() != 1)
585 panic("only one workload allowed");
586 return new AtomicSimpleCPU(this);