timing.cc revision 5529:9ae69b9cd7fd
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    Event::schedule(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                fetchEvent->deschedule();
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        fetchEvent->deschedule();
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, nextCycle(curTick + ticks(delay)));
232}
233
234
235void
236TimingSimpleCPU::suspendContext(int thread_num)
237{
238    DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num);
239
240    assert(thread_num == 0);
241    assert(thread);
242
243    assert(_status == Running);
244
245    // just change status to Idle... if status != Running,
246    // completeInst() will not initiate fetch of next instruction.
247
248    notIdleFraction--;
249    _status = Idle;
250}
251
252
253template <class T>
254Fault
255TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
256{
257    Request *req =
258        new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
259                    cpuId, /* thread ID */ 0);
260
261    if (traceData) {
262        traceData->setAddr(req->getVaddr());
263    }
264
265   // translate to physical address
266    Fault fault = thread->translateDataReadReq(req);
267
268    // Now do the access.
269    if (fault == NoFault) {
270        PacketPtr pkt =
271            new Packet(req,
272                       (req->isLocked() ?
273                        MemCmd::LoadLockedReq : MemCmd::ReadReq),
274                       Packet::Broadcast);
275        pkt->dataDynamic<T>(new T);
276
277        if (req->isMmapedIpr()) {
278            Tick delay;
279            delay = TheISA::handleIprRead(thread->getTC(), pkt);
280            new IprEvent(pkt, this, nextCycle(curTick + delay));
281            _status = DcacheWaitResponse;
282            dcache_pkt = NULL;
283        } else if (!dcachePort.sendTiming(pkt)) {
284            _status = DcacheRetry;
285            dcache_pkt = pkt;
286        } else {
287            _status = DcacheWaitResponse;
288            // memory system takes ownership of packet
289            dcache_pkt = NULL;
290        }
291
292        // This will need a new way to tell if it has a dcache attached.
293        if (req->isUncacheable())
294            recordEvent("Uncached Read");
295    } else {
296        delete req;
297    }
298
299    if (traceData) {
300        traceData->setData(data);
301    }
302    return fault;
303}
304
305Fault
306TimingSimpleCPU::translateDataReadAddr(Addr vaddr, Addr &paddr,
307        int size, unsigned flags)
308{
309    Request *req =
310        new Request(0, vaddr, size, flags, thread->readPC(), cpuId, 0);
311
312    if (traceData) {
313        traceData->setAddr(vaddr);
314    }
315
316    Fault fault = thread->translateDataWriteReq(req);
317
318    if (fault == NoFault)
319        paddr = req->getPaddr();
320
321    delete req;
322    return fault;
323}
324
325#ifndef DOXYGEN_SHOULD_SKIP_THIS
326
327template
328Fault
329TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
330
331template
332Fault
333TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
334
335template
336Fault
337TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
338
339template
340Fault
341TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
342
343template
344Fault
345TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
346
347template
348Fault
349TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
350
351#endif //DOXYGEN_SHOULD_SKIP_THIS
352
353template<>
354Fault
355TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
356{
357    return read(addr, *(uint64_t*)&data, flags);
358}
359
360template<>
361Fault
362TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
363{
364    return read(addr, *(uint32_t*)&data, flags);
365}
366
367
368template<>
369Fault
370TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
371{
372    return read(addr, (uint32_t&)data, flags);
373}
374
375
376template <class T>
377Fault
378TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
379{
380    Request *req =
381        new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
382                    cpuId, /* thread ID */ 0);
383
384    if (traceData) {
385        traceData->setAddr(req->getVaddr());
386    }
387
388    // translate to physical address
389    Fault fault = thread->translateDataWriteReq(req);
390
391    // Now do the access.
392    if (fault == NoFault) {
393        MemCmd cmd = MemCmd::WriteReq; // default
394        bool do_access = true;  // flag to suppress cache access
395
396        if (req->isLocked()) {
397            cmd = MemCmd::StoreCondReq;
398            do_access = TheISA::handleLockedWrite(thread, req);
399        } else if (req->isSwap()) {
400            cmd = MemCmd::SwapReq;
401            if (req->isCondSwap()) {
402                assert(res);
403                req->setExtraData(*res);
404            }
405        }
406
407        // Note: need to allocate dcache_pkt even if do_access is
408        // false, as it's used unconditionally to call completeAcc().
409        assert(dcache_pkt == NULL);
410        dcache_pkt = new Packet(req, cmd, Packet::Broadcast);
411        dcache_pkt->allocate();
412        dcache_pkt->set(data);
413
414        if (do_access) {
415            if (req->isMmapedIpr()) {
416                Tick delay;
417                dcache_pkt->set(htog(data));
418                delay = TheISA::handleIprWrite(thread->getTC(), dcache_pkt);
419                new IprEvent(dcache_pkt, this, nextCycle(curTick + delay));
420                _status = DcacheWaitResponse;
421                dcache_pkt = NULL;
422            } else if (!dcachePort.sendTiming(dcache_pkt)) {
423                _status = DcacheRetry;
424            } else {
425                _status = DcacheWaitResponse;
426                // memory system takes ownership of packet
427                dcache_pkt = NULL;
428            }
429        }
430        // This will need a new way to tell if it's hooked up to a cache or not.
431        if (req->isUncacheable())
432            recordEvent("Uncached Write");
433    } else {
434        delete req;
435    }
436
437    if (traceData) {
438        traceData->setData(data);
439    }
440
441    // If the write needs to have a fault on the access, consider calling
442    // changeStatus() and changing it to "bad addr write" or something.
443    return fault;
444}
445
446Fault
447TimingSimpleCPU::translateDataWriteAddr(Addr vaddr, Addr &paddr,
448        int size, unsigned flags)
449{
450    Request *req =
451        new Request(0, vaddr, size, flags, thread->readPC(), cpuId, 0);
452
453    if (traceData) {
454        traceData->setAddr(vaddr);
455    }
456
457    Fault fault = thread->translateDataWriteReq(req);
458
459    if (fault == NoFault)
460        paddr = req->getPaddr();
461
462    delete req;
463    return fault;
464}
465
466
467#ifndef DOXYGEN_SHOULD_SKIP_THIS
468template
469Fault
470TimingSimpleCPU::write(Twin32_t data, Addr addr,
471                       unsigned flags, uint64_t *res);
472
473template
474Fault
475TimingSimpleCPU::write(Twin64_t data, Addr addr,
476                       unsigned flags, uint64_t *res);
477
478template
479Fault
480TimingSimpleCPU::write(uint64_t data, Addr addr,
481                       unsigned flags, uint64_t *res);
482
483template
484Fault
485TimingSimpleCPU::write(uint32_t data, Addr addr,
486                       unsigned flags, uint64_t *res);
487
488template
489Fault
490TimingSimpleCPU::write(uint16_t data, Addr addr,
491                       unsigned flags, uint64_t *res);
492
493template
494Fault
495TimingSimpleCPU::write(uint8_t data, Addr addr,
496                       unsigned flags, uint64_t *res);
497
498#endif //DOXYGEN_SHOULD_SKIP_THIS
499
500template<>
501Fault
502TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
503{
504    return write(*(uint64_t*)&data, addr, flags, res);
505}
506
507template<>
508Fault
509TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
510{
511    return write(*(uint32_t*)&data, addr, flags, res);
512}
513
514
515template<>
516Fault
517TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
518{
519    return write((uint32_t)data, addr, flags, res);
520}
521
522
523void
524TimingSimpleCPU::fetch()
525{
526    DPRINTF(SimpleCPU, "Fetch\n");
527
528    if (!curStaticInst || !curStaticInst->isDelayedCommit())
529        checkForInterrupts();
530
531    checkPcEventQueue();
532
533    Request *ifetch_req = new Request();
534    ifetch_req->setThreadContext(cpuId, /* thread ID */ 0);
535    Fault fault = setupFetchRequest(ifetch_req);
536
537    ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast);
538    ifetch_pkt->dataStatic(&inst);
539
540    if (fault == NoFault) {
541        if (!icachePort.sendTiming(ifetch_pkt)) {
542            // Need to wait for retry
543            _status = IcacheRetry;
544        } else {
545            // Need to wait for cache to respond
546            _status = IcacheWaitResponse;
547            // ownership of packet transferred to memory system
548            ifetch_pkt = NULL;
549        }
550    } else {
551        delete ifetch_req;
552        delete ifetch_pkt;
553        // fetch fault: advance directly to next instruction (fault handler)
554        advanceInst(fault);
555    }
556
557    numCycles += tickToCycles(curTick - previousTick);
558    previousTick = curTick;
559}
560
561
562void
563TimingSimpleCPU::advanceInst(Fault fault)
564{
565    advancePC(fault);
566
567    if (_status == Running) {
568        // kick off fetch of next instruction... callback from icache
569        // response will cause that instruction to be executed,
570        // keeping the CPU running.
571        fetch();
572    }
573}
574
575
576void
577TimingSimpleCPU::completeIfetch(PacketPtr pkt)
578{
579    DPRINTF(SimpleCPU, "Complete ICache Fetch\n");
580
581    // received a response from the icache: execute the received
582    // instruction
583    assert(!pkt->isError());
584    assert(_status == IcacheWaitResponse);
585
586    _status = Running;
587
588    numCycles += tickToCycles(curTick - previousTick);
589    previousTick = curTick;
590
591    if (getState() == SimObject::Draining) {
592        delete pkt->req;
593        delete pkt;
594
595        completeDrain();
596        return;
597    }
598
599    preExecute();
600    if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
601        // load or store: just send to dcache
602        Fault fault = curStaticInst->initiateAcc(this, traceData);
603        if (_status != Running) {
604            // instruction will complete in dcache response callback
605            assert(_status == DcacheWaitResponse || _status == DcacheRetry);
606            assert(fault == NoFault);
607        } else {
608            if (fault == NoFault) {
609                // Note that ARM can have NULL packets if the instruction gets
610                // squashed due to predication
611                // early fail on store conditional: complete now
612                assert(dcache_pkt != NULL || THE_ISA == ARM_ISA);
613
614                fault = curStaticInst->completeAcc(dcache_pkt, this,
615                                                   traceData);
616                if (dcache_pkt != NULL)
617                {
618                    delete dcache_pkt->req;
619                    delete dcache_pkt;
620                    dcache_pkt = NULL;
621                }
622
623                // keep an instruction count
624                if (fault == NoFault)
625                    countInst();
626            } else if (traceData) {
627                // If there was a fault, we shouldn't trace this instruction.
628                delete traceData;
629                traceData = NULL;
630            }
631
632            postExecute();
633            // @todo remove me after debugging with legion done
634            if (curStaticInst && (!curStaticInst->isMicroop() ||
635                        curStaticInst->isFirstMicroop()))
636                instCnt++;
637            advanceInst(fault);
638        }
639    } else {
640        // non-memory instruction: execute completely now
641        Fault fault = curStaticInst->execute(this, traceData);
642
643        // keep an instruction count
644        if (fault == NoFault)
645            countInst();
646        else if (traceData) {
647            // If there was a fault, we shouldn't trace this instruction.
648            delete traceData;
649            traceData = NULL;
650        }
651
652        postExecute();
653        // @todo remove me after debugging with legion done
654        if (curStaticInst && (!curStaticInst->isMicroop() ||
655                    curStaticInst->isFirstMicroop()))
656            instCnt++;
657        advanceInst(fault);
658    }
659
660    delete pkt->req;
661    delete pkt;
662}
663
664void
665TimingSimpleCPU::IcachePort::ITickEvent::process()
666{
667    cpu->completeIfetch(pkt);
668}
669
670bool
671TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
672{
673    if (pkt->isResponse() && !pkt->wasNacked()) {
674        // delay processing of returned data until next CPU clock edge
675        Tick next_tick = cpu->nextCycle(curTick);
676
677        if (next_tick == curTick)
678            cpu->completeIfetch(pkt);
679        else
680            tickEvent.schedule(pkt, next_tick);
681
682        return true;
683    }
684    else if (pkt->wasNacked()) {
685        assert(cpu->_status == IcacheWaitResponse);
686        pkt->reinitNacked();
687        if (!sendTiming(pkt)) {
688            cpu->_status = IcacheRetry;
689            cpu->ifetch_pkt = pkt;
690        }
691    }
692    //Snooping a Coherence Request, do nothing
693    return true;
694}
695
696void
697TimingSimpleCPU::IcachePort::recvRetry()
698{
699    // we shouldn't get a retry unless we have a packet that we're
700    // waiting to transmit
701    assert(cpu->ifetch_pkt != NULL);
702    assert(cpu->_status == IcacheRetry);
703    PacketPtr tmp = cpu->ifetch_pkt;
704    if (sendTiming(tmp)) {
705        cpu->_status = IcacheWaitResponse;
706        cpu->ifetch_pkt = NULL;
707    }
708}
709
710void
711TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
712{
713    // received a response from the dcache: complete the load or store
714    // instruction
715    assert(!pkt->isError());
716    assert(_status == DcacheWaitResponse);
717    _status = Running;
718
719    numCycles += tickToCycles(curTick - previousTick);
720    previousTick = curTick;
721
722    Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
723
724    // keep an instruction count
725    if (fault == NoFault)
726        countInst();
727    else if (traceData) {
728        // If there was a fault, we shouldn't trace this instruction.
729        delete traceData;
730        traceData = NULL;
731    }
732
733    // the locked flag may be cleared on the response packet, so check
734    // pkt->req and not pkt to see if it was a load-locked
735    if (pkt->isRead() && pkt->req->isLocked()) {
736        TheISA::handleLockedRead(thread, pkt->req);
737    }
738
739    delete pkt->req;
740    delete pkt;
741
742    postExecute();
743
744    if (getState() == SimObject::Draining) {
745        advancePC(fault);
746        completeDrain();
747
748        return;
749    }
750
751    advanceInst(fault);
752}
753
754
755void
756TimingSimpleCPU::completeDrain()
757{
758    DPRINTF(Config, "Done draining\n");
759    changeState(SimObject::Drained);
760    drainEvent->process();
761}
762
763void
764TimingSimpleCPU::DcachePort::setPeer(Port *port)
765{
766    Port::setPeer(port);
767
768#if FULL_SYSTEM
769    // Update the ThreadContext's memory ports (Functional/Virtual
770    // Ports)
771    cpu->tcBase()->connectMemPorts(cpu->tcBase());
772#endif
773}
774
775bool
776TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
777{
778    if (pkt->isResponse() && !pkt->wasNacked()) {
779        // delay processing of returned data until next CPU clock edge
780        Tick next_tick = cpu->nextCycle(curTick);
781
782        if (next_tick == curTick)
783            cpu->completeDataAccess(pkt);
784        else
785            tickEvent.schedule(pkt, next_tick);
786
787        return true;
788    }
789    else if (pkt->wasNacked()) {
790        assert(cpu->_status == DcacheWaitResponse);
791        pkt->reinitNacked();
792        if (!sendTiming(pkt)) {
793            cpu->_status = DcacheRetry;
794            cpu->dcache_pkt = pkt;
795        }
796    }
797    //Snooping a Coherence Request, do nothing
798    return true;
799}
800
801void
802TimingSimpleCPU::DcachePort::DTickEvent::process()
803{
804    cpu->completeDataAccess(pkt);
805}
806
807void
808TimingSimpleCPU::DcachePort::recvRetry()
809{
810    // we shouldn't get a retry unless we have a packet that we're
811    // waiting to transmit
812    assert(cpu->dcache_pkt != NULL);
813    assert(cpu->_status == DcacheRetry);
814    PacketPtr tmp = cpu->dcache_pkt;
815    if (sendTiming(tmp)) {
816        cpu->_status = DcacheWaitResponse;
817        // memory system takes ownership of packet
818        cpu->dcache_pkt = NULL;
819    }
820}
821
822TimingSimpleCPU::IprEvent::IprEvent(Packet *_pkt, TimingSimpleCPU *_cpu, Tick t)
823    : Event(&mainEventQueue), pkt(_pkt), cpu(_cpu)
824{
825    schedule(t);
826}
827
828void
829TimingSimpleCPU::IprEvent::process()
830{
831    cpu->completeDataAccess(pkt);
832}
833
834const char *
835TimingSimpleCPU::IprEvent::description() const
836{
837    return "Timing Simple CPU Delay IPR event";
838}
839
840
841void
842TimingSimpleCPU::printAddr(Addr a)
843{
844    dcachePort.printAddr(a);
845}
846
847
848////////////////////////////////////////////////////////////////////////
849//
850//  TimingSimpleCPU Simulation Object
851//
852TimingSimpleCPU *
853TimingSimpleCPUParams::create()
854{
855    numThreads = 1;
856#if !FULL_SYSTEM
857    if (workload.size() != 1)
858        panic("only one workload allowed");
859#endif
860    return new TimingSimpleCPU(this);
861}
862