base.cc revision 2519
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
29#include <cmath>
30#include <cstdio>
31#include <cstdlib>
32#include <iostream>
33#include <iomanip>
34#include <list>
35#include <sstream>
36#include <string>
37
38#include "arch/utility.hh"
39#include "base/cprintf.hh"
40#include "base/inifile.hh"
41#include "base/loader/symtab.hh"
42#include "base/misc.hh"
43#include "base/pollevent.hh"
44#include "base/range.hh"
45#include "base/stats/events.hh"
46#include "base/trace.hh"
47#include "cpu/base.hh"
48#include "cpu/cpu_exec_context.hh"
49#include "cpu/exec_context.hh"
50#include "cpu/exetrace.hh"
51#include "cpu/profile.hh"
52#include "cpu/sampler/sampler.hh"
53#include "cpu/simple/cpu.hh"
54#include "cpu/smt.hh"
55#include "cpu/static_inst.hh"
56#include "kern/kernel_stats.hh"
57#include "sim/byteswap.hh"
58#include "sim/builder.hh"
59#include "sim/debug.hh"
60#include "sim/host.hh"
61#include "sim/sim_events.hh"
62#include "sim/sim_object.hh"
63#include "sim/stats.hh"
64
65#if FULL_SYSTEM
66#include "base/remote_gdb.hh"
67//#include "mem/functional/memory_control.hh"
68//#include "mem/functional/physical.hh"
69#include "sim/system.hh"
70#include "arch/tlb.hh"
71#include "arch/stacktrace.hh"
72#include "arch/vtophys.hh"
73#else // !FULL_SYSTEM
74#include "mem/mem_object.hh"
75#endif // FULL_SYSTEM
76
77using namespace std;
78using namespace TheISA;
79
80SimpleCPU::TickEvent::TickEvent(SimpleCPU *c, int w)
81    : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c), width(w)
82{
83}
84
85
86void
87SimpleCPU::init()
88{
89    //Create Memory Ports (conect them up)
90    Port *mem_dport = mem->getPort("");
91    dcachePort.setPeer(mem_dport);
92    mem_dport->setPeer(&dcachePort);
93
94    Port *mem_iport = mem->getPort("");
95    icachePort.setPeer(mem_iport);
96    mem_iport->setPeer(&icachePort);
97
98    BaseCPU::init();
99#if FULL_SYSTEM
100    for (int i = 0; i < execContexts.size(); ++i) {
101        ExecContext *xc = execContexts[i];
102
103        // initialize CPU, including PC
104        TheISA::initCPU(xc, xc->readCpuId());
105    }
106#endif
107}
108
109void
110SimpleCPU::TickEvent::process()
111{
112    int count = width;
113    do {
114        cpu->tick();
115    } while (--count > 0 && cpu->status() == Running);
116}
117
118const char *
119SimpleCPU::TickEvent::description()
120{
121    return "SimpleCPU tick event";
122}
123
124
125bool
126SimpleCPU::CpuPort::recvTiming(Packet &pkt)
127{
128    cpu->processResponse(pkt);
129    return true;
130}
131
132Tick
133SimpleCPU::CpuPort::recvAtomic(Packet &pkt)
134{
135    panic("CPU doesn't expect callback!");
136    return curTick;
137}
138
139void
140SimpleCPU::CpuPort::recvFunctional(Packet &pkt)
141{
142    panic("CPU doesn't expect callback!");
143}
144
145void
146SimpleCPU::CpuPort::recvStatusChange(Status status)
147{
148    cpu->recvStatusChange(status);
149}
150
151Packet *
152SimpleCPU::CpuPort::recvRetry()
153{
154    return cpu->processRetry();
155}
156
157SimpleCPU::SimpleCPU(Params *p)
158#if !FULL_SYSTEM
159    : BaseCPU(p), mem(p->mem), icachePort(this),
160      dcachePort(this), tickEvent(this, p->width), cpuXC(NULL)
161#else
162    : BaseCPU(p), icachePort(this), dcachePort(this),
163      tickEvent(this, p->width), cpuXC(NULL)
164#endif
165{
166    _status = Idle;
167
168#if FULL_SYSTEM
169    cpuXC = new CPUExecContext(this, 0, p->system, p->itb, p->dtb);
170#else
171    cpuXC = new CPUExecContext(this, /* thread_num */ 0, p->process,
172            /* asid */ 0);
173#endif // !FULL_SYSTEM
174
175    xcProxy = cpuXC->getProxy();
176
177#if SIMPLE_CPU_MEM_ATOMIC || SIMPLE_CPU_MEM_IMMEDIATE
178    ifetch_req = new CpuRequest;
179    ifetch_req->asid = 0;
180    ifetch_req->size = sizeof(MachInst);
181    ifetch_pkt = new Packet;
182    ifetch_pkt->cmd = Read;
183    ifetch_pkt->data = (uint8_t *)&inst;
184    ifetch_pkt->req = ifetch_req;
185    ifetch_pkt->size = sizeof(MachInst);
186
187    data_read_req = new CpuRequest;
188    data_read_req->asid = 0;
189    data_read_pkt = new Packet;
190    data_read_pkt->cmd = Read;
191    data_read_pkt->data = new uint8_t[8];
192    data_read_pkt->req = data_read_req;
193
194    data_write_req = new CpuRequest;
195    data_write_req->asid = 0;
196    data_write_pkt = new Packet;
197    data_write_pkt->cmd = Write;
198    data_write_pkt->req = data_write_req;
199#endif
200
201    numInst = 0;
202    startNumInst = 0;
203    numLoad = 0;
204    startNumLoad = 0;
205    lastIcacheStall = 0;
206    lastDcacheStall = 0;
207
208    execContexts.push_back(xcProxy);
209}
210
211SimpleCPU::~SimpleCPU()
212{
213}
214
215void
216SimpleCPU::switchOut(Sampler *s)
217{
218    sampler = s;
219    if (status() == DcacheWaitResponse) {
220        DPRINTF(Sampler,"Outstanding dcache access, waiting for completion\n");
221        _status = DcacheWaitSwitch;
222    }
223    else {
224        _status = SwitchedOut;
225
226        if (tickEvent.scheduled())
227            tickEvent.squash();
228
229        sampler->signalSwitched();
230    }
231}
232
233
234void
235SimpleCPU::takeOverFrom(BaseCPU *oldCPU)
236{
237    BaseCPU::takeOverFrom(oldCPU);
238
239    assert(!tickEvent.scheduled());
240
241    // if any of this CPU's ExecContexts are active, mark the CPU as
242    // running and schedule its tick event.
243    for (int i = 0; i < execContexts.size(); ++i) {
244        ExecContext *xc = execContexts[i];
245        if (xc->status() == ExecContext::Active && _status != Running) {
246            _status = Running;
247            tickEvent.schedule(curTick);
248        }
249    }
250}
251
252
253void
254SimpleCPU::activateContext(int thread_num, int delay)
255{
256    assert(thread_num == 0);
257    assert(cpuXC);
258
259    assert(_status == Idle);
260    notIdleFraction++;
261    scheduleTickEvent(delay);
262    _status = Running;
263}
264
265
266void
267SimpleCPU::suspendContext(int thread_num)
268{
269    assert(thread_num == 0);
270    assert(cpuXC);
271
272    assert(_status == Running);
273    notIdleFraction--;
274    unscheduleTickEvent();
275    _status = Idle;
276}
277
278
279void
280SimpleCPU::deallocateContext(int thread_num)
281{
282    // for now, these are equivalent
283    suspendContext(thread_num);
284}
285
286
287void
288SimpleCPU::haltContext(int thread_num)
289{
290    // for now, these are equivalent
291    suspendContext(thread_num);
292}
293
294
295void
296SimpleCPU::regStats()
297{
298    using namespace Stats;
299
300    BaseCPU::regStats();
301
302    numInsts
303        .name(name() + ".num_insts")
304        .desc("Number of instructions executed")
305        ;
306
307    numMemRefs
308        .name(name() + ".num_refs")
309        .desc("Number of memory references")
310        ;
311
312    notIdleFraction
313        .name(name() + ".not_idle_fraction")
314        .desc("Percentage of non-idle cycles")
315        ;
316
317    idleFraction
318        .name(name() + ".idle_fraction")
319        .desc("Percentage of idle cycles")
320        ;
321
322    icacheStallCycles
323        .name(name() + ".icache_stall_cycles")
324        .desc("ICache total stall cycles")
325        .prereq(icacheStallCycles)
326        ;
327
328    dcacheStallCycles
329        .name(name() + ".dcache_stall_cycles")
330        .desc("DCache total stall cycles")
331        .prereq(dcacheStallCycles)
332        ;
333
334    icacheRetryCycles
335        .name(name() + ".icache_retry_cycles")
336        .desc("ICache total retry cycles")
337        .prereq(icacheRetryCycles)
338        ;
339
340    dcacheRetryCycles
341        .name(name() + ".dcache_retry_cycles")
342        .desc("DCache total retry cycles")
343        .prereq(dcacheRetryCycles)
344        ;
345
346    idleFraction = constant(1.0) - notIdleFraction;
347}
348
349void
350SimpleCPU::resetStats()
351{
352    startNumInst = numInst;
353    notIdleFraction = (_status != Idle);
354}
355
356void
357SimpleCPU::serialize(ostream &os)
358{
359    BaseCPU::serialize(os);
360    SERIALIZE_ENUM(_status);
361    SERIALIZE_SCALAR(inst);
362    nameOut(os, csprintf("%s.xc", name()));
363    cpuXC->serialize(os);
364    nameOut(os, csprintf("%s.tickEvent", name()));
365    tickEvent.serialize(os);
366    nameOut(os, csprintf("%s.cacheCompletionEvent", name()));
367}
368
369void
370SimpleCPU::unserialize(Checkpoint *cp, const string &section)
371{
372    BaseCPU::unserialize(cp, section);
373    UNSERIALIZE_ENUM(_status);
374    UNSERIALIZE_SCALAR(inst);
375    cpuXC->unserialize(cp, csprintf("%s.xc", section));
376    tickEvent.unserialize(cp, csprintf("%s.tickEvent", section));
377}
378
379void
380change_thread_state(int thread_number, int activate, int priority)
381{
382}
383
384Fault
385SimpleCPU::copySrcTranslate(Addr src)
386{
387#if 0
388    static bool no_warn = true;
389    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
390    // Only support block sizes of 64 atm.
391    assert(blk_size == 64);
392    int offset = src & (blk_size - 1);
393
394    // Make sure block doesn't span page
395    if (no_warn &&
396        (src & PageMask) != ((src + blk_size) & PageMask) &&
397        (src >> 40) != 0xfffffc) {
398        warn("Copied block source spans pages %x.", src);
399        no_warn = false;
400    }
401
402    memReq->reset(src & ~(blk_size - 1), blk_size);
403
404    // translate to physical address    Fault fault = cpuXC->translateDataReadReq(req);
405
406    if (fault == NoFault) {
407        cpuXC->copySrcAddr = src;
408        cpuXC->copySrcPhysAddr = memReq->paddr + offset;
409    } else {
410        assert(!fault->isAlignmentFault());
411
412        cpuXC->copySrcAddr = 0;
413        cpuXC->copySrcPhysAddr = 0;
414    }
415    return fault;
416#else
417    return NoFault;
418#endif
419}
420
421Fault
422SimpleCPU::copy(Addr dest)
423{
424#if 0
425    static bool no_warn = true;
426    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
427    // Only support block sizes of 64 atm.
428    assert(blk_size == 64);
429    uint8_t data[blk_size];
430    //assert(cpuXC->copySrcAddr);
431    int offset = dest & (blk_size - 1);
432
433    // Make sure block doesn't span page
434    if (no_warn &&
435        (dest & PageMask) != ((dest + blk_size) & PageMask) &&
436        (dest >> 40) != 0xfffffc) {
437        no_warn = false;
438        warn("Copied block destination spans pages %x. ", dest);
439    }
440
441    memReq->reset(dest & ~(blk_size -1), blk_size);
442    // translate to physical address
443    Fault fault = cpuXC->translateDataWriteReq(req);
444
445    if (fault == NoFault) {
446        Addr dest_addr = memReq->paddr + offset;
447        // Need to read straight from memory since we have more than 8 bytes.
448        memReq->paddr = cpuXC->copySrcPhysAddr;
449        cpuXC->mem->read(memReq, data);
450        memReq->paddr = dest_addr;
451        cpuXC->mem->write(memReq, data);
452        if (dcacheInterface) {
453            memReq->cmd = Copy;
454            memReq->completionEvent = NULL;
455            memReq->paddr = cpuXC->copySrcPhysAddr;
456            memReq->dest = dest_addr;
457            memReq->size = 64;
458            memReq->time = curTick;
459            memReq->flags &= ~INST_READ;
460            dcacheInterface->access(memReq);
461        }
462    }
463    else
464        assert(!fault->isAlignmentFault());
465
466    return fault;
467#else
468    panic("copy not implemented");
469    return NoFault;
470#endif
471}
472
473// precise architected memory state accessor macros
474template <class T>
475Fault
476SimpleCPU::read(Addr addr, T &data, unsigned flags)
477{
478    if (status() == DcacheWaitResponse || status() == DcacheWaitSwitch) {
479//	Fault fault = xc->read(memReq,data);
480        // Not sure what to check for no fault...
481        if (data_read_pkt->result == Success) {
482            memcpy(&data, data_read_pkt->data, sizeof(T));
483        }
484
485        if (traceData) {
486            traceData->setAddr(addr);
487        }
488
489        // @todo: Figure out a way to create a Fault from the packet result.
490        return NoFault;
491    }
492
493//    memReq->reset(addr, sizeof(T), flags);
494
495#if SIMPLE_CPU_MEM_TIMING
496    CpuRequest *data_read_req = new CpuRequest;
497#endif
498
499    data_read_req->vaddr = addr;
500    data_read_req->size = sizeof(T);
501    data_read_req->flags = flags;
502    data_read_req->time = curTick;
503
504    // translate to physical address
505    Fault fault = cpuXC->translateDataReadReq(data_read_req);
506
507    // Now do the access.
508    if (fault == NoFault) {
509#if SIMPLE_CPU_MEM_TIMING
510        data_read_pkt = new Packet;
511        data_read_pkt->cmd = Read;
512        data_read_pkt->req = data_read_req;
513        data_read_pkt->data = new uint8_t[8];
514#endif
515        data_read_pkt->addr = data_read_req->paddr;
516        data_read_pkt->size = sizeof(T);
517
518        sendDcacheRequest(data_read_pkt);
519
520#if SIMPLE_CPU_MEM_IMMEDIATE
521        // Need to find a way to not duplicate code above.
522
523        if (data_read_pkt->result == Success) {
524            memcpy(&data, data_read_pkt->data, sizeof(T));
525        }
526
527        if (traceData) {
528            traceData->setAddr(addr);
529        }
530
531        // @todo: Figure out a way to create a Fault from the packet result.
532        return NoFault;
533#endif
534    }
535/*
536        memReq->cmd = Read;
537        memReq->completionEvent = NULL;
538        memReq->time = curTick;
539        memReq->flags &= ~INST_READ;
540        MemAccessResult result = dcacheInterface->access(memReq);
541
542        // Ugly hack to get an event scheduled *only* if the access is
543        // a miss.  We really should add first-class support for this
544        // at some point.
545        if (result != MA_HIT && dcacheInterface->doEvents()) {
546            memReq->completionEvent = &cacheCompletionEvent;
547            lastDcacheStall = curTick;
548            unscheduleTickEvent();
549            _status = DcacheMissStall;
550        } else {
551            // do functional access
552            fault = cpuXC->read(memReq, data);
553
554        }
555    } else if(fault == NoFault) {
556        // do functional access
557        fault = cpuXC->read(memReq, data);
558
559    }
560*/
561    // This will need a new way to tell if it has a dcache attached.
562    if (data_read_req->flags & UNCACHEABLE)
563        recordEvent("Uncached Read");
564
565    return fault;
566}
567
568#ifndef DOXYGEN_SHOULD_SKIP_THIS
569
570template
571Fault
572SimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
573
574template
575Fault
576SimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
577
578template
579Fault
580SimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
581
582template
583Fault
584SimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
585
586#endif //DOXYGEN_SHOULD_SKIP_THIS
587
588template<>
589Fault
590SimpleCPU::read(Addr addr, double &data, unsigned flags)
591{
592    return read(addr, *(uint64_t*)&data, flags);
593}
594
595template<>
596Fault
597SimpleCPU::read(Addr addr, float &data, unsigned flags)
598{
599    return read(addr, *(uint32_t*)&data, flags);
600}
601
602
603template<>
604Fault
605SimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
606{
607    return read(addr, (uint32_t&)data, flags);
608}
609
610
611template <class T>
612Fault
613SimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
614{
615    data_write_req->vaddr = addr;
616    data_write_req->time = curTick;
617    data_write_req->size = sizeof(T);
618    data_write_req->flags = flags;
619
620    // translate to physical address
621    Fault fault = cpuXC->translateDataWriteReq(data_write_req);
622    // Now do the access.
623    if (fault == NoFault) {
624#if SIMPLE_CPU_MEM_TIMING
625        data_write_pkt = new Packet;
626        data_write_pkt->cmd = Write;
627        data_write_pkt->req = data_write_req;
628        data_write_pkt->data = new uint8_t[64];
629        memcpy(data_write_pkt->data, &data, sizeof(T));
630#else
631        data_write_pkt->data = (uint8_t *)&data;
632#endif
633        data_write_pkt->addr = data_write_req->paddr;
634        data_write_pkt->size = sizeof(T);
635
636        sendDcacheRequest(data_write_pkt);
637    }
638
639/*
640    // do functional access
641    if (fault == NoFault)
642        fault = cpuXC->write(memReq, data);
643
644    if (fault == NoFault && dcacheInterface) {
645        memReq->cmd = Write;
646        memcpy(memReq->data,(uint8_t *)&data,memReq->size);
647        memReq->completionEvent = NULL;
648        memReq->time = curTick;
649        memReq->flags &= ~INST_READ;
650        MemAccessResult result = dcacheInterface->access(memReq);
651
652        // Ugly hack to get an event scheduled *only* if the access is
653        // a miss.  We really should add first-class support for this
654        // at some point.
655        if (result != MA_HIT && dcacheInterface->doEvents()) {
656            memReq->completionEvent = &cacheCompletionEvent;
657            lastDcacheStall = curTick;
658            unscheduleTickEvent();
659            _status = DcacheMissStall;
660        }
661    }
662*/
663    if (res && (fault == NoFault))
664        *res = data_write_pkt->result;
665
666    // This will need a new way to tell if it's hooked up to a cache or not.
667    if (data_write_req->flags & UNCACHEABLE)
668        recordEvent("Uncached Write");
669
670    // If the write needs to have a fault on the access, consider calling
671    // changeStatus() and changing it to "bad addr write" or something.
672    return fault;
673}
674
675
676#ifndef DOXYGEN_SHOULD_SKIP_THIS
677template
678Fault
679SimpleCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res);
680
681template
682Fault
683SimpleCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res);
684
685template
686Fault
687SimpleCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res);
688
689template
690Fault
691SimpleCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res);
692
693#endif //DOXYGEN_SHOULD_SKIP_THIS
694
695template<>
696Fault
697SimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
698{
699    return write(*(uint64_t*)&data, addr, flags, res);
700}
701
702template<>
703Fault
704SimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
705{
706    return write(*(uint32_t*)&data, addr, flags, res);
707}
708
709
710template<>
711Fault
712SimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
713{
714    return write((uint32_t)data, addr, flags, res);
715}
716
717
718#if FULL_SYSTEM
719Addr
720SimpleCPU::dbg_vtophys(Addr addr)
721{
722    return vtophys(xcProxy, addr);
723}
724#endif // FULL_SYSTEM
725
726void
727SimpleCPU::sendIcacheRequest(Packet *pkt)
728{
729    assert(!tickEvent.scheduled());
730#if SIMPLE_CPU_MEM_TIMING
731    retry_pkt = pkt;
732    bool success = icachePort.sendTiming(*pkt);
733
734    unscheduleTickEvent();
735
736    lastIcacheStall = curTick;
737
738    if (!success) {
739        // Need to wait for retry
740        _status = IcacheRetry;
741    } else {
742        // Need to wait for cache to respond
743        _status = IcacheWaitResponse;
744    }
745#elif SIMPLE_CPU_MEM_ATOMIC
746    Tick latency = icachePort.sendAtomic(*pkt);
747
748    unscheduleTickEvent();
749    scheduleTickEvent(latency);
750
751    // Note that Icache miss cycles will be incorrect.  Unless
752    // we check the status of the packet sent (is this valid?),
753    // we won't know if the latency is a hit or a miss.
754    icacheStallCycles += latency;
755
756    _status = IcacheAccessComplete;
757#elif SIMPLE_CPU_MEM_IMMEDIATE
758    icachePort.sendAtomic(*pkt);
759#else
760#error "SimpleCPU has no mem model set"
761#endif
762}
763
764void
765SimpleCPU::sendDcacheRequest(Packet *pkt)
766{
767    assert(!tickEvent.scheduled());
768#if SIMPLE_CPU_MEM_TIMING
769    unscheduleTickEvent();
770
771    retry_pkt = pkt;
772    bool success = dcachePort.sendTiming(*pkt);
773
774    lastDcacheStall = curTick;
775
776    if (!success) {
777        _status = DcacheRetry;
778    } else {
779        _status = DcacheWaitResponse;
780    }
781#elif SIMPLE_CPU_MEM_ATOMIC
782    unscheduleTickEvent();
783
784    Tick latency = dcachePort.sendAtomic(*pkt);
785
786    scheduleTickEvent(latency);
787
788    // Note that Dcache miss cycles will be incorrect.  Unless
789    // we check the status of the packet sent (is this valid?),
790    // we won't know if the latency is a hit or a miss.
791    dcacheStallCycles += latency;
792#elif SIMPLE_CPU_MEM_IMMEDIATE
793    dcachePort.sendAtomic(*pkt);
794#else
795#error "SimpleCPU has no mem model set"
796#endif
797}
798
799void
800SimpleCPU::processResponse(Packet &response)
801{
802    assert(SIMPLE_CPU_MEM_TIMING);
803
804    // For what things is the CPU the consumer of the packet it sent
805    // out?  This may create a memory leak if that's the case and it's
806    // expected of the SimpleCPU to delete its own packet.
807    Packet *pkt = &response;
808
809    switch (status()) {
810      case IcacheWaitResponse:
811        icacheStallCycles += curTick - lastIcacheStall;
812
813        _status = IcacheAccessComplete;
814        scheduleTickEvent(1);
815
816        // Copy the icache data into the instruction itself.
817        memcpy(&inst, pkt->data, sizeof(inst));
818
819        delete pkt;
820        break;
821      case DcacheWaitResponse:
822        if (pkt->cmd == Read) {
823            curStaticInst->execute(this,traceData);
824            if (traceData)
825                traceData->finalize();
826        }
827
828        delete pkt;
829
830        dcacheStallCycles += curTick - lastDcacheStall;
831        _status = Running;
832        scheduleTickEvent(1);
833        break;
834      case DcacheWaitSwitch:
835        if (pkt->cmd == Read) {
836            curStaticInst->execute(this,traceData);
837            if (traceData)
838                traceData->finalize();
839        }
840
841        delete pkt;
842
843        _status = SwitchedOut;
844        sampler->signalSwitched();
845      case SwitchedOut:
846        // If this CPU has been switched out due to sampling/warm-up,
847        // ignore any further status changes (e.g., due to cache
848        // misses outstanding at the time of the switch).
849        delete pkt;
850
851        return;
852      default:
853        panic("SimpleCPU::processCacheCompletion: bad state");
854        break;
855    }
856}
857
858Packet *
859SimpleCPU::processRetry()
860{
861#if SIMPLE_CPU_MEM_TIMING
862    switch(status()) {
863      case IcacheRetry:
864        icacheRetryCycles += curTick - lastIcacheStall;
865        return retry_pkt;
866        break;
867      case DcacheRetry:
868        dcacheRetryCycles += curTick - lastDcacheStall;
869        return retry_pkt;
870        break;
871      default:
872        panic("SimpleCPU::processRetry: bad state");
873        break;
874    }
875#else
876    panic("shouldn't be here");
877#endif
878}
879
880#if FULL_SYSTEM
881void
882SimpleCPU::post_interrupt(int int_num, int index)
883{
884    BaseCPU::post_interrupt(int_num, index);
885
886    if (cpuXC->status() == ExecContext::Suspended) {
887                DPRINTF(IPI,"Suspended Processor awoke\n");
888        cpuXC->activate();
889    }
890}
891#endif // FULL_SYSTEM
892
893/* start simulation, program loaded, processor precise state initialized */
894void
895SimpleCPU::tick()
896{
897    DPRINTF(SimpleCPU,"\n\n");
898
899    numCycles++;
900
901    traceData = NULL;
902
903    Fault fault = NoFault;
904
905#if FULL_SYSTEM
906    if (checkInterrupts && check_interrupts() && !cpuXC->inPalMode() &&
907        status() != IcacheAccessComplete) {
908        int ipl = 0;
909        int summary = 0;
910        checkInterrupts = false;
911
912        if (cpuXC->readMiscReg(IPR_SIRR)) {
913            for (int i = INTLEVEL_SOFTWARE_MIN;
914                 i < INTLEVEL_SOFTWARE_MAX; i++) {
915                if (cpuXC->readMiscReg(IPR_SIRR) & (ULL(1) << i)) {
916                    // See table 4-19 of 21164 hardware reference
917                    ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1;
918                    summary |= (ULL(1) << i);
919                }
920            }
921        }
922
923        uint64_t interrupts = cpuXC->cpu->intr_status();
924        for (int i = INTLEVEL_EXTERNAL_MIN;
925            i < INTLEVEL_EXTERNAL_MAX; i++) {
926            if (interrupts & (ULL(1) << i)) {
927                // See table 4-19 of 21164 hardware reference
928                ipl = i;
929                summary |= (ULL(1) << i);
930            }
931        }
932
933        if (cpuXC->readMiscReg(IPR_ASTRR))
934            panic("asynchronous traps not implemented\n");
935
936        if (ipl && ipl > cpuXC->readMiscReg(IPR_IPLR)) {
937            cpuXC->setMiscReg(IPR_ISR, summary);
938            cpuXC->setMiscReg(IPR_INTID, ipl);
939
940            Fault(new InterruptFault)->invoke(xcProxy);
941
942            DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n",
943                    cpuXC->readMiscReg(IPR_IPLR), ipl, summary);
944        }
945    }
946#endif
947
948    // maintain $r0 semantics
949    cpuXC->setIntReg(ZeroReg, 0);
950#if THE_ISA == ALPHA_ISA
951    cpuXC->setFloatReg(ZeroReg, 0.0);
952#endif // ALPHA_ISA
953
954    if (status() == IcacheAccessComplete) {
955        // We've already fetched an instruction and were stalled on an
956        // I-cache miss.  No need to fetch it again.
957
958        // Set status to running; tick event will get rescheduled if
959        // necessary at end of tick() function.
960        _status = Running;
961    } else {
962        // Try to fetch an instruction
963
964        // set up memory request for instruction fetch
965#if FULL_SYSTEM
966#define IFETCH_FLAGS(pc)	((pc) & 1) ? PHYSICAL : 0
967#else
968#define IFETCH_FLAGS(pc)	0
969#endif
970
971        DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",cpuXC->readPC(),
972                cpuXC->readNextPC(),cpuXC->readNextNPC());
973
974#if SIMPLE_CPU_MEM_TIMING
975        CpuRequest *ifetch_req = new CpuRequest();
976        ifetch_req->size = sizeof(MachInst);
977#endif
978
979        ifetch_req->vaddr = cpuXC->readPC() & ~3;
980        ifetch_req->time = curTick;
981
982/*	memReq->reset(xc->regs.pc & ~3, sizeof(uint32_t),
983                     IFETCH_FLAGS(xc->regs.pc));
984*/
985
986        fault = cpuXC->translateInstReq(ifetch_req);
987
988        if (fault == NoFault) {
989#if SIMPLE_CPU_MEM_TIMING
990            Packet *ifetch_pkt = new Packet;
991            ifetch_pkt->cmd = Read;
992            ifetch_pkt->data = (uint8_t *)&inst;
993            ifetch_pkt->req = ifetch_req;
994            ifetch_pkt->size = sizeof(MachInst);
995#endif
996            ifetch_pkt->addr = ifetch_req->paddr;
997
998            sendIcacheRequest(ifetch_pkt);
999#if SIMPLE_CPU_MEM_TIMING || SIMPLE_CPU_MEM_ATOMIC
1000            return;
1001#endif
1002/*
1003        if (icacheInterface && fault == NoFault) {
1004            memReq->completionEvent = NULL;
1005
1006            memReq->time = curTick;
1007            memReq->flags |= INST_READ;
1008            MemAccessResult result = icacheInterface->access(memReq);
1009
1010            // Ugly hack to get an event scheduled *only* if the access is
1011            // a miss.  We really should add first-class support for this
1012            // at some point.
1013                if (result != MA_HIT && icacheInterface->doEvents()) {
1014                memReq->completionEvent = &cacheCompletionEvent;
1015                lastIcacheStall = curTick;
1016                unscheduleTickEvent();
1017                _status = IcacheMissStall;
1018                return;
1019            }
1020        }
1021*/
1022        }
1023    }
1024
1025    // If we've got a valid instruction (i.e., no fault on instruction
1026    // fetch), then execute it.
1027    if (fault == NoFault) {
1028
1029        // keep an instruction count
1030        numInst++;
1031        numInsts++;
1032
1033        // check for instruction-count-based events
1034        comInstEventQueue[0]->serviceEvents(numInst);
1035
1036        // decode the instruction
1037        inst = gtoh(inst);
1038        curStaticInst = StaticInst::decode(makeExtMI(inst, cpuXC->readPC()));
1039
1040        traceData = Trace::getInstRecord(curTick, xcProxy, this, curStaticInst,
1041                                         cpuXC->readPC());
1042
1043        DPRINTF(Decode,"Decode: Decoded %s instruction (opcode: 0x%x): 0x%x\n",
1044                curStaticInst->getName(),curStaticInst->getOpcode(), curStaticInst->machInst);
1045
1046#if FULL_SYSTEM
1047        cpuXC->setInst(inst);
1048#endif // FULL_SYSTEM
1049
1050        cpuXC->func_exe_inst++;
1051
1052        fault = curStaticInst->execute(this, traceData);
1053
1054#if FULL_SYSTEM
1055        if (system->kernelBinning->fnbin) {
1056            assert(kernelStats);
1057            system->kernelBinning->execute(xcProxy, inst);
1058        }
1059
1060        if (cpuXC->profile) {
1061            bool usermode =
1062                (cpuXC->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0;
1063            cpuXC->profilePC = usermode ? 1 : cpuXC->readPC();
1064            ProfileNode *node = cpuXC->profile->consume(xcProxy, inst);
1065            if (node)
1066                cpuXC->profileNode = node;
1067        }
1068#endif
1069
1070        if (curStaticInst->isMemRef()) {
1071            numMemRefs++;
1072        }
1073
1074        if (curStaticInst->isLoad()) {
1075            ++numLoad;
1076            comLoadEventQueue[0]->serviceEvents(numLoad);
1077        }
1078
1079        // If we have a dcache miss, then we can't finialize the instruction
1080        // trace yet because we want to populate it with the data later
1081        if (traceData && (status() != DcacheWaitResponse)) {
1082            traceData->finalize();
1083        }
1084
1085        traceFunctions(cpuXC->readPC());
1086
1087    }	// if (fault == NoFault)
1088
1089    if (fault != NoFault) {
1090#if FULL_SYSTEM
1091        fault->invoke(xcProxy);
1092#else // !FULL_SYSTEM
1093        fatal("fault (%s) detected @ PC %08p", fault->name(), cpuXC->readPC());
1094#endif // FULL_SYSTEM
1095    }
1096    else {
1097#if THE_ISA != MIPS_ISA
1098        // go to the next instruction
1099        cpuXC->setPC(cpuXC->readNextPC());
1100        cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst));
1101#else
1102        // go to the next instruction
1103        cpuXC->setPC(cpuXC->readNextPC());
1104        cpuXC->setNextPC(cpuXC->readNextNPC());
1105        cpuXC->setNextNPC(cpuXC->readNextNPC() + sizeof(MachInst));
1106#endif
1107
1108    }
1109
1110#if FULL_SYSTEM
1111    Addr oldpc;
1112    do {
1113        oldpc = cpuXC->readPC();
1114        system->pcEventQueue.service(xcProxy);
1115    } while (oldpc != cpuXC->readPC());
1116#endif
1117
1118    assert(status() == Running ||
1119           status() == Idle ||
1120           status() == DcacheWaitResponse);
1121
1122    if (status() == Running && !tickEvent.scheduled())
1123        tickEvent.schedule(curTick + cycles(1));
1124}
1125
1126////////////////////////////////////////////////////////////////////////
1127//
1128//  SimpleCPU Simulation Object
1129//
1130BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU)
1131
1132    Param<Counter> max_insts_any_thread;
1133    Param<Counter> max_insts_all_threads;
1134    Param<Counter> max_loads_any_thread;
1135    Param<Counter> max_loads_all_threads;
1136
1137#if FULL_SYSTEM
1138    SimObjectParam<AlphaITB *> itb;
1139    SimObjectParam<AlphaDTB *> dtb;
1140    SimObjectParam<System *> system;
1141    Param<int> cpu_id;
1142    Param<Tick> profile;
1143#else
1144    SimObjectParam<MemObject *> mem;
1145    SimObjectParam<Process *> workload;
1146#endif // FULL_SYSTEM
1147
1148    Param<int> clock;
1149
1150    Param<bool> defer_registration;
1151    Param<int> width;
1152    Param<bool> function_trace;
1153    Param<Tick> function_trace_start;
1154
1155END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU)
1156
1157BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU)
1158
1159    INIT_PARAM(max_insts_any_thread,
1160               "terminate when any thread reaches this inst count"),
1161    INIT_PARAM(max_insts_all_threads,
1162               "terminate when all threads have reached this inst count"),
1163    INIT_PARAM(max_loads_any_thread,
1164               "terminate when any thread reaches this load count"),
1165    INIT_PARAM(max_loads_all_threads,
1166               "terminate when all threads have reached this load count"),
1167
1168#if FULL_SYSTEM
1169    INIT_PARAM(itb, "Instruction TLB"),
1170    INIT_PARAM(dtb, "Data TLB"),
1171    INIT_PARAM(system, "system object"),
1172    INIT_PARAM(cpu_id, "processor ID"),
1173    INIT_PARAM(profile, ""),
1174#else
1175    INIT_PARAM(mem, "memory"),
1176    INIT_PARAM(workload, "processes to run"),
1177#endif // FULL_SYSTEM
1178
1179    INIT_PARAM(clock, "clock speed"),
1180    INIT_PARAM(defer_registration, "defer system registration (for sampling)"),
1181    INIT_PARAM(width, "cpu width"),
1182    INIT_PARAM(function_trace, "Enable function trace"),
1183    INIT_PARAM(function_trace_start, "Cycle to start function trace")
1184
1185END_INIT_SIM_OBJECT_PARAMS(SimpleCPU)
1186
1187
1188CREATE_SIM_OBJECT(SimpleCPU)
1189{
1190    SimpleCPU::Params *params = new SimpleCPU::Params();
1191    params->name = getInstanceName();
1192    params->numberOfThreads = 1;
1193    params->max_insts_any_thread = max_insts_any_thread;
1194    params->max_insts_all_threads = max_insts_all_threads;
1195    params->max_loads_any_thread = max_loads_any_thread;
1196    params->max_loads_all_threads = max_loads_all_threads;
1197    params->deferRegistration = defer_registration;
1198    params->clock = clock;
1199    params->functionTrace = function_trace;
1200    params->functionTraceStart = function_trace_start;
1201    params->width = width;
1202
1203#if FULL_SYSTEM
1204    params->itb = itb;
1205    params->dtb = dtb;
1206    params->system = system;
1207    params->cpu_id = cpu_id;
1208    params->profile = profile;
1209#else
1210    params->mem = mem;
1211    params->process = workload;
1212#endif
1213
1214    SimpleCPU *cpu = new SimpleCPU(params);
1215    return cpu;
1216}
1217
1218REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU)
1219
1220