thread_context_impl.hh revision 12109
14120Sgblack@eecs.umich.edu/* 24120Sgblack@eecs.umich.edu * Copyright (c) 2010-2012, 2016 ARM Limited 34120Sgblack@eecs.umich.edu * Copyright (c) 2013 Advanced Micro Devices, Inc. 44120Sgblack@eecs.umich.edu * All rights reserved 54120Sgblack@eecs.umich.edu * 64120Sgblack@eecs.umich.edu * The license below extends only to copyright in the software and shall 74120Sgblack@eecs.umich.edu * not be construed as granting a license to any other intellectual 84120Sgblack@eecs.umich.edu * property including but not limited to intellectual property relating 94120Sgblack@eecs.umich.edu * to a hardware implementation of the functionality of the software 104120Sgblack@eecs.umich.edu * licensed hereunder. You may use the software subject to the license 114120Sgblack@eecs.umich.edu * terms below provided that you ensure that this notice is replicated 124120Sgblack@eecs.umich.edu * unmodified and in its entirety in all distributions of the software, 134120Sgblack@eecs.umich.edu * modified or unmodified, in source code or in binary form. 144120Sgblack@eecs.umich.edu * 154120Sgblack@eecs.umich.edu * Copyright (c) 2004-2006 The Regents of The University of Michigan 164120Sgblack@eecs.umich.edu * All rights reserved. 174120Sgblack@eecs.umich.edu * 184120Sgblack@eecs.umich.edu * Redistribution and use in source and binary forms, with or without 194120Sgblack@eecs.umich.edu * modification, are permitted provided that the following conditions are 204120Sgblack@eecs.umich.edu * met: redistributions of source code must retain the above copyright 214120Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer; 224120Sgblack@eecs.umich.edu * redistributions in binary form must reproduce the above copyright 234120Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the 244120Sgblack@eecs.umich.edu * documentation and/or other materials provided with the distribution; 254120Sgblack@eecs.umich.edu * neither the name of the copyright holders nor the names of its 264120Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from 274120Sgblack@eecs.umich.edu * this software without specific prior written permission. 284120Sgblack@eecs.umich.edu * 294120Sgblack@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 304120Sgblack@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 314120Sgblack@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 324120Sgblack@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 334120Sgblack@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 344120Sgblack@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 354120Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 364120Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 374120Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 384120Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 394120Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 404120Sgblack@eecs.umich.edu * 414120Sgblack@eecs.umich.edu * Authors: Kevin Lim 424120Sgblack@eecs.umich.edu * Korey Sewell 434120Sgblack@eecs.umich.edu */ 444120Sgblack@eecs.umich.edu 454120Sgblack@eecs.umich.edu#ifndef __CPU_O3_THREAD_CONTEXT_IMPL_HH__ 464120Sgblack@eecs.umich.edu#define __CPU_O3_THREAD_CONTEXT_IMPL_HH__ 474120Sgblack@eecs.umich.edu 484120Sgblack@eecs.umich.edu#include "arch/kernel_stats.hh" 494120Sgblack@eecs.umich.edu#include "arch/registers.hh" 504120Sgblack@eecs.umich.edu#include "config/the_isa.hh" 514120Sgblack@eecs.umich.edu#include "cpu/o3/thread_context.hh" 524120Sgblack@eecs.umich.edu#include "cpu/quiesce_event.hh" 534120Sgblack@eecs.umich.edu#include "debug/O3CPU.hh" 544120Sgblack@eecs.umich.edu 554120Sgblack@eecs.umich.edutemplate <class Impl> 564120Sgblack@eecs.umich.eduFSTranslatingPortProxy& 574120Sgblack@eecs.umich.eduO3ThreadContext<Impl>::getVirtProxy() 584120Sgblack@eecs.umich.edu{ 594120Sgblack@eecs.umich.edu return thread->getVirtProxy(); 604120Sgblack@eecs.umich.edu} 614139Sgblack@eecs.umich.edu 624135Sgblack@eecs.umich.edutemplate <class Impl> 634120Sgblack@eecs.umich.eduvoid 644120Sgblack@eecs.umich.eduO3ThreadContext<Impl>::dumpFuncProfile() 654120Sgblack@eecs.umich.edu{ 665114Sgblack@eecs.umich.edu thread->dumpFuncProfile(); 675114Sgblack@eecs.umich.edu} 684135Sgblack@eecs.umich.edu 694365Sgblack@eecs.umich.edutemplate <class Impl> 705114Sgblack@eecs.umich.eduvoid 715114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::takeOverFrom(ThreadContext *old_context) 725139Sgblack@eecs.umich.edu{ 735114Sgblack@eecs.umich.edu ::takeOverFrom(*this, *old_context); 745139Sgblack@eecs.umich.edu TheISA::Decoder *newDecoder = getDecoderPtr(); 755139Sgblack@eecs.umich.edu TheISA::Decoder *oldDecoder = old_context->getDecoderPtr(); 765139Sgblack@eecs.umich.edu newDecoder->takeOverFrom(oldDecoder); 775114Sgblack@eecs.umich.edu 785114Sgblack@eecs.umich.edu thread->kernelStats = old_context->getKernelStats(); 795114Sgblack@eecs.umich.edu thread->funcExeInst = old_context->readFuncExeInst(); 804729Sgblack@eecs.umich.edu 814365Sgblack@eecs.umich.edu thread->noSquashFromTC = false; 825114Sgblack@eecs.umich.edu thread->trapPending = false; 834365Sgblack@eecs.umich.edu} 844365Sgblack@eecs.umich.edu 855114Sgblack@eecs.umich.edutemplate <class Impl> 865114Sgblack@eecs.umich.eduvoid 875114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::activate() 885114Sgblack@eecs.umich.edu{ 895114Sgblack@eecs.umich.edu DPRINTF(O3CPU, "Calling activate on Thread Context %d\n", 905114Sgblack@eecs.umich.edu threadId()); 915114Sgblack@eecs.umich.edu 925114Sgblack@eecs.umich.edu if (thread->status() == ThreadContext::Active) 935114Sgblack@eecs.umich.edu return; 945114Sgblack@eecs.umich.edu 955114Sgblack@eecs.umich.edu thread->lastActivate = curTick(); 965114Sgblack@eecs.umich.edu thread->setStatus(ThreadContext::Active); 975114Sgblack@eecs.umich.edu 985114Sgblack@eecs.umich.edu // status() == Suspended 995114Sgblack@eecs.umich.edu cpu->activateContext(thread->threadId()); 1005114Sgblack@eecs.umich.edu} 1015139Sgblack@eecs.umich.edu 1025139Sgblack@eecs.umich.edutemplate <class Impl> 1035139Sgblack@eecs.umich.eduvoid 1045114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::suspend() 1055114Sgblack@eecs.umich.edu{ 1065114Sgblack@eecs.umich.edu DPRINTF(O3CPU, "Calling suspend on Thread Context %d\n", 1075114Sgblack@eecs.umich.edu threadId()); 1085114Sgblack@eecs.umich.edu 1095114Sgblack@eecs.umich.edu if (thread->status() == ThreadContext::Suspended) 1105114Sgblack@eecs.umich.edu return; 1115114Sgblack@eecs.umich.edu 1125139Sgblack@eecs.umich.edu thread->lastActivate = curTick(); 1135139Sgblack@eecs.umich.edu thread->lastSuspend = curTick(); 1145139Sgblack@eecs.umich.edu 1155114Sgblack@eecs.umich.edu thread->setStatus(ThreadContext::Suspended); 1165114Sgblack@eecs.umich.edu cpu->suspendContext(thread->threadId()); 1175114Sgblack@eecs.umich.edu} 1185124Sgblack@eecs.umich.edu 1195114Sgblack@eecs.umich.edutemplate <class Impl> 1205114Sgblack@eecs.umich.eduvoid 1215114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::halt() 1225114Sgblack@eecs.umich.edu{ 1235114Sgblack@eecs.umich.edu DPRINTF(O3CPU, "Calling halt on Thread Context %d\n", threadId()); 1245114Sgblack@eecs.umich.edu 1255114Sgblack@eecs.umich.edu if (thread->status() == ThreadContext::Halted) 1265139Sgblack@eecs.umich.edu return; 1275139Sgblack@eecs.umich.edu 1285139Sgblack@eecs.umich.edu thread->setStatus(ThreadContext::Halted); 1295114Sgblack@eecs.umich.edu cpu->haltContext(thread->threadId()); 1305114Sgblack@eecs.umich.edu} 1315114Sgblack@eecs.umich.edu 1325124Sgblack@eecs.umich.edutemplate <class Impl> 1335114Sgblack@eecs.umich.eduvoid 1345114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::regStats(const std::string &name) 1355114Sgblack@eecs.umich.edu{ 1365114Sgblack@eecs.umich.edu if (FullSystem) { 1375114Sgblack@eecs.umich.edu thread->kernelStats = new TheISA::Kernel::Statistics(cpu->system); 1385114Sgblack@eecs.umich.edu thread->kernelStats->regStats(name + ".kern"); 1395114Sgblack@eecs.umich.edu } 1405139Sgblack@eecs.umich.edu} 1415139Sgblack@eecs.umich.edu 1425139Sgblack@eecs.umich.edutemplate <class Impl> 1435114Sgblack@eecs.umich.eduTick 1445114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::readLastActivate() 1455114Sgblack@eecs.umich.edu{ 1465124Sgblack@eecs.umich.edu return thread->lastActivate; 1475114Sgblack@eecs.umich.edu} 1484135Sgblack@eecs.umich.edu 1494150Sgblack@eecs.umich.edutemplate <class Impl> 1504365Sgblack@eecs.umich.eduTick 1514365Sgblack@eecs.umich.eduO3ThreadContext<Impl>::readLastSuspend() 1524365Sgblack@eecs.umich.edu{ 1534729Sgblack@eecs.umich.edu return thread->lastSuspend; 1544365Sgblack@eecs.umich.edu} 1554365Sgblack@eecs.umich.edu 1564365Sgblack@eecs.umich.edutemplate <class Impl> 1574365Sgblack@eecs.umich.eduvoid 1584365Sgblack@eecs.umich.eduO3ThreadContext<Impl>::profileClear() 1594365Sgblack@eecs.umich.edu{ 1604365Sgblack@eecs.umich.edu thread->profileClear(); 1614365Sgblack@eecs.umich.edu} 1624365Sgblack@eecs.umich.edu 1634365Sgblack@eecs.umich.edutemplate <class Impl> 1644150Sgblack@eecs.umich.eduvoid 1654150Sgblack@eecs.umich.eduO3ThreadContext<Impl>::profileSample() 1664150Sgblack@eecs.umich.edu{ 1674150Sgblack@eecs.umich.edu thread->profileSample(); 1685114Sgblack@eecs.umich.edu} 1695114Sgblack@eecs.umich.edu 1705114Sgblack@eecs.umich.edutemplate <class Impl> 1715114Sgblack@eecs.umich.eduvoid 1725114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::copyArchRegs(ThreadContext *tc) 1735114Sgblack@eecs.umich.edu{ 1745114Sgblack@eecs.umich.edu // Prevent squashing 1755114Sgblack@eecs.umich.edu thread->noSquashFromTC = true; 1765114Sgblack@eecs.umich.edu TheISA::copyRegs(tc, this); 1775114Sgblack@eecs.umich.edu thread->noSquashFromTC = false; 1785114Sgblack@eecs.umich.edu 1795114Sgblack@eecs.umich.edu if (!FullSystem) 1805114Sgblack@eecs.umich.edu this->thread->funcExeInst = tc->readFuncExeInst(); 1815114Sgblack@eecs.umich.edu} 1825114Sgblack@eecs.umich.edu 1835114Sgblack@eecs.umich.edutemplate <class Impl> 1845114Sgblack@eecs.umich.eduvoid 1855114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::clearArchRegs() 1865114Sgblack@eecs.umich.edu{ 1875114Sgblack@eecs.umich.edu cpu->isa[thread->threadId()]->clear(); 1885114Sgblack@eecs.umich.edu} 1895114Sgblack@eecs.umich.edu 1905114Sgblack@eecs.umich.edutemplate <class Impl> 1915114Sgblack@eecs.umich.eduuint64_t 1925114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::readIntRegFlat(int reg_idx) 1935114Sgblack@eecs.umich.edu{ 1945114Sgblack@eecs.umich.edu return cpu->readArchIntReg(reg_idx, thread->threadId()); 1955114Sgblack@eecs.umich.edu} 1965114Sgblack@eecs.umich.edu 1975114Sgblack@eecs.umich.edutemplate <class Impl> 1985114Sgblack@eecs.umich.eduTheISA::FloatReg 1995114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::readFloatRegFlat(int reg_idx) 2005114Sgblack@eecs.umich.edu{ 2015114Sgblack@eecs.umich.edu return cpu->readArchFloatReg(reg_idx, thread->threadId()); 2025114Sgblack@eecs.umich.edu} 2035114Sgblack@eecs.umich.edu 2045114Sgblack@eecs.umich.edutemplate <class Impl> 2055114Sgblack@eecs.umich.eduTheISA::FloatRegBits 2065114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::readFloatRegBitsFlat(int reg_idx) 2075114Sgblack@eecs.umich.edu{ 2085114Sgblack@eecs.umich.edu return cpu->readArchFloatRegInt(reg_idx, thread->threadId()); 2095114Sgblack@eecs.umich.edu} 2105114Sgblack@eecs.umich.edu 2115114Sgblack@eecs.umich.edutemplate <class Impl> 2125114Sgblack@eecs.umich.educonst TheISA::VecRegContainer& 2135114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::readVecRegFlat(int reg_id) const 2145114Sgblack@eecs.umich.edu{ 2155114Sgblack@eecs.umich.edu return cpu->readArchVecReg(reg_id, thread->threadId()); 2165114Sgblack@eecs.umich.edu} 2175114Sgblack@eecs.umich.edu 2185114Sgblack@eecs.umich.edutemplate <class Impl> 2195114Sgblack@eecs.umich.eduTheISA::VecRegContainer& 2205114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::getWritableVecRegFlat(int reg_id) 2215114Sgblack@eecs.umich.edu{ 2225114Sgblack@eecs.umich.edu return cpu->getWritableArchVecReg(reg_id, thread->threadId()); 2235114Sgblack@eecs.umich.edu} 2245114Sgblack@eecs.umich.edu 2255114Sgblack@eecs.umich.edutemplate <class Impl> 2265114Sgblack@eecs.umich.educonst TheISA::VecElem& 2275114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::readVecElemFlat(const RegIndex& idx, 2285114Sgblack@eecs.umich.edu const ElemIndex& elemIndex) const 2295114Sgblack@eecs.umich.edu{ 2305114Sgblack@eecs.umich.edu return cpu->readArchVecElem(idx, elemIndex, thread->threadId()); 2315114Sgblack@eecs.umich.edu} 2325114Sgblack@eecs.umich.edu 2335114Sgblack@eecs.umich.edutemplate <class Impl> 2345114Sgblack@eecs.umich.eduTheISA::CCReg 2355114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::readCCRegFlat(int reg_idx) 2365114Sgblack@eecs.umich.edu{ 2375114Sgblack@eecs.umich.edu return cpu->readArchCCReg(reg_idx, thread->threadId()); 2385114Sgblack@eecs.umich.edu} 2395114Sgblack@eecs.umich.edu 2405114Sgblack@eecs.umich.edutemplate <class Impl> 2415114Sgblack@eecs.umich.eduvoid 2425114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::setIntRegFlat(int reg_idx, uint64_t val) 2435114Sgblack@eecs.umich.edu{ 2445114Sgblack@eecs.umich.edu cpu->setArchIntReg(reg_idx, val, thread->threadId()); 2455114Sgblack@eecs.umich.edu 2465114Sgblack@eecs.umich.edu conditionalSquash(); 2475114Sgblack@eecs.umich.edu} 2485114Sgblack@eecs.umich.edu 2495114Sgblack@eecs.umich.edutemplate <class Impl> 2505114Sgblack@eecs.umich.eduvoid 2515114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::setFloatRegFlat(int reg_idx, FloatReg val) 2525114Sgblack@eecs.umich.edu{ 2535114Sgblack@eecs.umich.edu cpu->setArchFloatReg(reg_idx, val, thread->threadId()); 2545114Sgblack@eecs.umich.edu 2555114Sgblack@eecs.umich.edu conditionalSquash(); 2565114Sgblack@eecs.umich.edu} 2575114Sgblack@eecs.umich.edu 2585114Sgblack@eecs.umich.edutemplate <class Impl> 2595114Sgblack@eecs.umich.eduvoid 2605114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::setFloatRegBitsFlat(int reg_idx, FloatRegBits val) 2615114Sgblack@eecs.umich.edu{ 2625114Sgblack@eecs.umich.edu cpu->setArchFloatRegInt(reg_idx, val, thread->threadId()); 2635114Sgblack@eecs.umich.edu 2645114Sgblack@eecs.umich.edu conditionalSquash(); 2655114Sgblack@eecs.umich.edu} 2665114Sgblack@eecs.umich.edu 2675114Sgblack@eecs.umich.edutemplate <class Impl> 2685114Sgblack@eecs.umich.eduvoid 2695114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::setVecRegFlat(int reg_idx, const VecRegContainer& val) 2705114Sgblack@eecs.umich.edu{ 2715114Sgblack@eecs.umich.edu cpu->setArchVecReg(reg_idx, val, thread->threadId()); 2725114Sgblack@eecs.umich.edu 2735114Sgblack@eecs.umich.edu conditionalSquash(); 2745114Sgblack@eecs.umich.edu} 2755114Sgblack@eecs.umich.edu 2765114Sgblack@eecs.umich.edutemplate <class Impl> 2775114Sgblack@eecs.umich.eduvoid 2785114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::setVecElemFlat(const RegIndex& idx, 2795114Sgblack@eecs.umich.edu const ElemIndex& elemIndex, const VecElem& val) 2805114Sgblack@eecs.umich.edu{ 2815114Sgblack@eecs.umich.edu cpu->setArchVecElem(idx, elemIndex, val, thread->threadId()); 2825114Sgblack@eecs.umich.edu conditionalSquash(); 2835114Sgblack@eecs.umich.edu} 2845114Sgblack@eecs.umich.edu 2855114Sgblack@eecs.umich.edutemplate <class Impl> 2865114Sgblack@eecs.umich.eduvoid 2875114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::setCCRegFlat(int reg_idx, TheISA::CCReg val) 2885114Sgblack@eecs.umich.edu{ 2895114Sgblack@eecs.umich.edu cpu->setArchCCReg(reg_idx, val, thread->threadId()); 2905114Sgblack@eecs.umich.edu 2915114Sgblack@eecs.umich.edu conditionalSquash(); 2925114Sgblack@eecs.umich.edu} 2935114Sgblack@eecs.umich.edu 2945114Sgblack@eecs.umich.edutemplate <class Impl> 2955114Sgblack@eecs.umich.eduvoid 2965114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::pcState(const TheISA::PCState &val) 2975114Sgblack@eecs.umich.edu{ 2985114Sgblack@eecs.umich.edu cpu->pcState(val, thread->threadId()); 2995139Sgblack@eecs.umich.edu 3005139Sgblack@eecs.umich.edu conditionalSquash(); 3015114Sgblack@eecs.umich.edu} 3025114Sgblack@eecs.umich.edu 3035114Sgblack@eecs.umich.edutemplate <class Impl> 3045114Sgblack@eecs.umich.eduvoid 3055114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::pcStateNoRecord(const TheISA::PCState &val) 3065114Sgblack@eecs.umich.edu{ 3075114Sgblack@eecs.umich.edu cpu->pcState(val, thread->threadId()); 3085114Sgblack@eecs.umich.edu 3095114Sgblack@eecs.umich.edu conditionalSquash(); 3105114Sgblack@eecs.umich.edu} 3115114Sgblack@eecs.umich.edu 3125114Sgblack@eecs.umich.edutemplate <class Impl> 3135114Sgblack@eecs.umich.eduRegId 3145114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::flattenRegId(const RegId& regId) const 3155114Sgblack@eecs.umich.edu{ 3165114Sgblack@eecs.umich.edu return cpu->isa[thread->threadId()]->flattenRegId(regId); 3175114Sgblack@eecs.umich.edu} 3185114Sgblack@eecs.umich.edu 3195114Sgblack@eecs.umich.edutemplate <class Impl> 3205652Sgblack@eecs.umich.eduvoid 3215114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::setMiscRegNoEffect(int misc_reg, const MiscReg &val) 3225114Sgblack@eecs.umich.edu{ 3235114Sgblack@eecs.umich.edu cpu->setMiscRegNoEffect(misc_reg, val, thread->threadId()); 3245114Sgblack@eecs.umich.edu 3255114Sgblack@eecs.umich.edu conditionalSquash(); 3265114Sgblack@eecs.umich.edu} 3275114Sgblack@eecs.umich.edu 3285652Sgblack@eecs.umich.edu#endif//__CPU_O3_THREAD_CONTEXT_IMPL_HH__ 3295114Sgblack@eecs.umich.edutemplate <class Impl> 3305114Sgblack@eecs.umich.eduvoid 3315114Sgblack@eecs.umich.eduO3ThreadContext<Impl>::setMiscReg(int misc_reg, const MiscReg &val) 3325114Sgblack@eecs.umich.edu{ 3335114Sgblack@eecs.umich.edu cpu->setMiscReg(misc_reg, val, thread->threadId()); 3345114Sgblack@eecs.umich.edu 3355114Sgblack@eecs.umich.edu conditionalSquash(); 3365652Sgblack@eecs.umich.edu} 3375114Sgblack@eecs.umich.edu 3385114Sgblack@eecs.umich.edu