rob_impl.hh revision 2733
16145Snate@binkert.org/*
26145Snate@binkert.org * Copyright (c) 2004-2006 The Regents of The University of Michigan
36145Snate@binkert.org * All rights reserved.
46145Snate@binkert.org *
56145Snate@binkert.org * Redistribution and use in source and binary forms, with or without
66145Snate@binkert.org * modification, are permitted provided that the following conditions are
76145Snate@binkert.org * met: redistributions of source code must retain the above copyright
86145Snate@binkert.org * notice, this list of conditions and the following disclaimer;
96145Snate@binkert.org * redistributions in binary form must reproduce the above copyright
106145Snate@binkert.org * notice, this list of conditions and the following disclaimer in the
116145Snate@binkert.org * documentation and/or other materials provided with the distribution;
126145Snate@binkert.org * neither the name of the copyright holders nor the names of its
136145Snate@binkert.org * contributors may be used to endorse or promote products derived from
146145Snate@binkert.org * this software without specific prior written permission.
156145Snate@binkert.org *
166145Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
176145Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
186145Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
196145Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
206145Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
216145Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
226145Snate@binkert.org * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
236145Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
246145Snate@binkert.org * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
256145Snate@binkert.org * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
266145Snate@binkert.org * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
276145Snate@binkert.org *
286145Snate@binkert.org * Authors: Kevin Lim
297454Snate@binkert.org */
307454Snate@binkert.org
317454Snate@binkert.org#include "config/full_system.hh"
327054Snate@binkert.org#include "cpu/o3/rob.hh"
337054Snate@binkert.org
347054Snate@binkert.orgusing namespace std;
357054Snate@binkert.org
367054Snate@binkert.orgtemplate <class Impl>
376154Snate@binkert.orgROB<Impl>::ROB(unsigned _numEntries, unsigned _squashWidth,
386154Snate@binkert.org               string _smtROBPolicy, unsigned _smtROBThreshold,
396145Snate@binkert.org               unsigned _numThreads)
407055Snate@binkert.org    : numEntries(_numEntries),
417454Snate@binkert.org      squashWidth(_squashWidth),
427454Snate@binkert.org      numInstsInROB(0),
437055Snate@binkert.org      squashedSeqNum(0),
446145Snate@binkert.org      numThreads(_numThreads)
456145Snate@binkert.org{
467054Snate@binkert.org    for (int tid=0; tid  < numThreads; tid++) {
477054Snate@binkert.org        doneSquashing[tid] = true;
486145Snate@binkert.org        threadEntries[tid] = 0;
496145Snate@binkert.org    }
506145Snate@binkert.org
516145Snate@binkert.org    string policy = _smtROBPolicy;
527054Snate@binkert.org
536145Snate@binkert.org    //Convert string to lowercase
547054Snate@binkert.org    std::transform(policy.begin(), policy.end(), policy.begin(),
557454Snate@binkert.org                   (int(*)(int)) tolower);
566145Snate@binkert.org
577054Snate@binkert.org    //Figure out rob policy
587454Snate@binkert.org    if (policy == "dynamic") {
596145Snate@binkert.org        robPolicy = Dynamic;
606145Snate@binkert.org
617054Snate@binkert.org        //Set Max Entries to Total ROB Capacity
627454Snate@binkert.org        for (int i = 0; i < numThreads; i++) {
636145Snate@binkert.org            maxEntries[i]=numEntries;
647054Snate@binkert.org        }
656145Snate@binkert.org
666145Snate@binkert.org    } else if (policy == "partitioned") {
677054Snate@binkert.org        robPolicy = Partitioned;
687454Snate@binkert.org        DPRINTF(Fetch, "ROB sharing policy set to Partitioned\n");
697054Snate@binkert.org
706145Snate@binkert.org        //@todo:make work if part_amt doesnt divide evenly.
717054Snate@binkert.org        int part_amt = numEntries / numThreads;
726145Snate@binkert.org
737054Snate@binkert.org        //Divide ROB up evenly
747054Snate@binkert.org        for (int i = 0; i < numThreads; i++) {
757054Snate@binkert.org            maxEntries[i]=part_amt;
767454Snate@binkert.org        }
776145Snate@binkert.org
787054Snate@binkert.org    } else if (policy == "threshold") {
797454Snate@binkert.org        robPolicy = Threshold;
807054Snate@binkert.org        DPRINTF(Fetch, "ROB sharing policy set to Threshold\n");
817054Snate@binkert.org
827054Snate@binkert.org        int threshold =  _smtROBThreshold;;
837054Snate@binkert.org
847054Snate@binkert.org        //Divide up by threshold amount
857054Snate@binkert.org        for (int i = 0; i < numThreads; i++) {
867454Snate@binkert.org            maxEntries[i]=threshold;
877054Snate@binkert.org        }
887454Snate@binkert.org    } else {
897454Snate@binkert.org        assert(0 && "Invalid ROB Sharing Policy.Options Are:{Dynamic,"
906145Snate@binkert.org                    "Partitioned, Threshold}");
916145Snate@binkert.org    }
927054Snate@binkert.org}
937054Snate@binkert.org
946145Snate@binkert.orgtemplate <class Impl>
957054Snate@binkert.orgstd::string
967054Snate@binkert.orgROB<Impl>::name() const
976145Snate@binkert.org{
986145Snate@binkert.org    return cpu->name() + ".rob";
997054Snate@binkert.org}
1007054Snate@binkert.org
1016145Snate@binkert.orgtemplate <class Impl>
1027054Snate@binkert.orgvoid
1036145Snate@binkert.orgROB<Impl>::setCPU(O3CPU *cpu_ptr)
1046145Snate@binkert.org{
1057054Snate@binkert.org    cpu = cpu_ptr;
1067054Snate@binkert.org
1076145Snate@binkert.org    // Set the per-thread iterators to the end of the instruction list.
1087054Snate@binkert.org    for (int i=0; i < numThreads;i++) {
1097054Snate@binkert.org        squashIt[i] = instList[i].end();
1107054Snate@binkert.org    }
1117054Snate@binkert.org
1127054Snate@binkert.org    // Initialize the "universal" ROB head & tail point to invalid
1136145Snate@binkert.org    // pointers
1146145Snate@binkert.org    head = instList[0].end();
1156145Snate@binkert.org    tail = instList[0].end();
1167054Snate@binkert.org}
1177054Snate@binkert.org
1186145Snate@binkert.orgtemplate <class Impl>
1197054Snate@binkert.orgvoid
1206145Snate@binkert.orgROB<Impl>::setActiveThreads(list<unsigned> *at_ptr)
1216145Snate@binkert.org{
1227054Snate@binkert.org    DPRINTF(ROB, "Setting active threads list pointer.\n");
1237054Snate@binkert.org    activeThreads = at_ptr;
1246145Snate@binkert.org}
1257054Snate@binkert.org
1267054Snate@binkert.orgtemplate <class Impl>
1276145Snate@binkert.orgvoid
1286145Snate@binkert.orgROB<Impl>::switchOut()
1297454Snate@binkert.org{
1307054Snate@binkert.org    for (int tid = 0; tid < numThreads; tid++) {
1316145Snate@binkert.org        instList[tid].clear();
1327054Snate@binkert.org    }
1336145Snate@binkert.org}
1346145Snate@binkert.org
1357054Snate@binkert.orgtemplate <class Impl>
1367054Snate@binkert.orgvoid
1376145Snate@binkert.orgROB<Impl>::takeOverFrom()
1387054Snate@binkert.org{
1397054Snate@binkert.org    for (int tid=0; tid  < numThreads; tid++) {
1407054Snate@binkert.org        doneSquashing[tid] = true;
1417054Snate@binkert.org        threadEntries[tid] = 0;
1426145Snate@binkert.org        squashIt[tid] = instList[tid].end();
1437054Snate@binkert.org    }
1447054Snate@binkert.org    numInstsInROB = 0;
1457054Snate@binkert.org
1466145Snate@binkert.org    // Initialize the "universal" ROB head & tail point to invalid
1477054Snate@binkert.org    // pointers
1487054Snate@binkert.org    head = instList[0].end();
1497054Snate@binkert.org    tail = instList[0].end();
1507054Snate@binkert.org}
1517054Snate@binkert.org
1527054Snate@binkert.orgtemplate <class Impl>
1536145Snate@binkert.orgvoid
1547054Snate@binkert.orgROB<Impl>::resetEntries()
1557054Snate@binkert.org{
1566145Snate@binkert.org    if (robPolicy != Dynamic || numThreads > 1) {
1577054Snate@binkert.org        int active_threads = (*activeThreads).size();
1587054Snate@binkert.org
1597054Snate@binkert.org        list<unsigned>::iterator threads  = (*activeThreads).begin();
1607054Snate@binkert.org        list<unsigned>::iterator list_end = (*activeThreads).end();
1617054Snate@binkert.org
1627054Snate@binkert.org        while (threads != list_end) {
1637054Snate@binkert.org            if (robPolicy == Partitioned) {
1647054Snate@binkert.org                maxEntries[*threads++] = numEntries / active_threads;
1657054Snate@binkert.org            } else if (robPolicy == Threshold && active_threads == 1) {
1667054Snate@binkert.org                maxEntries[*threads++] = numEntries;
1677054Snate@binkert.org            }
1687054Snate@binkert.org        }
1697054Snate@binkert.org    }
1706145Snate@binkert.org}
1717054Snate@binkert.org
1726145Snate@binkert.orgtemplate <class Impl>
1737054Snate@binkert.orgint
1747054Snate@binkert.orgROB<Impl>::entryAmount(int num_threads)
1757054Snate@binkert.org{
1767054Snate@binkert.org    if (robPolicy == Partitioned) {
1777054Snate@binkert.org        return numEntries / num_threads;
1787054Snate@binkert.org    } else {
1797454Snate@binkert.org        return 0;
1807054Snate@binkert.org    }
1817054Snate@binkert.org}
1827054Snate@binkert.org
1837454Snate@binkert.orgtemplate <class Impl>
1847454Snate@binkert.orgint
1857054Snate@binkert.orgROB<Impl>::countInsts()
1867054Snate@binkert.org{
1877054Snate@binkert.org    int total=0;
1887054Snate@binkert.org
1897054Snate@binkert.org    for (int i=0;i < numThreads;i++)
1907054Snate@binkert.org        total += countInsts(i);
1917454Snate@binkert.org
1927454Snate@binkert.org    return total;
1937454Snate@binkert.org}
1947054Snate@binkert.org
1956145Snate@binkert.orgtemplate <class Impl>
1966145Snate@binkert.orgint
1977054Snate@binkert.orgROB<Impl>::countInsts(unsigned tid)
1986145Snate@binkert.org{
1996145Snate@binkert.org    return instList[tid].size();
2007054Snate@binkert.org}
2017054Snate@binkert.org
2026145Snate@binkert.orgtemplate <class Impl>
2037054Snate@binkert.orgvoid
2047054Snate@binkert.orgROB<Impl>::insertInst(DynInstPtr &inst)
2057054Snate@binkert.org{
2067054Snate@binkert.org    //assert(numInstsInROB == countInsts());
2076145Snate@binkert.org    assert(inst);
2086145Snate@binkert.org
2096145Snate@binkert.org    DPRINTF(ROB, "Adding inst PC %#x to the ROB.\n", inst->readPC());
2107054Snate@binkert.org
2117054Snate@binkert.org    assert(numInstsInROB != numEntries);
2126145Snate@binkert.org
2137054Snate@binkert.org    int tid = inst->threadNumber;
2147054Snate@binkert.org
2157054Snate@binkert.org    instList[tid].push_back(inst);
2167054Snate@binkert.org
2176145Snate@binkert.org    //Set Up head iterator if this is the 1st instruction in the ROB
2186145Snate@binkert.org    if (numInstsInROB == 0) {
2196145Snate@binkert.org        head = instList[tid].begin();
2207054Snate@binkert.org        assert((*head) == inst);
2217054Snate@binkert.org    }
2226145Snate@binkert.org
2237054Snate@binkert.org    //Must Decrement for iterator to actually be valid  since __.end()
2247054Snate@binkert.org    //actually points to 1 after the last inst
2256145Snate@binkert.org    tail = instList[tid].end();
2266145Snate@binkert.org    tail--;
227
228    inst->setInROB();
229
230    ++numInstsInROB;
231    ++threadEntries[tid];
232
233    assert((*tail) == inst);
234
235    DPRINTF(ROB, "[tid:%i] Now has %d instructions.\n", tid, threadEntries[tid]);
236}
237
238// Whatever calls this function needs to ensure that it properly frees up
239// registers prior to this function.
240/*
241template <class Impl>
242void
243ROB<Impl>::retireHead()
244{
245    //assert(numInstsInROB == countInsts());
246    assert(numInstsInROB > 0);
247
248    int tid = (*head)->threadNumber;
249
250    retireHead(tid);
251
252    if (numInstsInROB == 0) {
253        tail = instList[tid].end();
254    }
255}
256*/
257
258template <class Impl>
259void
260ROB<Impl>::retireHead(unsigned tid)
261{
262    //assert(numInstsInROB == countInsts());
263    assert(numInstsInROB > 0);
264
265    // Get the head ROB instruction.
266    InstIt head_it = instList[tid].begin();
267
268    DynInstPtr head_inst = (*head_it);
269
270    assert(head_inst->readyToCommit());
271
272    DPRINTF(ROB, "[tid:%u]: Retiring head instruction, "
273            "instruction PC %#x,[sn:%lli]\n", tid, head_inst->readPC(),
274            head_inst->seqNum);
275
276    --numInstsInROB;
277    --threadEntries[tid];
278
279    head_inst->clearInROB();
280    head_inst->setCommitted();
281
282    instList[tid].erase(head_it);
283
284    //Update "Global" Head of ROB
285    updateHead();
286
287    // @todo: A special case is needed if the instruction being
288    // retired is the only instruction in the ROB; otherwise the tail
289    // iterator will become invalidated.
290    cpu->removeFrontInst(head_inst);
291}
292/*
293template <class Impl>
294bool
295ROB<Impl>::isHeadReady()
296{
297    if (numInstsInROB != 0) {
298        return (*head)->readyToCommit();
299    }
300
301    return false;
302}
303*/
304template <class Impl>
305bool
306ROB<Impl>::isHeadReady(unsigned tid)
307{
308    if (threadEntries[tid] != 0) {
309        return instList[tid].front()->readyToCommit();
310    }
311
312    return false;
313}
314
315template <class Impl>
316bool
317ROB<Impl>::canCommit()
318{
319    //@todo: set ActiveThreads through ROB or CPU
320    list<unsigned>::iterator threads = (*activeThreads).begin();
321
322    while (threads != (*activeThreads).end()) {
323        unsigned tid = *threads++;
324
325        if (isHeadReady(tid)) {
326            return true;
327        }
328    }
329
330    return false;
331}
332
333template <class Impl>
334unsigned
335ROB<Impl>::numFreeEntries()
336{
337    //assert(numInstsInROB == countInsts());
338
339    return numEntries - numInstsInROB;
340}
341
342template <class Impl>
343unsigned
344ROB<Impl>::numFreeEntries(unsigned tid)
345{
346    return maxEntries[tid] - threadEntries[tid];
347}
348
349template <class Impl>
350void
351ROB<Impl>::doSquash(unsigned tid)
352{
353    DPRINTF(ROB, "[tid:%u]: Squashing instructions until [sn:%i].\n",
354            tid, squashedSeqNum);
355
356    assert(squashIt[tid] != instList[tid].end());
357
358    if ((*squashIt[tid])->seqNum < squashedSeqNum) {
359        DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n",
360                tid);
361
362        squashIt[tid] = instList[tid].end();
363
364        doneSquashing[tid] = true;
365        return;
366    }
367
368    bool robTailUpdate = false;
369
370    for (int numSquashed = 0;
371         numSquashed < squashWidth &&
372         squashIt[tid] != instList[tid].end() &&
373         (*squashIt[tid])->seqNum > squashedSeqNum;
374         ++numSquashed)
375    {
376        DPRINTF(ROB, "[tid:%u]: Squashing instruction PC %#x, seq num %i.\n",
377                (*squashIt[tid])->threadNumber,
378                (*squashIt[tid])->readPC(),
379                (*squashIt[tid])->seqNum);
380
381        // Mark the instruction as squashed, and ready to commit so that
382        // it can drain out of the pipeline.
383        (*squashIt[tid])->setSquashed();
384
385        (*squashIt[tid])->setCanCommit();
386
387
388        if (squashIt[tid] == instList[tid].begin()) {
389            DPRINTF(ROB, "Reached head of instruction list while "
390                    "squashing.\n");
391
392            squashIt[tid] = instList[tid].end();
393
394            doneSquashing[tid] = true;
395
396            return;
397        }
398
399        InstIt tail_thread = instList[tid].end();
400        tail_thread--;
401
402        if ((*squashIt[tid]) == (*tail_thread))
403            robTailUpdate = true;
404
405        squashIt[tid]--;
406    }
407
408
409    // Check if ROB is done squashing.
410    if ((*squashIt[tid])->seqNum <= squashedSeqNum) {
411        DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n",
412                tid);
413
414        squashIt[tid] = instList[tid].end();
415
416        doneSquashing[tid] = true;
417    }
418
419    if (robTailUpdate) {
420        updateTail();
421    }
422}
423
424
425template <class Impl>
426void
427ROB<Impl>::updateHead()
428{
429    DynInstPtr head_inst;
430    InstSeqNum lowest_num = 0;
431    bool first_valid = true;
432
433    // @todo: set ActiveThreads through ROB or CPU
434    list<unsigned>::iterator threads = (*activeThreads).begin();
435
436    while (threads != (*activeThreads).end()) {
437        unsigned thread_num = *threads++;
438
439        if (instList[thread_num].empty())
440            continue;
441
442        if (first_valid) {
443            head = instList[thread_num].begin();
444            lowest_num = (*head)->seqNum;
445            first_valid = false;
446            continue;
447        }
448
449        InstIt head_thread = instList[thread_num].begin();
450
451        DynInstPtr head_inst = (*head_thread);
452
453        assert(head_inst != 0);
454
455        if (head_inst->seqNum < lowest_num) {
456            head = head_thread;
457            lowest_num = head_inst->seqNum;
458        }
459    }
460
461    if (first_valid) {
462        head = instList[0].end();
463    }
464
465}
466
467template <class Impl>
468void
469ROB<Impl>::updateTail()
470{
471    tail = instList[0].end();
472    bool first_valid = true;
473
474    list<unsigned>::iterator threads = (*activeThreads).begin();
475
476    while (threads != (*activeThreads).end()) {
477        unsigned tid = *threads++;
478
479        if (instList[tid].empty()) {
480            continue;
481        }
482
483        // If this is the first valid then assign w/out
484        // comparison
485        if (first_valid) {
486            tail = instList[tid].end();
487            tail--;
488            first_valid = false;
489            continue;
490        }
491
492        // Assign new tail if this thread's tail is younger
493        // than our current "tail high"
494        InstIt tail_thread = instList[tid].end();
495        tail_thread--;
496
497        if ((*tail_thread)->seqNum > (*tail)->seqNum) {
498            tail = tail_thread;
499        }
500    }
501}
502
503
504template <class Impl>
505void
506ROB<Impl>::squash(InstSeqNum squash_num,unsigned tid)
507{
508    if (isEmpty()) {
509        DPRINTF(ROB, "Does not need to squash due to being empty "
510                "[sn:%i]\n",
511                squash_num);
512
513        return;
514    }
515
516    DPRINTF(ROB, "Starting to squash within the ROB.\n");
517
518    robStatus[tid] = ROBSquashing;
519
520    doneSquashing[tid] = false;
521
522    squashedSeqNum = squash_num;
523
524    if (!instList[tid].empty()) {
525        InstIt tail_thread = instList[tid].end();
526        tail_thread--;
527
528        squashIt[tid] = tail_thread;
529
530        doSquash(tid);
531    }
532}
533/*
534template <class Impl>
535typename Impl::DynInstPtr
536ROB<Impl>::readHeadInst()
537{
538    if (numInstsInROB != 0) {
539        assert((*head)->isInROB()==true);
540        return *head;
541    } else {
542        return dummyInst;
543    }
544}
545*/
546template <class Impl>
547typename Impl::DynInstPtr
548ROB<Impl>::readHeadInst(unsigned tid)
549{
550    if (threadEntries[tid] != 0) {
551        InstIt head_thread = instList[tid].begin();
552
553        assert((*head_thread)->isInROB()==true);
554
555        return *head_thread;
556    } else {
557        return dummyInst;
558    }
559}
560/*
561template <class Impl>
562uint64_t
563ROB<Impl>::readHeadPC()
564{
565    //assert(numInstsInROB == countInsts());
566
567    DynInstPtr head_inst = *head;
568
569    return head_inst->readPC();
570}
571
572template <class Impl>
573uint64_t
574ROB<Impl>::readHeadPC(unsigned tid)
575{
576    //assert(numInstsInROB == countInsts());
577    InstIt head_thread = instList[tid].begin();
578
579    return (*head_thread)->readPC();
580}
581
582
583template <class Impl>
584uint64_t
585ROB<Impl>::readHeadNextPC()
586{
587    //assert(numInstsInROB == countInsts());
588
589    DynInstPtr head_inst = *head;
590
591    return head_inst->readNextPC();
592}
593
594template <class Impl>
595uint64_t
596ROB<Impl>::readHeadNextPC(unsigned tid)
597{
598    //assert(numInstsInROB == countInsts());
599    InstIt head_thread = instList[tid].begin();
600
601    return (*head_thread)->readNextPC();
602}
603
604template <class Impl>
605InstSeqNum
606ROB<Impl>::readHeadSeqNum()
607{
608    //assert(numInstsInROB == countInsts());
609    DynInstPtr head_inst = *head;
610
611    return head_inst->seqNum;
612}
613
614template <class Impl>
615InstSeqNum
616ROB<Impl>::readHeadSeqNum(unsigned tid)
617{
618    InstIt head_thread = instList[tid].begin();
619
620    return ((*head_thread)->seqNum);
621}
622
623template <class Impl>
624typename Impl::DynInstPtr
625ROB<Impl>::readTailInst()
626{
627    //assert(numInstsInROB == countInsts());
628    //assert(tail != instList[0].end());
629
630    return (*tail);
631}
632*/
633template <class Impl>
634typename Impl::DynInstPtr
635ROB<Impl>::readTailInst(unsigned tid)
636{
637    //assert(tail_thread[tid] != instList[tid].end());
638
639    InstIt tail_thread = instList[tid].end();
640    tail_thread--;
641
642    return *tail_thread;
643}
644
645/*
646template <class Impl>
647uint64_t
648ROB<Impl>::readTailPC()
649{
650    //assert(numInstsInROB == countInsts());
651
652    //assert(tail != instList[0].end());
653
654    return (*tail)->readPC();
655}
656
657template <class Impl>
658uint64_t
659ROB<Impl>::readTailPC(unsigned tid)
660{
661    //assert(tail_thread[tid] != instList[tid].end());
662
663    InstIt tail_thread = instList[tid].end();
664    tail_thread--;
665
666    return (*tail_thread)->readPC();
667}
668
669template <class Impl>
670InstSeqNum
671ROB<Impl>::readTailSeqNum()
672{
673    // Return the last sequence number that has not been squashed.  Other
674    // stages can use it to squash any instructions younger than the current
675    // tail.
676    return (*tail)->seqNum;
677}
678
679template <class Impl>
680InstSeqNum
681ROB<Impl>::readTailSeqNum(unsigned tid)
682{
683    // Return the last sequence number that has not been squashed.  Other
684    // stages can use it to squash any instructions younger than the current
685    // tail.
686    //    assert(tail_thread[tid] != instList[tid].end());
687
688    InstIt tail_thread = instList[tid].end();
689    tail_thread--;
690
691    return (*tail_thread)->seqNum;
692}
693*/
694