base.cc revision 10190:fb83d025d1c3
1/*
2 * Copyright (c) 2011-2012 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2002-2005 The Regents of The University of Michigan
15 * Copyright (c) 2011 Regents of the University of California
16 * Copyright (c) 2013 Advanced Micro Devices, Inc.
17 * Copyright (c) 2013 Mark D. Hill and David A. Wood
18 * All rights reserved.
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions are
22 * met: redistributions of source code must retain the above copyright
23 * notice, this list of conditions and the following disclaimer;
24 * redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution;
27 * neither the name of the copyright holders nor the names of its
28 * contributors may be used to endorse or promote products derived from
29 * this software without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 *
43 * Authors: Steve Reinhardt
44 *          Nathan Binkert
45 *          Rick Strong
46 */
47
48#include <iostream>
49#include <sstream>
50#include <string>
51
52#include "arch/tlb.hh"
53#include "base/loader/symtab.hh"
54#include "base/cprintf.hh"
55#include "base/misc.hh"
56#include "base/output.hh"
57#include "base/trace.hh"
58#include "cpu/base.hh"
59#include "cpu/checker/cpu.hh"
60#include "cpu/cpuevent.hh"
61#include "cpu/profile.hh"
62#include "cpu/thread_context.hh"
63#include "debug/SyscallVerbose.hh"
64#include "params/BaseCPU.hh"
65#include "sim/full_system.hh"
66#include "sim/process.hh"
67#include "sim/sim_events.hh"
68#include "sim/sim_exit.hh"
69#include "sim/system.hh"
70
71// Hack
72#include "sim/stat_control.hh"
73
74using namespace std;
75
76vector<BaseCPU *> BaseCPU::cpuList;
77
78// This variable reflects the max number of threads in any CPU.  Be
79// careful to only use it once all the CPUs that you care about have
80// been initialized
81int maxThreadsPerCPU = 1;
82
83CPUProgressEvent::CPUProgressEvent(BaseCPU *_cpu, Tick ival)
84    : Event(Event::Progress_Event_Pri), _interval(ival), lastNumInst(0),
85      cpu(_cpu), _repeatEvent(true)
86{
87    if (_interval)
88        cpu->schedule(this, curTick() + _interval);
89}
90
91void
92CPUProgressEvent::process()
93{
94    Counter temp = cpu->totalOps();
95#ifndef NDEBUG
96    double ipc = double(temp - lastNumInst) / (_interval / cpu->clockPeriod());
97
98    DPRINTFN("%s progress event, total committed:%i, progress insts committed: "
99             "%lli, IPC: %0.8d\n", cpu->name(), temp, temp - lastNumInst,
100             ipc);
101    ipc = 0.0;
102#else
103    cprintf("%lli: %s progress event, total committed:%i, progress insts "
104            "committed: %lli\n", curTick(), cpu->name(), temp,
105            temp - lastNumInst);
106#endif
107    lastNumInst = temp;
108
109    if (_repeatEvent)
110        cpu->schedule(this, curTick() + _interval);
111}
112
113const char *
114CPUProgressEvent::description() const
115{
116    return "CPU Progress";
117}
118
119BaseCPU::BaseCPU(Params *p, bool is_checker)
120    : MemObject(p), instCnt(0), _cpuId(p->cpu_id), _socketId(p->socket_id),
121      _instMasterId(p->system->getMasterId(name() + ".inst")),
122      _dataMasterId(p->system->getMasterId(name() + ".data")),
123      _taskId(ContextSwitchTaskId::Unknown), _pid(Request::invldPid),
124      _switchedOut(p->switched_out), _cacheLineSize(p->system->cacheLineSize()),
125      interrupts(p->interrupts), profileEvent(NULL),
126      numThreads(p->numThreads), system(p->system)
127{
128    // if Python did not provide a valid ID, do it here
129    if (_cpuId == -1 ) {
130        _cpuId = cpuList.size();
131    }
132
133    // add self to global list of CPUs
134    cpuList.push_back(this);
135
136    DPRINTF(SyscallVerbose, "Constructing CPU with id %d, socket id %d\n",
137                _cpuId, _socketId);
138
139    if (numThreads > maxThreadsPerCPU)
140        maxThreadsPerCPU = numThreads;
141
142    // allocate per-thread instruction-based event queues
143    comInstEventQueue = new EventQueue *[numThreads];
144    for (ThreadID tid = 0; tid < numThreads; ++tid)
145        comInstEventQueue[tid] =
146            new EventQueue("instruction-based event queue");
147
148    //
149    // set up instruction-count-based termination events, if any
150    //
151    if (p->max_insts_any_thread != 0) {
152        const char *cause = "a thread reached the max instruction count";
153        for (ThreadID tid = 0; tid < numThreads; ++tid)
154            scheduleInstStop(tid, p->max_insts_any_thread, cause);
155    }
156
157    // Set up instruction-count-based termination events for SimPoints
158    // Typically, there are more than one action points.
159    // Simulation.py is responsible to take the necessary actions upon
160    // exitting the simulation loop.
161    if (!p->simpoint_start_insts.empty()) {
162        const char *cause = "simpoint starting point found";
163        for (size_t i = 0; i < p->simpoint_start_insts.size(); ++i)
164            scheduleInstStop(0, p->simpoint_start_insts[i], cause);
165    }
166
167    if (p->max_insts_all_threads != 0) {
168        const char *cause = "all threads reached the max instruction count";
169
170        // allocate & initialize shared downcounter: each event will
171        // decrement this when triggered; simulation will terminate
172        // when counter reaches 0
173        int *counter = new int;
174        *counter = numThreads;
175        for (ThreadID tid = 0; tid < numThreads; ++tid) {
176            Event *event = new CountedExitEvent(cause, *counter);
177            comInstEventQueue[tid]->schedule(event, p->max_insts_all_threads);
178        }
179    }
180
181    // allocate per-thread load-based event queues
182    comLoadEventQueue = new EventQueue *[numThreads];
183    for (ThreadID tid = 0; tid < numThreads; ++tid)
184        comLoadEventQueue[tid] = new EventQueue("load-based event queue");
185
186    //
187    // set up instruction-count-based termination events, if any
188    //
189    if (p->max_loads_any_thread != 0) {
190        const char *cause = "a thread reached the max load count";
191        for (ThreadID tid = 0; tid < numThreads; ++tid)
192            scheduleLoadStop(tid, p->max_loads_any_thread, cause);
193    }
194
195    if (p->max_loads_all_threads != 0) {
196        const char *cause = "all threads reached the max load count";
197        // allocate & initialize shared downcounter: each event will
198        // decrement this when triggered; simulation will terminate
199        // when counter reaches 0
200        int *counter = new int;
201        *counter = numThreads;
202        for (ThreadID tid = 0; tid < numThreads; ++tid) {
203            Event *event = new CountedExitEvent(cause, *counter);
204            comLoadEventQueue[tid]->schedule(event, p->max_loads_all_threads);
205        }
206    }
207
208    functionTracingEnabled = false;
209    if (p->function_trace) {
210        const string fname = csprintf("ftrace.%s", name());
211        functionTraceStream = simout.find(fname);
212        if (!functionTraceStream)
213            functionTraceStream = simout.create(fname);
214
215        currentFunctionStart = currentFunctionEnd = 0;
216        functionEntryTick = p->function_trace_start;
217
218        if (p->function_trace_start == 0) {
219            functionTracingEnabled = true;
220        } else {
221            typedef EventWrapper<BaseCPU, &BaseCPU::enableFunctionTrace> wrap;
222            Event *event = new wrap(this, true);
223            schedule(event, p->function_trace_start);
224        }
225    }
226
227    // The interrupts should always be present unless this CPU is
228    // switched in later or in case it is a checker CPU
229    if (!params()->switched_out && !is_checker) {
230        if (interrupts) {
231            interrupts->setCPU(this);
232        } else {
233            fatal("CPU %s has no interrupt controller.\n"
234                  "Ensure createInterruptController() is called.\n", name());
235        }
236    }
237
238    if (FullSystem) {
239        if (params()->profile)
240            profileEvent = new ProfileEvent(this, params()->profile);
241    }
242    tracer = params()->tracer;
243
244    if (params()->isa.size() != numThreads) {
245        fatal("Number of ISAs (%i) assigned to the CPU does not equal number "
246              "of threads (%i).\n", params()->isa.size(), numThreads);
247    }
248}
249
250void
251BaseCPU::enableFunctionTrace()
252{
253    functionTracingEnabled = true;
254}
255
256BaseCPU::~BaseCPU()
257{
258    delete profileEvent;
259    delete[] comLoadEventQueue;
260    delete[] comInstEventQueue;
261}
262
263void
264BaseCPU::init()
265{
266    if (!params()->switched_out) {
267        registerThreadContexts();
268
269        verifyMemoryMode();
270    }
271}
272
273void
274BaseCPU::startup()
275{
276    if (FullSystem) {
277        if (!params()->switched_out && profileEvent)
278            schedule(profileEvent, curTick());
279    }
280
281    if (params()->progress_interval) {
282        new CPUProgressEvent(this, params()->progress_interval);
283    }
284}
285
286
287void
288BaseCPU::regStats()
289{
290    using namespace Stats;
291
292    numCycles
293        .name(name() + ".numCycles")
294        .desc("number of cpu cycles simulated")
295        ;
296
297    numWorkItemsStarted
298        .name(name() + ".numWorkItemsStarted")
299        .desc("number of work items this cpu started")
300        ;
301
302    numWorkItemsCompleted
303        .name(name() + ".numWorkItemsCompleted")
304        .desc("number of work items this cpu completed")
305        ;
306
307    int size = threadContexts.size();
308    if (size > 1) {
309        for (int i = 0; i < size; ++i) {
310            stringstream namestr;
311            ccprintf(namestr, "%s.ctx%d", name(), i);
312            threadContexts[i]->regStats(namestr.str());
313        }
314    } else if (size == 1)
315        threadContexts[0]->regStats(name());
316}
317
318BaseMasterPort &
319BaseCPU::getMasterPort(const string &if_name, PortID idx)
320{
321    // Get the right port based on name. This applies to all the
322    // subclasses of the base CPU and relies on their implementation
323    // of getDataPort and getInstPort. In all cases there methods
324    // return a MasterPort pointer.
325    if (if_name == "dcache_port")
326        return getDataPort();
327    else if (if_name == "icache_port")
328        return getInstPort();
329    else
330        return MemObject::getMasterPort(if_name, idx);
331}
332
333void
334BaseCPU::registerThreadContexts()
335{
336    ThreadID size = threadContexts.size();
337    for (ThreadID tid = 0; tid < size; ++tid) {
338        ThreadContext *tc = threadContexts[tid];
339
340        /** This is so that contextId and cpuId match where there is a
341         * 1cpu:1context relationship.  Otherwise, the order of registration
342         * could affect the assignment and cpu 1 could have context id 3, for
343         * example.  We may even want to do something like this for SMT so that
344         * cpu 0 has the lowest thread contexts and cpu N has the highest, but
345         * I'll just do this for now
346         */
347        if (numThreads == 1)
348            tc->setContextId(system->registerThreadContext(tc, _cpuId));
349        else
350            tc->setContextId(system->registerThreadContext(tc));
351
352        if (!FullSystem)
353            tc->getProcessPtr()->assignThreadContext(tc->contextId());
354    }
355}
356
357
358int
359BaseCPU::findContext(ThreadContext *tc)
360{
361    ThreadID size = threadContexts.size();
362    for (ThreadID tid = 0; tid < size; ++tid) {
363        if (tc == threadContexts[tid])
364            return tid;
365    }
366    return 0;
367}
368
369void
370BaseCPU::switchOut()
371{
372    assert(!_switchedOut);
373    _switchedOut = true;
374    if (profileEvent && profileEvent->scheduled())
375        deschedule(profileEvent);
376
377    // Flush all TLBs in the CPU to avoid having stale translations if
378    // it gets switched in later.
379    flushTLBs();
380}
381
382void
383BaseCPU::takeOverFrom(BaseCPU *oldCPU)
384{
385    assert(threadContexts.size() == oldCPU->threadContexts.size());
386    assert(_cpuId == oldCPU->cpuId());
387    assert(_switchedOut);
388    assert(oldCPU != this);
389    _pid = oldCPU->getPid();
390    _taskId = oldCPU->taskId();
391    _switchedOut = false;
392
393    ThreadID size = threadContexts.size();
394    for (ThreadID i = 0; i < size; ++i) {
395        ThreadContext *newTC = threadContexts[i];
396        ThreadContext *oldTC = oldCPU->threadContexts[i];
397
398        newTC->takeOverFrom(oldTC);
399
400        CpuEvent::replaceThreadContext(oldTC, newTC);
401
402        assert(newTC->contextId() == oldTC->contextId());
403        assert(newTC->threadId() == oldTC->threadId());
404        system->replaceThreadContext(newTC, newTC->contextId());
405
406        /* This code no longer works since the zero register (e.g.,
407         * r31 on Alpha) doesn't necessarily contain zero at this
408         * point.
409           if (DTRACE(Context))
410            ThreadContext::compare(oldTC, newTC);
411        */
412
413        BaseMasterPort *old_itb_port = oldTC->getITBPtr()->getMasterPort();
414        BaseMasterPort *old_dtb_port = oldTC->getDTBPtr()->getMasterPort();
415        BaseMasterPort *new_itb_port = newTC->getITBPtr()->getMasterPort();
416        BaseMasterPort *new_dtb_port = newTC->getDTBPtr()->getMasterPort();
417
418        // Move over any table walker ports if they exist
419        if (new_itb_port) {
420            assert(!new_itb_port->isConnected());
421            assert(old_itb_port);
422            assert(old_itb_port->isConnected());
423            BaseSlavePort &slavePort = old_itb_port->getSlavePort();
424            old_itb_port->unbind();
425            new_itb_port->bind(slavePort);
426        }
427        if (new_dtb_port) {
428            assert(!new_dtb_port->isConnected());
429            assert(old_dtb_port);
430            assert(old_dtb_port->isConnected());
431            BaseSlavePort &slavePort = old_dtb_port->getSlavePort();
432            old_dtb_port->unbind();
433            new_dtb_port->bind(slavePort);
434        }
435
436        // Checker whether or not we have to transfer CheckerCPU
437        // objects over in the switch
438        CheckerCPU *oldChecker = oldTC->getCheckerCpuPtr();
439        CheckerCPU *newChecker = newTC->getCheckerCpuPtr();
440        if (oldChecker && newChecker) {
441            BaseMasterPort *old_checker_itb_port =
442                oldChecker->getITBPtr()->getMasterPort();
443            BaseMasterPort *old_checker_dtb_port =
444                oldChecker->getDTBPtr()->getMasterPort();
445            BaseMasterPort *new_checker_itb_port =
446                newChecker->getITBPtr()->getMasterPort();
447            BaseMasterPort *new_checker_dtb_port =
448                newChecker->getDTBPtr()->getMasterPort();
449
450            // Move over any table walker ports if they exist for checker
451            if (new_checker_itb_port) {
452                assert(!new_checker_itb_port->isConnected());
453                assert(old_checker_itb_port);
454                assert(old_checker_itb_port->isConnected());
455                BaseSlavePort &slavePort =
456                    old_checker_itb_port->getSlavePort();
457                old_checker_itb_port->unbind();
458                new_checker_itb_port->bind(slavePort);
459            }
460            if (new_checker_dtb_port) {
461                assert(!new_checker_dtb_port->isConnected());
462                assert(old_checker_dtb_port);
463                assert(old_checker_dtb_port->isConnected());
464                BaseSlavePort &slavePort =
465                    old_checker_dtb_port->getSlavePort();
466                old_checker_dtb_port->unbind();
467                new_checker_dtb_port->bind(slavePort);
468            }
469        }
470    }
471
472    interrupts = oldCPU->interrupts;
473    interrupts->setCPU(this);
474    oldCPU->interrupts = NULL;
475
476    if (FullSystem) {
477        for (ThreadID i = 0; i < size; ++i)
478            threadContexts[i]->profileClear();
479
480        if (profileEvent)
481            schedule(profileEvent, curTick());
482    }
483
484    // All CPUs have an instruction and a data port, and the new CPU's
485    // ports are dangling while the old CPU has its ports connected
486    // already. Unbind the old CPU and then bind the ports of the one
487    // we are switching to.
488    assert(!getInstPort().isConnected());
489    assert(oldCPU->getInstPort().isConnected());
490    BaseSlavePort &inst_peer_port = oldCPU->getInstPort().getSlavePort();
491    oldCPU->getInstPort().unbind();
492    getInstPort().bind(inst_peer_port);
493
494    assert(!getDataPort().isConnected());
495    assert(oldCPU->getDataPort().isConnected());
496    BaseSlavePort &data_peer_port = oldCPU->getDataPort().getSlavePort();
497    oldCPU->getDataPort().unbind();
498    getDataPort().bind(data_peer_port);
499}
500
501void
502BaseCPU::flushTLBs()
503{
504    for (ThreadID i = 0; i < threadContexts.size(); ++i) {
505        ThreadContext &tc(*threadContexts[i]);
506        CheckerCPU *checker(tc.getCheckerCpuPtr());
507
508        tc.getITBPtr()->flushAll();
509        tc.getDTBPtr()->flushAll();
510        if (checker) {
511            checker->getITBPtr()->flushAll();
512            checker->getDTBPtr()->flushAll();
513        }
514    }
515}
516
517
518BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, Tick _interval)
519    : cpu(_cpu), interval(_interval)
520{ }
521
522void
523BaseCPU::ProfileEvent::process()
524{
525    ThreadID size = cpu->threadContexts.size();
526    for (ThreadID i = 0; i < size; ++i) {
527        ThreadContext *tc = cpu->threadContexts[i];
528        tc->profileSample();
529    }
530
531    cpu->schedule(this, curTick() + interval);
532}
533
534void
535BaseCPU::serialize(std::ostream &os)
536{
537    SERIALIZE_SCALAR(instCnt);
538
539    if (!_switchedOut) {
540        /* Unlike _pid, _taskId is not serialized, as they are dynamically
541         * assigned unique ids that are only meaningful for the duration of
542         * a specific run. We will need to serialize the entire taskMap in
543         * system. */
544        SERIALIZE_SCALAR(_pid);
545
546        interrupts->serialize(os);
547
548        // Serialize the threads, this is done by the CPU implementation.
549        for (ThreadID i = 0; i < numThreads; ++i) {
550            nameOut(os, csprintf("%s.xc.%i", name(), i));
551            serializeThread(os, i);
552        }
553    }
554}
555
556void
557BaseCPU::unserialize(Checkpoint *cp, const std::string &section)
558{
559    UNSERIALIZE_SCALAR(instCnt);
560
561    if (!_switchedOut) {
562        UNSERIALIZE_SCALAR(_pid);
563        interrupts->unserialize(cp, section);
564
565        // Unserialize the threads, this is done by the CPU implementation.
566        for (ThreadID i = 0; i < numThreads; ++i)
567            unserializeThread(cp, csprintf("%s.xc.%i", section, i), i);
568    }
569}
570
571void
572BaseCPU::scheduleInstStop(ThreadID tid, Counter insts, const char *cause)
573{
574    const Tick now(comInstEventQueue[tid]->getCurTick());
575    Event *event(new LocalSimLoopExitEvent(cause, 0));
576
577    comInstEventQueue[tid]->schedule(event, now + insts);
578}
579
580void
581BaseCPU::scheduleLoadStop(ThreadID tid, Counter loads, const char *cause)
582{
583    const Tick now(comLoadEventQueue[tid]->getCurTick());
584    Event *event(new LocalSimLoopExitEvent(cause, 0));
585
586    comLoadEventQueue[tid]->schedule(event, now + loads);
587}
588
589
590void
591BaseCPU::traceFunctionsInternal(Addr pc)
592{
593    if (!debugSymbolTable)
594        return;
595
596    // if pc enters different function, print new function symbol and
597    // update saved range.  Otherwise do nothing.
598    if (pc < currentFunctionStart || pc >= currentFunctionEnd) {
599        string sym_str;
600        bool found = debugSymbolTable->findNearestSymbol(pc, sym_str,
601                                                         currentFunctionStart,
602                                                         currentFunctionEnd);
603
604        if (!found) {
605            // no symbol found: use addr as label
606            sym_str = csprintf("0x%x", pc);
607            currentFunctionStart = pc;
608            currentFunctionEnd = pc + 1;
609        }
610
611        ccprintf(*functionTraceStream, " (%d)\n%d: %s",
612                 curTick() - functionEntryTick, curTick(), sym_str);
613        functionEntryTick = curTick();
614    }
615}
616