+template<class T>
+void
+IGbE::DescCache<T>::areaChanged()
+{
+ if (usedCache.size() > 0 || curFetching || wbOut)
+ panic("Descriptor Address, Length or Head changed. Bad\n");
+ reset();
+
+}
+
+template<class T>
+void
+IGbE::DescCache<T>::writeback(Addr aMask)
+{
+ int curHead = descHead();
+ int max_to_wb = usedCache.size();
+
+ // Check if this writeback is less restrictive that the previous
+ // and if so setup another one immediately following it
+ if (wbOut) {
+ if (aMask < wbAlignment) {
+ moreToWb = true;
+ wbAlignment = aMask;
+ }
+ DPRINTF(EthernetDesc,
+ "Writing back already in process, returning\n");
+ return;
+ }
+
+ moreToWb = false;
+ wbAlignment = aMask;
+
+
+ DPRINTF(EthernetDesc, "Writing back descriptors head: %d tail: "
+ "%d len: %d cachePnt: %d max_to_wb: %d descleft: %d\n",
+ curHead, descTail(), descLen(), cachePnt, max_to_wb,
+ descLeft());
+
+ if (max_to_wb + curHead >= descLen()) {
+ max_to_wb = descLen() - curHead;
+ moreToWb = true;
+ // this is by definition aligned correctly
+ } else if (wbAlignment != 0) {
+ // align the wb point to the mask
+ max_to_wb = max_to_wb & ~wbAlignment;
+ }
+
+ 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;
+ }
+
+ wbOut = max_to_wb;
+
+ assert(!wbDelayEvent.scheduled());
+ igbe->schedule(wbDelayEvent, curTick() + igbe->wbDelay);
+ igbe->anBegin(annSmWb, "Prepare Writeback Desc");
+}
+
+template<class T>
+void
+IGbE::DescCache<T>::writeback1()
+{
+ // If we're draining delay issuing this DMA
+ if (igbe->getDrainState() != Drainable::Running) {
+ igbe->schedule(wbDelayEvent, curTick() + igbe->wbDelay);
+ return;
+ }
+
+ 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));
+ igbe->anPq(annSmWb, annUsedCacheQ);
+ igbe->anPq(annSmWb, annDescQ);
+ igbe->anQ(annSmWb, annUsedDescQ);
+ }
+
+
+ igbe->anBegin(annSmWb, "Writeback Desc DMA");
+
+ assert(wbOut);
+ igbe->dmaWrite(pciToDma(descBase() + descHead() * sizeof(T)),
+ wbOut * sizeof(T), &wbEvent, (uint8_t*)wbBuf,
+ igbe->wbCompDelay);
+}
+
+template<class T>
+void
+IGbE::DescCache<T>::fetchDescriptors()
+{
+ size_t max_to_fetch;
+
+ if (curFetching) {
+ DPRINTF(EthernetDesc,
+ "Currently fetching %d descriptors, returning\n",
+ curFetching);
+ return;
+ }
+
+ if (descTail() >= cachePnt)
+ max_to_fetch = descTail() - cachePnt;
+ else
+ max_to_fetch = descLen() - cachePnt;
+
+ 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);
+
+
+ DPRINTF(EthernetDesc, "Fetching descriptors head: %d tail: "
+ "%d len: %d cachePnt: %d max_to_fetch: %d descleft: %d\n",
+ descHead(), descTail(), descLen(), cachePnt,
+ max_to_fetch, descLeft());
+
+ // Nothing to do
+ if (max_to_fetch == 0)
+ return;
+
+ // So we don't have two descriptor fetches going on at once
+ curFetching = max_to_fetch;
+
+ assert(!fetchDelayEvent.scheduled());
+ igbe->schedule(fetchDelayEvent, curTick() + igbe->fetchDelay);
+ igbe->anBegin(annSmFetch, "Prepare Fetch Desc");
+}
+
+template<class T>
+void
+IGbE::DescCache<T>::fetchDescriptors1()
+{
+ // If we're draining delay issuing this DMA
+ if (igbe->getDrainState() != Drainable::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),
+ pciToDma(descBase() + cachePnt * sizeof(T)),
+ curFetching * sizeof(T));
+ assert(curFetching);
+ igbe->dmaRead(pciToDma(descBase() + cachePnt * sizeof(T)),
+ curFetching * sizeof(T), &fetchEvent, (uint8_t*)fetchBuf,
+ igbe->fetchCompDelay);
+}
+
+template<class T>
+void
+IGbE::DescCache<T>::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);
+ }
+
+
+#ifndef NDEBUG
+ int oldCp = cachePnt;
+#endif
+
+ cachePnt += curFetching;
+ assert(cachePnt <= descLen());
+ if (cachePnt == descLen())
+ cachePnt = 0;
+
+ curFetching = 0;
+
+ 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();
+}
+
+template<class T>
+void
+IGbE::DescCache<T>::wbComplete()
+{
+
+ igbe->anBegin(annSmWb, "Finish Writeback");
+
+ long curHead = descHead();
+#ifndef NDEBUG
+ long oldHead = curHead;
+#endif
+
+ for (int x = 0; x < wbOut; x++) {
+ assert(usedCache.size());
+ delete usedCache[0];
+ usedCache.pop_front();
+
+ igbe->anDq(annSmWb, annUsedCacheQ);
+ igbe->anDq(annSmWb, annDescQ);
+ }
+
+ curHead += wbOut;
+ wbOut = 0;
+
+ if (curHead >= descLen())
+ curHead -= descLen();
+
+ // Update the head
+ updateHead(curHead);
+
+ DPRINTF(EthernetDesc, "Writeback complete curHead %d -> %d\n",
+ oldHead, curHead);
+
+ // If we still have more to wb, call wb now
+ actionAfterWb();
+ if (moreToWb) {
+ moreToWb = false;
+ DPRINTF(EthernetDesc, "Writeback has more todo\n");
+ writeback(wbAlignment);
+ }
+
+ if (!wbOut) {
+ igbe->checkDrain();
+ if (usedCache.size())
+ igbe->anBegin(annSmWb, "Wait", CPA::FL_WAIT);
+ else
+ igbe->anWe(annSmWb, annUsedCacheQ);
+ }
+ fetchAfterWb();
+}
+
+template<class T>
+void
+IGbE::DescCache<T>::reset()
+{
+ DPRINTF(EthernetDesc, "Reseting descriptor cache\n");
+ for (typename CacheType::size_type x = 0; x < usedCache.size(); x++)
+ delete usedCache[x];
+ for (typename CacheType::size_type x = 0; x < unusedCache.size(); x++)
+ delete unusedCache[x];
+
+ usedCache.clear();
+ unusedCache.clear();
+
+ cachePnt = 0;
+
+}
+
+template<class T>
+void
+IGbE::DescCache<T>::serialize(std::ostream &os)
+{
+ SERIALIZE_SCALAR(cachePnt);
+ SERIALIZE_SCALAR(curFetching);
+ SERIALIZE_SCALAR(wbOut);
+ SERIALIZE_SCALAR(moreToWb);
+ SERIALIZE_SCALAR(wbAlignment);
+
+ typename CacheType::size_type usedCacheSize = usedCache.size();
+ SERIALIZE_SCALAR(usedCacheSize);
+ for (typename CacheType::size_type x = 0; x < usedCacheSize; x++) {
+ arrayParamOut(os, csprintf("usedCache_%d", x),
+ (uint8_t*)usedCache[x],sizeof(T));
+ }
+
+ typename CacheType::size_type unusedCacheSize = unusedCache.size();
+ SERIALIZE_SCALAR(unusedCacheSize);
+ for (typename CacheType::size_type x = 0; x < unusedCacheSize; x++) {
+ arrayParamOut(os, csprintf("unusedCache_%d", x),
+ (uint8_t*)unusedCache[x],sizeof(T));
+ }
+
+ Tick fetch_delay = 0, wb_delay = 0;
+ if (fetchDelayEvent.scheduled())
+ fetch_delay = fetchDelayEvent.when();
+ SERIALIZE_SCALAR(fetch_delay);
+ if (wbDelayEvent.scheduled())
+ wb_delay = wbDelayEvent.when();
+ SERIALIZE_SCALAR(wb_delay);
+
+
+}
+
+template<class T>
+void
+IGbE::DescCache<T>::unserialize(Checkpoint *cp, const std::string §ion)
+{
+ UNSERIALIZE_SCALAR(cachePnt);
+ UNSERIALIZE_SCALAR(curFetching);
+ UNSERIALIZE_SCALAR(wbOut);
+ UNSERIALIZE_SCALAR(moreToWb);
+ UNSERIALIZE_SCALAR(wbAlignment);
+
+ typename CacheType::size_type usedCacheSize;
+ UNSERIALIZE_SCALAR(usedCacheSize);
+ T *temp;
+ for (typename CacheType::size_type x = 0; x < usedCacheSize; x++) {
+ temp = new T;
+ arrayParamIn(cp, section, csprintf("usedCache_%d", x),
+ (uint8_t*)temp,sizeof(T));
+ usedCache.push_back(temp);
+ }
+
+ typename CacheType::size_type unusedCacheSize;
+ UNSERIALIZE_SCALAR(unusedCacheSize);
+ for (typename CacheType::size_type x = 0; x < unusedCacheSize; x++) {
+ temp = new T;
+ arrayParamIn(cp, section, csprintf("unusedCache_%d", x),
+ (uint8_t*)temp,sizeof(T));
+ unusedCache.push_back(temp);
+ }
+ Tick fetch_delay = 0, wb_delay = 0;
+ UNSERIALIZE_SCALAR(fetch_delay);
+ UNSERIALIZE_SCALAR(wb_delay);
+ if (fetch_delay)
+ igbe->schedule(fetchDelayEvent, fetch_delay);
+ if (wb_delay)
+ igbe->schedule(wbDelayEvent, wb_delay);
+
+
+}
+
+///////////////////////////// IGbE::RxDescCache //////////////////////////////