Mem: Change isLlsc to isLLSC.
[gem5.git] / src / cpu / simple / timing.cc
1 /*
2 * Copyright (c) 2002-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
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.
15 *
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.
27 *
28 * Authors: Steve Reinhardt
29 */
30
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/timing.hh"
37 #include "mem/packet.hh"
38 #include "mem/packet_access.hh"
39 #include "params/TimingSimpleCPU.hh"
40 #include "sim/system.hh"
41
42 using namespace std;
43 using namespace TheISA;
44
45 Port *
46 TimingSimpleCPU::getPort(const std::string &if_name, int idx)
47 {
48 if (if_name == "dcache_port")
49 return &dcachePort;
50 else if (if_name == "icache_port")
51 return &icachePort;
52 else
53 panic("No Such Port\n");
54 }
55
56 void
57 TimingSimpleCPU::init()
58 {
59 BaseCPU::init();
60 #if FULL_SYSTEM
61 for (int i = 0; i < threadContexts.size(); ++i) {
62 ThreadContext *tc = threadContexts[i];
63
64 // initialize CPU, including PC
65 TheISA::initCPU(tc, _cpuId);
66 }
67 #endif
68 }
69
70 Tick
71 TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
72 {
73 panic("TimingSimpleCPU doesn't expect recvAtomic callback!");
74 return curTick;
75 }
76
77 void
78 TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
79 {
80 //No internal storage to update, jusst return
81 return;
82 }
83
84 void
85 TimingSimpleCPU::CpuPort::recvStatusChange(Status status)
86 {
87 if (status == RangeChange) {
88 if (!snoopRangeSent) {
89 snoopRangeSent = true;
90 sendStatusChange(Port::RangeChange);
91 }
92 return;
93 }
94
95 panic("TimingSimpleCPU doesn't expect recvStatusChange callback!");
96 }
97
98
99 void
100 TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t)
101 {
102 pkt = _pkt;
103 cpu->schedule(this, t);
104 }
105
106 TimingSimpleCPU::TimingSimpleCPU(TimingSimpleCPUParams *p)
107 : BaseSimpleCPU(p), fetchTranslation(this), icachePort(this, p->clock),
108 dcachePort(this, p->clock), fetchEvent(this)
109 {
110 _status = Idle;
111
112 icachePort.snoopRangeSent = false;
113 dcachePort.snoopRangeSent = false;
114
115 ifetch_pkt = dcache_pkt = NULL;
116 drainEvent = NULL;
117 previousTick = 0;
118 changeState(SimObject::Running);
119 }
120
121
122 TimingSimpleCPU::~TimingSimpleCPU()
123 {
124 }
125
126 void
127 TimingSimpleCPU::serialize(ostream &os)
128 {
129 SimObject::State so_state = SimObject::getState();
130 SERIALIZE_ENUM(so_state);
131 BaseSimpleCPU::serialize(os);
132 }
133
134 void
135 TimingSimpleCPU::unserialize(Checkpoint *cp, const string &section)
136 {
137 SimObject::State so_state;
138 UNSERIALIZE_ENUM(so_state);
139 BaseSimpleCPU::unserialize(cp, section);
140 }
141
142 unsigned int
143 TimingSimpleCPU::drain(Event *drain_event)
144 {
145 // TimingSimpleCPU is ready to drain if it's not waiting for
146 // an access to complete.
147 if (_status == Idle || _status == Running || _status == SwitchedOut) {
148 changeState(SimObject::Drained);
149 return 0;
150 } else {
151 changeState(SimObject::Draining);
152 drainEvent = drain_event;
153 return 1;
154 }
155 }
156
157 void
158 TimingSimpleCPU::resume()
159 {
160 DPRINTF(SimpleCPU, "Resume\n");
161 if (_status != SwitchedOut && _status != Idle) {
162 assert(system->getMemoryMode() == Enums::timing);
163
164 if (fetchEvent.scheduled())
165 deschedule(fetchEvent);
166
167 schedule(fetchEvent, nextCycle());
168 }
169
170 changeState(SimObject::Running);
171 }
172
173 void
174 TimingSimpleCPU::switchOut()
175 {
176 assert(_status == Running || _status == Idle);
177 _status = SwitchedOut;
178 numCycles += tickToCycles(curTick - previousTick);
179
180 // If we've been scheduled to resume but are then told to switch out,
181 // we'll need to cancel it.
182 if (fetchEvent.scheduled())
183 deschedule(fetchEvent);
184 }
185
186
187 void
188 TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
189 {
190 BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);
191
192 // if any of this CPU's ThreadContexts are active, mark the CPU as
193 // running and schedule its tick event.
194 for (int i = 0; i < threadContexts.size(); ++i) {
195 ThreadContext *tc = threadContexts[i];
196 if (tc->status() == ThreadContext::Active && _status != Running) {
197 _status = Running;
198 break;
199 }
200 }
201
202 if (_status != Running) {
203 _status = Idle;
204 }
205 assert(threadContexts.size() == 1);
206 previousTick = curTick;
207 }
208
209
210 void
211 TimingSimpleCPU::activateContext(int thread_num, int delay)
212 {
213 DPRINTF(SimpleCPU, "ActivateContext %d (%d cycles)\n", thread_num, delay);
214
215 assert(thread_num == 0);
216 assert(thread);
217
218 assert(_status == Idle);
219
220 notIdleFraction++;
221 _status = Running;
222
223 // kick things off by initiating the fetch of the next instruction
224 schedule(fetchEvent, nextCycle(curTick + ticks(delay)));
225 }
226
227
228 void
229 TimingSimpleCPU::suspendContext(int thread_num)
230 {
231 DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num);
232
233 assert(thread_num == 0);
234 assert(thread);
235
236 if (_status == Idle)
237 return;
238
239 assert(_status == Running);
240
241 // just change status to Idle... if status != Running,
242 // completeInst() will not initiate fetch of next instruction.
243
244 notIdleFraction--;
245 _status = Idle;
246 }
247
248 bool
249 TimingSimpleCPU::handleReadPacket(PacketPtr pkt)
250 {
251 RequestPtr req = pkt->req;
252 if (req->isMmapedIpr()) {
253 Tick delay;
254 delay = TheISA::handleIprRead(thread->getTC(), pkt);
255 new IprEvent(pkt, this, nextCycle(curTick + delay));
256 _status = DcacheWaitResponse;
257 dcache_pkt = NULL;
258 } else if (!dcachePort.sendTiming(pkt)) {
259 _status = DcacheRetry;
260 dcache_pkt = pkt;
261 } else {
262 _status = DcacheWaitResponse;
263 // memory system takes ownership of packet
264 dcache_pkt = NULL;
265 }
266 return dcache_pkt == NULL;
267 }
268
269 void
270 TimingSimpleCPU::sendData(Fault fault, RequestPtr req,
271 uint8_t *data, uint64_t *res, bool read)
272 {
273 _status = Running;
274 if (fault != NoFault) {
275 delete data;
276 delete req;
277
278 translationFault(fault);
279 return;
280 }
281 PacketPtr pkt;
282 buildPacket(pkt, req, read);
283 pkt->dataDynamic<uint8_t>(data);
284 if (req->getFlags().isSet(Request::NO_ACCESS)) {
285 assert(!dcache_pkt);
286 pkt->makeResponse();
287 completeDataAccess(pkt);
288 } else if (read) {
289 handleReadPacket(pkt);
290 } else {
291 bool do_access = true; // flag to suppress cache access
292
293 if (req->isLLSC()) {
294 do_access = TheISA::handleLockedWrite(thread, req);
295 } else if (req->isCondSwap()) {
296 assert(res);
297 req->setExtraData(*res);
298 }
299
300 if (do_access) {
301 dcache_pkt = pkt;
302 handleWritePacket();
303 } else {
304 _status = DcacheWaitResponse;
305 completeDataAccess(pkt);
306 }
307 }
308 }
309
310 void
311 TimingSimpleCPU::sendSplitData(Fault fault1, Fault fault2,
312 RequestPtr req1, RequestPtr req2, RequestPtr req,
313 uint8_t *data, bool read)
314 {
315 _status = Running;
316 if (fault1 != NoFault || fault2 != NoFault) {
317 delete data;
318 delete req1;
319 delete req2;
320 if (fault1 != NoFault)
321 translationFault(fault1);
322 else if (fault2 != NoFault)
323 translationFault(fault2);
324 return;
325 }
326 PacketPtr pkt1, pkt2;
327 buildSplitPacket(pkt1, pkt2, req1, req2, req, data, read);
328 if (req->getFlags().isSet(Request::NO_ACCESS)) {
329 assert(!dcache_pkt);
330 pkt1->makeResponse();
331 completeDataAccess(pkt1);
332 } else if (read) {
333 if (handleReadPacket(pkt1)) {
334 SplitFragmentSenderState * send_state =
335 dynamic_cast<SplitFragmentSenderState *>(pkt1->senderState);
336 send_state->clearFromParent();
337 if (handleReadPacket(pkt2)) {
338 send_state = dynamic_cast<SplitFragmentSenderState *>(
339 pkt1->senderState);
340 send_state->clearFromParent();
341 }
342 }
343 } else {
344 dcache_pkt = pkt1;
345 if (handleWritePacket()) {
346 SplitFragmentSenderState * send_state =
347 dynamic_cast<SplitFragmentSenderState *>(pkt1->senderState);
348 send_state->clearFromParent();
349 dcache_pkt = pkt2;
350 if (handleWritePacket()) {
351 send_state = dynamic_cast<SplitFragmentSenderState *>(
352 pkt1->senderState);
353 send_state->clearFromParent();
354 }
355 }
356 }
357 }
358
359 void
360 TimingSimpleCPU::translationFault(Fault fault)
361 {
362 numCycles += tickToCycles(curTick - previousTick);
363 previousTick = curTick;
364
365 if (traceData) {
366 // Since there was a fault, we shouldn't trace this instruction.
367 delete traceData;
368 traceData = NULL;
369 }
370
371 postExecute();
372
373 if (getState() == SimObject::Draining) {
374 advancePC(fault);
375 completeDrain();
376 } else {
377 advanceInst(fault);
378 }
379 }
380
381 void
382 TimingSimpleCPU::buildPacket(PacketPtr &pkt, RequestPtr req, bool read)
383 {
384 MemCmd cmd;
385 if (read) {
386 cmd = MemCmd::ReadReq;
387 if (req->isLLSC())
388 cmd = MemCmd::LoadLockedReq;
389 } else {
390 cmd = MemCmd::WriteReq;
391 if (req->isLLSC()) {
392 cmd = MemCmd::StoreCondReq;
393 } else if (req->isSwap()) {
394 cmd = MemCmd::SwapReq;
395 }
396 }
397 pkt = new Packet(req, cmd, Packet::Broadcast);
398 }
399
400 void
401 TimingSimpleCPU::buildSplitPacket(PacketPtr &pkt1, PacketPtr &pkt2,
402 RequestPtr req1, RequestPtr req2, RequestPtr req,
403 uint8_t *data, bool read)
404 {
405 pkt1 = pkt2 = NULL;
406
407 assert(!req1->isMmapedIpr() && !req2->isMmapedIpr());
408
409 if (req->getFlags().isSet(Request::NO_ACCESS)) {
410 buildPacket(pkt1, req, read);
411 return;
412 }
413
414 buildPacket(pkt1, req1, read);
415 buildPacket(pkt2, req2, read);
416
417 req->setPhys(req1->getPaddr(), req->getSize(), req1->getFlags());
418 PacketPtr pkt = new Packet(req, pkt1->cmd.responseCommand(),
419 Packet::Broadcast);
420
421 pkt->dataDynamic<uint8_t>(data);
422 pkt1->dataStatic<uint8_t>(data);
423 pkt2->dataStatic<uint8_t>(data + req1->getSize());
424
425 SplitMainSenderState * main_send_state = new SplitMainSenderState;
426 pkt->senderState = main_send_state;
427 main_send_state->fragments[0] = pkt1;
428 main_send_state->fragments[1] = pkt2;
429 main_send_state->outstanding = 2;
430 pkt1->senderState = new SplitFragmentSenderState(pkt, 0);
431 pkt2->senderState = new SplitFragmentSenderState(pkt, 1);
432 }
433
434 template <class T>
435 Fault
436 TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
437 {
438 Fault fault;
439 const int asid = 0;
440 const int thread_id = 0;
441 const Addr pc = thread->readPC();
442 int block_size = dcachePort.peerBlockSize();
443 int data_size = sizeof(T);
444
445 RequestPtr req = new Request(asid, addr, data_size,
446 flags, pc, _cpuId, thread_id);
447
448 Addr split_addr = roundDown(addr + data_size - 1, block_size);
449 assert(split_addr <= addr || split_addr - addr < block_size);
450
451
452 _status = DTBWaitResponse;
453 if (split_addr > addr) {
454 RequestPtr req1, req2;
455 assert(!req->isLLSC() && !req->isSwap());
456 req->splitOnVaddr(split_addr, req1, req2);
457
458 typedef SplitDataTranslation::WholeTranslationState WholeState;
459 WholeState *state = new WholeState(req1, req2, req,
460 (uint8_t *)(new T), BaseTLB::Read);
461 thread->dtb->translateTiming(req1, tc,
462 new SplitDataTranslation(this, 0, state), BaseTLB::Read);
463 thread->dtb->translateTiming(req2, tc,
464 new SplitDataTranslation(this, 1, state), BaseTLB::Read);
465 } else {
466 DataTranslation *translation =
467 new DataTranslation(this, (uint8_t *)(new T), NULL, BaseTLB::Read);
468 thread->dtb->translateTiming(req, tc, translation, BaseTLB::Read);
469 }
470
471 if (traceData) {
472 traceData->setData(data);
473 traceData->setAddr(addr);
474 }
475
476 // This will need a new way to tell if it has a dcache attached.
477 if (req->isUncacheable())
478 recordEvent("Uncached Read");
479
480 return NoFault;
481 }
482
483 #ifndef DOXYGEN_SHOULD_SKIP_THIS
484
485 template
486 Fault
487 TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
488
489 template
490 Fault
491 TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
492
493 template
494 Fault
495 TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
496
497 template
498 Fault
499 TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
500
501 template
502 Fault
503 TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
504
505 template
506 Fault
507 TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
508
509 #endif //DOXYGEN_SHOULD_SKIP_THIS
510
511 template<>
512 Fault
513 TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
514 {
515 return read(addr, *(uint64_t*)&data, flags);
516 }
517
518 template<>
519 Fault
520 TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
521 {
522 return read(addr, *(uint32_t*)&data, flags);
523 }
524
525
526 template<>
527 Fault
528 TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
529 {
530 return read(addr, (uint32_t&)data, flags);
531 }
532
533 bool
534 TimingSimpleCPU::handleWritePacket()
535 {
536 RequestPtr req = dcache_pkt->req;
537 if (req->isMmapedIpr()) {
538 Tick delay;
539 delay = TheISA::handleIprWrite(thread->getTC(), dcache_pkt);
540 new IprEvent(dcache_pkt, this, nextCycle(curTick + delay));
541 _status = DcacheWaitResponse;
542 dcache_pkt = NULL;
543 } else if (!dcachePort.sendTiming(dcache_pkt)) {
544 _status = DcacheRetry;
545 } else {
546 _status = DcacheWaitResponse;
547 // memory system takes ownership of packet
548 dcache_pkt = NULL;
549 }
550 return dcache_pkt == NULL;
551 }
552
553 template <class T>
554 Fault
555 TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
556 {
557 const int asid = 0;
558 const int thread_id = 0;
559 const Addr pc = thread->readPC();
560 int block_size = dcachePort.peerBlockSize();
561 int data_size = sizeof(T);
562
563 RequestPtr req = new Request(asid, addr, data_size,
564 flags, pc, _cpuId, thread_id);
565
566 Addr split_addr = roundDown(addr + data_size - 1, block_size);
567 assert(split_addr <= addr || split_addr - addr < block_size);
568
569 T *dataP = new T;
570 *dataP = TheISA::htog(data);
571 _status = DTBWaitResponse;
572 if (split_addr > addr) {
573 RequestPtr req1, req2;
574 assert(!req->isLLSC() && !req->isSwap());
575 req->splitOnVaddr(split_addr, req1, req2);
576
577 typedef SplitDataTranslation::WholeTranslationState WholeState;
578 WholeState *state = new WholeState(req1, req2, req,
579 (uint8_t *)dataP, BaseTLB::Write);
580 thread->dtb->translateTiming(req1, tc,
581 new SplitDataTranslation(this, 0, state), BaseTLB::Write);
582 thread->dtb->translateTiming(req2, tc,
583 new SplitDataTranslation(this, 1, state), BaseTLB::Write);
584 } else {
585 DataTranslation *translation =
586 new DataTranslation(this, (uint8_t *)dataP, res, BaseTLB::Write);
587 thread->dtb->translateTiming(req, tc, translation, BaseTLB::Write);
588 }
589
590 if (traceData) {
591 traceData->setAddr(req->getVaddr());
592 traceData->setData(data);
593 }
594
595 // This will need a new way to tell if it's hooked up to a cache or not.
596 if (req->isUncacheable())
597 recordEvent("Uncached Write");
598
599 // If the write needs to have a fault on the access, consider calling
600 // changeStatus() and changing it to "bad addr write" or something.
601 return NoFault;
602 }
603
604
605 #ifndef DOXYGEN_SHOULD_SKIP_THIS
606 template
607 Fault
608 TimingSimpleCPU::write(Twin32_t data, Addr addr,
609 unsigned flags, uint64_t *res);
610
611 template
612 Fault
613 TimingSimpleCPU::write(Twin64_t data, Addr addr,
614 unsigned flags, uint64_t *res);
615
616 template
617 Fault
618 TimingSimpleCPU::write(uint64_t data, Addr addr,
619 unsigned flags, uint64_t *res);
620
621 template
622 Fault
623 TimingSimpleCPU::write(uint32_t data, Addr addr,
624 unsigned flags, uint64_t *res);
625
626 template
627 Fault
628 TimingSimpleCPU::write(uint16_t data, Addr addr,
629 unsigned flags, uint64_t *res);
630
631 template
632 Fault
633 TimingSimpleCPU::write(uint8_t data, Addr addr,
634 unsigned flags, uint64_t *res);
635
636 #endif //DOXYGEN_SHOULD_SKIP_THIS
637
638 template<>
639 Fault
640 TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
641 {
642 return write(*(uint64_t*)&data, addr, flags, res);
643 }
644
645 template<>
646 Fault
647 TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
648 {
649 return write(*(uint32_t*)&data, addr, flags, res);
650 }
651
652
653 template<>
654 Fault
655 TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
656 {
657 return write((uint32_t)data, addr, flags, res);
658 }
659
660
661 void
662 TimingSimpleCPU::fetch()
663 {
664 DPRINTF(SimpleCPU, "Fetch\n");
665
666 if (!curStaticInst || !curStaticInst->isDelayedCommit())
667 checkForInterrupts();
668
669 checkPcEventQueue();
670
671 bool fromRom = isRomMicroPC(thread->readMicroPC());
672
673 if (!fromRom && !curMacroStaticInst) {
674 Request *ifetch_req = new Request();
675 ifetch_req->setThreadContext(_cpuId, /* thread ID */ 0);
676 setupFetchRequest(ifetch_req);
677 thread->itb->translateTiming(ifetch_req, tc, &fetchTranslation,
678 BaseTLB::Execute);
679 } else {
680 _status = IcacheWaitResponse;
681 completeIfetch(NULL);
682
683 numCycles += tickToCycles(curTick - previousTick);
684 previousTick = curTick;
685 }
686 }
687
688
689 void
690 TimingSimpleCPU::sendFetch(Fault fault, RequestPtr req, ThreadContext *tc)
691 {
692 if (fault == NoFault) {
693 ifetch_pkt = new Packet(req, MemCmd::ReadReq, Packet::Broadcast);
694 ifetch_pkt->dataStatic(&inst);
695
696 if (!icachePort.sendTiming(ifetch_pkt)) {
697 // Need to wait for retry
698 _status = IcacheRetry;
699 } else {
700 // Need to wait for cache to respond
701 _status = IcacheWaitResponse;
702 // ownership of packet transferred to memory system
703 ifetch_pkt = NULL;
704 }
705 } else {
706 delete req;
707 // fetch fault: advance directly to next instruction (fault handler)
708 advanceInst(fault);
709 }
710
711 numCycles += tickToCycles(curTick - previousTick);
712 previousTick = curTick;
713 }
714
715
716 void
717 TimingSimpleCPU::advanceInst(Fault fault)
718 {
719 if (fault != NoFault || !stayAtPC)
720 advancePC(fault);
721
722 if (_status == Running) {
723 // kick off fetch of next instruction... callback from icache
724 // response will cause that instruction to be executed,
725 // keeping the CPU running.
726 fetch();
727 }
728 }
729
730
731 void
732 TimingSimpleCPU::completeIfetch(PacketPtr pkt)
733 {
734 DPRINTF(SimpleCPU, "Complete ICache Fetch\n");
735
736 // received a response from the icache: execute the received
737 // instruction
738
739 assert(!pkt || !pkt->isError());
740 assert(_status == IcacheWaitResponse);
741
742 _status = Running;
743
744 numCycles += tickToCycles(curTick - previousTick);
745 previousTick = curTick;
746
747 if (getState() == SimObject::Draining) {
748 if (pkt) {
749 delete pkt->req;
750 delete pkt;
751 }
752
753 completeDrain();
754 return;
755 }
756
757 preExecute();
758 if (curStaticInst &&
759 curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
760 // load or store: just send to dcache
761 Fault fault = curStaticInst->initiateAcc(this, traceData);
762 if (_status != Running) {
763 // instruction will complete in dcache response callback
764 assert(_status == DcacheWaitResponse ||
765 _status == DcacheRetry || DTBWaitResponse);
766 assert(fault == NoFault);
767 } else {
768 if (fault != NoFault && traceData) {
769 // If there was a fault, we shouldn't trace this instruction.
770 delete traceData;
771 traceData = NULL;
772 }
773
774 postExecute();
775 // @todo remove me after debugging with legion done
776 if (curStaticInst && (!curStaticInst->isMicroop() ||
777 curStaticInst->isFirstMicroop()))
778 instCnt++;
779 advanceInst(fault);
780 }
781 } else if (curStaticInst) {
782 // non-memory instruction: execute completely now
783 Fault fault = curStaticInst->execute(this, traceData);
784
785 // keep an instruction count
786 if (fault == NoFault)
787 countInst();
788 else if (traceData) {
789 // If there was a fault, we shouldn't trace this instruction.
790 delete traceData;
791 traceData = NULL;
792 }
793
794 postExecute();
795 // @todo remove me after debugging with legion done
796 if (curStaticInst && (!curStaticInst->isMicroop() ||
797 curStaticInst->isFirstMicroop()))
798 instCnt++;
799 advanceInst(fault);
800 } else {
801 advanceInst(NoFault);
802 }
803
804 if (pkt) {
805 delete pkt->req;
806 delete pkt;
807 }
808 }
809
810 void
811 TimingSimpleCPU::IcachePort::ITickEvent::process()
812 {
813 cpu->completeIfetch(pkt);
814 }
815
816 bool
817 TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
818 {
819 if (pkt->isResponse() && !pkt->wasNacked()) {
820 // delay processing of returned data until next CPU clock edge
821 Tick next_tick = cpu->nextCycle(curTick);
822
823 if (next_tick == curTick)
824 cpu->completeIfetch(pkt);
825 else
826 tickEvent.schedule(pkt, next_tick);
827
828 return true;
829 }
830 else if (pkt->wasNacked()) {
831 assert(cpu->_status == IcacheWaitResponse);
832 pkt->reinitNacked();
833 if (!sendTiming(pkt)) {
834 cpu->_status = IcacheRetry;
835 cpu->ifetch_pkt = pkt;
836 }
837 }
838 //Snooping a Coherence Request, do nothing
839 return true;
840 }
841
842 void
843 TimingSimpleCPU::IcachePort::recvRetry()
844 {
845 // we shouldn't get a retry unless we have a packet that we're
846 // waiting to transmit
847 assert(cpu->ifetch_pkt != NULL);
848 assert(cpu->_status == IcacheRetry);
849 PacketPtr tmp = cpu->ifetch_pkt;
850 if (sendTiming(tmp)) {
851 cpu->_status = IcacheWaitResponse;
852 cpu->ifetch_pkt = NULL;
853 }
854 }
855
856 void
857 TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
858 {
859 // received a response from the dcache: complete the load or store
860 // instruction
861 assert(!pkt->isError());
862
863 numCycles += tickToCycles(curTick - previousTick);
864 previousTick = curTick;
865
866 if (pkt->senderState) {
867 SplitFragmentSenderState * send_state =
868 dynamic_cast<SplitFragmentSenderState *>(pkt->senderState);
869 assert(send_state);
870 delete pkt->req;
871 delete pkt;
872 PacketPtr big_pkt = send_state->bigPkt;
873 delete send_state;
874
875 SplitMainSenderState * main_send_state =
876 dynamic_cast<SplitMainSenderState *>(big_pkt->senderState);
877 assert(main_send_state);
878 // Record the fact that this packet is no longer outstanding.
879 assert(main_send_state->outstanding != 0);
880 main_send_state->outstanding--;
881
882 if (main_send_state->outstanding) {
883 return;
884 } else {
885 delete main_send_state;
886 big_pkt->senderState = NULL;
887 pkt = big_pkt;
888 }
889 }
890
891 assert(_status == DcacheWaitResponse || _status == DTBWaitResponse);
892 _status = Running;
893
894 Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
895
896 // keep an instruction count
897 if (fault == NoFault)
898 countInst();
899 else if (traceData) {
900 // If there was a fault, we shouldn't trace this instruction.
901 delete traceData;
902 traceData = NULL;
903 }
904
905 // the locked flag may be cleared on the response packet, so check
906 // pkt->req and not pkt to see if it was a load-locked
907 if (pkt->isRead() && pkt->req->isLLSC()) {
908 TheISA::handleLockedRead(thread, pkt->req);
909 }
910
911 delete pkt->req;
912 delete pkt;
913
914 postExecute();
915
916 if (getState() == SimObject::Draining) {
917 advancePC(fault);
918 completeDrain();
919
920 return;
921 }
922
923 advanceInst(fault);
924 }
925
926
927 void
928 TimingSimpleCPU::completeDrain()
929 {
930 DPRINTF(Config, "Done draining\n");
931 changeState(SimObject::Drained);
932 drainEvent->process();
933 }
934
935 void
936 TimingSimpleCPU::DcachePort::setPeer(Port *port)
937 {
938 Port::setPeer(port);
939
940 #if FULL_SYSTEM
941 // Update the ThreadContext's memory ports (Functional/Virtual
942 // Ports)
943 cpu->tcBase()->connectMemPorts(cpu->tcBase());
944 #endif
945 }
946
947 bool
948 TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
949 {
950 if (pkt->isResponse() && !pkt->wasNacked()) {
951 // delay processing of returned data until next CPU clock edge
952 Tick next_tick = cpu->nextCycle(curTick);
953
954 if (next_tick == curTick) {
955 cpu->completeDataAccess(pkt);
956 } else {
957 tickEvent.schedule(pkt, next_tick);
958 }
959
960 return true;
961 }
962 else if (pkt->wasNacked()) {
963 assert(cpu->_status == DcacheWaitResponse);
964 pkt->reinitNacked();
965 if (!sendTiming(pkt)) {
966 cpu->_status = DcacheRetry;
967 cpu->dcache_pkt = pkt;
968 }
969 }
970 //Snooping a Coherence Request, do nothing
971 return true;
972 }
973
974 void
975 TimingSimpleCPU::DcachePort::DTickEvent::process()
976 {
977 cpu->completeDataAccess(pkt);
978 }
979
980 void
981 TimingSimpleCPU::DcachePort::recvRetry()
982 {
983 // we shouldn't get a retry unless we have a packet that we're
984 // waiting to transmit
985 assert(cpu->dcache_pkt != NULL);
986 assert(cpu->_status == DcacheRetry);
987 PacketPtr tmp = cpu->dcache_pkt;
988 if (tmp->senderState) {
989 // This is a packet from a split access.
990 SplitFragmentSenderState * send_state =
991 dynamic_cast<SplitFragmentSenderState *>(tmp->senderState);
992 assert(send_state);
993 PacketPtr big_pkt = send_state->bigPkt;
994
995 SplitMainSenderState * main_send_state =
996 dynamic_cast<SplitMainSenderState *>(big_pkt->senderState);
997 assert(main_send_state);
998
999 if (sendTiming(tmp)) {
1000 // If we were able to send without retrying, record that fact
1001 // and try sending the other fragment.
1002 send_state->clearFromParent();
1003 int other_index = main_send_state->getPendingFragment();
1004 if (other_index > 0) {
1005 tmp = main_send_state->fragments[other_index];
1006 cpu->dcache_pkt = tmp;
1007 if ((big_pkt->isRead() && cpu->handleReadPacket(tmp)) ||
1008 (big_pkt->isWrite() && cpu->handleWritePacket())) {
1009 main_send_state->fragments[other_index] = NULL;
1010 }
1011 } else {
1012 cpu->_status = DcacheWaitResponse;
1013 // memory system takes ownership of packet
1014 cpu->dcache_pkt = NULL;
1015 }
1016 }
1017 } else if (sendTiming(tmp)) {
1018 cpu->_status = DcacheWaitResponse;
1019 // memory system takes ownership of packet
1020 cpu->dcache_pkt = NULL;
1021 }
1022 }
1023
1024 TimingSimpleCPU::IprEvent::IprEvent(Packet *_pkt, TimingSimpleCPU *_cpu,
1025 Tick t)
1026 : pkt(_pkt), cpu(_cpu)
1027 {
1028 cpu->schedule(this, t);
1029 }
1030
1031 void
1032 TimingSimpleCPU::IprEvent::process()
1033 {
1034 cpu->completeDataAccess(pkt);
1035 }
1036
1037 const char *
1038 TimingSimpleCPU::IprEvent::description() const
1039 {
1040 return "Timing Simple CPU Delay IPR event";
1041 }
1042
1043
1044 void
1045 TimingSimpleCPU::printAddr(Addr a)
1046 {
1047 dcachePort.printAddr(a);
1048 }
1049
1050
1051 ////////////////////////////////////////////////////////////////////////
1052 //
1053 // TimingSimpleCPU Simulation Object
1054 //
1055 TimingSimpleCPU *
1056 TimingSimpleCPUParams::create()
1057 {
1058 numThreads = 1;
1059 #if !FULL_SYSTEM
1060 if (workload.size() != 1)
1061 panic("only one workload allowed");
1062 #endif
1063 return new TimingSimpleCPU(this);
1064 }