iew_impl.hh revision 1689
12686Sksewell@umich.edu/* 22100SN/A * Copyright (c) 2004-2005 The Regents of The University of Michigan 35254Sksewell@umich.edu * All rights reserved. 45254Sksewell@umich.edu * 55254Sksewell@umich.edu * Redistribution and use in source and binary forms, with or without 65254Sksewell@umich.edu * modification, are permitted provided that the following conditions are 75254Sksewell@umich.edu * met: redistributions of source code must retain the above copyright 85254Sksewell@umich.edu * notice, this list of conditions and the following disclaimer; 95254Sksewell@umich.edu * redistributions in binary form must reproduce the above copyright 105254Sksewell@umich.edu * notice, this list of conditions and the following disclaimer in the 115254Sksewell@umich.edu * documentation and/or other materials provided with the distribution; 125254Sksewell@umich.edu * neither the name of the copyright holders nor the names of its 135254Sksewell@umich.edu * contributors may be used to endorse or promote products derived from 145254Sksewell@umich.edu * this software without specific prior written permission. 155254Sksewell@umich.edu * 165254Sksewell@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 175254Sksewell@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 185254Sksewell@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 195254Sksewell@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 205254Sksewell@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 215254Sksewell@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 225254Sksewell@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 235254Sksewell@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 245254Sksewell@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 255254Sksewell@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 265254Sksewell@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 275254Sksewell@umich.edu */ 285254Sksewell@umich.edu 295254Sksewell@umich.edu// @todo: Fix the instantaneous communication among all the stages within 305254Sksewell@umich.edu// iew. There's a clear delay between issue and execute, yet backwards 315254Sksewell@umich.edu// communication happens simultaneously. 322706Sksewell@umich.edu// Update the statuses for each stage. 332022SN/A 342022SN/A#include <queue> 352043SN/A 362024SN/A#include "base/timebuf.hh" 372024SN/A#include "cpu/beta_cpu/iew.hh" 382043SN/A 392686Sksewell@umich.edutemplate<class Impl> 404661Sksewell@umich.eduSimpleIEW<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, 412022SN/A SimpleIEW<Impl> *_iew) 422083SN/A : Event(&mainEventQueue, CPU_Tick_Pri), inst(_inst), iewStage(_iew) 432686Sksewell@umich.edu{ 442101SN/A this->setFlags(Event::AutoDelete); 452043SN/A} 462043SN/A 472101SN/Atemplate<class Impl> 482101SN/Avoid 496384Sgblack@eecs.umich.eduSimpleIEW<Impl>::WritebackEvent::process() 506384Sgblack@eecs.umich.edu{ 516384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: WRITEBACK EVENT!!!!\n"); 526384Sgblack@eecs.umich.edu 536384Sgblack@eecs.umich.edu // Need to insert instruction into queue to commit 546384Sgblack@eecs.umich.edu iewStage->instToCommit(inst); 552101SN/A // Need to execute second half of the instruction, do actual writing to 562101SN/A // registers and such 572101SN/A inst->execute(); 582046SN/A} 592686Sksewell@umich.edu 602686Sksewell@umich.edutemplate<class Impl> 612686Sksewell@umich.educonst char * 622470SN/ASimpleIEW<Impl>::WritebackEvent::description() 632686Sksewell@umich.edu{ 644661Sksewell@umich.edu return "LSQ writeback event"; 655222Sksewell@umich.edu} 665222Sksewell@umich.edu 672686Sksewell@umich.edutemplate<class Impl> 688588Sgblack@eecs.umich.eduSimpleIEW<Impl>::SimpleIEW(Params ¶ms) 692470SN/A : // Just make this time buffer really big for now 702241SN/A issueToExecQueue(5, 5), 712101SN/A instQueue(params), 722495SN/A ldstQueue(params), 732495SN/A commitToIEWDelay(params.commitToIEWDelay), 748588Sgblack@eecs.umich.edu renameToIEWDelay(params.renameToIEWDelay), 752101SN/A issueToExecuteDelay(params.issueToExecuteDelay), 766384Sgblack@eecs.umich.edu issueReadWidth(params.issueWidth), 776384Sgblack@eecs.umich.edu issueWidth(params.issueWidth), 786384Sgblack@eecs.umich.edu executeWidth(params.executeWidth) 798588Sgblack@eecs.umich.edu{ 806384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: executeIntWidth: %i.\n", params.executeIntWidth); 812495SN/A _status = Idle; 822101SN/A _issueStatus = Idle; 832101SN/A _exeStatus = Idle; 842495SN/A _wbStatus = Idle; 852495SN/A 862495SN/A // Setup wire to read instructions coming from issue. 872495SN/A fromIssue = issueToExecQueue.getWire(-issueToExecuteDelay); 882495SN/A 892495SN/A // Instruction queue needs the queue between issue and execute. 902495SN/A instQueue.setIssueToExecuteQueue(&issueToExecQueue); 912495SN/A 922495SN/A ldstQueue.setIEW(this); 932495SN/A} 942495SN/A 952495SN/Atemplate <class Impl> 962495SN/Avoid 972101SN/ASimpleIEW<Impl>::regStats() 988588Sgblack@eecs.umich.edu{ 992101SN/A instQueue.regStats(); 1002101SN/A 1018588Sgblack@eecs.umich.edu iewIdleCycles 1022101SN/A .name(name() + ".iewIdleCycles") 1036384Sgblack@eecs.umich.edu .desc("Number of cycles IEW is idle"); 1046384Sgblack@eecs.umich.edu 1056384Sgblack@eecs.umich.edu iewSquashCycles 1068588Sgblack@eecs.umich.edu .name(name() + ".iewSquashCycles") 1078588Sgblack@eecs.umich.edu .desc("Number of cycles IEW is squashing"); 1086384Sgblack@eecs.umich.edu 1092101SN/A iewBlockCycles 1102101SN/A .name(name() + ".iewBlockCycles") 1112495SN/A .desc("Number of cycles IEW is blocking"); 1122495SN/A 1132495SN/A iewUnblockCycles 1142495SN/A .name(name() + ".iewUnblockCycles") 1152495SN/A .desc("Number of cycles IEW is unblocking"); 1166384Sgblack@eecs.umich.edu 1176384Sgblack@eecs.umich.edu// iewWBInsts; 1186384Sgblack@eecs.umich.edu 1196384Sgblack@eecs.umich.edu iewDispatchedInsts 1206384Sgblack@eecs.umich.edu .name(name() + ".iewDispatchedInsts") 1212495SN/A .desc("Number of instructions dispatched to IQ"); 1226384Sgblack@eecs.umich.edu 1232495SN/A iewDispSquashedInsts 1242495SN/A .name(name() + ".iewDispSquashedInsts") 1252043SN/A .desc("Number of squashed instructions skipped by dispatch"); 1262043SN/A 1272025SN/A iewDispLoadInsts 1282043SN/A .name(name() + ".iewDispLoadInsts") 1292686Sksewell@umich.edu .desc("Number of dispatched load instructions"); 1302686Sksewell@umich.edu 1312123SN/A iewDispStoreInsts 1322101SN/A .name(name() + ".iewDispStoreInsts") 1336376Sgblack@eecs.umich.edu .desc("Number of dispatched store instructions"); 1346376Sgblack@eecs.umich.edu 1356376Sgblack@eecs.umich.edu iewDispNonSpecInsts 1367792Sgblack@eecs.umich.edu .name(name() + ".iewDispNonSpecInsts") 1376376Sgblack@eecs.umich.edu .desc("Number of dispatched non-speculative instructions"); 1386376Sgblack@eecs.umich.edu 1396376Sgblack@eecs.umich.edu iewIQFullEvents 1406376Sgblack@eecs.umich.edu .name(name() + ".iewIQFullEvents") 1416376Sgblack@eecs.umich.edu .desc("Number of times the IQ has become full, causing a stall"); 1426376Sgblack@eecs.umich.edu 1436376Sgblack@eecs.umich.edu iewExecutedInsts 1447792Sgblack@eecs.umich.edu .name(name() + ".iewExecutedInsts") 1456376Sgblack@eecs.umich.edu .desc("Number of executed instructions"); 1466376Sgblack@eecs.umich.edu 1476376Sgblack@eecs.umich.edu iewExecLoadInsts 1486376Sgblack@eecs.umich.edu .name(name() + ".iewExecLoadInsts") 1492101SN/A .desc("Number of load instructions executed"); 1502042SN/A 1512101SN/A iewExecStoreInsts 1527720Sgblack@eecs.umich.edu .name(name() + ".iewExecStoreInsts") 1537792Sgblack@eecs.umich.edu .desc("Number of store instructions executed"); 1547792Sgblack@eecs.umich.edu 1557720Sgblack@eecs.umich.edu iewExecSquashedInsts 1567720Sgblack@eecs.umich.edu .name(name() + ".iewExecSquashedInsts") 1577792Sgblack@eecs.umich.edu .desc("Number of squashed instructions skipped in execute"); 1587792Sgblack@eecs.umich.edu 1597720Sgblack@eecs.umich.edu memOrderViolationEvents 1602101SN/A .name(name() + ".memOrderViolationEvents") 1612101SN/A .desc("Number of memory order violations"); 1622042SN/A 1632101SN/A predictedTakenIncorrect 1642686Sksewell@umich.edu .name(name() + ".predictedTakenIncorrect") 1652686Sksewell@umich.edu .desc("Number of branches that were predicted taken incorrectly"); 1668901Sandreas.hansson@arm.com} 16711877Sbrandon.potter@amd.com 1688564Sgblack@eecs.umich.edutemplate<class Impl> 16910474Sandreas.hansson@arm.comvoid 1708564Sgblack@eecs.umich.eduSimpleIEW<Impl>::setCPU(FullCPU *cpu_ptr) 1712686Sksewell@umich.edu{ 17210474Sandreas.hansson@arm.com DPRINTF(IEW, "IEW: Setting CPU pointer.\n"); 1732101SN/A cpu = cpu_ptr; 1742083SN/A 1752043SN/A instQueue.setCPU(cpu_ptr); 1762025SN/A ldstQueue.setCPU(cpu_ptr); 1772043SN/A} 1786384Sgblack@eecs.umich.edu 1796384Sgblack@eecs.umich.edutemplate<class Impl> 1804661Sksewell@umich.eduvoid 1816384Sgblack@eecs.umich.eduSimpleIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) 1826384Sgblack@eecs.umich.edu{ 1834661Sksewell@umich.edu DPRINTF(IEW, "IEW: Setting time buffer pointer.\n"); 1842083SN/A timeBuffer = tb_ptr; 1852025SN/A 1862043SN/A // Setup wire to read information from time buffer, from commit. 1874661Sksewell@umich.edu fromCommit = timeBuffer->getWire(-commitToIEWDelay); 1888588Sgblack@eecs.umich.edu 1898588Sgblack@eecs.umich.edu // Setup wire to write information back to previous stages. 1904661Sksewell@umich.edu toRename = timeBuffer->getWire(0); 1914661Sksewell@umich.edu 1922686Sksewell@umich.edu // Instruction queue also needs main time buffer. 1936384Sgblack@eecs.umich.edu instQueue.setTimeBuffer(tb_ptr); 1948588Sgblack@eecs.umich.edu} 1958588Sgblack@eecs.umich.edu 1968588Sgblack@eecs.umich.edutemplate<class Impl> 1976384Sgblack@eecs.umich.eduvoid 1985222Sksewell@umich.eduSimpleIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) 1995222Sksewell@umich.edu{ 2006384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Setting rename queue pointer.\n"); 2018588Sgblack@eecs.umich.edu renameQueue = rq_ptr; 2028588Sgblack@eecs.umich.edu 2038588Sgblack@eecs.umich.edu // Setup wire to read information from rename queue. 2046384Sgblack@eecs.umich.edu fromRename = renameQueue->getWire(-renameToIEWDelay); 2055222Sksewell@umich.edu} 2062101SN/A 2072084SN/Atemplate<class Impl> 2082025SN/Avoid 2092495SN/ASimpleIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) 2102495SN/A{ 2112495SN/A DPRINTF(IEW, "IEW: Setting IEW queue pointer.\n"); 2126384Sgblack@eecs.umich.edu iewQueue = iq_ptr; 2138564Sgblack@eecs.umich.edu 2148564Sgblack@eecs.umich.edu // Setup wire to write instructions to commit. 2158738Sgblack@eecs.umich.edu toCommit = iewQueue->getWire(0); 2168564Sgblack@eecs.umich.edu} 21710474Sandreas.hansson@arm.com 2186384Sgblack@eecs.umich.edutemplate<class Impl> 2196384Sgblack@eecs.umich.eduvoid 2208588Sgblack@eecs.umich.eduSimpleIEW<Impl>::setRenameMap(RenameMap *rm_ptr) 2215222Sksewell@umich.edu{ 2228564Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Setting rename map pointer.\n"); 2238564Sgblack@eecs.umich.edu renameMap = rm_ptr; 2248738Sgblack@eecs.umich.edu} 2258564Sgblack@eecs.umich.edu 22610474Sandreas.hansson@arm.comtemplate<class Impl> 2276384Sgblack@eecs.umich.eduvoid 2286384Sgblack@eecs.umich.eduSimpleIEW<Impl>::squash() 2298588Sgblack@eecs.umich.edu{ 2306384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Squashing all instructions.\n"); 2316384Sgblack@eecs.umich.edu _status = Squashing; 2326384Sgblack@eecs.umich.edu 2336384Sgblack@eecs.umich.edu // Tell the IQ to start squashing. 2342495SN/A instQueue.squash(); 2352101SN/A 2362043SN/A // Tell the LDSTQ to start squashing. 2372025SN/A ldstQueue.squash(fromCommit->commitInfo.doneSeqNum); 2382495SN/A} 2392495SN/A 2402495SN/Atemplate<class Impl> 2418588Sgblack@eecs.umich.eduvoid 2428588Sgblack@eecs.umich.eduSimpleIEW<Impl>::squashDueToBranch(DynInstPtr &inst) 2432495SN/A{ 2442101SN/A DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n", 2452084SN/A inst->PC); 2462024SN/A // Perhaps leave the squashing up to the ROB stage to tell it when to 2472043SN/A // squash? 2482239SN/A _status = Squashing; 2498588Sgblack@eecs.umich.edu 2508588Sgblack@eecs.umich.edu // Tell rename to squash through the time buffer. 2518588Sgblack@eecs.umich.edu toCommit->squash = true; 2528588Sgblack@eecs.umich.edu // Also send PC update information back to prior stages. 2538588Sgblack@eecs.umich.edu toCommit->squashedSeqNum = inst->seqNum; 2548588Sgblack@eecs.umich.edu toCommit->mispredPC = inst->readPC(); 2552101SN/A toCommit->nextPC = inst->readNextPC(); 2562043SN/A toCommit->branchMispredict = true; 2572043SN/A // Prediction was incorrect, so send back inverse. 2582025SN/A toCommit->branchTaken = inst->readNextPC() != 2592043SN/A (inst->readPC() + sizeof(MachInst)); 2602043SN/A} 2612101SN/A 2628588Sgblack@eecs.umich.edutemplate<class Impl> 2638588Sgblack@eecs.umich.eduvoid 2648588Sgblack@eecs.umich.eduSimpleIEW<Impl>::squashDueToMem(DynInstPtr &inst) 2658588Sgblack@eecs.umich.edu{ 2662101SN/A DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n", 2672043SN/A inst->PC); 2682025SN/A // Perhaps leave the squashing up to the ROB stage to tell it when to 2692043SN/A // squash? 2705222Sksewell@umich.edu _status = Squashing; 2718588Sgblack@eecs.umich.edu 2726384Sgblack@eecs.umich.edu // Tell rename to squash through the time buffer. 2738588Sgblack@eecs.umich.edu toCommit->squash = true; 2746384Sgblack@eecs.umich.edu // Also send PC update information back to prior stages. 2758588Sgblack@eecs.umich.edu toCommit->squashedSeqNum = inst->seqNum; 2766384Sgblack@eecs.umich.edu toCommit->nextPC = inst->readNextPC(); 2778588Sgblack@eecs.umich.edu} 2786384Sgblack@eecs.umich.edu 2798588Sgblack@eecs.umich.edutemplate<class Impl> 2808588Sgblack@eecs.umich.eduvoid 2812101SN/ASimpleIEW<Impl>::block() 2822043SN/A{ 2832043SN/A DPRINTF(IEW, "IEW: Blocking.\n"); 2842043SN/A // Set the status to Blocked. 2852101SN/A _status = Blocked; 2868588Sgblack@eecs.umich.edu 2872686Sksewell@umich.edu // Add the current inputs to the skid buffer so they can be 2882686Sksewell@umich.edu // reprocessed when this stage unblocks. 2898588Sgblack@eecs.umich.edu skidBuffer.push(*fromRename); 2902686Sksewell@umich.edu 2918588Sgblack@eecs.umich.edu // Note that this stage only signals previous stages to stall when 2928588Sgblack@eecs.umich.edu // it is the cause of the stall originates at this stage. Otherwise 2932101SN/A // the previous stages are expected to check all possible stall signals. 2942043SN/A} 2952043SN/A 2962043SN/Atemplate<class Impl> 2976384Sgblack@eecs.umich.eduinline void 2986384Sgblack@eecs.umich.eduSimpleIEW<Impl>::unblock() 2994661Sksewell@umich.edu{ 3002101SN/A // Check if there's information in the skid buffer. If there is, then 3012101SN/A // set status to unblocking, otherwise set it directly to running. 3022101SN/A DPRINTF(IEW, "IEW: Reading instructions out of the skid " 3032043SN/A "buffer.\n"); 3042043SN/A // Remove the now processed instructions from the skid buffer. 3052043SN/A skidBuffer.pop(); 3062123SN/A 3077792Sgblack@eecs.umich.edu // If there's still information in the skid buffer, then 3087792Sgblack@eecs.umich.edu // continue to tell previous stages to stall. They will be 3097792Sgblack@eecs.umich.edu // able to restart once the skid buffer is empty. 3102043SN/A if (!skidBuffer.empty()) { 3112043SN/A toRename->iewInfo.stall = true; 3122100SN/A } else { 3132686Sksewell@umich.edu DPRINTF(IEW, "IEW: Stage is done unblocking.\n"); 3142686Sksewell@umich.edu _status = Running; 3158588Sgblack@eecs.umich.edu } 3162686Sksewell@umich.edu} 3178588Sgblack@eecs.umich.edu 3188588Sgblack@eecs.umich.edutemplate<class Impl> 3198588Sgblack@eecs.umich.eduvoid 3202043SN/ASimpleIEW<Impl>::wakeDependents(DynInstPtr &inst) 3212084SN/A{ 3222024SN/A instQueue.wakeDependents(inst); 3232101SN/A} 3242686Sksewell@umich.edu 3255222Sksewell@umich.edu 3268564Sgblack@eecs.umich.edutemplate<class Impl> 3278564Sgblack@eecs.umich.eduvoid 3288738Sgblack@eecs.umich.eduSimpleIEW<Impl>::instToCommit(DynInstPtr &inst) 3298564Sgblack@eecs.umich.edu{ 33010474Sandreas.hansson@arm.com 3316384Sgblack@eecs.umich.edu} 3326384Sgblack@eecs.umich.edu 3338588Sgblack@eecs.umich.edutemplate <class Impl> 3348588Sgblack@eecs.umich.eduvoid 3358588Sgblack@eecs.umich.eduSimpleIEW<Impl>::dispatchInsts() 3368588Sgblack@eecs.umich.edu{ 3378588Sgblack@eecs.umich.edu //////////////////////////////////////// 3388588Sgblack@eecs.umich.edu // DISPATCH/ISSUE stage 3392495SN/A //////////////////////////////////////// 3402495SN/A 3416384Sgblack@eecs.umich.edu //Put into its own function? 3422495SN/A //Add instructions to IQ if there are any instructions there 3432084SN/A 3442084SN/A // Check if there are any instructions coming from rename, and we're. 3452024SN/A // not squashing. 3462101SN/A if (fromRename->size > 0) { 3472101SN/A int insts_to_add = fromRename->size; 3482101SN/A 3492101SN/A // Loop through the instructions, putting them in the instruction 3506384Sgblack@eecs.umich.edu // queue. 3516384Sgblack@eecs.umich.edu for (int inst_num = 0; inst_num < insts_to_add; ++inst_num) 3526384Sgblack@eecs.umich.edu { 3536384Sgblack@eecs.umich.edu DynInstPtr inst = fromRename->insts[inst_num]; 3546384Sgblack@eecs.umich.edu 3556384Sgblack@eecs.umich.edu // Make sure there's a valid instruction there. 3566384Sgblack@eecs.umich.edu assert(inst); 3576384Sgblack@eecs.umich.edu 3586384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Issue: Adding PC %#x to IQ.\n", 3596384Sgblack@eecs.umich.edu inst->readPC()); 3606384Sgblack@eecs.umich.edu 3616384Sgblack@eecs.umich.edu // Be sure to mark these instructions as ready so that the 36211320Ssteve.reinhardt@amd.com // commit stage can go ahead and execute them, and mark 3636384Sgblack@eecs.umich.edu // them as issued so the IQ doesn't reprocess them. 3646384Sgblack@eecs.umich.edu if (inst->isSquashed()) { 3656384Sgblack@eecs.umich.edu ++iewDispSquashedInsts; 3666384Sgblack@eecs.umich.edu continue; 3676384Sgblack@eecs.umich.edu } else if (instQueue.isFull()) { 3686384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Issue: IQ has become full.\n"); 3696384Sgblack@eecs.umich.edu // Call function to start blocking. 3706384Sgblack@eecs.umich.edu block(); 3716384Sgblack@eecs.umich.edu // Tell previous stage to stall. 3726384Sgblack@eecs.umich.edu toRename->iewInfo.stall = true; 3736384Sgblack@eecs.umich.edu 3746384Sgblack@eecs.umich.edu ++iewIQFullEvents; 3756384Sgblack@eecs.umich.edu break; 3766384Sgblack@eecs.umich.edu } else if (inst->isLoad()) { 3776384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Issue: Memory instruction " 3786384Sgblack@eecs.umich.edu "encountered, adding to LDSTQ.\n"); 3796384Sgblack@eecs.umich.edu 3806384Sgblack@eecs.umich.edu // Reserve a spot in the load store queue for this 3816384Sgblack@eecs.umich.edu // memory access. 3826384Sgblack@eecs.umich.edu ldstQueue.insertLoad(inst); 3836384Sgblack@eecs.umich.edu 3846384Sgblack@eecs.umich.edu ++iewDispLoadInsts; 3854661Sksewell@umich.edu } else if (inst->isStore()) { 3866376Sgblack@eecs.umich.edu ldstQueue.insertStore(inst); 38712104Snathanael.premillieu@arm.com 38812104Snathanael.premillieu@arm.com ++iewDispStoreInsts; 3896376Sgblack@eecs.umich.edu } else if (inst->isNonSpeculative()) { 3904661Sksewell@umich.edu DPRINTF(IEW, "IEW: Issue: Nonspeculative instruction " 3916384Sgblack@eecs.umich.edu "encountered, skipping.\n"); 39212104Snathanael.premillieu@arm.com 39312104Snathanael.premillieu@arm.com // Same hack as with stores. 3946384Sgblack@eecs.umich.edu inst->setCanCommit(); 3954661Sksewell@umich.edu 39612104Snathanael.premillieu@arm.com // Specificall insert it as nonspeculative. 39712104Snathanael.premillieu@arm.com instQueue.insertNonSpec(inst); 39812104Snathanael.premillieu@arm.com 39912104Snathanael.premillieu@arm.com ++iewDispNonSpecInsts; 40012104Snathanael.premillieu@arm.com 40112104Snathanael.premillieu@arm.com continue; 40212104Snathanael.premillieu@arm.com } else if (inst->isNop()) { 40312104Snathanael.premillieu@arm.com DPRINTF(IEW, "IEW: Issue: Nop instruction encountered " 40412104Snathanael.premillieu@arm.com ", skipping.\n"); 40512104Snathanael.premillieu@arm.com 40612104Snathanael.premillieu@arm.com inst->setIssued(); 40712104Snathanael.premillieu@arm.com inst->setExecuted(); 40812104Snathanael.premillieu@arm.com inst->setCanCommit(); 40912104Snathanael.premillieu@arm.com 41012104Snathanael.premillieu@arm.com instQueue.advanceTail(inst); 41112104Snathanael.premillieu@arm.com 41212104Snathanael.premillieu@arm.com continue; 41312104Snathanael.premillieu@arm.com } else if (inst->isExecuted()) { 41412104Snathanael.premillieu@arm.com assert(0 && "Instruction shouldn't be executed.\n"); 41512104Snathanael.premillieu@arm.com DPRINTF(IEW, "IEW: Issue: Executed branch encountered, " 41612104Snathanael.premillieu@arm.com "skipping.\n"); 41712104Snathanael.premillieu@arm.com 41812104Snathanael.premillieu@arm.com inst->setIssued(); 41912104Snathanael.premillieu@arm.com inst->setCanCommit(); 42012104Snathanael.premillieu@arm.com 42112104Snathanael.premillieu@arm.com instQueue.advanceTail(inst); 42212104Snathanael.premillieu@arm.com 42312104Snathanael.premillieu@arm.com continue; 42412104Snathanael.premillieu@arm.com } 42512104Snathanael.premillieu@arm.com 42612104Snathanael.premillieu@arm.com // If the instruction queue is not full, then add the 42712104Snathanael.premillieu@arm.com // instruction. 42812104Snathanael.premillieu@arm.com instQueue.insert(fromRename->insts[inst_num]); 42912104Snathanael.premillieu@arm.com 43012104Snathanael.premillieu@arm.com ++iewDispatchedInsts; 43112104Snathanael.premillieu@arm.com } 43212104Snathanael.premillieu@arm.com } 43312104Snathanael.premillieu@arm.com} 43412104Snathanael.premillieu@arm.com 43512104Snathanael.premillieu@arm.comtemplate <class Impl> 43612104Snathanael.premillieu@arm.comvoid 43712104Snathanael.premillieu@arm.comSimpleIEW<Impl>::executeInsts() 43812104Snathanael.premillieu@arm.com{ 43912104Snathanael.premillieu@arm.com //////////////////////////////////////// 44012104Snathanael.premillieu@arm.com //EXECUTE/WRITEBACK stage 44112104Snathanael.premillieu@arm.com //////////////////////////////////////// 44212104Snathanael.premillieu@arm.com 44312104Snathanael.premillieu@arm.com //Put into its own function? 44412104Snathanael.premillieu@arm.com //Similarly should probably have separate execution for int vs FP. 44512104Snathanael.premillieu@arm.com // Above comment is handled by the issue queue only issuing a valid 44612104Snathanael.premillieu@arm.com // mix of int/fp instructions. 44712104Snathanael.premillieu@arm.com //Actually okay to just have one execution, buuuuuut will need 4486384Sgblack@eecs.umich.edu //somewhere that defines the execution latency of all instructions. 4492686Sksewell@umich.edu // @todo: Move to the FU pool used in the current full cpu. 4504661Sksewell@umich.edu 45112104Snathanael.premillieu@arm.com int fu_usage = 0; 45212104Snathanael.premillieu@arm.com bool fetch_redirect = false; 45312104Snathanael.premillieu@arm.com int inst_slot = 0; 4546384Sgblack@eecs.umich.edu int time_slot = 0; 45512104Snathanael.premillieu@arm.com 45612104Snathanael.premillieu@arm.com // Execute/writeback any instructions that are available. 45712104Snathanael.premillieu@arm.com for (int inst_num = 0; 4586384Sgblack@eecs.umich.edu fu_usage < executeWidth && /* Haven't exceeded available FU's. */ 4596384Sgblack@eecs.umich.edu inst_num < issueWidth && 4606384Sgblack@eecs.umich.edu fromIssue->insts[inst_num]; 46112104Snathanael.premillieu@arm.com ++inst_num) { 46212104Snathanael.premillieu@arm.com 4636384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Execute: Executing instructions from IQ.\n"); 4646384Sgblack@eecs.umich.edu 46512104Snathanael.premillieu@arm.com // Get instruction from issue's queue. 46612104Snathanael.premillieu@arm.com DynInstPtr inst = fromIssue->insts[inst_num]; 4676384Sgblack@eecs.umich.edu 4686384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Execute: Processing PC %#x.\n", inst->readPC()); 4696384Sgblack@eecs.umich.edu 4706384Sgblack@eecs.umich.edu // Check if the instruction is squashed; if so then skip it 4716384Sgblack@eecs.umich.edu // and don't count it towards the FU usage. 4726384Sgblack@eecs.umich.edu if (inst->isSquashed()) { 4736384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Execute: Instruction was squashed.\n"); 4746384Sgblack@eecs.umich.edu 4756384Sgblack@eecs.umich.edu // Consider this instruction executed so that commit can go 4766384Sgblack@eecs.umich.edu // ahead and retire the instruction. 4776384Sgblack@eecs.umich.edu inst->setExecuted(); 4786384Sgblack@eecs.umich.edu 4796384Sgblack@eecs.umich.edu toCommit->insts[inst_num] = inst; 4806384Sgblack@eecs.umich.edu 4816384Sgblack@eecs.umich.edu ++iewExecSquashedInsts; 4826384Sgblack@eecs.umich.edu 4836384Sgblack@eecs.umich.edu continue; 4846384Sgblack@eecs.umich.edu } 4856384Sgblack@eecs.umich.edu 4866384Sgblack@eecs.umich.edu inst->setExecuted(); 4876384Sgblack@eecs.umich.edu 4882101SN/A // If an instruction is executed, then count it towards FU usage. 4896384Sgblack@eecs.umich.edu ++fu_usage; 4902686Sksewell@umich.edu 4912027SN/A // Execute instruction. 4926384Sgblack@eecs.umich.edu // Note that if the instruction faults, it will be handled 4936384Sgblack@eecs.umich.edu // at the commit stage. 4944661Sksewell@umich.edu if (inst->isMemRef()) { 49512104Snathanael.premillieu@arm.com DPRINTF(IEW, "IEW: Execute: Calculating address for memory " 49612104Snathanael.premillieu@arm.com "reference.\n"); 4974661Sksewell@umich.edu 4984661Sksewell@umich.edu // Tell the LDSTQ to execute this instruction (if it is a load). 49912104Snathanael.premillieu@arm.com if (inst->isLoad()) { 50012104Snathanael.premillieu@arm.com ldstQueue.executeLoad(inst); 50112104Snathanael.premillieu@arm.com 5024661Sksewell@umich.edu ++iewExecLoadInsts; 50312104Snathanael.premillieu@arm.com } else if (inst->isStore()) { 50412104Snathanael.premillieu@arm.com ldstQueue.executeStore(inst); 50512104Snathanael.premillieu@arm.com 50612104Snathanael.premillieu@arm.com ++iewExecStoreInsts; 50712104Snathanael.premillieu@arm.com } else { 50812104Snathanael.premillieu@arm.com panic("IEW: Unexpected memory type!\n"); 50912104Snathanael.premillieu@arm.com } 51012104Snathanael.premillieu@arm.com 51112104Snathanael.premillieu@arm.com } else { 51212104Snathanael.premillieu@arm.com inst->execute(); 51312104Snathanael.premillieu@arm.com 51412104Snathanael.premillieu@arm.com ++iewExecutedInsts; 51512104Snathanael.premillieu@arm.com } 51612104Snathanael.premillieu@arm.com 51712104Snathanael.premillieu@arm.com // First check the time slot that this instruction will write 51812104Snathanael.premillieu@arm.com // to. If there are free write ports at the time, then go ahead 51912104Snathanael.premillieu@arm.com // and write the instruction to that time. If there are not, 52012104Snathanael.premillieu@arm.com // keep looking back to see where's the first time there's a 52112104Snathanael.premillieu@arm.com // free slot. What happens if you run out of free spaces? 52212104Snathanael.premillieu@arm.com // For now naively assume that all instructions take one cycle. 52312104Snathanael.premillieu@arm.com // Otherwise would have to look into the time buffer based on the 52412104Snathanael.premillieu@arm.com // latency of the instruction. 52512104Snathanael.premillieu@arm.com (*iewQueue)[time_slot].insts[inst_slot]; 52612104Snathanael.premillieu@arm.com while ((*iewQueue)[time_slot].insts[inst_slot]) { 52712104Snathanael.premillieu@arm.com if (inst_slot < issueWidth) { 52812104Snathanael.premillieu@arm.com ++inst_slot; 52912104Snathanael.premillieu@arm.com } else { 53012104Snathanael.premillieu@arm.com ++time_slot; 53112104Snathanael.premillieu@arm.com inst_slot = 0; 53212104Snathanael.premillieu@arm.com } 53312104Snathanael.premillieu@arm.com 53412104Snathanael.premillieu@arm.com assert(time_slot < 5); 53512104Snathanael.premillieu@arm.com } 53612104Snathanael.premillieu@arm.com 53712104Snathanael.premillieu@arm.com // May actually have to work this out, especially with loads and stores 53812104Snathanael.premillieu@arm.com 53912104Snathanael.premillieu@arm.com // Add finished instruction to queue to commit. 54012104Snathanael.premillieu@arm.com (*iewQueue)[time_slot].insts[inst_slot] = inst; 54112104Snathanael.premillieu@arm.com (*iewQueue)[time_slot].size++; 5426384Sgblack@eecs.umich.edu 5435222Sksewell@umich.edu // Check if branch was correct. This check happens after the 5444661Sksewell@umich.edu // instruction is added to the queue because even if the branch 5456384Sgblack@eecs.umich.edu // is mispredicted, the branch instruction itself is still valid. 54612104Snathanael.premillieu@arm.com // Only handle this if there hasn't already been something that 54712104Snathanael.premillieu@arm.com // redirects fetch in this group of instructions. 5488607Sgblack@eecs.umich.edu if (!fetch_redirect) { 5498607Sgblack@eecs.umich.edu if (inst->mispredicted()) { 55012104Snathanael.premillieu@arm.com fetch_redirect = true; 5516384Sgblack@eecs.umich.edu 5526384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Execute: Branch mispredict detected.\n"); 5536384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Execute: Redirecting fetch to PC: %#x.\n", 5546384Sgblack@eecs.umich.edu inst->nextPC); 5556384Sgblack@eecs.umich.edu 5566384Sgblack@eecs.umich.edu // If incorrect, then signal the ROB that it must be squashed. 5578588Sgblack@eecs.umich.edu squashDueToBranch(inst); 5586384Sgblack@eecs.umich.edu 5596384Sgblack@eecs.umich.edu if (inst->predTaken()) { 5606384Sgblack@eecs.umich.edu predictedTakenIncorrect++; 5616384Sgblack@eecs.umich.edu } 5626384Sgblack@eecs.umich.edu } else if (ldstQueue.violation()) { 5638588Sgblack@eecs.umich.edu fetch_redirect = true; 5646384Sgblack@eecs.umich.edu 5658588Sgblack@eecs.umich.edu // Get the DynInst that caused the violation. 5666384Sgblack@eecs.umich.edu DynInstPtr violator = ldstQueue.getMemDepViolator(); 5676384Sgblack@eecs.umich.edu 5686384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: LDSTQ detected a violation. Violator PC: " 5696384Sgblack@eecs.umich.edu "%#x, inst PC: %#x. Addr is: %#x.\n", 5708588Sgblack@eecs.umich.edu violator->readPC(), inst->readPC(), inst->physEffAddr); 5716384Sgblack@eecs.umich.edu 5728588Sgblack@eecs.umich.edu // Tell the instruction queue that a violation has occured. 5736384Sgblack@eecs.umich.edu instQueue.violation(inst, violator); 5748588Sgblack@eecs.umich.edu 5756384Sgblack@eecs.umich.edu // Squash. 5766384Sgblack@eecs.umich.edu squashDueToMem(inst); 5778588Sgblack@eecs.umich.edu 5786384Sgblack@eecs.umich.edu ++memOrderViolationEvents; 5796384Sgblack@eecs.umich.edu } 5806384Sgblack@eecs.umich.edu } 5816384Sgblack@eecs.umich.edu } 5826384Sgblack@eecs.umich.edu} 5838607Sgblack@eecs.umich.edu 5846384Sgblack@eecs.umich.edutemplate<class Impl> 58512104Snathanael.premillieu@arm.comvoid 58612104Snathanael.premillieu@arm.comSimpleIEW<Impl>::tick() 5876384Sgblack@eecs.umich.edu{ 5886384Sgblack@eecs.umich.edu // Considering putting all the state-determining stuff in this section. 5894661Sksewell@umich.edu 5904661Sksewell@umich.edu // Try to fill up issue queue with as many instructions as bandwidth 5912101SN/A // allows. 5924661Sksewell@umich.edu // Decode should try to execute as many instructions as its bandwidth 5934661Sksewell@umich.edu // will allow, as long as it is not currently blocked. 5944661Sksewell@umich.edu 5954661Sksewell@umich.edu // Check if the stage is in a running status. 5964661Sksewell@umich.edu if (_status != Blocked && _status != Squashing) { 5976376Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Status is not blocked, attempting to run " 5986376Sgblack@eecs.umich.edu "stage.\n"); 5996376Sgblack@eecs.umich.edu iew(); 6006376Sgblack@eecs.umich.edu 6016376Sgblack@eecs.umich.edu // If it's currently unblocking, check to see if it should switch 6026376Sgblack@eecs.umich.edu // to running. 6036376Sgblack@eecs.umich.edu if (_status == Unblocking) { 6046376Sgblack@eecs.umich.edu unblock(); 6056376Sgblack@eecs.umich.edu 6066376Sgblack@eecs.umich.edu ++iewUnblockCycles; 6076376Sgblack@eecs.umich.edu } 6086376Sgblack@eecs.umich.edu } else if (_status == Squashing) { 6096376Sgblack@eecs.umich.edu 6106376Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Still squashing.\n"); 6116376Sgblack@eecs.umich.edu 6126376Sgblack@eecs.umich.edu // Check if stage should remain squashing. Stop squashing if the 6135222Sksewell@umich.edu // squash signal clears. 6144661Sksewell@umich.edu if (!fromCommit->commitInfo.squash && 6156384Sgblack@eecs.umich.edu !fromCommit->commitInfo.robSquashing) { 6164661Sksewell@umich.edu DPRINTF(IEW, "IEW: Done squashing, changing status to " 6176384Sgblack@eecs.umich.edu "running.\n"); 6186384Sgblack@eecs.umich.edu 6194661Sksewell@umich.edu _status = Running; 6204661Sksewell@umich.edu instQueue.stopSquash(); 6214661Sksewell@umich.edu } else { 6226376Sgblack@eecs.umich.edu instQueue.doSquash(); 6236376Sgblack@eecs.umich.edu } 6246376Sgblack@eecs.umich.edu 6256376Sgblack@eecs.umich.edu ++iewSquashCycles; 6266376Sgblack@eecs.umich.edu } else if (_status == Blocked) { 6276376Sgblack@eecs.umich.edu // Continue to tell previous stage to stall. 6286376Sgblack@eecs.umich.edu toRename->iewInfo.stall = true; 6296376Sgblack@eecs.umich.edu 6306376Sgblack@eecs.umich.edu // Check if possible stall conditions have cleared. 6316376Sgblack@eecs.umich.edu if (!fromCommit->commitInfo.stall && 6326376Sgblack@eecs.umich.edu !instQueue.isFull()) { 6336376Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Stall signals cleared, going to unblock.\n"); 6345222Sksewell@umich.edu _status = Unblocking; 6354661Sksewell@umich.edu } 6366384Sgblack@eecs.umich.edu 6374661Sksewell@umich.edu // If there's still instructions coming from rename, continue to 6385222Sksewell@umich.edu // put them on the skid buffer. 6394661Sksewell@umich.edu if (fromRename->size == 0) { 6404661Sksewell@umich.edu block(); 6414661Sksewell@umich.edu } 6426384Sgblack@eecs.umich.edu 6436384Sgblack@eecs.umich.edu if (fromCommit->commitInfo.squash || 6446384Sgblack@eecs.umich.edu fromCommit->commitInfo.robSquashing) { 6456384Sgblack@eecs.umich.edu squash(); 6466384Sgblack@eecs.umich.edu } 6476384Sgblack@eecs.umich.edu 6486384Sgblack@eecs.umich.edu ++iewBlockCycles; 6496384Sgblack@eecs.umich.edu } 6506384Sgblack@eecs.umich.edu 6516384Sgblack@eecs.umich.edu // @todo: Maybe put these at the beginning, so if it's idle it can 6526384Sgblack@eecs.umich.edu // return early. 65310474Sandreas.hansson@arm.com // Write back number of free IQ entries here. 6546384Sgblack@eecs.umich.edu toRename->iewInfo.freeIQEntries = instQueue.numFreeEntries(); 6556384Sgblack@eecs.umich.edu 6566384Sgblack@eecs.umich.edu ldstQueue.writebackStores(); 6576384Sgblack@eecs.umich.edu 6586384Sgblack@eecs.umich.edu // Check the committed load/store signals to see if there's a load 6596384Sgblack@eecs.umich.edu // or store to commit. Also check if it's being told to execute a 6606384Sgblack@eecs.umich.edu // nonspeculative instruction. 6616384Sgblack@eecs.umich.edu // This is pretty inefficient... 6626384Sgblack@eecs.umich.edu if (!fromCommit->commitInfo.squash && 6636384Sgblack@eecs.umich.edu !fromCommit->commitInfo.robSquashing) { 66410474Sandreas.hansson@arm.com ldstQueue.commitStores(fromCommit->commitInfo.doneSeqNum); 6656384Sgblack@eecs.umich.edu ldstQueue.commitLoads(fromCommit->commitInfo.doneSeqNum); 6666384Sgblack@eecs.umich.edu } 6676384Sgblack@eecs.umich.edu 6686384Sgblack@eecs.umich.edu if (fromCommit->commitInfo.nonSpecSeqNum != 0) { 6694661Sksewell@umich.edu instQueue.scheduleNonSpec(fromCommit->commitInfo.nonSpecSeqNum); 6706384Sgblack@eecs.umich.edu } 6714661Sksewell@umich.edu 6724661Sksewell@umich.edu DPRINTF(IEW, "IEW: IQ has %i free entries.\n", 6734661Sksewell@umich.edu instQueue.numFreeEntries()); 6746376Sgblack@eecs.umich.edu} 6756376Sgblack@eecs.umich.edu 6766376Sgblack@eecs.umich.edutemplate<class Impl> 6776376Sgblack@eecs.umich.eduvoid 6786376Sgblack@eecs.umich.eduSimpleIEW<Impl>::iew() 67910474Sandreas.hansson@arm.com{ 6804661Sksewell@umich.edu // Might want to put all state checks in the tick() function. 6816376Sgblack@eecs.umich.edu // Check if being told to stall from commit. 6824661Sksewell@umich.edu if (fromCommit->commitInfo.stall) { 6836376Sgblack@eecs.umich.edu block(); 6846376Sgblack@eecs.umich.edu return; 6856376Sgblack@eecs.umich.edu } else if (fromCommit->commitInfo.squash || 6866376Sgblack@eecs.umich.edu fromCommit->commitInfo.robSquashing) { 6876376Sgblack@eecs.umich.edu // Also check if commit is telling this stage to squash. 68810474Sandreas.hansson@arm.com squash(); 6894661Sksewell@umich.edu return; 6906376Sgblack@eecs.umich.edu } 6914661Sksewell@umich.edu 6926384Sgblack@eecs.umich.edu dispatchInsts(); 6932101SN/A 6942101SN/A // Have the instruction queue try to schedule any ready instructions. 6952101SN/A instQueue.scheduleReadyInsts(); 6966384Sgblack@eecs.umich.edu 6976384Sgblack@eecs.umich.edu executeInsts(); 6986384Sgblack@eecs.umich.edu 6996384Sgblack@eecs.umich.edu // Loop through the head of the time buffer and wake any dependents. 7006384Sgblack@eecs.umich.edu // These instructions are about to write back. In the simple model 7016384Sgblack@eecs.umich.edu // this loop can really happen within the previous loop, but when 7026384Sgblack@eecs.umich.edu // instructions have actual latencies, this loop must be separate. 7036384Sgblack@eecs.umich.edu // Also mark scoreboard that this instruction is finally complete. 7047792Sgblack@eecs.umich.edu // Either have IEW have direct access to rename map, or have this as 7056384Sgblack@eecs.umich.edu // part of backwards communication. 7067792Sgblack@eecs.umich.edu for (int inst_num = 0; inst_num < issueWidth && 7076384Sgblack@eecs.umich.edu toCommit->insts[inst_num]; inst_num++) 7087792Sgblack@eecs.umich.edu { 7096384Sgblack@eecs.umich.edu DynInstPtr inst = toCommit->insts[inst_num]; 7107792Sgblack@eecs.umich.edu 7116384Sgblack@eecs.umich.edu DPRINTF(IEW, "IEW: Sending instructions to commit, PC %#x.\n", 7126384Sgblack@eecs.umich.edu inst->readPC()); 7136384Sgblack@eecs.umich.edu 7146384Sgblack@eecs.umich.edu if(!inst->isSquashed()) { 7156384Sgblack@eecs.umich.edu instQueue.wakeDependents(inst); 7166384Sgblack@eecs.umich.edu 7176384Sgblack@eecs.umich.edu for (int i = 0; i < inst->numDestRegs(); i++) 7186376Sgblack@eecs.umich.edu { 7196384Sgblack@eecs.umich.edu renameMap->markAsReady(inst->renamedDestRegIdx(i)); 7206384Sgblack@eecs.umich.edu } 7216384Sgblack@eecs.umich.edu } 7226384Sgblack@eecs.umich.edu } 7235222Sksewell@umich.edu 7246384Sgblack@eecs.umich.edu // Also should advance its own time buffers if the stage ran. 7256384Sgblack@eecs.umich.edu // Not the best place for it, but this works (hopefully). 7266384Sgblack@eecs.umich.edu issueToExecQueue.advance(); 7276384Sgblack@eecs.umich.edu} 7286384Sgblack@eecs.umich.edu 7297792Sgblack@eecs.umich.edu#ifndef FULL_SYSTEM 7306384Sgblack@eecs.umich.edutemplate<class Impl> 7317792Sgblack@eecs.umich.eduvoid 7326384Sgblack@eecs.umich.eduSimpleIEW<Impl>::lsqWriteback() 7336384Sgblack@eecs.umich.edu{ 7346384Sgblack@eecs.umich.edu ldstQueue.writebackAllInsts(); 7356384Sgblack@eecs.umich.edu} 7366384Sgblack@eecs.umich.edu#endif 7376384Sgblack@eecs.umich.edu