timing.cc revision 5669
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    bool fromRom = isRomMicroPC(thread->readMicroPC());
535
536    if (!fromRom) {
537        Request *ifetch_req = new Request();
538        ifetch_req->setThreadContext(cpuId, /* thread ID */ 0);
539        Fault fault = setupFetchRequest(ifetch_req);
540
541        ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast);
542        ifetch_pkt->dataStatic(&inst);
543
544        if (fault == NoFault) {
545            if (!icachePort.sendTiming(ifetch_pkt)) {
546                // Need to wait for retry
547                _status = IcacheRetry;
548            } else {
549                // Need to wait for cache to respond
550                _status = IcacheWaitResponse;
551                // ownership of packet transferred to memory system
552                ifetch_pkt = NULL;
553            }
554        } else {
555            delete ifetch_req;
556            delete ifetch_pkt;
557            // fetch fault: advance directly to next instruction (fault handler)
558            advanceInst(fault);
559        }
560    } else {
561        _status = IcacheWaitResponse;
562        completeIfetch(NULL);
563    }
564
565    numCycles += tickToCycles(curTick - previousTick);
566    previousTick = curTick;
567}
568
569
570void
571TimingSimpleCPU::advanceInst(Fault fault)
572{
573    advancePC(fault);
574
575    if (_status == Running) {
576        // kick off fetch of next instruction... callback from icache
577        // response will cause that instruction to be executed,
578        // keeping the CPU running.
579        fetch();
580    }
581}
582
583
584void
585TimingSimpleCPU::completeIfetch(PacketPtr pkt)
586{
587    DPRINTF(SimpleCPU, "Complete ICache Fetch\n");
588
589    // received a response from the icache: execute the received
590    // instruction
591
592    assert(!pkt || !pkt->isError());
593    assert(_status == IcacheWaitResponse);
594
595    _status = Running;
596
597    numCycles += tickToCycles(curTick - previousTick);
598    previousTick = curTick;
599
600    if (getState() == SimObject::Draining) {
601        if (pkt) {
602            delete pkt->req;
603            delete pkt;
604        }
605
606        completeDrain();
607        return;
608    }
609
610    preExecute();
611    if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
612        // load or store: just send to dcache
613        Fault fault = curStaticInst->initiateAcc(this, traceData);
614        if (_status != Running) {
615            // instruction will complete in dcache response callback
616            assert(_status == DcacheWaitResponse || _status == DcacheRetry);
617            assert(fault == NoFault);
618        } else {
619            if (fault == NoFault) {
620                // Note that ARM can have NULL packets if the instruction gets
621                // squashed due to predication
622                // early fail on store conditional: complete now
623                assert(dcache_pkt != NULL || THE_ISA == ARM_ISA);
624
625                fault = curStaticInst->completeAcc(dcache_pkt, this,
626                                                   traceData);
627                if (dcache_pkt != NULL)
628                {
629                    delete dcache_pkt->req;
630                    delete dcache_pkt;
631                    dcache_pkt = NULL;
632                }
633
634                // keep an instruction count
635                if (fault == NoFault)
636                    countInst();
637            } else if (traceData) {
638                // If there was a fault, we shouldn't trace this instruction.
639                delete traceData;
640                traceData = NULL;
641            }
642
643            postExecute();
644            // @todo remove me after debugging with legion done
645            if (curStaticInst && (!curStaticInst->isMicroop() ||
646                        curStaticInst->isFirstMicroop()))
647                instCnt++;
648            advanceInst(fault);
649        }
650    } else {
651        // non-memory instruction: execute completely now
652        Fault fault = curStaticInst->execute(this, traceData);
653
654        // keep an instruction count
655        if (fault == NoFault)
656            countInst();
657        else if (traceData) {
658            // If there was a fault, we shouldn't trace this instruction.
659            delete traceData;
660            traceData = NULL;
661        }
662
663        postExecute();
664        // @todo remove me after debugging with legion done
665        if (curStaticInst && (!curStaticInst->isMicroop() ||
666                    curStaticInst->isFirstMicroop()))
667            instCnt++;
668        advanceInst(fault);
669    }
670
671    if (pkt) {
672        delete pkt->req;
673        delete pkt;
674    }
675}
676
677void
678TimingSimpleCPU::IcachePort::ITickEvent::process()
679{
680    cpu->completeIfetch(pkt);
681}
682
683bool
684TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
685{
686    if (pkt->isResponse() && !pkt->wasNacked()) {
687        // delay processing of returned data until next CPU clock edge
688        Tick next_tick = cpu->nextCycle(curTick);
689
690        if (next_tick == curTick)
691            cpu->completeIfetch(pkt);
692        else
693            tickEvent.schedule(pkt, next_tick);
694
695        return true;
696    }
697    else if (pkt->wasNacked()) {
698        assert(cpu->_status == IcacheWaitResponse);
699        pkt->reinitNacked();
700        if (!sendTiming(pkt)) {
701            cpu->_status = IcacheRetry;
702            cpu->ifetch_pkt = pkt;
703        }
704    }
705    //Snooping a Coherence Request, do nothing
706    return true;
707}
708
709void
710TimingSimpleCPU::IcachePort::recvRetry()
711{
712    // we shouldn't get a retry unless we have a packet that we're
713    // waiting to transmit
714    assert(cpu->ifetch_pkt != NULL);
715    assert(cpu->_status == IcacheRetry);
716    PacketPtr tmp = cpu->ifetch_pkt;
717    if (sendTiming(tmp)) {
718        cpu->_status = IcacheWaitResponse;
719        cpu->ifetch_pkt = NULL;
720    }
721}
722
723void
724TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
725{
726    // received a response from the dcache: complete the load or store
727    // instruction
728    assert(!pkt->isError());
729    assert(_status == DcacheWaitResponse);
730    _status = Running;
731
732    numCycles += tickToCycles(curTick - previousTick);
733    previousTick = curTick;
734
735    Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
736
737    // keep an instruction count
738    if (fault == NoFault)
739        countInst();
740    else if (traceData) {
741        // If there was a fault, we shouldn't trace this instruction.
742        delete traceData;
743        traceData = NULL;
744    }
745
746    // the locked flag may be cleared on the response packet, so check
747    // pkt->req and not pkt to see if it was a load-locked
748    if (pkt->isRead() && pkt->req->isLocked()) {
749        TheISA::handleLockedRead(thread, pkt->req);
750    }
751
752    delete pkt->req;
753    delete pkt;
754
755    postExecute();
756
757    if (getState() == SimObject::Draining) {
758        advancePC(fault);
759        completeDrain();
760
761        return;
762    }
763
764    advanceInst(fault);
765}
766
767
768void
769TimingSimpleCPU::completeDrain()
770{
771    DPRINTF(Config, "Done draining\n");
772    changeState(SimObject::Drained);
773    drainEvent->process();
774}
775
776void
777TimingSimpleCPU::DcachePort::setPeer(Port *port)
778{
779    Port::setPeer(port);
780
781#if FULL_SYSTEM
782    // Update the ThreadContext's memory ports (Functional/Virtual
783    // Ports)
784    cpu->tcBase()->connectMemPorts(cpu->tcBase());
785#endif
786}
787
788bool
789TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
790{
791    if (pkt->isResponse() && !pkt->wasNacked()) {
792        // delay processing of returned data until next CPU clock edge
793        Tick next_tick = cpu->nextCycle(curTick);
794
795        if (next_tick == curTick)
796            cpu->completeDataAccess(pkt);
797        else
798            tickEvent.schedule(pkt, next_tick);
799
800        return true;
801    }
802    else if (pkt->wasNacked()) {
803        assert(cpu->_status == DcacheWaitResponse);
804        pkt->reinitNacked();
805        if (!sendTiming(pkt)) {
806            cpu->_status = DcacheRetry;
807            cpu->dcache_pkt = pkt;
808        }
809    }
810    //Snooping a Coherence Request, do nothing
811    return true;
812}
813
814void
815TimingSimpleCPU::DcachePort::DTickEvent::process()
816{
817    cpu->completeDataAccess(pkt);
818}
819
820void
821TimingSimpleCPU::DcachePort::recvRetry()
822{
823    // we shouldn't get a retry unless we have a packet that we're
824    // waiting to transmit
825    assert(cpu->dcache_pkt != NULL);
826    assert(cpu->_status == DcacheRetry);
827    PacketPtr tmp = cpu->dcache_pkt;
828    if (sendTiming(tmp)) {
829        cpu->_status = DcacheWaitResponse;
830        // memory system takes ownership of packet
831        cpu->dcache_pkt = NULL;
832    }
833}
834
835TimingSimpleCPU::IprEvent::IprEvent(Packet *_pkt, TimingSimpleCPU *_cpu,
836    Tick t)
837    : pkt(_pkt), cpu(_cpu)
838{
839    cpu->schedule(this, t);
840}
841
842void
843TimingSimpleCPU::IprEvent::process()
844{
845    cpu->completeDataAccess(pkt);
846}
847
848const char *
849TimingSimpleCPU::IprEvent::description() const
850{
851    return "Timing Simple CPU Delay IPR event";
852}
853
854
855void
856TimingSimpleCPU::printAddr(Addr a)
857{
858    dcachePort.printAddr(a);
859}
860
861
862////////////////////////////////////////////////////////////////////////
863//
864//  TimingSimpleCPU Simulation Object
865//
866TimingSimpleCPU *
867TimingSimpleCPUParams::create()
868{
869    numThreads = 1;
870#if !FULL_SYSTEM
871    if (workload.size() != 1)
872        panic("only one workload allowed");
873#endif
874    return new TimingSimpleCPU(this);
875}
876