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