commit_impl.hh revision 1681
1// @todo: Bug when something reaches execute, and mispredicts, but is never 2// put into the ROB because the ROB is full. Need rename stage to predict 3// the free ROB entries better. 4 5#ifndef __COMMIT_IMPL_HH__ 6#define __COMMIT_IMPL_HH__ 7 8#include "base/timebuf.hh" 9#include "cpu/beta_cpu/commit.hh" 10#include "cpu/exetrace.hh" 11 12template <class Impl> 13SimpleCommit<Impl>::SimpleCommit(Params ¶ms) 14 : dcacheInterface(params.dcacheInterface), 15 iewToCommitDelay(params.iewToCommitDelay), 16 renameToROBDelay(params.renameToROBDelay), 17 renameWidth(params.renameWidth), 18 iewWidth(params.executeWidth), 19 commitWidth(params.commitWidth) 20{ 21 _status = Idle; 22} 23 24template <class Impl> 25void 26SimpleCommit<Impl>::regStats() 27{ 28 commitCommittedInsts 29 .name(name() + ".commitCommittedInsts") 30 .desc("The number of committed instructions") 31 .prereq(commitCommittedInsts); 32 commitSquashedInsts 33 .name(name() + ".commitSquashedInsts") 34 .desc("The number of squashed insts skipped by commit") 35 .prereq(commitSquashedInsts); 36 commitSquashEvents 37 .name(name() + ".commitSquashEvents") 38 .desc("The number of times commit is told to squash") 39 .prereq(commitSquashEvents); 40 commitNonSpecStalls 41 .name(name() + ".commitNonSpecStalls") 42 .desc("The number of times commit has been forced to stall to " 43 "communicate backwards") 44 .prereq(commitNonSpecStalls); 45 commitCommittedBranches 46 .name(name() + ".commitCommittedBranches") 47 .desc("The number of committed branches") 48 .prereq(commitCommittedBranches); 49 commitCommittedLoads 50 .name(name() + ".commitCommittedLoads") 51 .desc("The number of committed loads") 52 .prereq(commitCommittedLoads); 53 commitCommittedMemRefs 54 .name(name() + ".commitCommittedMemRefs") 55 .desc("The number of committed memory references") 56 .prereq(commitCommittedMemRefs); 57 branchMispredicts 58 .name(name() + ".branchMispredicts") 59 .desc("The number of times a branch was mispredicted") 60 .prereq(branchMispredicts); 61 n_committed_dist 62 .init(0,commitWidth,1) 63 .name(name() + ".COM:committed_per_cycle") 64 .desc("Number of insts commited each cycle") 65 .flags(Stats::pdf) 66 ; 67} 68 69template <class Impl> 70void 71SimpleCommit<Impl>::setCPU(FullCPU *cpu_ptr) 72{ 73 DPRINTF(Commit, "Commit: Setting CPU pointer.\n"); 74 cpu = cpu_ptr; 75} 76 77template <class Impl> 78void 79SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) 80{ 81 DPRINTF(Commit, "Commit: Setting time buffer pointer.\n"); 82 timeBuffer = tb_ptr; 83 84 // Setup wire to send information back to IEW. 85 toIEW = timeBuffer->getWire(0); 86 87 // Setup wire to read data from IEW (for the ROB). 88 robInfoFromIEW = timeBuffer->getWire(-iewToCommitDelay); 89} 90 91template <class Impl> 92void 93SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) 94{ 95 DPRINTF(Commit, "Commit: Setting rename queue pointer.\n"); 96 renameQueue = rq_ptr; 97 98 // Setup wire to get instructions from rename (for the ROB). 99 fromRename = renameQueue->getWire(-renameToROBDelay); 100} 101 102template <class Impl> 103void 104SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) 105{ 106 DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n"); 107 iewQueue = iq_ptr; 108 109 // Setup wire to get instructions from IEW. 110 fromIEW = iewQueue->getWire(-iewToCommitDelay); 111} 112 113template <class Impl> 114void 115SimpleCommit<Impl>::setROB(ROB *rob_ptr) 116{ 117 DPRINTF(Commit, "Commit: Setting ROB pointer.\n"); 118 rob = rob_ptr; 119} 120 121template <class Impl> 122void 123SimpleCommit<Impl>::tick() 124{ 125 // If the ROB is currently in its squash sequence, then continue 126 // to squash. In this case, commit does not do anything. Otherwise 127 // run commit. 128 if (_status == ROBSquashing) { 129 if (rob->isDoneSquashing()) { 130 _status = Running; 131 } else { 132 rob->doSquash(); 133 134 // Send back sequence number of tail of ROB, so other stages 135 // can squash younger instructions. Note that really the only 136 // stage that this is important for is the IEW stage; other 137 // stages can just clear all their state as long as selective 138 // replay isn't used. 139 toIEW->commitInfo.doneSeqNum = rob->readTailSeqNum(); 140 toIEW->commitInfo.robSquashing = true; 141 } 142 } else { 143 commit(); 144 } 145 146 markCompletedInsts(); 147 148 // Writeback number of free ROB entries here. 149 DPRINTF(Commit, "Commit: ROB has %d free entries.\n", 150 rob->numFreeEntries()); 151 toIEW->commitInfo.freeROBEntries = rob->numFreeEntries(); 152} 153 154template <class Impl> 155void 156SimpleCommit<Impl>::commit() 157{ 158 ////////////////////////////////////// 159 // Check for interrupts 160 ////////////////////////////////////// 161 162 // Process interrupts if interrupts are enabled and not in PAL mode. 163 // Take the PC from commit and write it to the IPR, then squash. The 164 // interrupt completing will take care of restoring the PC from that value 165 // in the IPR. Look at IPR[EXC_ADDR]; 166 // hwrei() is what resets the PC to the place where instruction execution 167 // beings again. 168#ifdef FULL_SYSTEM 169 if (//checkInterrupts && 170 cpu->check_interrupts() && 171 !cpu->inPalMode(readCommitPC())) { 172 // Will need to squash all instructions currently in flight and have 173 // the interrupt handler restart at the last non-committed inst. 174 // Most of that can be handled through the trap() function. The 175 // processInterrupts() function really just checks for interrupts 176 // and then calls trap() if there is an interrupt present. 177 178 // CPU will handle implementation of the interrupt. 179 cpu->processInterrupts(); 180 } 181#endif // FULL_SYSTEM 182 183 //////////////////////////////////// 184 // Check for squash signal, handle that first 185 //////////////////////////////////// 186 187 // Want to mainly check if the IEW stage is telling the ROB to squash. 188 // Should I also check if the commit stage is telling the ROB to squah? 189 // This might be necessary to keep the same timing between the IQ and 190 // the ROB... 191 if (fromIEW->squash) { 192 DPRINTF(Commit, "Commit: Squashing instructions in the ROB.\n"); 193 194 _status = ROBSquashing; 195 196 InstSeqNum squashed_inst = fromIEW->squashedSeqNum; 197 198 rob->squash(squashed_inst); 199 200 // Send back the sequence number of the squashed instruction. 201 toIEW->commitInfo.doneSeqNum = squashed_inst; 202 203 // Send back the squash signal to tell stages that they should squash. 204 toIEW->commitInfo.squash = true; 205 206 // Send back the rob squashing signal so other stages know that the 207 // ROB is in the process of squashing. 208 toIEW->commitInfo.robSquashing = true; 209 210 toIEW->commitInfo.branchMispredict = fromIEW->branchMispredict; 211 212 toIEW->commitInfo.branchTaken = fromIEW->branchTaken; 213 214 toIEW->commitInfo.nextPC = fromIEW->nextPC; 215 216 toIEW->commitInfo.mispredPC = fromIEW->mispredPC; 217 218 if (toIEW->commitInfo.branchMispredict) { 219 ++branchMispredicts; 220 } 221 } 222 223 if (_status != ROBSquashing) { 224 // If we're not currently squashing, then get instructions. 225 getInsts(); 226 227 // Try to commit any instructions. 228 commitInsts(); 229 } 230 231 // If the ROB is empty, we can set this stage to idle. Use this 232 // in the future when the Idle status will actually be utilized. 233#if 0 234 if (rob->isEmpty()) { 235 DPRINTF(Commit, "Commit: ROB is empty. Status changed to idle.\n"); 236 _status = Idle; 237 // Schedule an event so that commit will actually wake up 238 // once something gets put in the ROB. 239 } 240#endif 241} 242 243// Loop that goes through as many instructions in the ROB as possible and 244// tries to commit them. The actual work for committing is done by the 245// commitHead() function. 246template <class Impl> 247void 248SimpleCommit<Impl>::commitInsts() 249{ 250 //////////////////////////////////// 251 // Handle commit 252 // Note that commit will be handled prior to the ROB so that the ROB 253 // only tries to commit instructions it has in this current cycle, and 254 // not instructions it is writing in during this cycle. 255 // Can't commit and squash things at the same time... 256 //////////////////////////////////// 257 258 if (rob->isEmpty()) 259 return; 260 261 DynInstPtr head_inst = rob->readHeadInst(); 262 263 unsigned num_committed = 0; 264 265 // Commit as many instructions as possible until the commit bandwidth 266 // limit is reached, or it becomes impossible to commit any more. 267 while (!rob->isEmpty() && 268 head_inst->readyToCommit() && 269 num_committed < commitWidth) 270 { 271 DPRINTF(Commit, "Commit: Trying to commit head instruction.\n"); 272 273 // If the head instruction is squashed, it is ready to retire at any 274 // time. However, we need to avoid updating any other state 275 // incorrectly if it's already been squashed. 276 if (head_inst->isSquashed()) { 277 // Hack to avoid the instruction being retired (and deleted) if 278 // it hasn't been through the IEW stage yet. 279/* 280 if (!head_inst->isExecuted()) { 281 break; 282 } 283*/ 284 285 DPRINTF(Commit, "Commit: Retiring squashed instruction from " 286 "ROB.\n"); 287 288 // Tell ROB to retire head instruction. This retires the head 289 // inst in the ROB without affecting any other stages. 290 rob->retireHead(); 291 292 ++commitSquashedInsts; 293 294 } else { 295 // Increment the total number of non-speculative instructions 296 // executed. 297 // Hack for now: it really shouldn't happen until after the 298 // commit is deemed to be successful, but this count is needed 299 // for syscalls. 300 cpu->funcExeInst++; 301 302 // Try to commit the head instruction. 303 bool commit_success = commitHead(head_inst, num_committed); 304 305 // Update what instruction we are looking at if the commit worked. 306 if (commit_success) { 307 ++num_committed; 308 309 // Send back which instruction has been committed. 310 // @todo: Update this later when a wider pipeline is used. 311 // Hmm, can't really give a pointer here...perhaps the 312 // sequence number instead (copy). 313 toIEW->commitInfo.doneSeqNum = head_inst->seqNum; 314 315 ++commitCommittedInsts; 316 317 if (!head_inst->isNop()) { 318 cpu->instDone(); 319 } 320 } else { 321 break; 322 } 323 } 324 325 // Update the pointer to read the next instruction in the ROB. 326 head_inst = rob->readHeadInst(); 327 } 328 329 DPRINTF(CommitRate, "%i\n", num_committed); 330 n_committed_dist.sample(num_committed); 331} 332 333template <class Impl> 334bool 335SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) 336{ 337 // Make sure instruction is valid 338 assert(head_inst); 339 340 // If the instruction is not executed yet, then it is a non-speculative 341 // or store inst. Signal backwards that it should be executed. 342 if (!head_inst->isExecuted()) { 343 // Keep this number correct. We have not yet actually executed 344 // and committed this instruction. 345 cpu->funcExeInst--; 346 347 if (head_inst->isNonSpeculative()) { 348 DPRINTF(Commit, "Commit: Encountered a store or non-speculative " 349 "instruction at the head of the ROB, PC %#x.\n", 350 head_inst->readPC()); 351 352 toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum; 353 354 // Change the instruction so it won't try to commit again until 355 // it is executed. 356 head_inst->clearCanCommit(); 357 358 ++commitNonSpecStalls; 359 360 return false; 361 } else { 362 panic("Commit: Trying to commit un-executed instruction " 363 "of unknown type!\n"); 364 } 365 } 366 367 // Now check if it's one of the special trap or barrier or 368 // serializing instructions. 369 if (head_inst->isThreadSync() || 370 head_inst->isSerializing() || 371 head_inst->isMemBarrier() || 372 head_inst->isWriteBarrier() ) 373 { 374 // Not handled for now. Mem barriers and write barriers are safe 375 // to simply let commit as memory accesses only happen once they 376 // reach the head of commit. Not sure about the other two. 377 panic("Serializing or barrier instructions" 378 " are not handled yet.\n"); 379 } 380 381 // Check if the instruction caused a fault. If so, trap. 382 Fault inst_fault = head_inst->getFault(); 383 384 if (inst_fault != No_Fault && inst_fault != Fake_Mem_Fault) { 385 if (!head_inst->isNop()) { 386#ifdef FULL_SYSTEM 387 cpu->trap(inst_fault); 388#else // !FULL_SYSTEM 389 panic("fault (%d) detected @ PC %08p", inst_fault, 390 head_inst->PC); 391#endif // FULL_SYSTEM 392 } 393 } 394 395 // Check if we're really ready to commit. If not then return false. 396 // I'm pretty sure all instructions should be able to commit if they've 397 // reached this far. For now leave this in as a check. 398 if (!rob->isHeadReady()) { 399 panic("Commit: Unable to commit head instruction!\n"); 400 return false; 401 } 402 403 // If it's a branch, then send back branch prediction update info 404 // to the fetch stage. 405 // This should be handled in the iew stage if a mispredict happens... 406 407 if (head_inst->isControl()) { 408 409#if 0 410 toIEW->nextPC = head_inst->readPC(); 411 //Maybe switch over to BTB incorrect. 412 toIEW->btbMissed = head_inst->btbMiss(); 413 toIEW->target = head_inst->nextPC; 414 //Maybe also include global history information. 415 //This simple version will have no branch prediction however. 416#endif 417 418 ++commitCommittedBranches; 419 } 420 421#if 0 422 // Explicit communication back to the LDSTQ that a load has been committed 423 // and can be removed from the LDSTQ. Stores don't need this because 424 // the LDSTQ will already have been told that a store has reached the head 425 // of the ROB. Consider including communication if it's a store as well 426 // to keep things orthagonal. 427 if (head_inst->isMemRef()) { 428 ++commitCommittedMemRefs; 429 if (head_inst->isLoad()) { 430 toIEW->commitInfo.commitIsLoad = true; 431 ++commitCommittedLoads; 432 } 433 } 434#endif 435 436 // Now that the instruction is going to be committed, finalize its 437 // trace data. 438 if (head_inst->traceData) { 439 head_inst->traceData->finalize(); 440 } 441 442 //Finally clear the head ROB entry. 443 rob->retireHead(); 444 445 // Return true to indicate that we have committed an instruction. 446 return true; 447} 448 449template <class Impl> 450void 451SimpleCommit<Impl>::getInsts() 452{ 453 ////////////////////////////////////// 454 // Handle ROB functions 455 ////////////////////////////////////// 456 457 // Read any issued instructions and place them into the ROB. Do this 458 // prior to squashing to avoid having instructions in the ROB that 459 // don't get squashed properly. 460 int insts_to_process = min((int)renameWidth, fromRename->size); 461 462 for (int inst_num = 0; 463 inst_num < insts_to_process; 464 ++inst_num) 465 { 466 if (!fromRename->insts[inst_num]->isSquashed()) { 467 DPRINTF(Commit, "Commit: Inserting PC %#x into ROB.\n", 468 fromRename->insts[inst_num]->readPC()); 469 rob->insertInst(fromRename->insts[inst_num]); 470 } else { 471 DPRINTF(Commit, "Commit: Instruction %i PC %#x was " 472 "squashed, skipping.\n", 473 fromRename->insts[inst_num]->seqNum, 474 fromRename->insts[inst_num]->readPC()); 475 } 476 } 477} 478 479template <class Impl> 480void 481SimpleCommit<Impl>::markCompletedInsts() 482{ 483 // Grab completed insts out of the IEW instruction queue, and mark 484 // instructions completed within the ROB. 485 for (int inst_num = 0; 486 inst_num < fromIEW->size && fromIEW->insts[inst_num]; 487 ++inst_num) 488 { 489 DPRINTF(Commit, "Commit: Marking PC %#x, SN %i ready within ROB.\n", 490 fromIEW->insts[inst_num]->readPC(), 491 fromIEW->insts[inst_num]->seqNum); 492 493 // Mark the instruction as ready to commit. 494 fromIEW->insts[inst_num]->setCanCommit(); 495 } 496} 497 498template <class Impl> 499uint64_t 500SimpleCommit<Impl>::readCommitPC() 501{ 502 return rob->readHeadPC(); 503} 504 505#endif // __COMMIT_IMPL_HH__ 506