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