base.cc revision 2
1/*
2 * Copyright (c) 2003 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 <iostream>
30#include <iomanip>
31#include <list>
32#include <sstream>
33#include <string>
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <math.h>
38
39#include "host.hh"
40#include "cprintf.hh"
41#include "misc.hh"
42#include "stats.hh"
43#include "smt.hh"
44
45#include "annotation.hh"
46#include "exec_context.hh"
47#include "base_cpu.hh"
48#include "debug.hh"
49#include "simple_cpu.hh"
50#include "inifile.hh"
51#include "mem_interface.hh"
52#include "base_mem.hh"
53#include "static_inst.hh"
54
55#ifdef FULL_SYSTEM
56#include "memory_control.hh"
57#include "physical_memory.hh"
58#include "alpha_memory.hh"
59#include "system.hh"
60#else // !FULL_SYSTEM
61#include "functional_memory.hh"
62#include "prog.hh"
63#include "eio.hh"
64#endif // FULL_SYSTEM
65
66#include "exetrace.hh"
67#include "trace.hh"
68#include "sim_events.hh"
69#include "pollevent.hh"
70#include "sim_object.hh"
71#include "sim_stats.hh"
72
73#include "range.hh"
74#include "symtab.hh"
75
76#ifdef FULL_SYSTEM
77#include "vtophys.hh"
78#include "pciareg.h"
79#include "remote_gdb.hh"
80#include "alpha_access.h"
81#endif
82
83
84using namespace std;
85
86SimpleCPU::CacheCompletionEvent::CacheCompletionEvent(SimpleCPU *_cpu)
87    : Event(&mainEventQueue),
88      cpu(_cpu)
89{
90}
91
92void SimpleCPU::CacheCompletionEvent::process()
93{
94    cpu->processCacheCompletion();
95}
96
97const char *
98SimpleCPU::CacheCompletionEvent::description()
99{
100    return "cache completion event";
101}
102
103#ifdef FULL_SYSTEM
104SimpleCPU::SimpleCPU(const string &_name,
105                     System *_system,
106                     Counter max_insts_any_thread,
107                     Counter max_insts_all_threads,
108                     AlphaItb *itb, AlphaDtb *dtb,
109                     FunctionalMemory *mem,
110                     MemInterface *icache_interface,
111                     MemInterface *dcache_interface,
112                     int cpu_id, Tick freq)
113    : BaseCPU(_name, /* number_of_threads */ 1,
114              max_insts_any_thread, max_insts_all_threads,
115              _system, cpu_id, freq),
116#else
117SimpleCPU::SimpleCPU(const string &_name, Process *_process,
118                     Counter max_insts_any_thread,
119                     Counter max_insts_all_threads,
120                     MemInterface *icache_interface,
121                     MemInterface *dcache_interface)
122    : BaseCPU(_name, /* number_of_threads */ 1,
123              max_insts_any_thread, max_insts_all_threads),
124#endif
125      tickEvent(this), xc(NULL), cacheCompletionEvent(this)
126{
127#ifdef FULL_SYSTEM
128    xc = new ExecContext(this, 0, system, itb, dtb, mem, cpu_id);
129
130    _status = Running;
131    if (cpu_id != 0) {
132
133       xc->setStatus(ExecContext::Unallocated);
134
135       //Open a GDB debug session on port (7000 + the cpu_id)
136       (new GDBListener(new RemoteGDB(system, xc), 7000 + cpu_id))->listen();
137
138       AlphaISA::init(system->physmem, &xc->regs);
139
140       fault = Reset_Fault;
141
142       IntReg *ipr = xc->regs.ipr;
143       ipr[TheISA::IPR_MCSR] = 0x6;
144
145       AlphaISA::swap_palshadow(&xc->regs, true);
146
147       xc->regs.pc =
148           ipr[TheISA::IPR_PAL_BASE] + AlphaISA::fault_addr[fault];
149       xc->regs.npc = xc->regs.pc + sizeof(MachInst);
150
151       _status = Idle;
152    }
153    else {
154      system->initBootContext(xc);
155
156      // Reset the system
157      //
158      AlphaISA::init(system->physmem, &xc->regs);
159
160      fault = Reset_Fault;
161
162      IntReg *ipr = xc->regs.ipr;
163      ipr[TheISA::IPR_MCSR] = 0x6;
164
165      AlphaISA::swap_palshadow(&xc->regs, true);
166
167      xc->regs.pc = ipr[TheISA::IPR_PAL_BASE] + AlphaISA::fault_addr[fault];
168      xc->regs.npc = xc->regs.pc + sizeof(MachInst);
169
170       _status = Running;
171       tickEvent.schedule(0);
172    }
173
174#else
175    xc = new ExecContext(this, /* thread_num */ 0, _process, /* asid */ 0);
176    fault = No_Fault;
177    if (xc->status() == ExecContext::Active) {
178        _status = Running;
179       tickEvent.schedule(0);
180    } else
181        _status = Idle;
182#endif // !FULL_SYSTEM
183
184    icacheInterface = icache_interface;
185    dcacheInterface = dcache_interface;
186
187    memReq = new MemReq();
188    memReq->xc = xc;
189    memReq->asid = 0;
190
191    numInst = 0;
192    last_idle = 0;
193    lastIcacheStall = 0;
194    lastDcacheStall = 0;
195
196    contexts.push_back(xc);
197}
198
199SimpleCPU::~SimpleCPU()
200{
201}
202
203void
204SimpleCPU::regStats()
205{
206    BaseCPU::regStats();
207
208    numInsts
209        .name(name() + ".num_insts")
210        .desc("Number of instructions executed")
211        ;
212
213    numMemRefs
214        .name(name() + ".num_refs")
215        .desc("Number of memory references")
216        ;
217
218    idleCycles
219        .name(name() + ".idle_cycles")
220        .desc("Number of idle cycles")
221        ;
222
223    idleFraction
224        .name(name() + ".idle_fraction")
225        .desc("Percentage of idle cycles")
226        ;
227
228    icacheStallCycles
229        .name(name() + ".icache_stall_cycles")
230        .desc("ICache total stall cycles")
231        .prereq(icacheStallCycles)
232        ;
233
234    dcacheStallCycles
235        .name(name() + ".dcache_stall_cycles")
236        .desc("DCache total stall cycles")
237        .prereq(dcacheStallCycles)
238        ;
239
240    idleFraction = idleCycles / simTicks;
241
242    numInsts = Statistics::scalar(numInst);
243    simInsts += numInsts;
244}
245
246void
247SimpleCPU::serialize()
248{
249    nameOut();
250
251#ifdef FULL_SYSTEM
252#if 0
253    // do we need this anymore?? egh
254    childOut("itb", xc->itb);
255    childOut("dtb", xc->dtb);
256    childOut("physmem", physmem);
257#endif
258#endif
259
260    for (int i = 0; i < NumIntRegs; i++) {
261        stringstream buf;
262        ccprintf(buf, "R%02d", i);
263        paramOut(buf.str(), xc->regs.intRegFile[i]);
264    }
265    for (int i = 0; i < NumFloatRegs; i++) {
266        stringstream buf;
267        ccprintf(buf, "F%02d", i);
268        paramOut(buf.str(), xc->regs.floatRegFile.d[i]);
269    }
270    // CPUTraitsType::serializeSpecialRegs(getProxy(), xc->regs);
271}
272
273void
274SimpleCPU::unserialize(IniFile &db, const string &category, ConfigNode *node)
275{
276    string data;
277
278    for (int i = 0; i < NumIntRegs; i++) {
279        stringstream buf;
280        ccprintf(buf, "R%02d", i);
281        db.findDefault(category, buf.str(), data);
282        to_number(data,xc->regs.intRegFile[i]);
283    }
284    for (int i = 0; i < NumFloatRegs; i++) {
285        stringstream buf;
286        ccprintf(buf, "F%02d", i);
287        db.findDefault(category, buf.str(), data);
288        xc->regs.floatRegFile.d[i] = strtod(data.c_str(),NULL);
289    }
290
291    // Read in Special registers
292
293    // CPUTraitsType::unserializeSpecialRegs(db,category,node,xc->regs);
294}
295
296void
297change_thread_state(int thread_number, int activate, int priority)
298{
299}
300
301// precise architected memory state accessor macros
302template <class T>
303Fault
304SimpleCPU::read(Addr addr, T& data, unsigned flags)
305{
306    memReq->reset(addr, sizeof(T), flags);
307
308    // translate to physical address
309    Fault fault = xc->translateDataReadReq(memReq);
310
311    // do functional access
312    if (fault == No_Fault)
313        fault = xc->read(memReq, data);
314
315    if (traceData) {
316        traceData->setAddr(addr);
317        if (fault == No_Fault)
318            traceData->setData(data);
319    }
320
321    // if we have a cache, do cache access too
322    if (fault == No_Fault && dcacheInterface) {
323        memReq->cmd = Read;
324        memReq->completionEvent = NULL;
325        memReq->time = curTick;
326        memReq->flags &= ~UNCACHEABLE;
327        MemAccessResult result = dcacheInterface->access(memReq);
328
329        // Ugly hack to get an event scheduled *only* if the access is
330        // a miss.  We really should add first-class support for this
331        // at some point.
332        if (result != MA_HIT && dcacheInterface->doEvents) {
333            memReq->completionEvent = &cacheCompletionEvent;
334            setStatus(DcacheMissStall);
335        }
336    }
337
338    return fault;
339}
340
341#ifndef DOXYGEN_SHOULD_SKIP_THIS
342
343template
344Fault
345SimpleCPU::read(Addr addr, uint64_t& data, unsigned flags);
346
347template
348Fault
349SimpleCPU::read(Addr addr, uint32_t& data, unsigned flags);
350
351template
352Fault
353SimpleCPU::read(Addr addr, uint16_t& data, unsigned flags);
354
355template
356Fault
357SimpleCPU::read(Addr addr, uint8_t& data, unsigned flags);
358
359#endif //DOXYGEN_SHOULD_SKIP_THIS
360
361template<>
362Fault
363SimpleCPU::read(Addr addr, double& data, unsigned flags)
364{
365    return read(addr, *(uint64_t*)&data, flags);
366}
367
368template<>
369Fault
370SimpleCPU::read(Addr addr, float& data, unsigned flags)
371{
372    return read(addr, *(uint32_t*)&data, flags);
373}
374
375
376template<>
377Fault
378SimpleCPU::read(Addr addr, int32_t& data, unsigned flags)
379{
380    return read(addr, (uint32_t&)data, flags);
381}
382
383
384template <class T>
385Fault
386SimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
387{
388    if (traceData) {
389        traceData->setAddr(addr);
390        traceData->setData(data);
391    }
392
393    memReq->reset(addr, sizeof(T), flags);
394
395    // translate to physical address
396    Fault fault = xc->translateDataWriteReq(memReq);
397
398    // do functional access
399    if (fault == No_Fault)
400        fault = xc->write(memReq, data);
401
402    if (fault == No_Fault && dcacheInterface) {
403        memReq->cmd = Write;
404        memReq->data = (uint8_t *)&data;
405        memReq->completionEvent = NULL;
406        memReq->time = curTick;
407        memReq->flags &= ~UNCACHEABLE;
408        MemAccessResult result = dcacheInterface->access(memReq);
409
410        // Ugly hack to get an event scheduled *only* if the access is
411        // a miss.  We really should add first-class support for this
412        // at some point.
413        if (result != MA_HIT && dcacheInterface->doEvents) {
414            memReq->completionEvent = &cacheCompletionEvent;
415            setStatus(DcacheMissStall);
416        }
417    }
418
419    if (res && (fault == No_Fault))
420        *res = memReq->result;
421
422    return fault;
423}
424
425
426#ifndef DOXYGEN_SHOULD_SKIP_THIS
427template
428Fault
429SimpleCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res);
430
431template
432Fault
433SimpleCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res);
434
435template
436Fault
437SimpleCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res);
438
439template
440Fault
441SimpleCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res);
442
443#endif //DOXYGEN_SHOULD_SKIP_THIS
444
445template<>
446Fault
447SimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
448{
449    return write(*(uint64_t*)&data, addr, flags, res);
450}
451
452template<>
453Fault
454SimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
455{
456    return write(*(uint32_t*)&data, addr, flags, res);
457}
458
459
460template<>
461Fault
462SimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
463{
464    return write((uint32_t)data, addr, flags, res);
465}
466
467
468#ifdef FULL_SYSTEM
469Addr
470SimpleCPU::dbg_vtophys(Addr addr)
471{
472    return vtophys(xc, addr);
473}
474#endif // FULL_SYSTEM
475
476Tick save_cycle = 0;
477
478
479void
480SimpleCPU::processCacheCompletion()
481{
482    switch (status()) {
483      case IcacheMissStall:
484        icacheStallCycles += curTick - lastIcacheStall;
485        setStatus(IcacheMissComplete);
486        break;
487      case DcacheMissStall:
488        dcacheStallCycles += curTick - lastDcacheStall;
489        setStatus(Running);
490        break;
491      default:
492        panic("SimpleCPU::processCacheCompletion: bad state");
493        break;
494    }
495}
496
497#ifdef FULL_SYSTEM
498void
499SimpleCPU::post_interrupt(int int_num, int index)
500{
501    BaseCPU::post_interrupt(int_num, index);
502
503    if (xc->status() == ExecContext::Suspended) {
504                DPRINTF(IPI,"Suspended Processor awoke\n");
505        xc->setStatus(ExecContext::Active);
506        Annotate::Resume(xc);
507    }
508}
509#endif // FULL_SYSTEM
510
511/* start simulation, program loaded, processor precise state initialized */
512void
513SimpleCPU::tick()
514{
515    traceData = NULL;
516
517#ifdef FULL_SYSTEM
518    if (fault == No_Fault && AlphaISA::check_interrupts &&
519        xc->cpu->check_interrupts() &&
520        !PC_PAL(xc->regs.pc) &&
521        status() != IcacheMissComplete) {
522        int ipl = 0;
523        int summary = 0;
524        AlphaISA::check_interrupts = 0;
525        IntReg *ipr = xc->regs.ipr;
526
527        if (xc->regs.ipr[TheISA::IPR_SIRR]) {
528            for (int i = TheISA::INTLEVEL_SOFTWARE_MIN;
529                 i < TheISA::INTLEVEL_SOFTWARE_MAX; i++) {
530                if (ipr[TheISA::IPR_SIRR] & (ULL(1) << i)) {
531                    // See table 4-19 of 21164 hardware reference
532                    ipl = (i - TheISA::INTLEVEL_SOFTWARE_MIN) + 1;
533                    summary |= (ULL(1) << i);
534                }
535            }
536        }
537
538        uint64_t interrupts = xc->cpu->intr_status();
539        for(int i = TheISA::INTLEVEL_EXTERNAL_MIN;
540            i < TheISA::INTLEVEL_EXTERNAL_MAX; i++) {
541            if (interrupts & (ULL(1) << i)) {
542                // See table 4-19 of 21164 hardware reference
543                ipl = i;
544                summary |= (ULL(1) << i);
545            }
546        }
547
548        if (ipr[TheISA::IPR_ASTRR])
549            panic("asynchronous traps not implemented\n");
550
551        if (ipl && ipl > xc->regs.ipr[TheISA::IPR_IPLR]) {
552            ipr[TheISA::IPR_ISR] = summary;
553            ipr[TheISA::IPR_INTID] = ipl;
554            xc->ev5_trap(Interrupt_Fault);
555
556            DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n",
557                    ipr[TheISA::IPR_IPLR], ipl, summary);
558        }
559    }
560#endif
561
562    // maintain $r0 semantics
563    xc->regs.intRegFile[ZeroReg] = 0;
564#ifdef TARGET_ALPHA
565    xc->regs.floatRegFile.d[ZeroReg] = 0.0;
566#endif // TARGET_ALPHA
567
568    if (status() == IcacheMissComplete) {
569        // We've already fetched an instruction and were stalled on an
570        // I-cache miss.  No need to fetch it again.
571
572        setStatus(Running);
573    }
574    else {
575        // Try to fetch an instruction
576
577        // set up memory request for instruction fetch
578#ifdef FULL_SYSTEM
579#define IFETCH_FLAGS(pc)	((pc) & 1) ? PHYSICAL : 0
580#else
581#define IFETCH_FLAGS(pc)	0
582#endif
583
584        memReq->cmd = Read;
585        memReq->reset(xc->regs.pc & ~3, sizeof(uint32_t),
586                     IFETCH_FLAGS(xc->regs.pc));
587
588        fault = xc->translateInstReq(memReq);
589
590        if (fault == No_Fault)
591            fault = xc->mem->read(memReq, inst);
592
593        if (icacheInterface && fault == No_Fault) {
594            memReq->completionEvent = NULL;
595
596            memReq->time = curTick;
597            memReq->flags &= ~UNCACHEABLE;
598            MemAccessResult result = icacheInterface->access(memReq);
599
600            // Ugly hack to get an event scheduled *only* if the access is
601            // a miss.  We really should add first-class support for this
602            // at some point.
603            if (result != MA_HIT && icacheInterface->doEvents) {
604                memReq->completionEvent = &cacheCompletionEvent;
605                setStatus(IcacheMissStall);
606                return;
607            }
608        }
609    }
610
611    // If we've got a valid instruction (i.e., no fault on instruction
612    // fetch), then execute it.
613    if (fault == No_Fault) {
614
615        // keep an instruction count
616        numInst++;
617
618        // check for instruction-count-based events
619        comInsnEventQueue[0]->serviceEvents(numInst);
620
621        // decode the instruction
622        StaticInstPtr<TheISA> si(inst);
623
624        traceData = Trace::getInstRecord(curTick, xc, this, si,
625                                         xc->regs.pc);
626
627#ifdef FULL_SYSTEM
628        xc->regs.opcode = (inst >> 26) & 0x3f;
629        xc->regs.ra = (inst >> 21) & 0x1f;
630#endif // FULL_SYSTEM
631
632        xc->func_exe_insn++;
633
634        fault = si->execute(this, xc, traceData);
635
636        if (si->isMemRef()) {
637            numMemRefs++;
638        }
639
640        if (traceData)
641            traceData->finalize();
642
643    }	// if (fault == No_Fault)
644
645    if (fault != No_Fault) {
646#ifdef FULL_SYSTEM
647        xc->ev5_trap(fault);
648#else // !FULL_SYSTEM
649        fatal("fault (%d) detected @ PC 0x%08p", fault, xc->regs.pc);
650#endif // FULL_SYSTEM
651    }
652    else {
653        // go to the next instruction
654        xc->regs.pc = xc->regs.npc;
655        xc->regs.npc += sizeof(MachInst);
656    }
657
658#ifdef FULL_SYSTEM
659    Addr oldpc;
660    do {
661        oldpc = xc->regs.pc;
662        system->pcEventQueue.service(xc);
663    } while (oldpc != xc->regs.pc);
664#endif
665
666    assert(status() == Running ||
667           status() == Idle ||
668           status() == DcacheMissStall);
669
670    if (status() == Running && !tickEvent.scheduled())
671        tickEvent.schedule(curTick + 1);
672}
673
674
675////////////////////////////////////////////////////////////////////////
676//
677//  SimpleCPU Simulation Object
678//
679BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU)
680
681    Param<Counter> max_insts_any_thread;
682    Param<Counter> max_insts_all_threads;
683
684#ifdef FULL_SYSTEM
685    SimObjectParam<AlphaItb *> itb;
686    SimObjectParam<AlphaDtb *> dtb;
687    SimObjectParam<FunctionalMemory *> mem;
688    SimObjectParam<System *> system;
689    Param<int> cpu_id;
690    Param<int> mult;
691#else
692    SimObjectParam<Process *> workload;
693#endif // FULL_SYSTEM
694
695    SimObjectParam<BaseMem *> icache;
696    SimObjectParam<BaseMem *> dcache;
697
698END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU)
699
700BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU)
701
702    INIT_PARAM_DFLT(max_insts_any_thread,
703                    "terminate when any thread reaches this insn count",
704                    0),
705    INIT_PARAM_DFLT(max_insts_all_threads,
706                    "terminate when all threads have reached this insn count",
707                    0),
708
709#ifdef FULL_SYSTEM
710    INIT_PARAM(itb, "Instruction TLB"),
711    INIT_PARAM(dtb, "Data TLB"),
712    INIT_PARAM(mem, "memory"),
713    INIT_PARAM(system, "system object"),
714    INIT_PARAM_DFLT(cpu_id, "CPU identification number", 0),
715    INIT_PARAM_DFLT(mult, "system clock multiplier", 1),
716#else
717    INIT_PARAM(workload, "processes to run"),
718#endif // FULL_SYSTEM
719
720    INIT_PARAM_DFLT(icache, "L1 instruction cache object", NULL),
721    INIT_PARAM_DFLT(dcache, "L1 data cache object", NULL)
722
723END_INIT_SIM_OBJECT_PARAMS(SimpleCPU)
724
725
726CREATE_SIM_OBJECT(SimpleCPU)
727{
728#ifdef FULL_SYSTEM
729    if (mult != 1)
730        panic("processor clock multiplier must be 1\n");
731
732    return new SimpleCPU(getInstanceName(), system,
733                         max_insts_any_thread, max_insts_all_threads,
734                         itb, dtb, mem,
735                         (icache) ? icache->getInterface() : NULL,
736                         (dcache) ? dcache->getInterface() : NULL,
737                         cpu_id, ticksPerSecond * mult);
738#else
739
740    return new SimpleCPU(getInstanceName(), workload,
741                         max_insts_any_thread, max_insts_all_threads,
742                         icache->getInterface(), dcache->getInterface());
743
744#endif // FULL_SYSTEM
745}
746
747REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU)
748