timing.cc revision 3667:1d57100f8bf0
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/utility.hh"
33#include "cpu/exetrace.hh"
34#include "cpu/simple/timing.hh"
35#include "mem/packet.hh"
36#include "mem/packet_access.hh"
37#include "sim/builder.hh"
38#include "sim/system.hh"
39
40using namespace std;
41using namespace TheISA;
42
43Port *
44TimingSimpleCPU::getPort(const std::string &if_name, int idx)
45{
46    if (if_name == "dcache_port")
47        return &dcachePort;
48    else if (if_name == "icache_port")
49        return &icachePort;
50    else
51        panic("No Such Port\n");
52}
53
54void
55TimingSimpleCPU::init()
56{
57    BaseCPU::init();
58#if FULL_SYSTEM
59    for (int i = 0; i < threadContexts.size(); ++i) {
60        ThreadContext *tc = threadContexts[i];
61
62        // initialize CPU, including PC
63        TheISA::initCPU(tc, tc->readCpuId());
64    }
65#endif
66}
67
68Tick
69TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
70{
71    panic("TimingSimpleCPU doesn't expect recvAtomic callback!");
72    return curTick;
73}
74
75void
76TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
77{
78    //No internal storage to update, jusst return
79    return;
80}
81
82void
83TimingSimpleCPU::CpuPort::recvStatusChange(Status status)
84{
85    if (status == RangeChange) {
86        if (!snoopRangeSent) {
87            snoopRangeSent = true;
88            sendStatusChange(Port::RangeChange);
89        }
90        return;
91    }
92
93    panic("TimingSimpleCPU doesn't expect recvStatusChange callback!");
94}
95
96
97void
98TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t)
99{
100    pkt = _pkt;
101    Event::schedule(t);
102}
103
104TimingSimpleCPU::TimingSimpleCPU(Params *p)
105    : BaseSimpleCPU(p), icachePort(this, p->clock), dcachePort(this, p->clock),
106      cpu_id(p->cpu_id)
107{
108    _status = Idle;
109
110    icachePort.snoopRangeSent = false;
111    dcachePort.snoopRangeSent = false;
112
113    ifetch_pkt = dcache_pkt = NULL;
114    drainEvent = NULL;
115    fetchEvent = 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    if (_status != SwitchedOut && _status != Idle) {
160        assert(system->getMemoryMode() == System::Timing);
161
162        // Delete the old event if it existed.
163        if (fetchEvent) {
164            if (fetchEvent->scheduled())
165                fetchEvent->deschedule();
166
167            delete fetchEvent;
168        }
169
170        fetchEvent =
171            new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
172        fetchEvent->schedule(nextCycle());
173    }
174
175    changeState(SimObject::Running);
176    previousTick = curTick;
177}
178
179void
180TimingSimpleCPU::switchOut()
181{
182    assert(status() == Running || status() == Idle);
183    _status = SwitchedOut;
184    numCycles += 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);
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
212    Port *peer;
213    if (icachePort.getPeer() == NULL) {
214        peer = oldCPU->getPort("icache_port")->getPeer();
215        icachePort.setPeer(peer);
216    } else {
217        peer = icachePort.getPeer();
218    }
219    peer->setPeer(&icachePort);
220
221    if (dcachePort.getPeer() == NULL) {
222        peer = oldCPU->getPort("dcache_port")->getPeer();
223        dcachePort.setPeer(peer);
224    } else {
225        peer = dcachePort.getPeer();
226    }
227    peer->setPeer(&dcachePort);
228}
229
230
231void
232TimingSimpleCPU::activateContext(int thread_num, int delay)
233{
234    assert(thread_num == 0);
235    assert(thread);
236
237    assert(_status == Idle);
238
239    notIdleFraction++;
240    _status = Running;
241    // kick things off by initiating the fetch of the next instruction
242    fetchEvent =
243        new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
244    fetchEvent->schedule(nextCycle(curTick + cycles(delay)));
245}
246
247
248void
249TimingSimpleCPU::suspendContext(int thread_num)
250{
251    assert(thread_num == 0);
252    assert(thread);
253
254    assert(_status == Running);
255
256    // just change status to Idle... if status != Running,
257    // completeInst() will not initiate fetch of next instruction.
258
259    notIdleFraction--;
260    _status = Idle;
261}
262
263
264template <class T>
265Fault
266TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
267{
268    Request *req =
269        new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
270                    cpu_id, /* thread ID */ 0);
271
272    if (traceData) {
273        traceData->setAddr(req->getVaddr());
274    }
275
276   // translate to physical address
277    Fault fault = thread->translateDataReadReq(req);
278
279    // Now do the access.
280    if (fault == NoFault) {
281        PacketPtr pkt =
282            new Packet(req, Packet::ReadReq, Packet::Broadcast);
283        pkt->dataDynamic<T>(new T);
284
285        if (!dcachePort.sendTiming(pkt)) {
286            _status = DcacheRetry;
287            dcache_pkt = pkt;
288        } else {
289            _status = DcacheWaitResponse;
290            // memory system takes ownership of packet
291            dcache_pkt = NULL;
292        }
293    } else {
294        delete req;
295    }
296
297    // This will need a new way to tell if it has a dcache attached.
298    if (req->isUncacheable())
299        recordEvent("Uncached Read");
300
301    return fault;
302}
303
304#ifndef DOXYGEN_SHOULD_SKIP_THIS
305
306template
307Fault
308TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
309
310template
311Fault
312TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
313
314template
315Fault
316TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
317
318template
319Fault
320TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
321
322#endif //DOXYGEN_SHOULD_SKIP_THIS
323
324template<>
325Fault
326TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
327{
328    return read(addr, *(uint64_t*)&data, flags);
329}
330
331template<>
332Fault
333TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
334{
335    return read(addr, *(uint32_t*)&data, flags);
336}
337
338
339template<>
340Fault
341TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
342{
343    return read(addr, (uint32_t&)data, flags);
344}
345
346
347template <class T>
348Fault
349TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
350{
351    Request *req =
352        new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
353                    cpu_id, /* thread ID */ 0);
354
355    // translate to physical address
356    Fault fault = thread->translateDataWriteReq(req);
357
358    // Now do the access.
359    if (fault == NoFault) {
360        assert(dcache_pkt == NULL);
361        dcache_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast);
362        dcache_pkt->allocate();
363        dcache_pkt->set(data);
364
365        bool do_access = true;  // flag to suppress cache access
366
367        if (req->isLocked()) {
368            do_access = TheISA::handleLockedWrite(thread, req);
369        }
370
371        if (do_access) {
372            if (!dcachePort.sendTiming(dcache_pkt)) {
373                _status = DcacheRetry;
374            } else {
375                _status = DcacheWaitResponse;
376                // memory system takes ownership of packet
377                dcache_pkt = NULL;
378            }
379        }
380    } else {
381        delete req;
382    }
383
384    // This will need a new way to tell if it's hooked up to a cache or not.
385    if (req->isUncacheable())
386        recordEvent("Uncached Write");
387
388    // If the write needs to have a fault on the access, consider calling
389    // changeStatus() and changing it to "bad addr write" or something.
390    return fault;
391}
392
393
394#ifndef DOXYGEN_SHOULD_SKIP_THIS
395template
396Fault
397TimingSimpleCPU::write(uint64_t data, Addr addr,
398                       unsigned flags, uint64_t *res);
399
400template
401Fault
402TimingSimpleCPU::write(uint32_t data, Addr addr,
403                       unsigned flags, uint64_t *res);
404
405template
406Fault
407TimingSimpleCPU::write(uint16_t data, Addr addr,
408                       unsigned flags, uint64_t *res);
409
410template
411Fault
412TimingSimpleCPU::write(uint8_t data, Addr addr,
413                       unsigned flags, uint64_t *res);
414
415#endif //DOXYGEN_SHOULD_SKIP_THIS
416
417template<>
418Fault
419TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
420{
421    return write(*(uint64_t*)&data, addr, flags, res);
422}
423
424template<>
425Fault
426TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
427{
428    return write(*(uint32_t*)&data, addr, flags, res);
429}
430
431
432template<>
433Fault
434TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
435{
436    return write((uint32_t)data, addr, flags, res);
437}
438
439
440void
441TimingSimpleCPU::fetch()
442{
443    if (!curStaticInst || !curStaticInst->isDelayedCommit())
444        checkForInterrupts();
445
446    Request *ifetch_req = new Request();
447    ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0);
448    Fault fault = setupFetchRequest(ifetch_req);
449
450    ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast);
451    ifetch_pkt->dataStatic(&inst);
452
453    if (fault == NoFault) {
454        if (!icachePort.sendTiming(ifetch_pkt)) {
455            // Need to wait for retry
456            _status = IcacheRetry;
457        } else {
458            // Need to wait for cache to respond
459            _status = IcacheWaitResponse;
460            // ownership of packet transferred to memory system
461            ifetch_pkt = NULL;
462        }
463    } else {
464        delete ifetch_req;
465        delete ifetch_pkt;
466        // fetch fault: advance directly to next instruction (fault handler)
467        advanceInst(fault);
468    }
469
470    numCycles += curTick - previousTick;
471    previousTick = curTick;
472}
473
474
475void
476TimingSimpleCPU::advanceInst(Fault fault)
477{
478    advancePC(fault);
479
480    if (_status == Running) {
481        // kick off fetch of next instruction... callback from icache
482        // response will cause that instruction to be executed,
483        // keeping the CPU running.
484        fetch();
485    }
486}
487
488
489void
490TimingSimpleCPU::completeIfetch(PacketPtr pkt)
491{
492    // received a response from the icache: execute the received
493    // instruction
494    assert(pkt->result == Packet::Success);
495    assert(_status == IcacheWaitResponse);
496
497    _status = Running;
498
499    numCycles += curTick - previousTick;
500    previousTick = curTick;
501
502    if (getState() == SimObject::Draining) {
503        delete pkt->req;
504        delete pkt;
505
506        completeDrain();
507        return;
508    }
509
510    preExecute();
511    if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
512        // load or store: just send to dcache
513        Fault fault = curStaticInst->initiateAcc(this, traceData);
514        if (_status != Running) {
515            // instruction will complete in dcache response callback
516            assert(_status == DcacheWaitResponse || _status == DcacheRetry);
517            assert(fault == NoFault);
518        } else {
519            if (fault == NoFault) {
520                // early fail on store conditional: complete now
521                assert(dcache_pkt != NULL);
522                fault = curStaticInst->completeAcc(dcache_pkt, this,
523                                                   traceData);
524                delete dcache_pkt->req;
525                delete dcache_pkt;
526                dcache_pkt = NULL;
527            }
528            postExecute();
529            advanceInst(fault);
530        }
531    } else {
532        // non-memory instruction: execute completely now
533        Fault fault = curStaticInst->execute(this, traceData);
534        postExecute();
535        advanceInst(fault);
536    }
537
538    delete pkt->req;
539    delete pkt;
540}
541
542void
543TimingSimpleCPU::IcachePort::ITickEvent::process()
544{
545    cpu->completeIfetch(pkt);
546}
547
548bool
549TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
550{
551    if (pkt->isResponse()) {
552        // delay processing of returned data until next CPU clock edge
553        Tick mem_time = pkt->req->getTime();
554        Tick next_tick = cpu->nextCycle(mem_time);
555
556        if (next_tick == curTick)
557            cpu->completeIfetch(pkt);
558        else
559            tickEvent.schedule(pkt, next_tick);
560
561        return true;
562    }
563    else {
564        //Snooping a Coherence Request, do nothing
565        return true;
566    }
567}
568
569void
570TimingSimpleCPU::IcachePort::recvRetry()
571{
572    // we shouldn't get a retry unless we have a packet that we're
573    // waiting to transmit
574    assert(cpu->ifetch_pkt != NULL);
575    assert(cpu->_status == IcacheRetry);
576    PacketPtr tmp = cpu->ifetch_pkt;
577    if (sendTiming(tmp)) {
578        cpu->_status = IcacheWaitResponse;
579        cpu->ifetch_pkt = NULL;
580    }
581}
582
583void
584TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
585{
586    // received a response from the dcache: complete the load or store
587    // instruction
588    assert(pkt->result == Packet::Success);
589    assert(_status == DcacheWaitResponse);
590    _status = Running;
591
592    numCycles += curTick - previousTick;
593    previousTick = curTick;
594
595    Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
596
597    if (pkt->isRead() && pkt->req->isLocked()) {
598        TheISA::handleLockedRead(thread, pkt->req);
599    }
600
601    delete pkt->req;
602    delete pkt;
603
604    postExecute();
605
606    if (getState() == SimObject::Draining) {
607        advancePC(fault);
608        completeDrain();
609
610        return;
611    }
612
613    advanceInst(fault);
614}
615
616
617void
618TimingSimpleCPU::completeDrain()
619{
620    DPRINTF(Config, "Done draining\n");
621    changeState(SimObject::Drained);
622    drainEvent->process();
623}
624
625bool
626TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
627{
628    if (pkt->isResponse()) {
629        // delay processing of returned data until next CPU clock edge
630        Tick mem_time = pkt->req->getTime();
631        Tick next_tick = cpu->nextCycle(mem_time);
632
633        if (next_tick == curTick)
634            cpu->completeDataAccess(pkt);
635        else
636            tickEvent.schedule(pkt, next_tick);
637
638        return true;
639    }
640    else {
641        //Snooping a coherence req, do nothing
642        return true;
643    }
644}
645
646void
647TimingSimpleCPU::DcachePort::DTickEvent::process()
648{
649    cpu->completeDataAccess(pkt);
650}
651
652void
653TimingSimpleCPU::DcachePort::recvRetry()
654{
655    // we shouldn't get a retry unless we have a packet that we're
656    // waiting to transmit
657    assert(cpu->dcache_pkt != NULL);
658    assert(cpu->_status == DcacheRetry);
659    PacketPtr tmp = cpu->dcache_pkt;
660    if (sendTiming(tmp)) {
661        cpu->_status = DcacheWaitResponse;
662        // memory system takes ownership of packet
663        cpu->dcache_pkt = NULL;
664    }
665}
666
667
668////////////////////////////////////////////////////////////////////////
669//
670//  TimingSimpleCPU Simulation Object
671//
672BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
673
674    Param<Counter> max_insts_any_thread;
675    Param<Counter> max_insts_all_threads;
676    Param<Counter> max_loads_any_thread;
677    Param<Counter> max_loads_all_threads;
678    Param<Tick> progress_interval;
679    SimObjectParam<System *> system;
680    Param<int> cpu_id;
681
682#if FULL_SYSTEM
683    SimObjectParam<TheISA::ITB *> itb;
684    SimObjectParam<TheISA::DTB *> dtb;
685    Param<Tick> profile;
686
687    Param<bool> do_quiesce;
688    Param<bool> do_checkpoint_insts;
689    Param<bool> do_statistics_insts;
690#else
691    SimObjectParam<Process *> workload;
692#endif // FULL_SYSTEM
693
694    Param<int> clock;
695    Param<int> phase;
696
697    Param<bool> defer_registration;
698    Param<int> width;
699    Param<bool> function_trace;
700    Param<Tick> function_trace_start;
701    Param<bool> simulate_stalls;
702
703END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
704
705BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
706
707    INIT_PARAM(max_insts_any_thread,
708               "terminate when any thread reaches this inst count"),
709    INIT_PARAM(max_insts_all_threads,
710               "terminate when all threads have reached this inst count"),
711    INIT_PARAM(max_loads_any_thread,
712               "terminate when any thread reaches this load count"),
713    INIT_PARAM(max_loads_all_threads,
714               "terminate when all threads have reached this load count"),
715    INIT_PARAM(progress_interval, "Progress interval"),
716    INIT_PARAM(system, "system object"),
717    INIT_PARAM(cpu_id, "processor ID"),
718
719#if FULL_SYSTEM
720    INIT_PARAM(itb, "Instruction TLB"),
721    INIT_PARAM(dtb, "Data TLB"),
722    INIT_PARAM(profile, ""),
723    INIT_PARAM(do_quiesce, ""),
724    INIT_PARAM(do_checkpoint_insts, ""),
725    INIT_PARAM(do_statistics_insts, ""),
726#else
727    INIT_PARAM(workload, "processes to run"),
728#endif // FULL_SYSTEM
729
730    INIT_PARAM(clock, "clock speed"),
731    INIT_PARAM_DFLT(phase, "clock phase", 0),
732    INIT_PARAM(defer_registration, "defer system registration (for sampling)"),
733    INIT_PARAM(width, "cpu width"),
734    INIT_PARAM(function_trace, "Enable function trace"),
735    INIT_PARAM(function_trace_start, "Cycle to start function trace"),
736    INIT_PARAM(simulate_stalls, "Simulate cache stall cycles")
737
738END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
739
740
741CREATE_SIM_OBJECT(TimingSimpleCPU)
742{
743    TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params();
744    params->name = getInstanceName();
745    params->numberOfThreads = 1;
746    params->max_insts_any_thread = max_insts_any_thread;
747    params->max_insts_all_threads = max_insts_all_threads;
748    params->max_loads_any_thread = max_loads_any_thread;
749    params->max_loads_all_threads = max_loads_all_threads;
750    params->progress_interval = progress_interval;
751    params->deferRegistration = defer_registration;
752    params->clock = clock;
753    params->phase = phase;
754    params->functionTrace = function_trace;
755    params->functionTraceStart = function_trace_start;
756    params->system = system;
757    params->cpu_id = cpu_id;
758
759#if FULL_SYSTEM
760    params->itb = itb;
761    params->dtb = dtb;
762    params->profile = profile;
763    params->do_quiesce = do_quiesce;
764    params->do_checkpoint_insts = do_checkpoint_insts;
765    params->do_statistics_insts = do_statistics_insts;
766#else
767    params->process = workload;
768#endif
769
770    TimingSimpleCPU *cpu = new TimingSimpleCPU(params);
771    return cpu;
772}
773
774REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU)
775
776