| /* | |
| * Copyright (c) 2010-2012, 2015, 2017, 2018 ARM Limited | |
| * Copyright (c) 2013 Advanced Micro Devices, Inc. | |
| * All rights reserved | |
| * | |
| * The license below extends only to copyright in the software and shall | |
| * not be construed as granting a license to any other intellectual | |
| * property including but not limited to intellectual property relating | |
| * to a hardware implementation of the functionality of the software | |
| * licensed hereunder. You may use the software subject to the license | |
| * terms below provided that you ensure that this notice is replicated | |
| * unmodified and in its entirety in all distributions of the software, | |
| * modified or unmodified, in source code or in binary form. | |
| * | |
| * Copyright (c) 2002-2005 The Regents of The University of Michigan | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are | |
| * met: redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer; | |
| * redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution; | |
| * neither the name of the copyright holders nor the names of its | |
| * contributors may be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * Authors: Steve Reinhardt | |
| */ | |
| #include "cpu/simple/base.hh" | |
| #include "arch/stacktrace.hh" | |
| #include "arch/utility.hh" | |
| #include "arch/vtophys.hh" | |
| #include "base/cp_annotate.hh" | |
| #include "base/cprintf.hh" | |
| #include "base/inifile.hh" | |
| #include "base/loader/symtab.hh" | |
| #include "base/logging.hh" | |
| #include "base/pollevent.hh" | |
| #include "base/trace.hh" | |
| #include "base/types.hh" | |
| #include "config/the_isa.hh" | |
| #include "cpu/base.hh" | |
| #include "cpu/checker/cpu.hh" | |
| #include "cpu/checker/thread_context.hh" | |
| #include "cpu/exetrace.hh" | |
| #include "cpu/pred/bpred_unit.hh" | |
| #include "cpu/profile.hh" | |
| #include "cpu/simple/exec_context.hh" | |
| #include "cpu/simple_thread.hh" | |
| #include "cpu/smt.hh" | |
| #include "cpu/static_inst.hh" | |
| #include "cpu/thread_context.hh" | |
| #include "debug/Decode.hh" | |
| #include "debug/Fetch.hh" | |
| #include "debug/Quiesce.hh" | |
| #include "mem/packet.hh" | |
| #include "mem/request.hh" | |
| #include "params/BaseSimpleCPU.hh" | |
| #include "sim/byteswap.hh" | |
| #include "sim/debug.hh" | |
| #include "sim/faults.hh" | |
| #include "sim/full_system.hh" | |
| #include "sim/sim_events.hh" | |
| #include "sim/sim_object.hh" | |
| #include "sim/stats.hh" | |
| #include "sim/system.hh" | |
| using namespace std; | |
| using namespace TheISA; | |
| BaseSimpleCPU::BaseSimpleCPU(BaseSimpleCPUParams *p) | |
| : BaseCPU(p), | |
| curThread(0), | |
| branchPred(p->branchPred), | |
| traceData(NULL), | |
| inst(), | |
| _status(Idle) | |
| { | |
| SimpleThread *thread; | |
| for (unsigned i = 0; i < numThreads; i++) { | |
| if (FullSystem) { | |
| thread = new SimpleThread(this, i, p->system, | |
| p->itb, p->dtb, p->isa[i]); | |
| } else { | |
| thread = new SimpleThread(this, i, p->system, p->workload[i], | |
| p->itb, p->dtb, p->isa[i]); | |
| } | |
| threadInfo.push_back(new SimpleExecContext(this, thread)); | |
| ThreadContext *tc = thread->getTC(); | |
| threadContexts.push_back(tc); | |
| } | |
| if (p->checker) { | |
| if (numThreads != 1) | |
| fatal("Checker currently does not support SMT"); | |
| BaseCPU *temp_checker = p->checker; | |
| checker = dynamic_cast<CheckerCPU *>(temp_checker); | |
| checker->setSystem(p->system); | |
| // Manipulate thread context | |
| ThreadContext *cpu_tc = threadContexts[0]; | |
| threadContexts[0] = new CheckerThreadContext<ThreadContext>(cpu_tc, this->checker); | |
| } else { | |
| checker = NULL; | |
| } | |
| } | |
| void | |
| BaseSimpleCPU::init() | |
| { | |
| BaseCPU::init(); | |
| for (auto tc : threadContexts) { | |
| // Initialise the ThreadContext's memory proxies | |
| tc->initMemProxies(tc); | |
| if (FullSystem && !params()->switched_out) { | |
| // initialize CPU, including PC | |
| TheISA::initCPU(tc, tc->contextId()); | |
| } | |
| } | |
| } | |
| void | |
| BaseSimpleCPU::checkPcEventQueue() | |
| { | |
| Addr oldpc, pc = threadInfo[curThread]->thread->instAddr(); | |
| do { | |
| oldpc = pc; | |
| system->pcEventQueue.service(threadContexts[curThread]); | |
| pc = threadInfo[curThread]->thread->instAddr(); | |
| } while (oldpc != pc); | |
| } | |
| void | |
| BaseSimpleCPU::swapActiveThread() | |
| { | |
| if (numThreads > 1) { | |
| if ((!curStaticInst || !curStaticInst->isDelayedCommit()) && | |
| !threadInfo[curThread]->stayAtPC) { | |
| // Swap active threads | |
| if (!activeThreads.empty()) { | |
| curThread = activeThreads.front(); | |
| activeThreads.pop_front(); | |
| activeThreads.push_back(curThread); | |
| } | |
| } | |
| } | |
| } | |
| void | |
| BaseSimpleCPU::countInst() | |
| { | |
| SimpleExecContext& t_info = *threadInfo[curThread]; | |
| if (!curStaticInst->isMicroop() || curStaticInst->isLastMicroop()) { | |
| t_info.numInst++; | |
| t_info.numInsts++; | |
| system->totalNumInsts++; | |
| t_info.thread->funcExeInst++; | |
| } | |
| t_info.numOp++; | |
| t_info.numOps++; | |
| } | |
| Counter | |
| BaseSimpleCPU::totalInsts() const | |
| { | |
| Counter total_inst = 0; | |
| for (auto& t_info : threadInfo) { | |
| total_inst += t_info->numInst; | |
| } | |
| return total_inst; | |
| } | |
| Counter | |
| BaseSimpleCPU::totalOps() const | |
| { | |
| Counter total_op = 0; | |
| for (auto& t_info : threadInfo) { | |
| total_op += t_info->numOp; | |
| } | |
| return total_op; | |
| } | |
| BaseSimpleCPU::~BaseSimpleCPU() | |
| { | |
| } | |
| void | |
| BaseSimpleCPU::haltContext(ThreadID thread_num) | |
| { | |
| // for now, these are equivalent | |
| suspendContext(thread_num); | |
| updateCycleCounters(BaseCPU::CPU_STATE_SLEEP); | |
| } | |
| void | |
| BaseSimpleCPU::regStats() | |
| { | |
| using namespace Stats; | |
| BaseCPU::regStats(); | |
| for (ThreadID tid = 0; tid < numThreads; tid++) { | |
| SimpleExecContext& t_info = *threadInfo[tid]; | |
| std::string thread_str = name(); | |
| if (numThreads > 1) | |
| thread_str += ".thread" + std::to_string(tid); | |
| t_info.numInsts | |
| .name(thread_str + ".committedInsts") | |
| .desc("Number of instructions committed") | |
| ; | |
| t_info.numOps | |
| .name(thread_str + ".committedOps") | |
| .desc("Number of ops (including micro ops) committed") | |
| ; | |
| t_info.numIntAluAccesses | |
| .name(thread_str + ".num_int_alu_accesses") | |
| .desc("Number of integer alu accesses") | |
| ; | |
| t_info.numFpAluAccesses | |
| .name(thread_str + ".num_fp_alu_accesses") | |
| .desc("Number of float alu accesses") | |
| ; | |
| t_info.numVecAluAccesses | |
| .name(thread_str + ".num_vec_alu_accesses") | |
| .desc("Number of vector alu accesses") | |
| ; | |
| t_info.numCallsReturns | |
| .name(thread_str + ".num_func_calls") | |
| .desc("number of times a function call or return occured") | |
| ; | |
| t_info.numCondCtrlInsts | |
| .name(thread_str + ".num_conditional_control_insts") | |
| .desc("number of instructions that are conditional controls") | |
| ; | |
| t_info.numIntInsts | |
| .name(thread_str + ".num_int_insts") | |
| .desc("number of integer instructions") | |
| ; | |
| t_info.numFpInsts | |
| .name(thread_str + ".num_fp_insts") | |
| .desc("number of float instructions") | |
| ; | |
| t_info.numVecInsts | |
| .name(thread_str + ".num_vec_insts") | |
| .desc("number of vector instructions") | |
| ; | |
| t_info.numIntRegReads | |
| .name(thread_str + ".num_int_register_reads") | |
| .desc("number of times the integer registers were read") | |
| ; | |
| t_info.numIntRegWrites | |
| .name(thread_str + ".num_int_register_writes") | |
| .desc("number of times the integer registers were written") | |
| ; | |
| t_info.numFpRegReads | |
| .name(thread_str + ".num_fp_register_reads") | |
| .desc("number of times the floating registers were read") | |
| ; | |
| t_info.numFpRegWrites | |
| .name(thread_str + ".num_fp_register_writes") | |
| .desc("number of times the floating registers were written") | |
| ; | |
| t_info.numVecRegReads | |
| .name(thread_str + ".num_vec_register_reads") | |
| .desc("number of times the vector registers were read") | |
| ; | |
| t_info.numVecRegWrites | |
| .name(thread_str + ".num_vec_register_writes") | |
| .desc("number of times the vector registers were written") | |
| ; | |
| t_info.numCCRegReads | |
| .name(thread_str + ".num_cc_register_reads") | |
| .desc("number of times the CC registers were read") | |
| .flags(nozero) | |
| ; | |
| t_info.numCCRegWrites | |
| .name(thread_str + ".num_cc_register_writes") | |
| .desc("number of times the CC registers were written") | |
| .flags(nozero) | |
| ; | |
| t_info.numMemRefs | |
| .name(thread_str + ".num_mem_refs") | |
| .desc("number of memory refs") | |
| ; | |
| t_info.numStoreInsts | |
| .name(thread_str + ".num_store_insts") | |
| .desc("Number of store instructions") | |
| ; | |
| t_info.numLoadInsts | |
| .name(thread_str + ".num_load_insts") | |
| .desc("Number of load instructions") | |
| ; | |
| t_info.notIdleFraction | |
| .name(thread_str + ".not_idle_fraction") | |
| .desc("Percentage of non-idle cycles") | |
| ; | |
| t_info.idleFraction | |
| .name(thread_str + ".idle_fraction") | |
| .desc("Percentage of idle cycles") | |
| ; | |
| t_info.numBusyCycles | |
| .name(thread_str + ".num_busy_cycles") | |
| .desc("Number of busy cycles") | |
| ; | |
| t_info.numIdleCycles | |
| .name(thread_str + ".num_idle_cycles") | |
| .desc("Number of idle cycles") | |
| ; | |
| t_info.icacheStallCycles | |
| .name(thread_str + ".icache_stall_cycles") | |
| .desc("ICache total stall cycles") | |
| .prereq(t_info.icacheStallCycles) | |
| ; | |
| t_info.dcacheStallCycles | |
| .name(thread_str + ".dcache_stall_cycles") | |
| .desc("DCache total stall cycles") | |
| .prereq(t_info.dcacheStallCycles) | |
| ; | |
| t_info.statExecutedInstType | |
| .init(Enums::Num_OpClass) | |
| .name(thread_str + ".op_class") | |
| .desc("Class of executed instruction") | |
| .flags(total | pdf | dist) | |
| ; | |
| for (unsigned i = 0; i < Num_OpClasses; ++i) { | |
| t_info.statExecutedInstType.subname(i, Enums::OpClassStrings[i]); | |
| } | |
| t_info.idleFraction = constant(1.0) - t_info.notIdleFraction; | |
| t_info.numIdleCycles = t_info.idleFraction * numCycles; | |
| t_info.numBusyCycles = t_info.notIdleFraction * numCycles; | |
| t_info.numBranches | |
| .name(thread_str + ".Branches") | |
| .desc("Number of branches fetched") | |
| .prereq(t_info.numBranches); | |
| t_info.numPredictedBranches | |
| .name(thread_str + ".predictedBranches") | |
| .desc("Number of branches predicted as taken") | |
| .prereq(t_info.numPredictedBranches); | |
| t_info.numBranchMispred | |
| .name(thread_str + ".BranchMispred") | |
| .desc("Number of branch mispredictions") | |
| .prereq(t_info.numBranchMispred); | |
| } | |
| } | |
| void | |
| BaseSimpleCPU::resetStats() | |
| { | |
| for (auto &thread_info : threadInfo) { | |
| thread_info->notIdleFraction = (_status != Idle); | |
| } | |
| } | |
| void | |
| BaseSimpleCPU::serializeThread(CheckpointOut &cp, ThreadID tid) const | |
| { | |
| assert(_status == Idle || _status == Running); | |
| threadInfo[tid]->thread->serialize(cp); | |
| } | |
| void | |
| BaseSimpleCPU::unserializeThread(CheckpointIn &cp, ThreadID tid) | |
| { | |
| threadInfo[tid]->thread->unserialize(cp); | |
| } | |
| void | |
| change_thread_state(ThreadID tid, int activate, int priority) | |
| { | |
| } | |
| Addr | |
| BaseSimpleCPU::dbg_vtophys(Addr addr) | |
| { | |
| return vtophys(threadContexts[curThread], addr); | |
| } | |
| void | |
| BaseSimpleCPU::wakeup(ThreadID tid) | |
| { | |
| getCpuAddrMonitor(tid)->gotWakeup = true; | |
| if (threadInfo[tid]->thread->status() == ThreadContext::Suspended) { | |
| DPRINTF(Quiesce,"[tid:%d] Suspended Processor awoke\n", tid); | |
| threadInfo[tid]->thread->activate(); | |
| } | |
| } | |
| void | |
| BaseSimpleCPU::checkForInterrupts() | |
| { | |
| SimpleExecContext&t_info = *threadInfo[curThread]; | |
| SimpleThread* thread = t_info.thread; | |
| ThreadContext* tc = thread->getTC(); | |
| if (checkInterrupts(tc)) { | |
| Fault interrupt = interrupts[curThread]->getInterrupt(tc); | |
| if (interrupt != NoFault) { | |
| t_info.fetchOffset = 0; | |
| interrupts[curThread]->updateIntrInfo(tc); | |
| interrupt->invoke(tc); | |
| thread->decoder.reset(); | |
| } | |
| } | |
| } | |
| void | |
| BaseSimpleCPU::setupFetchRequest(const RequestPtr &req) | |
| { | |
| SimpleExecContext &t_info = *threadInfo[curThread]; | |
| SimpleThread* thread = t_info.thread; | |
| Addr instAddr = thread->instAddr(); | |
| Addr fetchPC = (instAddr & PCMask) + t_info.fetchOffset; | |
| // set up memory request for instruction fetch | |
| DPRINTF(Fetch, "Fetch: Inst PC:%08p, Fetch PC:%08p\n", instAddr, fetchPC); | |
| req->setVirt(0, fetchPC, sizeof(MachInst), Request::INST_FETCH, | |
| instMasterId(), instAddr); | |
| } | |
| void | |
| BaseSimpleCPU::preExecute() | |
| { | |
| SimpleExecContext &t_info = *threadInfo[curThread]; | |
| SimpleThread* thread = t_info.thread; | |
| // maintain $r0 semantics | |
| thread->setIntReg(ZeroReg, 0); | |
| #if THE_ISA == ALPHA_ISA | |
| thread->setFloatReg(ZeroReg, 0); | |
| #endif // ALPHA_ISA | |
| // resets predicates | |
| t_info.setPredicate(true); | |
| t_info.setMemAccPredicate(true); | |
| // check for instruction-count-based events | |
| comInstEventQueue[curThread]->serviceEvents(t_info.numInst); | |
| system->instEventQueue.serviceEvents(system->totalNumInsts); | |
| // decode the instruction | |
| inst = gtoh(inst); | |
| TheISA::PCState pcState = thread->pcState(); | |
| if (isRomMicroPC(pcState.microPC())) { | |
| t_info.stayAtPC = false; | |
| curStaticInst = microcodeRom.fetchMicroop(pcState.microPC(), | |
| curMacroStaticInst); | |
| } else if (!curMacroStaticInst) { | |
| //We're not in the middle of a macro instruction | |
| StaticInstPtr instPtr = NULL; | |
| TheISA::Decoder *decoder = &(thread->decoder); | |
| //Predecode, ie bundle up an ExtMachInst | |
| //If more fetch data is needed, pass it in. | |
| Addr fetchPC = (pcState.instAddr() & PCMask) + t_info.fetchOffset; | |
| //if (decoder->needMoreBytes()) | |
| decoder->moreBytes(pcState, fetchPC, inst); | |
| //else | |
| // decoder->process(); | |
| //Decode an instruction if one is ready. Otherwise, we'll have to | |
| //fetch beyond the MachInst at the current pc. | |
| instPtr = decoder->decode(pcState); | |
| if (instPtr) { | |
| t_info.stayAtPC = false; | |
| thread->pcState(pcState); | |
| } else { | |
| t_info.stayAtPC = true; | |
| t_info.fetchOffset += sizeof(MachInst); | |
| } | |
| //If we decoded an instruction and it's microcoded, start pulling | |
| //out micro ops | |
| if (instPtr && instPtr->isMacroop()) { | |
| curMacroStaticInst = instPtr; | |
| curStaticInst = | |
| curMacroStaticInst->fetchMicroop(pcState.microPC()); | |
| } else { | |
| curStaticInst = instPtr; | |
| } | |
| } else { | |
| //Read the next micro op from the macro op | |
| curStaticInst = curMacroStaticInst->fetchMicroop(pcState.microPC()); | |
| } | |
| //If we decoded an instruction this "tick", record information about it. | |
| if (curStaticInst) { | |
| #if TRACING_ON | |
| traceData = tracer->getInstRecord(curTick(), thread->getTC(), | |
| curStaticInst, thread->pcState(), curMacroStaticInst); | |
| DPRINTF(Decode,"Decode: Decoded %s instruction: %#x\n", | |
| curStaticInst->getName(), curStaticInst->machInst); | |
| #endif // TRACING_ON | |
| } | |
| if (branchPred && curStaticInst && | |
| curStaticInst->isControl()) { | |
| // Use a fake sequence number since we only have one | |
| // instruction in flight at the same time. | |
| const InstSeqNum cur_sn(0); | |
| t_info.predPC = thread->pcState(); | |
| const bool predict_taken( | |
| branchPred->predict(curStaticInst, cur_sn, t_info.predPC, | |
| curThread)); | |
| if (predict_taken) | |
| ++t_info.numPredictedBranches; | |
| } | |
| } | |
| void | |
| BaseSimpleCPU::postExecute() | |
| { | |
| SimpleExecContext &t_info = *threadInfo[curThread]; | |
| SimpleThread* thread = t_info.thread; | |
| assert(curStaticInst); | |
| TheISA::PCState pc = threadContexts[curThread]->pcState(); | |
| Addr instAddr = pc.instAddr(); | |
| if (FullSystem && thread->profile) { | |
| bool usermode = TheISA::inUserMode(threadContexts[curThread]); | |
| thread->profilePC = usermode ? 1 : instAddr; | |
| ProfileNode *node = thread->profile->consume(threadContexts[curThread], | |
| curStaticInst); | |
| if (node) | |
| thread->profileNode = node; | |
| } | |
| if (curStaticInst->isMemRef()) { | |
| t_info.numMemRefs++; | |
| } | |
| if (curStaticInst->isLoad()) { | |
| ++t_info.numLoad; | |
| comLoadEventQueue[curThread]->serviceEvents(t_info.numLoad); | |
| } | |
| if (CPA::available()) { | |
| CPA::cpa()->swAutoBegin(threadContexts[curThread], pc.nextInstAddr()); | |
| } | |
| if (curStaticInst->isControl()) { | |
| ++t_info.numBranches; | |
| } | |
| /* Power model statistics */ | |
| //integer alu accesses | |
| if (curStaticInst->isInteger()){ | |
| t_info.numIntAluAccesses++; | |
| t_info.numIntInsts++; | |
| } | |
| //float alu accesses | |
| if (curStaticInst->isFloating()){ | |
| t_info.numFpAluAccesses++; | |
| t_info.numFpInsts++; | |
| } | |
| //vector alu accesses | |
| if (curStaticInst->isVector()){ | |
| t_info.numVecAluAccesses++; | |
| t_info.numVecInsts++; | |
| } | |
| //number of function calls/returns to get window accesses | |
| if (curStaticInst->isCall() || curStaticInst->isReturn()){ | |
| t_info.numCallsReturns++; | |
| } | |
| //the number of branch predictions that will be made | |
| if (curStaticInst->isCondCtrl()){ | |
| t_info.numCondCtrlInsts++; | |
| } | |
| //result bus acceses | |
| if (curStaticInst->isLoad()){ | |
| t_info.numLoadInsts++; | |
| } | |
| if (curStaticInst->isStore() || curStaticInst->isAtomic()){ | |
| t_info.numStoreInsts++; | |
| } | |
| /* End power model statistics */ | |
| t_info.statExecutedInstType[curStaticInst->opClass()]++; | |
| if (FullSystem) | |
| traceFunctions(instAddr); | |
| if (traceData) { | |
| traceData->dump(); | |
| delete traceData; | |
| traceData = NULL; | |
| } | |
| // Call CPU instruction commit probes | |
| probeInstCommit(curStaticInst, instAddr); | |
| } | |
| void | |
| BaseSimpleCPU::advancePC(const Fault &fault) | |
| { | |
| SimpleExecContext &t_info = *threadInfo[curThread]; | |
| SimpleThread* thread = t_info.thread; | |
| const bool branching(thread->pcState().branching()); | |
| //Since we're moving to a new pc, zero out the offset | |
| t_info.fetchOffset = 0; | |
| if (fault != NoFault) { | |
| curMacroStaticInst = StaticInst::nullStaticInstPtr; | |
| fault->invoke(threadContexts[curThread], curStaticInst); | |
| thread->decoder.reset(); | |
| } else { | |
| if (curStaticInst) { | |
| if (curStaticInst->isLastMicroop()) | |
| curMacroStaticInst = StaticInst::nullStaticInstPtr; | |
| TheISA::PCState pcState = thread->pcState(); | |
| TheISA::advancePC(pcState, curStaticInst); | |
| thread->pcState(pcState); | |
| } | |
| } | |
| if (branchPred && curStaticInst && curStaticInst->isControl()) { | |
| // Use a fake sequence number since we only have one | |
| // instruction in flight at the same time. | |
| const InstSeqNum cur_sn(0); | |
| if (t_info.predPC == thread->pcState()) { | |
| // Correctly predicted branch | |
| branchPred->update(cur_sn, curThread); | |
| } else { | |
| // Mis-predicted branch | |
| branchPred->squash(cur_sn, thread->pcState(), branching, curThread); | |
| ++t_info.numBranchMispred; | |
| } | |
| } | |
| } | |
| void | |
| BaseSimpleCPU::startup() | |
| { | |
| BaseCPU::startup(); | |
| for (auto& t_info : threadInfo) | |
| t_info->thread->startup(); | |
| } |