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