commit_impl.hh revision 1062
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 (ISA::check_interrupts &&
170        cpu->check_interrupts() &&
171        !xc->inPalMode()) {
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        toIEW->commitInfo.globalHist = fromIEW->globalHist;
219
220        if (toIEW->commitInfo.branchMispredict) {
221            ++branchMispredicts;
222        }
223    }
224
225    if (_status != ROBSquashing) {
226        // If we're not currently squashing, then get instructions.
227        getInsts();
228
229        // Try to commit any instructions.
230        commitInsts();
231    }
232
233    // If the ROB is empty, we can set this stage to idle.  Use this
234    // in the future when the Idle status will actually be utilized.
235#if 0
236    if (rob->isEmpty()) {
237        DPRINTF(Commit, "Commit: ROB is empty.  Status changed to idle.\n");
238        _status = Idle;
239        // Schedule an event so that commit will actually wake up
240        // once something gets put in the ROB.
241    }
242#endif
243}
244
245// Loop that goes through as many instructions in the ROB as possible and
246// tries to commit them.  The actual work for committing is done by the
247// commitHead() function.
248template <class Impl>
249void
250SimpleCommit<Impl>::commitInsts()
251{
252    ////////////////////////////////////
253    // Handle commit
254    // Note that commit will be handled prior to the ROB so that the ROB
255    // only tries to commit instructions it has in this current cycle, and
256    // not instructions it is writing in during this cycle.
257    // Can't commit and squash things at the same time...
258    ////////////////////////////////////
259
260    DynInstPtr head_inst = rob->readHeadInst();
261
262    unsigned num_committed = 0;
263
264    // Commit as many instructions as possible until the commit bandwidth
265    // limit is reached, or it becomes impossible to commit any more.
266    while (!rob->isEmpty() &&
267           head_inst->readyToCommit() &&
268           num_committed < commitWidth)
269    {
270        DPRINTF(Commit, "Commit: Trying to commit head instruction.\n");
271
272        // If the head instruction is squashed, it is ready to retire at any
273        // time.  However, we need to avoid updating any other state
274        // incorrectly if it's already been squashed.
275        if (head_inst->isSquashed()) {
276            // Hack to avoid the instruction being retired (and deleted) if
277            // it hasn't been through the IEW stage yet.
278            if (!head_inst->isExecuted()) {
279                break;
280            }
281
282            DPRINTF(Commit, "Commit: Retiring squashed instruction from "
283                    "ROB.\n");
284
285            // Tell ROB to retire head instruction.  This retires the head
286            // inst in the ROB without affecting any other stages.
287            rob->retireHead();
288
289            ++commitSquashedInsts;
290
291        } else {
292            // Increment the total number of non-speculative instructions
293            // executed.
294            // Hack for now: it really shouldn't happen until after the
295            // commit is deemed to be successful, but this count is needed
296            // for syscalls.
297            cpu->funcExeInst++;
298
299            // Try to commit the head instruction.
300            bool commit_success = commitHead(head_inst, num_committed);
301
302            // Update what instruction we are looking at if the commit worked.
303            if (commit_success) {
304                ++num_committed;
305
306                // Send back which instruction has been committed.
307                // @todo: Update this later when a wider pipeline is used.
308                // Hmm, can't really give a pointer here...perhaps the
309                // sequence number instead (copy).
310                toIEW->commitInfo.doneSeqNum = head_inst->seqNum;
311
312                ++commitCommittedInsts;
313
314                if (!head_inst->isNop()) {
315                    cpu->instDone();
316                }
317            } else {
318                break;
319            }
320        }
321
322        // Update the pointer to read the next instruction in the ROB.
323        head_inst = rob->readHeadInst();
324    }
325
326    n_committed_dist.sample(num_committed);
327}
328
329template <class Impl>
330bool
331SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num)
332{
333    // Make sure instruction is valid
334    assert(head_inst);
335
336    // If the instruction is not executed yet, then it is a non-speculative
337    // or store inst.  Signal backwards that it should be executed.
338    if (!head_inst->isExecuted()) {
339        // Keep this number correct.  We have not yet actually executed
340        // and committed this instruction.
341        cpu->funcExeInst--;
342
343        if (head_inst->isStore() || head_inst->isNonSpeculative()) {
344            DPRINTF(Commit, "Commit: Encountered a store or non-speculative "
345                    "instruction at the head of the ROB, PC %#x.\n",
346                    head_inst->readPC());
347
348            toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum;
349
350            // Change the instruction so it won't try to commit again until
351            // it is executed.
352            head_inst->clearCanCommit();
353
354            ++commitNonSpecStalls;
355
356            return false;
357        } else {
358            panic("Commit: Trying to commit un-executed instruction "
359                  "of unknown type!\n");
360        }
361    }
362
363    // Now check if it's one of the special trap or barrier or
364    // serializing instructions.
365    if (head_inst->isThreadSync()  ||
366        head_inst->isSerializing() ||
367        head_inst->isMemBarrier()  ||
368        head_inst->isWriteBarrier() )
369    {
370        // Not handled for now.  Mem barriers and write barriers are safe
371        // to simply let commit as memory accesses only happen once they
372        // reach the head of commit.  Not sure about the other two.
373        panic("Serializing or barrier instructions"
374              " are not handled yet.\n");
375    }
376
377    // Check if the instruction caused a fault.  If so, trap.
378    if (head_inst->getFault() != No_Fault) {
379        if (!head_inst->isNop()) {
380#ifdef FULL_SYSTEM
381            cpu->trap(fault);
382#else // !FULL_SYSTEM
383            panic("fault (%d) detected @ PC %08p", head_inst->getFault(),
384                  head_inst->PC);
385#endif // FULL_SYSTEM
386        }
387    }
388
389    // Check if we're really ready to commit.  If not then return false.
390    // I'm pretty sure all instructions should be able to commit if they've
391    // reached this far.  For now leave this in as a check.
392    if(!rob->isHeadReady()) {
393        panic("Commit: Unable to commit head instruction!\n");
394        return false;
395    }
396
397    // If it's a branch, then send back branch prediction update info
398    // to the fetch stage.
399    // This should be handled in the iew stage if a mispredict happens...
400
401    if (head_inst->isControl()) {
402
403#if 0
404        toIEW->nextPC = head_inst->readPC();
405        //Maybe switch over to BTB incorrect.
406        toIEW->btbMissed = head_inst->btbMiss();
407        toIEW->target = head_inst->nextPC;
408        //Maybe also include global history information.
409        //This simple version will have no branch prediction however.
410#endif
411
412        ++commitCommittedBranches;
413    }
414
415
416#if 0
417    // Check if the instruction has a destination register.
418    // If so add the previous physical register of its logical register's
419    // destination to the free list through the time buffer.
420    for (int i = 0; i < head_inst->numDestRegs(); i++)
421    {
422        toIEW->commitInfo.freeRegs.push_back(head_inst->prevDestRegIdx(i));
423    }
424#endif
425
426    // Explicit communication back to the LDSTQ that a load has been committed
427    // and can be removed from the LDSTQ.  Stores don't need this because
428    // the LDSTQ will already have been told that a store has reached the head
429    // of the ROB.  Consider including communication if it's a store as well
430    // to keep things orthagonal.
431    if (head_inst->isMemRef()) {
432        ++commitCommittedMemRefs;
433        if (head_inst->isLoad()) {
434            toIEW->commitInfo.commitIsLoad = true;
435            ++commitCommittedLoads;
436        }
437    }
438
439    // Now that the instruction is going to be committed, finalize its
440    // trace data.
441    if (head_inst->traceData) {
442        head_inst->traceData->finalize();
443    }
444
445    //Finally clear the head ROB entry.
446    rob->retireHead();
447
448    // Return true to indicate that we have committed an instruction.
449    return true;
450}
451
452template <class Impl>
453void
454SimpleCommit<Impl>::getInsts()
455{
456    //////////////////////////////////////
457    // Handle ROB functions
458    //////////////////////////////////////
459
460    // Read any issued instructions and place them into the ROB.  Do this
461    // prior to squashing to avoid having instructions in the ROB that
462    // don't get squashed properly.
463    int insts_to_process = min((int)renameWidth, fromRename->size);
464
465    for (int inst_num = 0;
466         inst_num < insts_to_process;
467         ++inst_num)
468    {
469        if (!fromRename->insts[inst_num]->isSquashed()) {
470            DPRINTF(Commit, "Commit: Inserting PC %#x into ROB.\n",
471                    fromRename->insts[inst_num]->readPC());
472            rob->insertInst(fromRename->insts[inst_num]);
473        } else {
474            DPRINTF(Commit, "Commit: Instruction %i PC %#x was "
475                    "squashed, skipping.\n",
476                    fromRename->insts[inst_num]->seqNum,
477                    fromRename->insts[inst_num]->readPC());
478        }
479    }
480}
481
482template <class Impl>
483void
484SimpleCommit<Impl>::markCompletedInsts()
485{
486    // Grab completed insts out of the IEW instruction queue, and mark
487    // instructions completed within the ROB.
488    for (int inst_num = 0;
489         inst_num < iewWidth && fromIEW->insts[inst_num];
490         ++inst_num)
491    {
492        DPRINTF(Commit, "Commit: Marking PC %#x, SN %i ready within ROB.\n",
493                fromIEW->insts[inst_num]->readPC(),
494                fromIEW->insts[inst_num]->seqNum);
495
496        // Mark the instruction as ready to commit.
497        fromIEW->insts[inst_num]->setCanCommit();
498    }
499}
500
501template <class Impl>
502uint64_t
503SimpleCommit<Impl>::readCommitPC()
504{
505    return rob->readHeadPC();
506}
507
508#endif // __COMMIT_IMPL_HH__
509