Deleted Added
sdiff udiff text old ( 9443:0cb3209bc5c7 ) new ( 9448:569d1e8f74e4 )
full compact
1/*
2 * Copyright (c) 2012 ARM Limited
3 * All rights reserved.
4 *
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.
13 *
14 * Copyright (c) 2002-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
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.
27 *
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.
39 *
40 * Authors: Steve Reinhardt
41 */
42
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"
60
61using namespace std;
62using namespace TheISA;
63
64AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c)
65 : Event(CPU_Tick_Pri), cpu(c)
66{
67}
68
69
70void
71AtomicSimpleCPU::TickEvent::process()
72{
73 cpu->tick();
74}
75
76const char *
77AtomicSimpleCPU::TickEvent::description() const
78{
79 return "AtomicSimpleCPU tick";
80}
81
82void
83AtomicSimpleCPU::init()
84{
85 BaseCPU::init();
86
87 if (!params()->switched_out &&
88 system->getMemoryMode() != Enums::atomic) {
89 fatal("The atomic CPU requires the memory system to be in "
90 "'atomic' mode.\n");
91 }
92
93 // Initialise the ThreadContext's memory proxies
94 tcBase()->initMemProxies(tcBase());
95
96 if (FullSystem && !params()->switched_out) {
97 ThreadID size = threadContexts.size();
98 for (ThreadID i = 0; i < size; ++i) {
99 ThreadContext *tc = threadContexts[i];
100 // initialize CPU, including PC
101 TheISA::initCPU(tc, tc->contextId());
102 }
103 }
104
105 // Atomic doesn't do MT right now, so contextId == threadId
106 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
107 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
108 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too
109}
110
111AtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p)
112 : BaseSimpleCPU(p), tickEvent(this), width(p->width), locked(false),
113 simulate_data_stalls(p->simulate_data_stalls),
114 simulate_inst_stalls(p->simulate_inst_stalls),
115 drain_manager(NULL),
116 icachePort(name() + ".icache_port", this),
117 dcachePort(name() + ".dcache_port", this),
118 fastmem(p->fastmem)
119{
120 _status = Idle;
121}
122
123
124AtomicSimpleCPU::~AtomicSimpleCPU()
125{
126 if (tickEvent.scheduled()) {
127 deschedule(tickEvent);
128 }
129}
130
131unsigned int
132AtomicSimpleCPU::drain(DrainManager *dm)
133{
134 assert(!drain_manager);
135 if (switchedOut())
136 return 0;
137
138 if (!isDrained()) {
139 DPRINTF(Drain, "Requesting drain: %s\n", pcState());
140 drain_manager = dm;
141 return 1;
142 } else {
143 if (tickEvent.scheduled())
144 deschedule(tickEvent);
145
146 DPRINTF(Drain, "Not executing microcode, no need to drain.\n");
147 return 0;
148 }
149}
150
151void
152AtomicSimpleCPU::drainResume()
153{
154 assert(!tickEvent.scheduled());
155 assert(!drain_manager);
156 if (switchedOut())
157 return;
158
159 DPRINTF(SimpleCPU, "Resume\n");
160 if (system->getMemoryMode() != Enums::atomic) {
161 fatal("The atomic CPU requires the memory system to be in "
162 "'atomic' mode.\n");
163 }
164
165 assert(!threadContexts.empty());
166 if (threadContexts.size() > 1)
167 fatal("The atomic CPU only supports one thread.\n");
168
169 if (thread->status() == ThreadContext::Active) {
170 schedule(tickEvent, nextCycle());
171 _status = BaseSimpleCPU::Running;
172 } else {
173 _status = BaseSimpleCPU::Idle;
174 }
175
176 system->totalNumInsts = 0;
177}
178
179bool
180AtomicSimpleCPU::tryCompleteDrain()
181{
182 if (!drain_manager)
183 return false;
184
185 DPRINTF(Drain, "tryCompleteDrain: %s\n", pcState());
186 if (!isDrained())
187 return false;
188
189 DPRINTF(Drain, "CPU done draining, processing drain event\n");
190 drain_manager->signalDrainDone();
191 drain_manager = NULL;
192
193 return true;
194}
195
196
197void
198AtomicSimpleCPU::switchOut()
199{
200 BaseSimpleCPU::switchOut();
201
202 assert(!tickEvent.scheduled());
203 assert(_status == BaseSimpleCPU::Running || _status == Idle);
204 assert(isDrained());
205}
206
207
208void
209AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
210{
211 BaseSimpleCPU::takeOverFrom(oldCPU);
212
213 // The tick event should have been descheduled by drain()
214 assert(!tickEvent.scheduled());
215
216 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
217 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
218 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too
219}
220
221
222void
223AtomicSimpleCPU::activateContext(ThreadID thread_num, Cycles delay)
224{
225 DPRINTF(SimpleCPU, "ActivateContext %d (%d cycles)\n", thread_num, delay);
226
227 assert(thread_num == 0);
228 assert(thread);
229
230 assert(_status == Idle);
231 assert(!tickEvent.scheduled());
232
233 notIdleFraction++;
234 numCycles += ticksToCycles(thread->lastActivate - thread->lastSuspend);
235
236 //Make sure ticks are still on multiples of cycles
237 schedule(tickEvent, clockEdge(delay));
238 _status = BaseSimpleCPU::Running;
239}
240
241
242void
243AtomicSimpleCPU::suspendContext(ThreadID thread_num)
244{
245 DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num);
246
247 assert(thread_num == 0);
248 assert(thread);
249
250 if (_status == Idle)
251 return;
252
253 assert(_status == BaseSimpleCPU::Running);
254
255 // tick event may not be scheduled if this gets called from inside
256 // an instruction's execution, e.g. "quiesce"
257 if (tickEvent.scheduled())
258 deschedule(tickEvent);
259
260 notIdleFraction--;
261 _status = Idle;
262}
263
264
265Fault
266AtomicSimpleCPU::readMem(Addr addr, uint8_t * data,
267 unsigned size, unsigned flags)
268{
269 // use the CPU's statically allocated read request and packet objects
270 Request *req = &data_read_req;
271
272 if (traceData) {
273 traceData->setAddr(addr);
274 }
275
276 //The block size of our peer.
277 unsigned blockSize = dcachePort.peerBlockSize();
278 //The size of the data we're trying to read.
279 int fullSize = size;
280
281 //The address of the second part of this access if it needs to be split
282 //across a cache line boundary.
283 Addr secondAddr = roundDown(addr + size - 1, blockSize);
284
285 if (secondAddr > addr)
286 size = secondAddr - addr;
287
288 dcache_latency = 0;
289
290 while (1) {
291 req->setVirt(0, addr, size, flags, dataMasterId(), thread->pcState().instAddr());
292
293 // translate to physical address
294 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Read);
295
296 // Now do the access.
297 if (fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) {
298 Packet pkt = Packet(req,
299 req->isLLSC() ? MemCmd::LoadLockedReq :
300 MemCmd::ReadReq);
301 pkt.dataStatic(data);
302
303 if (req->isMmappedIpr())
304 dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt);
305 else {
306 if (fastmem && system->isMemAddr(pkt.getAddr()))
307 system->getPhysMem().access(&pkt);
308 else
309 dcache_latency += dcachePort.sendAtomic(&pkt);
310 }
311 dcache_access = true;
312
313 assert(!pkt.isError());
314
315 if (req->isLLSC()) {
316 TheISA::handleLockedRead(thread, req);
317 }
318 }
319
320 //If there's a fault, return it
321 if (fault != NoFault) {
322 if (req->isPrefetch()) {
323 return NoFault;
324 } else {
325 return fault;
326 }
327 }
328
329 //If we don't need to access a second cache line, stop now.
330 if (secondAddr <= addr)
331 {
332 if (req->isLocked() && fault == NoFault) {
333 assert(!locked);
334 locked = true;
335 }
336 return fault;
337 }
338
339 /*
340 * Set up for accessing the second cache line.
341 */
342
343 //Move the pointer we're reading into to the correct location.
344 data += size;
345 //Adjust the size to get the remaining bytes.
346 size = addr + fullSize - secondAddr;
347 //And access the right address.
348 addr = secondAddr;
349 }
350}
351
352
353Fault
354AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size,
355 Addr addr, unsigned flags, uint64_t *res)
356{
357 // use the CPU's statically allocated write request and packet objects
358 Request *req = &data_write_req;
359
360 if (traceData) {
361 traceData->setAddr(addr);
362 }
363
364 //The block size of our peer.
365 unsigned blockSize = dcachePort.peerBlockSize();
366 //The size of the data we're trying to read.
367 int fullSize = size;
368
369 //The address of the second part of this access if it needs to be split
370 //across a cache line boundary.
371 Addr secondAddr = roundDown(addr + size - 1, blockSize);
372
373 if(secondAddr > addr)
374 size = secondAddr - addr;
375
376 dcache_latency = 0;
377
378 while(1) {
379 req->setVirt(0, addr, size, flags, dataMasterId(), thread->pcState().instAddr());
380
381 // translate to physical address
382 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Write);
383
384 // Now do the access.
385 if (fault == NoFault) {
386 MemCmd cmd = MemCmd::WriteReq; // default
387 bool do_access = true; // flag to suppress cache access
388
389 if (req->isLLSC()) {
390 cmd = MemCmd::StoreCondReq;
391 do_access = TheISA::handleLockedWrite(thread, req);
392 } else if (req->isSwap()) {
393 cmd = MemCmd::SwapReq;
394 if (req->isCondSwap()) {
395 assert(res);
396 req->setExtraData(*res);
397 }
398 }
399
400 if (do_access && !req->getFlags().isSet(Request::NO_ACCESS)) {
401 Packet pkt = Packet(req, cmd);
402 pkt.dataStatic(data);
403
404 if (req->isMmappedIpr()) {
405 dcache_latency +=
406 TheISA::handleIprWrite(thread->getTC(), &pkt);
407 } else {
408 if (fastmem && system->isMemAddr(pkt.getAddr()))
409 system->getPhysMem().access(&pkt);
410 else
411 dcache_latency += dcachePort.sendAtomic(&pkt);
412 }
413 dcache_access = true;
414 assert(!pkt.isError());
415
416 if (req->isSwap()) {
417 assert(res);
418 memcpy(res, pkt.getPtr<uint8_t>(), fullSize);
419 }
420 }
421
422 if (res && !req->isSwap()) {
423 *res = req->getExtraData();
424 }
425 }
426
427 //If there's a fault or we don't need to access a second cache line,
428 //stop now.
429 if (fault != NoFault || secondAddr <= addr)
430 {
431 if (req->isLocked() && fault == NoFault) {
432 assert(locked);
433 locked = false;
434 }
435 if (fault != NoFault && req->isPrefetch()) {
436 return NoFault;
437 } else {
438 return fault;
439 }
440 }
441
442 /*
443 * Set up for accessing the second cache line.
444 */
445
446 //Move the pointer we're reading into to the correct location.
447 data += size;
448 //Adjust the size to get the remaining bytes.
449 size = addr + fullSize - secondAddr;
450 //And access the right address.
451 addr = secondAddr;
452 }
453}
454
455
456void
457AtomicSimpleCPU::tick()
458{
459 DPRINTF(SimpleCPU, "Tick\n");
460
461 Tick latency = 0;
462
463 for (int i = 0; i < width || locked; ++i) {
464 numCycles++;
465
466 if (!curStaticInst || !curStaticInst->isDelayedCommit())
467 checkForInterrupts();
468
469 checkPcEventQueue();
470 // We must have just got suspended by a PC event
471 if (_status == Idle) {
472 tryCompleteDrain();
473 return;
474 }
475
476 Fault fault = NoFault;
477
478 TheISA::PCState pcState = thread->pcState();
479
480 bool needToFetch = !isRomMicroPC(pcState.microPC()) &&
481 !curMacroStaticInst;
482 if (needToFetch) {
483 setupFetchRequest(&ifetch_req);
484 fault = thread->itb->translateAtomic(&ifetch_req, tc,
485 BaseTLB::Execute);
486 }
487
488 if (fault == NoFault) {
489 Tick icache_latency = 0;
490 bool icache_access = false;
491 dcache_access = false; // assume no dcache access
492
493 if (needToFetch) {
494 // This is commented out because the decoder would act like
495 // a tiny cache otherwise. It wouldn't be flushed when needed
496 // like the I cache. It should be flushed, and when that works
497 // this code should be uncommented.
498 //Fetch more instruction memory if necessary
499 //if(decoder.needMoreBytes())
500 //{
501 icache_access = true;
502 Packet ifetch_pkt = Packet(&ifetch_req, MemCmd::ReadReq);
503 ifetch_pkt.dataStatic(&inst);
504
505 if (fastmem && system->isMemAddr(ifetch_pkt.getAddr()))
506 system->getPhysMem().access(&ifetch_pkt);
507 else
508 icache_latency = icachePort.sendAtomic(&ifetch_pkt);
509
510 assert(!ifetch_pkt.isError());
511
512 // ifetch_req is initialized to read the instruction directly
513 // into the CPU object's inst field.
514 //}
515 }
516
517 preExecute();
518
519 if (curStaticInst) {
520 fault = curStaticInst->execute(this, traceData);
521
522 // keep an instruction count
523 if (fault == NoFault)
524 countInst();
525 else if (traceData && !DTRACE(ExecFaulting)) {
526 delete traceData;
527 traceData = NULL;
528 }
529
530 postExecute();
531 }
532
533 // @todo remove me after debugging with legion done
534 if (curStaticInst && (!curStaticInst->isMicroop() ||
535 curStaticInst->isFirstMicroop()))
536 instCnt++;
537
538 Tick stall_ticks = 0;
539 if (simulate_inst_stalls && icache_access)
540 stall_ticks += icache_latency;
541
542 if (simulate_data_stalls && dcache_access)
543 stall_ticks += dcache_latency;
544
545 if (stall_ticks) {
546 // the atomic cpu does its accounting in ticks, so
547 // keep counting in ticks but round to the clock
548 // period
549 latency += divCeil(stall_ticks, clockPeriod()) *
550 clockPeriod();
551 }
552
553 }
554 if(fault != NoFault || !stayAtPC)
555 advancePC(fault);
556 }
557
558 if (tryCompleteDrain())
559 return;
560
561 // instruction takes at least one cycle
562 if (latency < clockPeriod())
563 latency = clockPeriod();
564
565 if (_status != Idle)
566 schedule(tickEvent, curTick() + latency);
567}
568
569
570void
571AtomicSimpleCPU::printAddr(Addr a)
572{
573 dcachePort.printAddr(a);
574}
575
576
577////////////////////////////////////////////////////////////////////////
578//
579// AtomicSimpleCPU Simulation Object
580//
581AtomicSimpleCPU *
582AtomicSimpleCPUParams::create()
583{
584 numThreads = 1;
585 if (!FullSystem && workload.size() != 1)
586 panic("only one workload allowed");
587 return new AtomicSimpleCPU(this);
588}