base.cc revision 2361
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 <iostream>
30#include <string>
31#include <sstream>
32
33#include "base/cprintf.hh"
34#include "base/loader/symtab.hh"
35#include "base/misc.hh"
36#include "base/output.hh"
37#include "cpu/base.hh"
38#include "cpu/exec_context.hh"
39#include "cpu/profile.hh"
40#include "cpu/sampler/sampler.hh"
41#include "sim/param.hh"
42#include "sim/process.hh"
43#include "sim/sim_events.hh"
44#include "sim/system.hh"
45
46#include "base/trace.hh"
47
48// Hack
49#include "sim/stat_control.hh"
50
51using namespace std;
52
53vector<BaseCPU *> BaseCPU::cpuList;
54
55// This variable reflects the max number of threads in any CPU.  Be
56// careful to only use it once all the CPUs that you care about have
57// been initialized
58int maxThreadsPerCPU = 1;
59
60void
61CPUProgressEvent::process()
62{
63#ifndef NDEBUG
64    Counter temp = cpu->totalInstructions();
65    double ipc = double(temp - lastNumInst) / (interval / cpu->cycles(1));
66    DPRINTFN("%s progress event, instructions committed: %lli, IPC: %0.8d\n",
67             cpu->name(), temp - lastNumInst, ipc);
68    ipc = 0.0;
69    lastNumInst = temp;
70    schedule(curTick + interval);
71#endif
72}
73
74const char *
75CPUProgressEvent::description()
76{
77    return "CPU Progress event";
78}
79
80#if FULL_SYSTEM
81BaseCPU::BaseCPU(Params *p)
82    : SimObject(p->name), clock(p->clock), checkInterrupts(true),
83      params(p), number_of_threads(p->numberOfThreads), system(p->system)
84#else
85BaseCPU::BaseCPU(Params *p)
86    : SimObject(p->name), clock(p->clock), params(p),
87      number_of_threads(p->numberOfThreads)
88#endif
89{
90//    currentTick = curTick;
91    DPRINTF(FullCPU, "BaseCPU: Creating object, mem address %#x.\n", this);
92
93    // add self to global list of CPUs
94    cpuList.push_back(this);
95
96    DPRINTF(FullCPU, "BaseCPU: CPU added to cpuList, mem address %#x.\n",
97            this);
98
99    if (number_of_threads > maxThreadsPerCPU)
100        maxThreadsPerCPU = number_of_threads;
101
102    // allocate per-thread instruction-based event queues
103    comInstEventQueue = new EventQueue *[number_of_threads];
104    for (int i = 0; i < number_of_threads; ++i)
105        comInstEventQueue[i] = new EventQueue("instruction-based event queue");
106
107    //
108    // set up instruction-count-based termination events, if any
109    //
110    if (p->max_insts_any_thread != 0)
111        for (int i = 0; i < number_of_threads; ++i)
112            new SimExitEvent(comInstEventQueue[i], p->max_insts_any_thread,
113                "a thread reached the max instruction count");
114
115    if (p->max_insts_all_threads != 0) {
116        // allocate & initialize shared downcounter: each event will
117        // decrement this when triggered; simulation will terminate
118        // when counter reaches 0
119        int *counter = new int;
120        *counter = number_of_threads;
121        for (int i = 0; i < number_of_threads; ++i)
122            new CountedExitEvent(comInstEventQueue[i],
123                "all threads reached the max instruction count",
124                p->max_insts_all_threads, *counter);
125    }
126
127    // allocate per-thread load-based event queues
128    comLoadEventQueue = new EventQueue *[number_of_threads];
129    for (int i = 0; i < number_of_threads; ++i)
130        comLoadEventQueue[i] = new EventQueue("load-based event queue");
131
132    //
133    // set up instruction-count-based termination events, if any
134    //
135    if (p->max_loads_any_thread != 0)
136        for (int i = 0; i < number_of_threads; ++i)
137            new SimExitEvent(comLoadEventQueue[i], p->max_loads_any_thread,
138                "a thread reached the max load count");
139
140    if (p->max_loads_all_threads != 0) {
141        // allocate & initialize shared downcounter: each event will
142        // decrement this when triggered; simulation will terminate
143        // when counter reaches 0
144        int *counter = new int;
145        *counter = number_of_threads;
146        for (int i = 0; i < number_of_threads; ++i)
147            new CountedExitEvent(comLoadEventQueue[i],
148                "all threads reached the max load count",
149                p->max_loads_all_threads, *counter);
150    }
151
152    if (p->stats_reset_inst != 0) {
153        Stats::SetupEvent(Stats::Reset, p->stats_reset_inst, 0, comInstEventQueue[0]);
154        cprintf("Stats reset event scheduled for %lli insts\n",
155                p->stats_reset_inst);
156    }
157
158#if FULL_SYSTEM
159    memset(interrupts, 0, sizeof(interrupts));
160    intstatus = 0;
161#endif
162
163    functionTracingEnabled = false;
164    if (p->functionTrace) {
165        functionTraceStream = simout.find(csprintf("ftrace.%s", name()));
166        currentFunctionStart = currentFunctionEnd = 0;
167        functionEntryTick = p->functionTraceStart;
168
169        if (p->functionTraceStart == 0) {
170            functionTracingEnabled = true;
171        } else {
172            Event *e =
173                new EventWrapper<BaseCPU, &BaseCPU::enableFunctionTrace>(this,
174                                                                         true);
175            e->schedule(p->functionTraceStart);
176        }
177    }
178#if FULL_SYSTEM
179    profileEvent = NULL;
180    if (params->profile)
181        profileEvent = new ProfileEvent(this, params->profile);
182#endif
183}
184
185BaseCPU::Params::Params()
186{
187#if FULL_SYSTEM
188    profile = false;
189#endif
190    checker = NULL;
191}
192
193void
194BaseCPU::enableFunctionTrace()
195{
196    functionTracingEnabled = true;
197}
198
199BaseCPU::~BaseCPU()
200{
201}
202
203void
204BaseCPU::init()
205{
206    if (!params->deferRegistration)
207        registerExecContexts();
208}
209
210void
211BaseCPU::startup()
212{
213#if FULL_SYSTEM
214    if (!params->deferRegistration && profileEvent)
215        profileEvent->schedule(curTick);
216#endif
217
218    if (params->progress_interval) {
219        new CPUProgressEvent(&mainEventQueue, params->progress_interval,
220                             this);
221    }
222}
223
224
225void
226BaseCPU::regStats()
227{
228    using namespace Stats;
229
230    numCycles
231        .name(name() + ".numCycles")
232        .desc("number of cpu cycles simulated")
233        ;
234
235    int size = execContexts.size();
236    if (size > 1) {
237        for (int i = 0; i < size; ++i) {
238            stringstream namestr;
239            ccprintf(namestr, "%s.ctx%d", name(), i);
240            execContexts[i]->regStats(namestr.str());
241        }
242    } else if (size == 1)
243        execContexts[0]->regStats(name());
244
245#if FULL_SYSTEM
246#endif
247}
248
249
250void
251BaseCPU::registerExecContexts()
252{
253    for (int i = 0; i < execContexts.size(); ++i) {
254        ExecContext *xc = execContexts[i];
255
256        if (xc->status() == ExecContext::Suspended) {
257#if FULL_SYSTEM
258            int id = params->cpu_id;
259            if (id != -1)
260                id += i;
261
262        xc->setCpuId(system->registerExecContext(xc, id));
263#else
264        xc->setCpuId(xc->getProcessPtr()->registerExecContext(xc));
265#endif
266        }
267    }
268}
269
270
271void
272BaseCPU::switchOut(Sampler *sampler)
273{
274//    panic("This CPU doesn't support sampling!");
275#if FULL_SYSTEM
276    if (profileEvent && profileEvent->scheduled())
277        profileEvent->deschedule();
278#endif
279}
280
281void
282BaseCPU::takeOverFrom(BaseCPU *oldCPU)
283{
284//    currentTick = oldCPU->currentTick;
285    assert(execContexts.size() == oldCPU->execContexts.size());
286
287    for (int i = 0; i < execContexts.size(); ++i) {
288        ExecContext *newXC = execContexts[i];
289        ExecContext *oldXC = oldCPU->execContexts[i];
290
291        newXC->takeOverFrom(oldXC);
292        assert(newXC->readCpuId() == oldXC->readCpuId());
293#if FULL_SYSTEM
294        system->replaceExecContext(newXC, newXC->readCpuId());
295#else
296        assert(newXC->getProcessPtr() == oldXC->getProcessPtr());
297        newXC->getProcessPtr()->replaceExecContext(newXC, newXC->readCpuId());
298#endif
299
300//    TheISA::compareXCs(oldXC, newXC);
301    }
302
303#if FULL_SYSTEM
304    for (int i = 0; i < TheISA::NumInterruptLevels; ++i)
305        interrupts[i] = oldCPU->interrupts[i];
306    intstatus = oldCPU->intstatus;
307    checkInterrupts = oldCPU->checkInterrupts;
308
309//    for (int i = 0; i < execContexts.size(); ++i)
310//        execContexts[i]->profileClear();
311
312    // The Sampler must take care of this!
313//    if (profileEvent)
314//        profileEvent->schedule(curTick);
315#endif
316}
317
318
319#if FULL_SYSTEM
320BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, int _interval)
321    : Event(&mainEventQueue), cpu(_cpu), interval(_interval)
322{ }
323
324void
325BaseCPU::ProfileEvent::process()
326{
327    for (int i = 0, size = cpu->execContexts.size(); i < size; ++i) {
328        ExecContext *xc = cpu->execContexts[i];
329        xc->profileSample();
330    }
331
332    schedule(curTick + interval);
333}
334
335void
336BaseCPU::post_interrupt(int int_num, int index)
337{
338    DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index);
339
340    if (int_num < 0 || int_num >= TheISA::NumInterruptLevels)
341        panic("int_num out of bounds\n");
342
343    if (index < 0 || index >= sizeof(uint64_t) * 8)
344        panic("int_num out of bounds\n");
345
346    checkInterrupts = true;
347    interrupts[int_num] |= 1 << index;
348    intstatus |= (ULL(1) << int_num);
349}
350
351void
352BaseCPU::clear_interrupt(int int_num, int index)
353{
354    DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index);
355
356    if (int_num < 0 || int_num >= TheISA::NumInterruptLevels)
357        panic("int_num out of bounds\n");
358
359    if (index < 0 || index >= sizeof(uint64_t) * 8)
360        panic("int_num out of bounds\n");
361
362    interrupts[int_num] &= ~(1 << index);
363    if (interrupts[int_num] == 0)
364        intstatus &= ~(ULL(1) << int_num);
365}
366
367void
368BaseCPU::clear_interrupts()
369{
370    DPRINTF(Interrupt, "Interrupts all cleared\n");
371
372    memset(interrupts, 0, sizeof(interrupts));
373    intstatus = 0;
374}
375
376
377void
378BaseCPU::serialize(std::ostream &os)
379{
380    SERIALIZE_ARRAY(interrupts, TheISA::NumInterruptLevels);
381    SERIALIZE_SCALAR(intstatus);
382}
383
384void
385BaseCPU::unserialize(Checkpoint *cp, const std::string &section)
386{
387    UNSERIALIZE_ARRAY(interrupts, TheISA::NumInterruptLevels);
388    UNSERIALIZE_SCALAR(intstatus);
389}
390
391#endif // FULL_SYSTEM
392
393void
394BaseCPU::traceFunctionsInternal(Addr pc)
395{
396    if (!debugSymbolTable)
397        return;
398
399    // if pc enters different function, print new function symbol and
400    // update saved range.  Otherwise do nothing.
401    if (pc < currentFunctionStart || pc >= currentFunctionEnd) {
402        string sym_str;
403        bool found = debugSymbolTable->findNearestSymbol(pc, sym_str,
404                                                         currentFunctionStart,
405                                                         currentFunctionEnd);
406
407        if (!found) {
408            // no symbol found: use addr as label
409            sym_str = csprintf("0x%x", pc);
410            currentFunctionStart = pc;
411            currentFunctionEnd = pc + 1;
412        }
413
414        ccprintf(*functionTraceStream, " (%d)\n%d: %s",
415                 curTick - functionEntryTick, curTick, sym_str);
416        functionEntryTick = curTick;
417    }
418}
419
420
421DEFINE_SIM_OBJECT_CLASS_NAME("BaseCPU", BaseCPU)
422