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 &params)
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