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