// Magic happy checksum value
     flash[EEPROM_SIZE-1] = htobe((uint16_t)(EEPROM_CSUM - csum));
 
+    // Store the MAC address as queue ID
+    macAddr = p->hardware_address;
+
     rxFifo.clear();
     txFifo.clear();
 }
 
+void
+IGbE::init()
+{
+    cpa = CPA::cpa();
+    PciDev::init();
+}
+
 EtherInt*
 IGbE::getEthPort(const std::string &if_name, int idx)
 {
       pktEvent(this), pktHdrEvent(this), pktDataEvent(this)
 
 {
+    annSmFetch = "RX Desc Fetch";
+    annSmWb = "RX Desc Writeback";
+    annUnusedDescQ = "RX Unused Descriptors";
+    annUnusedCacheQ = "RX Unused Descriptor Cache";
+    annUsedCacheQ = "RX Used Descriptor Cache";
+    annUsedDescQ = "RX Used Descriptors";
+    annDescQ = "RX Descriptors";
 }
 
 void
     RxDesc *desc;
     desc = unusedCache.front();
 
+    igbe->anBegin("RXS", "Update Desc");
+
     uint16_t crcfixup = igbe->regs.rctl.secrc() ? 0 : 4 ;
     DPRINTF(EthernetDesc, "pktPtr->length: %d bytesCopied: %d stripcrc offset: %d value written: %d %d\n",
             pktPtr->length, bytesCopied, crcfixup,
     enableSm();
     pktDone = true;
 
+    igbe->anBegin("RXS", "Done Updating Desc");
     DPRINTF(EthernetDesc, "Processing of this descriptor complete\n");
+    igbe->anDq("RXS", annUnusedCacheQ);
     unusedCache.pop_front();
+    igbe->anQ("RXS", annUsedCacheQ);
     usedCache.push_back(desc);
 }
 
        useTso(false), pktEvent(this), headerEvent(this), nullEvent(this)
 
 {
+    annSmFetch = "TX Desc Fetch";
+    annSmWb = "TX Desc Writeback";
+    annUnusedDescQ = "TX Unused Descriptors";
+    annUnusedCacheQ = "TX Unused Descriptor Cache";
+    annUsedCacheQ = "TX Used Descriptor Cache";
+    annUsedDescQ = "TX Used Descriptors";
+    annDescQ = "TX Descriptors";
 }
 
 void
 
         TxdOp::setDd(desc);
         unusedCache.pop_front();
+        igbe->anDq("TXS", annUnusedCacheQ);
         usedCache.push_back(desc);
+        igbe->anQ("TXS", annUsedCacheQ);
     }
 
     if (!unusedCache.size())
     assert(unusedCache.size());
     assert(pktPtr);
 
+    igbe->anBegin("TXS", "Update Desc");
+
     DPRINTF(EthernetDesc, "DMA of packet complete\n");
 
 
             (pktPtr->length < ( tsoMss + tsoHeaderLen) &&
              tsoTotalLen != tsoUsedLen && useTso)) {
         assert(!useTso || (tsoDescBytesUsed == TxdOp::getLen(desc)));
+        igbe->anDq("TXS", annUnusedCacheQ);
         unusedCache.pop_front();
+        igbe->anQ("TXS", annUsedCacheQ);
         usedCache.push_back(desc);
 
         tsoDescBytesUsed = 0;
 
     if (!useTso ||  TxdOp::getLen(desc) == tsoDescBytesUsed) {
         DPRINTF(EthernetDesc, "Descriptor Done\n");
+        igbe->anDq("TXS", annUnusedCacheQ);
         unusedCache.pop_front();
+        igbe->anQ("TXS", annUsedCacheQ);
         usedCache.push_back(desc);
         tsoDescBytesUsed = 0;
     }
     tsoPktHasHeader = false;
 
     if (igbe->regs.txdctl.wthresh() == 0) {
+        igbe->anBegin("TXS", "Desc Writeback");
         DPRINTF(EthernetDesc, "WTHRESH == 0, writing back descriptor\n");
         writeback(0);
     } else if (igbe->regs.txdctl.gran() && igbe->regs.txdctl.wthresh() >=
             descInBlock(usedCache.size())) {
         DPRINTF(EthernetDesc, "used > WTHRESH, writing back descriptor\n");
+        igbe->anBegin("TXS", "Desc Writeback");
         writeback((igbe->cacheBlockSize()-1)>>4);
     } else if (igbe->regs.txdctl.wthresh() >= usedCache.size()) {
         DPRINTF(EthernetDesc, "used > WTHRESH, writing back descriptor\n");
+        igbe->anBegin("TXS", "Desc Writeback");
         writeback((igbe->cacheBlockSize()-1)>>4);
     }
 
     else
         changeState(Drained);
 
+    DPRINTF(EthernetSM, "got drain() returning %d", count);
     return count;
 }
 
     rxTick = true;
 
     restartClock();
+    DPRINTF(EthernetSM, "resuming from drain");
 }
 
 void
     if (!drainEvent)
         return;
 
+    DPRINTF(EthernetSM, "checkDrain() in drain\n");
     txFifoTick = false;
     txTick = false;
     rxTick = false;
                  && !txDescCache.packetMultiDesc() && txPacket->length) {
         bool success;
 
+        anQ("TXS", "TX FIFO Q");
         DPRINTF(EthernetSM, "TXS: packet placed in TX FIFO\n");
         success = txFifo.push(txPacket);
         txFifoTick = true && !drainEvent;
         assert(success);
         txPacket = NULL;
+        anBegin("TXS", "Desc Writeback");
         txDescCache.writeback((cacheBlockSize()-1)>>4);
         return;
     }
     if (!txDescCache.packetWaiting()) {
         if (txDescCache.descLeft() == 0) {
             postInterrupt(IT_TXQE);
+            anBegin("TXS", "Desc Writeback");
             txDescCache.writeback(0);
+            anBegin("TXS", "Desc Fetch");
+            anWe("TXS", txDescCache.annUnusedCacheQ);
             txDescCache.fetchDescriptors();
             DPRINTF(EthernetSM, "TXS: No descriptors left in ring, forcing "
                     "writeback stopping ticking and posting TXQE\n");
 
 
         if (!(txDescCache.descUnused())) {
+            anBegin("TXS", "Desc Fetch");
             txDescCache.fetchDescriptors();
+            anWe("TXS", txDescCache.annUnusedCacheQ);
             DPRINTF(EthernetSM, "TXS: No descriptors available in cache, fetching and stopping ticking\n");
             txTick = false;
             return;
         }
+        anPq("TXS", txDescCache.annUnusedCacheQ);
 
 
         txDescCache.processContextDesc();
         int size;
         size = txDescCache.getPacketSize(txPacket);
         if (size > 0 && txFifo.avail() > size) {
+            anRq("TXS", "TX FIFO Q");
+            anBegin("TXS", "DMA Packet");
             DPRINTF(EthernetSM, "TXS: Reserving %d bytes in FIFO and begining "
                     "DMA of next packet\n", size);
             txFifo.reserve(size);
         } else if (size <= 0) {
             DPRINTF(EthernetSM, "TXS: getPacketSize returned: %d\n", size);
             DPRINTF(EthernetSM, "TXS: No packets to get, writing back used descriptors\n");
+            anBegin("TXS", "Desc Writeback");
             txDescCache.writeback(0);
         } else {
+            anWf("TXS", "TX FIFO Q");
             DPRINTF(EthernetSM, "TXS: FIFO full, stopping ticking until space "
                     "available in FIFO\n");
             txTick = false;
     rxPackets++;
 
     DPRINTF(Ethernet, "RxFIFO: Receiving pcakte from wire\n");
+    anBegin("RXQ", "Wire Recv");
+
 
     if (!regs.rctl.en()) {
         DPRINTF(Ethernet, "RxFIFO: RX not enabled, dropping\n");
+        anBegin("RXQ", "FIFO Drop", CPA::FL_BAD);
         return true;
     }
 
     if (!rxFifo.push(pkt)) {
         DPRINTF(Ethernet, "RxFIFO: Packet won't fit in fifo... dropped\n");
         postInterrupt(IT_RXO, true);
+        anBegin("RXQ", "FIFO Drop", CPA::FL_BAD);
         return false;
     }
 
+    if (CPA::available() && cpa->enabled()) {
+        assert(sys->numSystemsRunning <= 2);
+        System *other_sys;
+        if (sys->systemList[0] == sys)
+            other_sys = sys->systemList[1];
+        else
+            other_sys = sys->systemList[0];
+
+        cpa->hwDq(CPA::FL_NONE, sys, macAddr, "RXQ", "WireQ", 0, other_sys);
+        anQ("RXQ", "RX FIFO Q");
+        cpa->hwWe(CPA::FL_NONE, sys, macAddr, "RXQ", "WireQ", 0, other_sys);
+    }
+
     return true;
 }
 
             rxDescCache.writeback(0);
 
         if (descLeft == 0) {
+            anBegin("RXS", "Writeback Descriptors");
             rxDescCache.writeback(0);
             DPRINTF(EthernetSM, "RXS: No descriptors left in ring, forcing"
                     " writeback and stopping ticking\n");
 
         if (regs.rxdctl.wthresh() >= rxDescCache.descUsed()) {
             DPRINTF(EthernetSM, "RXS: Writing back because WTHRESH >= descUsed\n");
+            anBegin("RXS", "Writeback Descriptors");
             if (regs.rxdctl.wthresh() < (cacheBlockSize()>>4))
                 rxDescCache.writeback(regs.rxdctl.wthresh()-1);
             else
         if ((rxDescCache.descUnused() < regs.rxdctl.pthresh()) &&
              ((rxDescCache.descLeft() - rxDescCache.descUnused()) > regs.rxdctl.hthresh())) {
             DPRINTF(EthernetSM, "RXS: Fetching descriptors because descUnused < PTHRESH\n");
+            anBegin("RXS", "Fetch Descriptors");
             rxDescCache.fetchDescriptors();
         }
 
         if (rxDescCache.descUnused() == 0) {
+            anBegin("RXS", "Fetch Descriptors");
             rxDescCache.fetchDescriptors();
+            anWe("RXS", rxDescCache.annUnusedCacheQ);
             DPRINTF(EthernetSM, "RXS: No descriptors available in cache, "
                     "fetching descriptors and stopping ticking\n");
             rxTick = false;
     }
 
     if (!rxDescCache.descUnused()) {
+        anBegin("RXS", "Fetch Descriptors");
         rxDescCache.fetchDescriptors();
+        anWe("RXS", rxDescCache.annUnusedCacheQ);
         DPRINTF(EthernetSM, "RXS: No descriptors available in cache, stopping ticking\n");
         rxTick = false;
         DPRINTF(EthernetSM, "RXS: No descriptors available, fetching\n");
         return;
     }
+    anPq("RXS", rxDescCache.annUnusedCacheQ);
 
     if (rxFifo.empty()) {
+        anWe("RXS", "RX FIFO Q");
         DPRINTF(EthernetSM, "RXS: RxFIFO empty, stopping ticking\n");
         rxTick = false;
         return;
     }
+    anPq("RXS", "RX FIFO Q");
+    anBegin("RXS", "Get Desc");
 
     EthPacketPtr pkt;
     pkt = rxFifo.front();
     pktOffset = rxDescCache.writePacket(pkt, pktOffset);
     DPRINTF(EthernetSM, "RXS: Writing packet into memory\n");
     if (pktOffset == pkt->length) {
+        anBegin( "RXS", "FIFO Dequeue");
         DPRINTF(EthernetSM, "RXS: Removing packet from FIFO\n");
         pktOffset = 0;
+        anDq("RXS", "RX FIFO Q");
         rxFifo.pop();
     }
 
     DPRINTF(EthernetSM, "RXS: stopping ticking until packet DMA completes\n");
     rxTick = false;
     rxDmaPacket = true;
+    anBegin("RXS", "DMA Packet");
 }
 
 void
 IGbE::txWire()
 {
     if (txFifo.empty()) {
+        anWe("TXQ", "TX FIFO Q");
         txFifoTick = false;
         return;
     }
 
 
+    anPq("TXQ", "TX FIFO Q");
     if (etherInt->sendPacket(txFifo.front())) {
+        cpa->hwQ(CPA::FL_NONE, sys, macAddr, "TXQ", "WireQ", 0);
         if (DTRACE(EthernetSM)) {
             IpPtr ip(txFifo.front());
             if (ip)
             else
                 DPRINTF(EthernetSM, "Transmitting Non-Ip packet\n");
         }
+        anDq("TXQ", "TX FIFO Q");
+        anBegin("TXQ", "Wire Send");
         DPRINTF(EthernetSM, "TxFIFO: Successful transmit, bytes available in fifo: %d\n",
                 txFifo.avail());
 
 void
 IGbE::ethTxDone()
 {
+    anBegin("TXQ", "Send Done");
     // restart the tx state machines if they are stopped
     // fifo to send another packet
     // tx sm to put more data into the fifo
 
 #include <deque>
 #include <string>
 
+#include "base/cp_annotate.hh"
 #include "base/inet.hh"
 #include "dev/etherdevice.hh"
 #include "dev/etherint.hh"
 {
   private:
     IGbEInt *etherInt;
+    CPA *cpa;
 
     // device registers
     iGbReg::Regs regs;
      */
     void checkDrain();
 
+    void anBegin(std::string sm, std::string st, int flags = CPA::FL_NONE) {
+        cpa->hwBegin((CPA::flags)flags, sys, macAddr, sm, st);
+    }
+
+    void anQ(std::string sm, std::string q) { 
+        cpa->hwQ(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
+    }
+
+    void anDq(std::string sm, std::string q) {
+        cpa->hwDq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
+    }
+
+    void anPq(std::string sm, std::string q, int num = 1) {
+        cpa->hwPq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr, NULL, num);
+    }
+
+    void anRq(std::string sm, std::string q, int num = 1) {
+        cpa->hwPq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr, NULL, num);
+    }
+
+    void anWe(std::string sm, std::string q) {
+        cpa->hwWe(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
+    }
+
+    void anWf(std::string sm, std::string q) {
+        cpa->hwWf(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
+    }
+
+
     template<class T>
     class DescCache
     {
         EthPacketPtr pktPtr;
 
       public:
+        /** Annotate sm*/
+        std::string annSmFetch, annSmWb, annUnusedDescQ, annUsedCacheQ,
+            annUsedDescQ, annUnusedCacheQ, annDescQ;
+
         DescCache(IGbE *i, const std::string n, int s)
             : igbe(i), _name(n), cachePnt(0), size(s), curFetching(0), wbOut(0),
               pktPtr(NULL), wbDelayEvent(this), fetchDelayEvent(this), 
             DPRINTF(EthernetDesc, "Writing back %d descriptors\n", max_to_wb);
 
             if (max_to_wb <= 0) {
+                if (usedCache.size())
+                    igbe->anBegin(annSmWb, "Wait Alignment", CPA::FL_WAIT);
+                else
+                    igbe->anWe(annSmWb, annUsedCacheQ);
                 return;
             }
 
 
             assert(!wbDelayEvent.scheduled()); 
             igbe->schedule(wbDelayEvent, curTick + igbe->wbDelay);
+            igbe->anBegin(annSmWb, "Prepare Writeback Desc");
         }
             
         void writeback1()
         {
             // If we're draining delay issuing this DMA
-            if (igbe->drainEvent) {
+            if (igbe->getState() != SimObject::Running) {
                 igbe->schedule(wbDelayEvent, curTick + igbe->wbDelay);
                 return;
             }
 
-            DPRINTF(EthernetDesc, "Beining DMA of %d descriptors\n", wbOut);
+            DPRINTF(EthernetDesc, "Begining DMA of %d descriptors\n", wbOut);
             
             for (int x = 0; x < wbOut; x++) {
                 assert(usedCache.size());
                 memcpy(&wbBuf[x], usedCache[x], sizeof(T));
-                 //delete usedCache[0];
-                //usedCache.pop_front();
+                igbe->anPq(annSmWb, annUsedCacheQ);
+                igbe->anPq(annSmWb, annDescQ);
+                igbe->anQ(annSmWb, annUsedDescQ);
             }
 
+    
+            igbe->anBegin(annSmWb, "Writeback Desc DMA");
+
             assert(wbOut);
             igbe->dmaWrite(igbe->platform->pciToDma(descBase() + descHead() * sizeof(T)),
                     wbOut * sizeof(T), &wbEvent, (uint8_t*)wbBuf,
 
             size_t free_cache = size - usedCache.size() - unusedCache.size();
 
+            if (!max_to_fetch)
+                igbe->anWe(annSmFetch, annUnusedDescQ);
+            else
+                igbe->anPq(annSmFetch, annUnusedDescQ, max_to_fetch);
+
+            if (max_to_fetch) {
+                if (!free_cache)
+                    igbe->anWf(annSmFetch, annDescQ);
+                else
+                    igbe->anRq(annSmFetch, annDescQ, free_cache);
+            }
+
             max_to_fetch = std::min(max_to_fetch, free_cache);
             
 
 
             assert(!fetchDelayEvent.scheduled());
             igbe->schedule(fetchDelayEvent, curTick + igbe->fetchDelay);
+            igbe->anBegin(annSmFetch, "Prepare Fetch Desc");
         }
 
         void fetchDescriptors1()
         {
             // If we're draining delay issuing this DMA
-            if (igbe->drainEvent) {
+            if (igbe->getState() != SimObject::Running) {
                 igbe->schedule(fetchDelayEvent, curTick + igbe->fetchDelay);
                 return;
             }
 
+            igbe->anBegin(annSmFetch, "Fetch Desc");
+
             DPRINTF(EthernetDesc, "Fetching descriptors at %#x (%#x), size: %#x\n",
                     descBase() + cachePnt * sizeof(T),
                     igbe->platform->pciToDma(descBase() + cachePnt * sizeof(T)),
         void fetchComplete()
         {
             T *newDesc;
+            igbe->anBegin(annSmFetch, "Fetch Complete");
             for (int x = 0; x < curFetching; x++) {
                 newDesc = new T;
                 memcpy(newDesc, &fetchBuf[x], sizeof(T));
                 unusedCache.push_back(newDesc);
+                igbe->anDq(annSmFetch, annUnusedDescQ);
+                igbe->anQ(annSmFetch, annUnusedCacheQ);
+                igbe->anQ(annSmFetch, annDescQ);
             }
 
 
             DPRINTF(EthernetDesc, "Fetching complete cachePnt %d -> %d\n",
                     oldCp, cachePnt);
 
+            if ((descTail() >= cachePnt ? (descTail() - cachePnt) : (descLen() -
+                    cachePnt)) == 0)
+            {
+                igbe->anWe(annSmFetch, annUnusedDescQ);
+            } else if (!(size - usedCache.size() - unusedCache.size())) {
+                igbe->anWf(annSmFetch, annDescQ);
+            } else {
+                igbe->anBegin(annSmFetch, "Wait", CPA::FL_WAIT);
+            }
+
             enableSm();
             igbe->checkDrain();
         }
         void wbComplete()
         {
 
+            igbe->anBegin(annSmWb, "Finish Writeback");
+
             long  curHead = descHead();
 #ifndef NDEBUG
             long oldHead = curHead;
                 assert(usedCache.size());
                 delete usedCache[0];
                 usedCache.pop_front();
+
+                igbe->anDq(annSmWb, annUsedCacheQ);
+                igbe->anDq(annSmWb, annDescQ);
             }
 
             curHead += wbOut;
 
             if (!wbOut) {
                 igbe->checkDrain();
+                if (usedCache.size())
+                    igbe->anBegin(annSmWb, "Wait", CPA::FL_WAIT);
+                else
+                    igbe->anWe(annSmWb, annUsedCacheQ);
             }
             fetchAfterWb();
         }
     }
     IGbE(const Params *params);
     ~IGbE() {}
+    virtual void init();
 
     virtual EtherInt *getEthPort(const std::string &if_name, int idx);