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