i8254xGBe.hh revision 5954
1/*
2 * Copyright (c) 2006 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Ali Saidi
29 */
30
31/* @file
32 * Device model for Intel's 8254x line of gigabit ethernet controllers.
33 */
34
35#ifndef __DEV_I8254XGBE_HH__
36#define __DEV_I8254XGBE_HH__
37
38#include <deque>
39#include <string>
40
41#include "base/cp_annotate.hh"
42#include "base/inet.hh"
43#include "dev/etherdevice.hh"
44#include "dev/etherint.hh"
45#include "dev/etherpkt.hh"
46#include "dev/i8254xGBe_defs.hh"
47#include "dev/pcidev.hh"
48#include "dev/pktfifo.hh"
49#include "params/IGbE.hh"
50#include "sim/eventq.hh"
51
52class IGbEInt;
53
54class IGbE : public EtherDevice
55{
56  private:
57    IGbEInt *etherInt;
58    CPA *cpa;
59
60    // device registers
61    iGbReg::Regs regs;
62
63    // eeprom data, status and control bits
64    int eeOpBits, eeAddrBits, eeDataBits;
65    uint8_t eeOpcode, eeAddr;
66    uint16_t flash[iGbReg::EEPROM_SIZE];
67
68    // The drain event if we have one
69    Event *drainEvent;
70
71    // cached parameters from params struct
72    bool useFlowControl;
73
74    // packet fifos
75    PacketFifo rxFifo;
76    PacketFifo txFifo;
77
78    // Packet that we are currently putting into the txFifo
79    EthPacketPtr txPacket;
80
81    // Should to Rx/Tx State machine tick?
82    bool rxTick;
83    bool txTick;
84    bool txFifoTick;
85
86    bool rxDmaPacket;
87
88    // Number of bytes copied from current RX packet
89    int pktOffset;
90
91    // Delays in managaging descriptors
92    Tick fetchDelay, wbDelay;
93    Tick fetchCompDelay, wbCompDelay;
94    Tick rxWriteDelay, txReadDelay;
95
96    // Event and function to deal with RDTR timer expiring
97    void rdtrProcess() {
98        rxDescCache.writeback(0);
99        DPRINTF(EthernetIntr, "Posting RXT interrupt because RDTR timer expired\n");
100        postInterrupt(iGbReg::IT_RXT);
101    }
102
103    //friend class EventWrapper<IGbE, &IGbE::rdtrProcess>;
104    EventWrapper<IGbE, &IGbE::rdtrProcess> rdtrEvent;
105
106    // Event and function to deal with RADV timer expiring
107    void radvProcess() {
108        rxDescCache.writeback(0);
109        DPRINTF(EthernetIntr, "Posting RXT interrupt because RADV timer expired\n");
110        postInterrupt(iGbReg::IT_RXT);
111    }
112
113    //friend class EventWrapper<IGbE, &IGbE::radvProcess>;
114    EventWrapper<IGbE, &IGbE::radvProcess> radvEvent;
115
116    // Event and function to deal with TADV timer expiring
117    void tadvProcess() {
118        txDescCache.writeback(0);
119        DPRINTF(EthernetIntr, "Posting TXDW interrupt because TADV timer expired\n");
120        postInterrupt(iGbReg::IT_TXDW);
121    }
122
123    //friend class EventWrapper<IGbE, &IGbE::tadvProcess>;
124    EventWrapper<IGbE, &IGbE::tadvProcess> tadvEvent;
125
126    // Event and function to deal with TIDV timer expiring
127    void tidvProcess() {
128        txDescCache.writeback(0);
129        DPRINTF(EthernetIntr, "Posting TXDW interrupt because TIDV timer expired\n");
130        postInterrupt(iGbReg::IT_TXDW);
131    }
132    //friend class EventWrapper<IGbE, &IGbE::tidvProcess>;
133    EventWrapper<IGbE, &IGbE::tidvProcess> tidvEvent;
134
135    // Main event to tick the device
136    void tick();
137    //friend class EventWrapper<IGbE, &IGbE::tick>;
138    EventWrapper<IGbE, &IGbE::tick> tickEvent;
139
140
141    uint64_t macAddr;
142
143    void rxStateMachine();
144    void txStateMachine();
145    void txWire();
146
147    /** Write an interrupt into the interrupt pending register and check mask
148     * and interrupt limit timer before sending interrupt to CPU
149     * @param t the type of interrupt we are posting
150     * @param now should we ignore the interrupt limiting timer
151     */
152    void postInterrupt(iGbReg::IntTypes t, bool now = false);
153
154    /** Check and see if changes to the mask register have caused an interrupt
155     * to need to be sent or perhaps removed an interrupt cause.
156     */
157    void chkInterrupt();
158
159    /** Send an interrupt to the cpu
160     */
161    void delayIntEvent();
162    void cpuPostInt();
163    // Event to moderate interrupts
164    EventWrapper<IGbE, &IGbE::delayIntEvent> interEvent;
165
166    /** Clear the interupt line to the cpu
167     */
168    void cpuClearInt();
169
170    Tick intClock() { return Clock::Int::ns * 1024; }
171
172    /** This function is used to restart the clock so it can handle things like
173     * draining and resume in one place. */
174    void restartClock();
175
176    /** Check if all the draining things that need to occur have occured and
177     * handle the drain event if so.
178     */
179    void checkDrain();
180
181    void anBegin(std::string sm, std::string st, int flags = CPA::FL_NONE) {
182        cpa->hwBegin((CPA::flags)flags, sys, macAddr, sm, st);
183    }
184
185    void anQ(std::string sm, std::string q) {
186        cpa->hwQ(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
187    }
188
189    void anDq(std::string sm, std::string q) {
190        cpa->hwDq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
191    }
192
193    void anPq(std::string sm, std::string q, int num = 1) {
194        cpa->hwPq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr, NULL, num);
195    }
196
197    void anRq(std::string sm, std::string q, int num = 1) {
198        cpa->hwPq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr, NULL, num);
199    }
200
201    void anWe(std::string sm, std::string q) {
202        cpa->hwWe(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
203    }
204
205    void anWf(std::string sm, std::string q) {
206        cpa->hwWf(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
207    }
208
209
210    template<class T>
211    class DescCache
212    {
213      protected:
214        virtual Addr descBase() const = 0;
215        virtual long descHead() const = 0;
216        virtual long descTail() const = 0;
217        virtual long descLen() const = 0;
218        virtual void updateHead(long h) = 0;
219        virtual void enableSm() = 0;
220        virtual void actionAfterWb() {}
221        virtual void fetchAfterWb() = 0;
222
223        std::deque<T*> usedCache;
224        std::deque<T*> unusedCache;
225
226        T *fetchBuf;
227        T *wbBuf;
228
229        // Pointer to the device we cache for
230        IGbE *igbe;
231
232        // Name of this  descriptor cache
233        std::string _name;
234
235        // How far we've cached
236        int cachePnt;
237
238        // The size of the descriptor cache
239        int size;
240
241        // How many descriptors we are currently fetching
242        int curFetching;
243
244        // How many descriptors we are currently writing back
245        int wbOut;
246
247        // if the we wrote back to the end of the descriptor ring and are going
248        // to have to wrap and write more
249        bool moreToWb;
250
251        // What the alignment is of the next descriptor writeback
252        Addr wbAlignment;
253
254       /** The packet that is currently being dmad to memory if any
255         */
256        EthPacketPtr pktPtr;
257
258      public:
259        /** Annotate sm*/
260        std::string annSmFetch, annSmWb, annUnusedDescQ, annUsedCacheQ,
261            annUsedDescQ, annUnusedCacheQ, annDescQ;
262
263        DescCache(IGbE *i, const std::string n, int s)
264            : igbe(i), _name(n), cachePnt(0), size(s), curFetching(0), wbOut(0),
265              pktPtr(NULL), wbDelayEvent(this), fetchDelayEvent(this),
266              fetchEvent(this), wbEvent(this)
267        {
268            fetchBuf = new T[size];
269            wbBuf = new T[size];
270        }
271
272        virtual ~DescCache()
273        {
274            reset();
275        }
276
277        std::string name() { return _name; }
278
279        /** If the address/len/head change when we've got descriptors that are
280         * dirty that is very bad. This function checks that we don't and if we
281         * do panics.
282         */
283        void areaChanged()
284        {
285            if (usedCache.size() > 0 || curFetching || wbOut)
286                panic("Descriptor Address, Length or Head changed. Bad\n");
287            reset();
288
289        }
290
291        void writeback(Addr aMask)
292        {
293            int curHead = descHead();
294            int max_to_wb = usedCache.size();
295
296            // Check if this writeback is less restrictive that the previous
297            // and if so setup another one immediately following it
298            if (wbOut) {
299                if (aMask < wbAlignment) {
300                    moreToWb = true;
301                    wbAlignment = aMask;
302                }
303                DPRINTF(EthernetDesc, "Writing back already in process, returning\n");
304                return;
305            }
306
307            moreToWb = false;
308            wbAlignment = aMask;
309
310
311            DPRINTF(EthernetDesc, "Writing back descriptors head: %d tail: "
312                    "%d len: %d cachePnt: %d max_to_wb: %d descleft: %d\n",
313                    curHead, descTail(), descLen(), cachePnt, max_to_wb,
314                    descLeft());
315
316            if (max_to_wb + curHead >= descLen()) {
317                max_to_wb = descLen() - curHead;
318                moreToWb = true;
319                // this is by definition aligned correctly
320            } else if (wbAlignment != 0) {
321                // align the wb point to the mask
322                max_to_wb = max_to_wb & ~wbAlignment;
323            }
324
325            DPRINTF(EthernetDesc, "Writing back %d descriptors\n", max_to_wb);
326
327            if (max_to_wb <= 0) {
328                if (usedCache.size())
329                    igbe->anBegin(annSmWb, "Wait Alignment", CPA::FL_WAIT);
330                else
331                    igbe->anWe(annSmWb, annUsedCacheQ);
332                return;
333            }
334
335            wbOut = max_to_wb;
336
337            assert(!wbDelayEvent.scheduled());
338            igbe->schedule(wbDelayEvent, curTick + igbe->wbDelay);
339            igbe->anBegin(annSmWb, "Prepare Writeback Desc");
340        }
341
342        void writeback1()
343        {
344            // If we're draining delay issuing this DMA
345            if (igbe->getState() != SimObject::Running) {
346                igbe->schedule(wbDelayEvent, curTick + igbe->wbDelay);
347                return;
348            }
349
350            DPRINTF(EthernetDesc, "Begining DMA of %d descriptors\n", wbOut);
351
352            for (int x = 0; x < wbOut; x++) {
353                assert(usedCache.size());
354                memcpy(&wbBuf[x], usedCache[x], sizeof(T));
355                igbe->anPq(annSmWb, annUsedCacheQ);
356                igbe->anPq(annSmWb, annDescQ);
357                igbe->anQ(annSmWb, annUsedDescQ);
358            }
359
360
361            igbe->anBegin(annSmWb, "Writeback Desc DMA");
362
363            assert(wbOut);
364            igbe->dmaWrite(igbe->platform->pciToDma(descBase() + descHead() * sizeof(T)),
365                    wbOut * sizeof(T), &wbEvent, (uint8_t*)wbBuf,
366                    igbe->wbCompDelay);
367        }
368        EventWrapper<DescCache, &DescCache::writeback1> wbDelayEvent;
369
370        /** Fetch a chunk of descriptors into the descriptor cache.
371         * Calls fetchComplete when the memory system returns the data
372         */
373
374        void fetchDescriptors()
375        {
376            size_t max_to_fetch;
377
378            if (curFetching) {
379                DPRINTF(EthernetDesc, "Currently fetching %d descriptors, returning\n", curFetching);
380                return;
381            }
382
383            if (descTail() >= cachePnt)
384                max_to_fetch = descTail() - cachePnt;
385            else
386                max_to_fetch = descLen() - cachePnt;
387
388            size_t free_cache = size - usedCache.size() - unusedCache.size();
389
390            if (!max_to_fetch)
391                igbe->anWe(annSmFetch, annUnusedDescQ);
392            else
393                igbe->anPq(annSmFetch, annUnusedDescQ, max_to_fetch);
394
395            if (max_to_fetch) {
396                if (!free_cache)
397                    igbe->anWf(annSmFetch, annDescQ);
398                else
399                    igbe->anRq(annSmFetch, annDescQ, free_cache);
400            }
401
402            max_to_fetch = std::min(max_to_fetch, free_cache);
403
404
405            DPRINTF(EthernetDesc, "Fetching descriptors head: %d tail: "
406                    "%d len: %d cachePnt: %d max_to_fetch: %d descleft: %d\n",
407                    descHead(), descTail(), descLen(), cachePnt,
408                    max_to_fetch, descLeft());
409
410            // Nothing to do
411            if (max_to_fetch == 0)
412                return;
413
414            // So we don't have two descriptor fetches going on at once
415            curFetching = max_to_fetch;
416
417            assert(!fetchDelayEvent.scheduled());
418            igbe->schedule(fetchDelayEvent, curTick + igbe->fetchDelay);
419            igbe->anBegin(annSmFetch, "Prepare Fetch Desc");
420        }
421
422        void fetchDescriptors1()
423        {
424            // If we're draining delay issuing this DMA
425            if (igbe->getState() != SimObject::Running) {
426                igbe->schedule(fetchDelayEvent, curTick + igbe->fetchDelay);
427                return;
428            }
429
430            igbe->anBegin(annSmFetch, "Fetch Desc");
431
432            DPRINTF(EthernetDesc, "Fetching descriptors at %#x (%#x), size: %#x\n",
433                    descBase() + cachePnt * sizeof(T),
434                    igbe->platform->pciToDma(descBase() + cachePnt * sizeof(T)),
435                    curFetching * sizeof(T));
436            assert(curFetching);
437            igbe->dmaRead(igbe->platform->pciToDma(descBase() + cachePnt * sizeof(T)),
438                    curFetching * sizeof(T), &fetchEvent, (uint8_t*)fetchBuf,
439                    igbe->fetchCompDelay);
440        }
441
442        EventWrapper<DescCache, &DescCache::fetchDescriptors1> fetchDelayEvent;
443
444        /** Called by event when dma to read descriptors is completed
445         */
446        void fetchComplete()
447        {
448            T *newDesc;
449            igbe->anBegin(annSmFetch, "Fetch Complete");
450            for (int x = 0; x < curFetching; x++) {
451                newDesc = new T;
452                memcpy(newDesc, &fetchBuf[x], sizeof(T));
453                unusedCache.push_back(newDesc);
454                igbe->anDq(annSmFetch, annUnusedDescQ);
455                igbe->anQ(annSmFetch, annUnusedCacheQ);
456                igbe->anQ(annSmFetch, annDescQ);
457            }
458
459
460#ifndef NDEBUG
461            int oldCp = cachePnt;
462#endif
463
464            cachePnt += curFetching;
465            assert(cachePnt <= descLen());
466            if (cachePnt == descLen())
467                cachePnt = 0;
468
469            curFetching = 0;
470
471            DPRINTF(EthernetDesc, "Fetching complete cachePnt %d -> %d\n",
472                    oldCp, cachePnt);
473
474            if ((descTail() >= cachePnt ? (descTail() - cachePnt) : (descLen() -
475                    cachePnt)) == 0)
476            {
477                igbe->anWe(annSmFetch, annUnusedDescQ);
478            } else if (!(size - usedCache.size() - unusedCache.size())) {
479                igbe->anWf(annSmFetch, annDescQ);
480            } else {
481                igbe->anBegin(annSmFetch, "Wait", CPA::FL_WAIT);
482            }
483
484            enableSm();
485            igbe->checkDrain();
486        }
487
488        EventWrapper<DescCache, &DescCache::fetchComplete> fetchEvent;
489
490        /** Called by event when dma to writeback descriptors is completed
491         */
492        void wbComplete()
493        {
494
495            igbe->anBegin(annSmWb, "Finish Writeback");
496
497            long  curHead = descHead();
498#ifndef NDEBUG
499            long oldHead = curHead;
500#endif
501
502            for (int x = 0; x < wbOut; x++) {
503                assert(usedCache.size());
504                delete usedCache[0];
505                usedCache.pop_front();
506
507                igbe->anDq(annSmWb, annUsedCacheQ);
508                igbe->anDq(annSmWb, annDescQ);
509            }
510
511            curHead += wbOut;
512            wbOut = 0;
513
514            if (curHead >= descLen())
515                curHead -= descLen();
516
517            // Update the head
518            updateHead(curHead);
519
520            DPRINTF(EthernetDesc, "Writeback complete curHead %d -> %d\n",
521                    oldHead, curHead);
522
523            // If we still have more to wb, call wb now
524            actionAfterWb();
525            if (moreToWb) {
526                moreToWb = false;
527                DPRINTF(EthernetDesc, "Writeback has more todo\n");
528                writeback(wbAlignment);
529            }
530
531            if (!wbOut) {
532                igbe->checkDrain();
533                if (usedCache.size())
534                    igbe->anBegin(annSmWb, "Wait", CPA::FL_WAIT);
535                else
536                    igbe->anWe(annSmWb, annUsedCacheQ);
537            }
538            fetchAfterWb();
539        }
540
541
542        EventWrapper<DescCache, &DescCache::wbComplete> wbEvent;
543
544        /* Return the number of descriptors left in the ring, so the device has
545         * a way to figure out if it needs to interrupt.
546         */
547        int descLeft() const
548        {
549            int left = unusedCache.size();
550            if (cachePnt >= descTail())
551                left += (descLen() - cachePnt + descTail());
552            else
553                left += (descTail() - cachePnt);
554
555            return left;
556        }
557
558        /* Return the number of descriptors used and not written back.
559         */
560        int descUsed() const { return usedCache.size(); }
561
562        /* Return the number of cache unused descriptors we have. */
563        int descUnused() const {return unusedCache.size(); }
564
565        /* Get into a state where the descriptor address/head/etc colud be
566         * changed */
567        void reset()
568        {
569            DPRINTF(EthernetDesc, "Reseting descriptor cache\n");
570            for (int x = 0; x < usedCache.size(); x++)
571                delete usedCache[x];
572            for (int x = 0; x < unusedCache.size(); x++)
573                delete unusedCache[x];
574
575            usedCache.clear();
576            unusedCache.clear();
577
578            cachePnt = 0;
579
580        }
581
582        virtual void serialize(std::ostream &os)
583        {
584            SERIALIZE_SCALAR(cachePnt);
585            SERIALIZE_SCALAR(curFetching);
586            SERIALIZE_SCALAR(wbOut);
587            SERIALIZE_SCALAR(moreToWb);
588            SERIALIZE_SCALAR(wbAlignment);
589
590            int usedCacheSize = usedCache.size();
591            SERIALIZE_SCALAR(usedCacheSize);
592            for(int x = 0; x < usedCacheSize; x++) {
593                arrayParamOut(os, csprintf("usedCache_%d", x),
594                        (uint8_t*)usedCache[x],sizeof(T));
595            }
596
597            int unusedCacheSize = unusedCache.size();
598            SERIALIZE_SCALAR(unusedCacheSize);
599            for(int x = 0; x < unusedCacheSize; x++) {
600                arrayParamOut(os, csprintf("unusedCache_%d", x),
601                        (uint8_t*)unusedCache[x],sizeof(T));
602            }
603
604            Tick fetch_delay = 0, wb_delay = 0;
605            if (fetchDelayEvent.scheduled())
606                fetch_delay = fetchDelayEvent.when();
607            SERIALIZE_SCALAR(fetch_delay);
608            if (wbDelayEvent.scheduled())
609                wb_delay = wbDelayEvent.when();
610            SERIALIZE_SCALAR(wb_delay);
611
612
613        }
614
615        virtual void unserialize(Checkpoint *cp, const std::string &section)
616        {
617            UNSERIALIZE_SCALAR(cachePnt);
618            UNSERIALIZE_SCALAR(curFetching);
619            UNSERIALIZE_SCALAR(wbOut);
620            UNSERIALIZE_SCALAR(moreToWb);
621            UNSERIALIZE_SCALAR(wbAlignment);
622
623            int usedCacheSize;
624            UNSERIALIZE_SCALAR(usedCacheSize);
625            T *temp;
626            for(int x = 0; x < usedCacheSize; x++) {
627                temp = new T;
628                arrayParamIn(cp, section, csprintf("usedCache_%d", x),
629                        (uint8_t*)temp,sizeof(T));
630                usedCache.push_back(temp);
631            }
632
633            int unusedCacheSize;
634            UNSERIALIZE_SCALAR(unusedCacheSize);
635            for(int x = 0; x < unusedCacheSize; x++) {
636                temp = new T;
637                arrayParamIn(cp, section, csprintf("unusedCache_%d", x),
638                        (uint8_t*)temp,sizeof(T));
639                unusedCache.push_back(temp);
640            }
641            Tick fetch_delay = 0, wb_delay = 0;
642            UNSERIALIZE_SCALAR(fetch_delay);
643            UNSERIALIZE_SCALAR(wb_delay);
644            if (fetch_delay)
645                igbe->schedule(fetchDelayEvent, fetch_delay);
646            if (wb_delay)
647                igbe->schedule(wbDelayEvent, wb_delay);
648
649
650        }
651        virtual bool hasOutstandingEvents() {
652            return wbEvent.scheduled() || fetchEvent.scheduled();
653        }
654
655     };
656
657
658    class RxDescCache : public DescCache<iGbReg::RxDesc>
659    {
660      protected:
661        virtual Addr descBase() const { return igbe->regs.rdba(); }
662        virtual long descHead() const { return igbe->regs.rdh(); }
663        virtual long descLen() const { return igbe->regs.rdlen() >> 4; }
664        virtual long descTail() const { return igbe->regs.rdt(); }
665        virtual void updateHead(long h) { igbe->regs.rdh(h); }
666        virtual void enableSm();
667        virtual void fetchAfterWb() {
668            if (!igbe->rxTick && igbe->getState() == SimObject::Running)
669                fetchDescriptors();
670        }
671
672        bool pktDone;
673
674        /** Variable to head with header/data completion events */
675        int splitCount;
676
677        /** Bytes of packet that have been copied, so we know when to set EOP */
678        int bytesCopied;
679
680      public:
681        RxDescCache(IGbE *i, std::string n, int s);
682
683        /** Write the given packet into the buffer(s) pointed to by the
684         * descriptor and update the book keeping. Should only be called when
685         * there are no dma's pending.
686         * @param packet ethernet packet to write
687         * @param pkt_offset bytes already copied from the packet to memory
688         * @return pkt_offset + number of bytes copied during this call
689         */
690        int writePacket(EthPacketPtr packet, int pkt_offset);
691
692        /** Called by event when dma to write packet is completed
693         */
694        void pktComplete();
695
696        /** Check if the dma on the packet has completed and RX state machine
697         * can continue
698         */
699        bool packetDone();
700
701        EventWrapper<RxDescCache, &RxDescCache::pktComplete> pktEvent;
702
703        // Event to handle issuing header and data write at the same time
704        // and only callking pktComplete() when both are completed
705        void pktSplitDone();
706        EventWrapper<RxDescCache, &RxDescCache::pktSplitDone> pktHdrEvent;
707        EventWrapper<RxDescCache, &RxDescCache::pktSplitDone> pktDataEvent;
708
709        virtual bool hasOutstandingEvents();
710
711        virtual void serialize(std::ostream &os);
712        virtual void unserialize(Checkpoint *cp, const std::string &section);
713    };
714    friend class RxDescCache;
715
716    RxDescCache rxDescCache;
717
718    class TxDescCache  : public DescCache<iGbReg::TxDesc>
719    {
720      protected:
721        virtual Addr descBase() const { return igbe->regs.tdba(); }
722        virtual long descHead() const { return igbe->regs.tdh(); }
723        virtual long descTail() const { return igbe->regs.tdt(); }
724        virtual long descLen() const { return igbe->regs.tdlen() >> 4; }
725        virtual void updateHead(long h) { igbe->regs.tdh(h); }
726        virtual void enableSm();
727        virtual void actionAfterWb();
728        virtual void fetchAfterWb() {
729            if (!igbe->txTick && igbe->getState() == SimObject::Running)
730                fetchDescriptors();
731        }
732
733
734
735        bool pktDone;
736        bool isTcp;
737        bool pktWaiting;
738        bool pktMultiDesc;
739        Addr completionAddress;
740        bool completionEnabled;
741        uint32_t descEnd;
742
743
744        // tso variables
745        bool useTso;
746        Addr tsoHeaderLen;
747        Addr tsoMss;
748        Addr tsoTotalLen;
749        Addr tsoUsedLen;
750        Addr tsoPrevSeq;;
751        Addr tsoPktPayloadBytes;
752        bool tsoLoadedHeader;
753        bool tsoPktHasHeader;
754        uint8_t tsoHeader[256];
755        Addr tsoDescBytesUsed;
756        Addr tsoCopyBytes;
757        int tsoPkts;
758
759      public:
760        TxDescCache(IGbE *i, std::string n, int s);
761
762        /** Tell the cache to DMA a packet from main memory into its buffer and
763         * return the size the of the packet to reserve space in tx fifo.
764         * @return size of the packet
765         */
766        int getPacketSize(EthPacketPtr p);
767        void getPacketData(EthPacketPtr p);
768        void processContextDesc();
769
770        /** Return the number of dsecriptors in a cache block for threshold
771         * operations.
772         */
773        int descInBlock(int num_desc) { return num_desc /
774            igbe->cacheBlockSize() / sizeof(iGbReg::TxDesc); }
775        /** Ask if the packet has been transfered so the state machine can give
776         * it to the fifo.
777         * @return packet available in descriptor cache
778         */
779        bool packetAvailable();
780
781        /** Ask if we are still waiting for the packet to be transfered.
782         * @return packet still in transit.
783         */
784        bool packetWaiting() { return pktWaiting; }
785
786        /** Ask if this packet is composed of multiple descriptors
787         * so even if we've got data, we need to wait for more before
788         * we can send it out.
789         * @return packet can't be sent out because it's a multi-descriptor
790         * packet
791         */
792        bool packetMultiDesc() { return pktMultiDesc;}
793
794        /** Called by event when dma to write packet is completed
795         */
796        void pktComplete();
797        EventWrapper<TxDescCache, &TxDescCache::pktComplete> pktEvent;
798
799        void headerComplete();
800        EventWrapper<TxDescCache, &TxDescCache::headerComplete> headerEvent;
801
802
803        void completionWriteback(Addr a, bool enabled) {
804            DPRINTF(EthernetDesc, "Completion writeback Addr: %#x enabled: %d\n",
805                    a, enabled);
806            completionAddress = a;
807            completionEnabled = enabled;
808        }
809
810        virtual bool hasOutstandingEvents();
811
812        void nullCallback() { DPRINTF(EthernetDesc, "Completion writeback complete\n"); }
813        EventWrapper<TxDescCache, &TxDescCache::nullCallback> nullEvent;
814
815        virtual void serialize(std::ostream &os);
816        virtual void unserialize(Checkpoint *cp, const std::string &section);
817
818    };
819    friend class TxDescCache;
820
821    TxDescCache txDescCache;
822
823  public:
824    typedef IGbEParams Params;
825    const Params *
826    params() const
827    {
828        return dynamic_cast<const Params *>(_params);
829    }
830    IGbE(const Params *params);
831    ~IGbE() {}
832    virtual void init();
833
834    virtual EtherInt *getEthPort(const std::string &if_name, int idx);
835
836    Tick clock;
837    Tick lastInterrupt;
838    inline Tick ticks(int numCycles) const { return numCycles * clock; }
839
840    virtual Tick read(PacketPtr pkt);
841    virtual Tick write(PacketPtr pkt);
842
843    virtual Tick writeConfig(PacketPtr pkt);
844
845    bool ethRxPkt(EthPacketPtr packet);
846    void ethTxDone();
847
848    virtual void serialize(std::ostream &os);
849    virtual void unserialize(Checkpoint *cp, const std::string &section);
850    virtual unsigned int drain(Event *de);
851    virtual void resume();
852
853};
854
855class IGbEInt : public EtherInt
856{
857  private:
858    IGbE *dev;
859
860  public:
861    IGbEInt(const std::string &name, IGbE *d)
862        : EtherInt(name), dev(d)
863    { }
864
865    virtual bool recvPacket(EthPacketPtr pkt) { return dev->ethRxPkt(pkt); }
866    virtual void sendDone() { dev->ethTxDone(); }
867};
868
869
870
871
872
873#endif //__DEV_I8254XGBE_HH__
874
875