source: trunk/modules/vci_ethernet_tsar/caba/source/src/vci_ethernet.cpp @ 528

Last change on this file since 528 was 528, checked in by lambert, 11 years ago

Introduicing VciEthernetTsar?
This component is modified version of VciEthernet? from soclib
Main differences are :

  • DMA access avoid overlapinging cache line boundaries
  • Pktid is now 0x4 in write cmd
  • IRQ can be reset with the status register
File size: 15.6 KB
Line 
1/* -*- c++ -*-
2 *
3 * SOCLIB_LGPL_HEADER_BEGIN
4 *
5 * This file is part of SoCLib, GNU LGPLv2.1.
6 *
7 * SoCLib is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; version 2.1 of the License.
10 *
11 * SoCLib is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with SoCLib; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301 USA
20 *
21 * SOCLIB_LGPL_HEADER_END
22 *
23 * Copyright (c) Telecom ParisTech
24 *         Alexandre Becoulet <alexandre.becoulet@enst.fr>, 2012
25 *
26 * Maintainers: becoulet
27 */
28
29#include <unistd.h>
30#include <stdint.h>
31#include <fcntl.h>
32#include <string.h>
33#include <errno.h>
34
35#include <iostream>
36
37#include "register.h"
38#include "../include/vci_ethernet.h"
39#include "ethernet.h"
40
41#define CHUNCK_SIZE (64) // Width of a cache line
42#define CHUNCK_MASK ((~0ULL) << (6)) // MASK of a cache line
43
44//#define SOCLIB_MODULE_DEBUG
45
46namespace soclib { namespace caba {
47
48#define tmpl(t) template<typename vci_param> t VciEthernet<vci_param>
49
50    tmpl(bool)::on_write(int seg, typename vci_param::addr_t addr, typename vci_param::data_t data, int be)
51    {
52        int cell = (int)addr / vci_param::B;
53
54#ifdef SOCLIB_MODULE_DEBUG
55        std::cout
56            << name()
57            << ": write config register "
58            << cell
59            << " with data 0x"
60            << std::hex << data
61            << std::dec
62            << std::endl;
63#endif
64
65        fifo_entry_t *f;
66
67        switch ((enum SoclibEthernetRegisters)cell)
68        {
69            case ETHERNET_TX_SIZE:
70                _tx_size = std::min((int)data, VCI_ETHERNET_MAX_PKT_SIZE);
71                return true;
72
73            case ETHERNET_TX_FIFO:
74                if (_tx_count >= VCI_ETHERNET_FIFO_SIZE) {
75                    std::cout << name() << ": TX fifo full, can not push more packets!" << std::endl;
76                    return false;
77                }
78
79                f = _tx_fifo + (_tx_start + _tx_count++) % VCI_ETHERNET_FIFO_SIZE;
80                _tx_waiting++;
81
82                f->addr = data;
83                f->size = _tx_size;
84                f->status = 0;
85                f->data = (uint8_t*)malloc(_tx_size);
86                return true;
87
88            case ETHERNET_RX_SIZE:
89                _rx_size = std::min((int)data, VCI_ETHERNET_MAX_PKT_SIZE);
90                return true;
91
92            case ETHERNET_RX_FIFO:
93                if (_rx_count >= VCI_ETHERNET_FIFO_SIZE) {
94                    std::cout << name() << ": RX fifo full, can not push more packet buffers!" << std::endl;
95                    return false;
96                }
97
98                f = _rx_fifo + (_rx_start + _rx_count++) % VCI_ETHERNET_FIFO_SIZE;
99                _rx_free++;
100
101                f->addr = data;
102                f->size = _rx_size;
103                f->status = 0;
104                f->data = (uint8_t*)malloc(_rx_size);
105                return true;
106
107            case ETHERNET_CTRL:
108                if (data & ETHERNET_CTRL_RESET)
109                    _soft_reset = true;
110                if (data & ETHERNET_CTRL_TX_IRQ)
111                    _tx_irq_en = true;
112                else
113                    _tx_irq_en = false;
114                if (data & ETHERNET_CTRL_RX_IRQ) 
115                    _rx_irq_en = true;
116                else
117                    _rx_irq_en = false;
118                if (data & ETHERNET_CTRL_LINK_IRQ)
119                    _link_irq_en = true;
120                else
121                    _link_irq_en = false;
122                return true;
123
124            default:
125                return false;
126        }
127    }
128
129    tmpl(bool)::on_read(int seg, typename vci_param::addr_t addr, typename vci_param::data_t &data)
130    {
131        int cell = (int)addr / vci_param::B;
132
133#ifdef SOCLIB_MODULE_DEBUG
134        std::cout
135            << name()
136            << ": read config register "
137            << cell
138            << std::endl;
139#endif
140
141        fifo_entry_t *f;
142
143        switch ((enum SoclibEthernetRegisters)cell) {
144            case ETHERNET_TX_FIFO:
145                if (_tx_done == 0) {
146                    data = 0;
147                } else {
148                    f = _tx_fifo + _tx_start++ % VCI_ETHERNET_FIFO_SIZE;
149                    _tx_count--;
150                    _tx_done--;
151                    data = f->status;
152                    free(f->data);
153                }
154                return true;
155
156            case ETHERNET_RX_SIZE:
157                if (_rx_done == 0) {
158                    data = 0;
159                } else {
160                    f = _rx_fifo + _rx_start % VCI_ETHERNET_FIFO_SIZE;
161                    data = f->size;
162                }
163                return true;
164
165            case ETHERNET_RX_FIFO:
166                if (_rx_done == 0) {
167                    data = 0;
168                } else {
169                    f = _rx_fifo + _rx_start++ % VCI_ETHERNET_FIFO_SIZE;
170                    _rx_count--;
171                    _rx_done--;
172                    free(f->data);
173                    data = f->status;
174                }
175                return true;
176
177            case ETHERNET_STATUS:
178                data = 0;
179                _link_changed = false;
180                if (_link_up)
181                    data |= ETHERNET_ST_LINK_UP;
182                if (_rx_done > 0)
183                    data |= ETHERNET_ST_RX_DONE;
184                if (_tx_done > 0)
185                    data |= ETHERNET_ST_TX_DONE;
186                return true;
187
188            case ETHERNET_FIFO_SIZE:
189                data = VCI_ETHERNET_FIFO_SIZE;
190                return true;
191
192            case ETHERNET_MAC_LOW:
193                data = _mac[0] | (_mac[1] << 8) | (_mac[2] << 16) | (_mac[3] << 24);
194                return true;
195
196            case ETHERNET_MAC_HIGH:
197                data = _mac[4] | (_mac[5] << 8);
198                return true;
199
200            default:
201                return false;
202        }
203    }
204
205    tmpl(void)::write_finish( req_t *req )
206    {
207        fifo_entry_t *f = _rx_fifo + (_rx_start + _rx_count - _rx_free - 1) % VCI_ETHERNET_FIFO_SIZE;
208
209        assert(req == f->rd_req);
210        delete req;
211
212        if (req->failed()) {
213            std::cout << name() << ": RX dma write error for packet @" << f->addr << std::endl;
214            f->status = ETHERNET_RX_DMA_ERR;
215            _rx_done++;
216            _dma_busy = false;
217
218        } else if ((unsigned)f->dma_offset >= f->size) {
219#ifdef SOCLIB_MODULE_DEBUG
220            std::cout << name() << ": RX dma done for packet @" << f->addr << std::endl;
221#endif
222            f->status = ETHERNET_RX_DONE;
223            _rx_done++;
224            _dma_busy = false;
225
226        } else {
227            int chunck_size = std::min((unsigned)CHUNCK_SIZE, f->size - f->dma_offset);
228            f->wr_req = new VciInitSimpleWriteReq<vci_param>(f->addr + f->dma_offset, f->data + f->dma_offset, chunck_size);
229            f->dma_offset += chunck_size;
230            f->wr_req->setDone(this, ON_T(write_finish));
231            f->wr_req->setPacket(0x4);
232            m_vci_init_fsm.doReq(f->wr_req);
233        }
234    }
235
236    tmpl(void)::read_finish( req_t *req )
237    {
238        fifo_entry_t *f = _tx_fifo + (_tx_start + _tx_count - _tx_waiting - 1) % VCI_ETHERNET_FIFO_SIZE;
239
240        assert(req == f->rd_req);
241        delete req;
242
243        if (req->failed()) {
244            std::cout << name() << ": TX dma read error for packet @" << f->addr << std::endl;
245            f->status = ETHERNET_TX_DMA_ERR;
246            _tx_done++;
247            _dma_busy = false;
248
249        } else if ((unsigned)f->dma_offset >= f->size) {
250#ifdef SOCLIB_MODULE_DEBUG
251            std::cout << name() << ": TX dma done for packet @" << f->addr << std::endl;
252#endif
253            f->status = ETHERNET_TX_DONE;
254
255            if (!_link_up || ::write(_fd, f->data, f->size) != f->size) {
256                std::cout << name() << ": TX tap write error for packet @" << f->addr << std::endl;
257                f->status = ETHERNET_TX_PHY_ERR;
258                _link_check_counter = 1;
259            }
260
261            _tx_done++;
262            _dma_busy = false;
263
264        } else {
265            int chunck_size = std::min((unsigned)CHUNCK_SIZE, f->size - f->dma_offset);
266            f->rd_req = new VciInitSimpleReadReq<vci_param>(f->data + f->dma_offset, f->addr + f->dma_offset, chunck_size);
267            f->dma_offset += chunck_size;
268            f->rd_req->setDone(this, ON_T(read_finish));
269            m_vci_init_fsm.doReq(f->rd_req);
270        }
271    }
272
273    tmpl(void)::cleanup_fifos()
274    {
275        for (int i = 0; i < _rx_count; i++) {
276            fifo_entry_t *f = _rx_fifo + (_rx_start + i) % VCI_ETHERNET_FIFO_SIZE;
277            free(f->data);
278        }
279
280        for (int i = 0; i < _tx_count; i++) {
281            fifo_entry_t *f = _tx_fifo + (_tx_start + i) % VCI_ETHERNET_FIFO_SIZE;
282            free(f->data);
283        }
284    }
285
286    tmpl(void)::transition()
287    {
288        if (!p_resetn || _soft_reset) {
289            m_vci_target_fsm.reset();
290            m_vci_init_fsm.reset();
291            _soft_reset = false;
292
293            cleanup_fifos();
294
295            _rx_start = _rx_free = _rx_done = _rx_count = 0;
296            _tx_start = _tx_waiting = _tx_done = _tx_count = 0;
297            _rx_irq_en = _tx_irq_en = _link_irq_en = false;       
298            _link_check_counter = 1;
299            _link_changed = _link_up = false;
300            _dma_busy = false;
301            return;
302        }
303
304        if (_fd >= 0 && --_link_check_counter <= 0) {
305            _link_check_counter = 100000;
306
307            int sock = socket(AF_INET, SOCK_DGRAM, 0);
308
309            if (sock >= 0) {
310                if (ioctl(sock, SIOCGIFFLAGS, &_tap_ifr) < 0) {
311                    std::cout << name() << ": link status check error: " << _tap_ifr.ifr_flags << std::endl;
312                } else if (_link_up != !!(_tap_ifr.ifr_flags & IFF_UP)) {
313                    _link_up = !_link_up;
314                    _link_changed = true;
315#ifdef SOCLIB_MODULE_DEBUG
316                    std::cout << name() << ": link status changed to: " << (_link_up ? "up" : "down") << std::endl;
317#endif
318                }
319                close(sock);
320            }
321        }
322
323        if (!_dma_busy) {
324
325            if (_tx_waiting > 0) {
326
327                fifo_entry_t *f = _tx_fifo + (_tx_start + _tx_count - _tx_waiting--) % VCI_ETHERNET_FIFO_SIZE;
328
329                // start DMA read
330                int chunck_size = std::min((unsigned)CHUNCK_SIZE, f->size);
331                if ((f->addr & CHUNCK_MASK) != ((f->addr + chunck_size) & CHUNCK_MASK)) {
332                    chunck_size = ((f->addr & CHUNCK_MASK) + CHUNCK_SIZE) - f->addr;
333                }
334                f->rd_req = new VciInitSimpleReadReq<vci_param>(f->data, f->addr, chunck_size);
335                f->dma_offset = chunck_size;
336                f->rd_req->setDone(this, ON_T(read_finish));
337                m_vci_init_fsm.doReq(f->rd_req);
338                _dma_busy = true;
339
340#ifdef SOCLIB_MODULE_DEBUG
341                std::cout << name() << ": started TX dma read @" << f->addr << ", " << f->size << " bytes " << std::endl;
342#endif
343            } else if (_rx_free > 0 && _link_up) {
344
345                fifo_entry_t *f = _rx_fifo + (_rx_start + _rx_count - _rx_free) % VCI_ETHERNET_FIFO_SIZE;
346                int rd = ::read(_fd, f->data, f->size);
347
348                if (rd < 0 && errno != EAGAIN) {
349                    f->status = ETHERNET_RX_PHY_ERR;
350                    _rx_done++;
351                    std::cout << name() << ": tap read error on RX" << std::endl;
352                    _link_check_counter = 1;
353
354                }  else if (rd > 0) {
355                    if (_rx_free > 0) {
356                        f->size = rd;
357                        _rx_free--;
358
359                        // start DMA write
360                        int chunck_size = std::min((unsigned)CHUNCK_SIZE, f->size);
361                        if ((f->addr & CHUNCK_MASK) != ((f->addr + chunck_size) & CHUNCK_MASK)) {
362                            chunck_size = ((f->addr & CHUNCK_MASK) + CHUNCK_SIZE) - f->addr;
363                        }
364                        f->wr_req = new VciInitSimpleWriteReq<vci_param>(f->addr, f->data, chunck_size);
365                        f->dma_offset = chunck_size;
366                        f->wr_req->setDone(this, ON_T(write_finish));
367                        f->wr_req->setPacket(0x4);
368                        m_vci_init_fsm.doReq(f->wr_req);
369                        _dma_busy = true;
370
371#ifdef SOCLIB_MODULE_DEBUG
372                        std::cout << name() << ": started RX dma write @" << f->addr << ", " << f->size << " bytes " << std::endl;
373#endif
374                    }
375                    else {
376                        std::cout << name() << ": No rx buffer free so paquet is dropped" << std::endl;
377                    }
378                }
379            }
380        }
381
382        m_vci_target_fsm.transition();
383        m_vci_init_fsm.transition();
384    }
385
386    tmpl(void)::genMoore()
387    {
388        m_vci_target_fsm.genMoore();
389        m_vci_init_fsm.genMoore();
390
391        p_irq = (_rx_irq_en && _rx_done > 0) || (_tx_irq_en && _tx_done > 0) || (_link_changed && _link_irq_en);
392    }
393
394    tmpl(/**/)::VciEthernet(sc_module_name name, const MappingTable &mt,
395            const IntTab &srcid, const IntTab &tgtid,
396            const std::string &if_name)
397        : caba::BaseModule(name),
398        m_vci_target_fsm(p_vci_target, mt.getSegmentList(tgtid)),
399        m_vci_init_fsm(p_vci_initiator, mt.indexForId(srcid)),
400        p_clk("clk"),
401        p_resetn("resetn"),
402        p_vci_target("vci_target"),
403        p_vci_initiator("vci_initiator"),
404        p_irq("irq")
405    {
406        m_vci_target_fsm.on_read_write(on_read, on_write);
407        _fd = open("/dev/net/tun", O_RDWR);
408
409        if ( _fd < 0 ) {
410            std::cerr << name << ": Unable to open /dev/net/tun" << std::endl;
411        } else {
412            int flags = fcntl(_fd, F_GETFL, 0);
413            fcntl(_fd, F_SETFL, flags | O_NONBLOCK);
414
415            memset((void*)&_tap_ifr, 0, sizeof(_tap_ifr));
416            _tap_ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
417            strncpy(_tap_ifr.ifr_name, if_name.c_str(), IFNAMSIZ);
418
419
420            if (ioctl(_fd, TUNSETIFF, (void *) &_tap_ifr) < 0) {
421                close(_fd);
422                _fd = -1;
423                std::cerr << name << ": Unable to setup tap interface, check privileges."
424#ifdef __linux__
425                    << " (try: sudo setcap cap_net_admin=eip ./system.x)"
426#endif
427                    << std::endl;
428            }
429            flags = 2;
430            if (ioctl(_fd, TUNSETDEBUG, (void *) &flags) < 0) {
431                std::cerr << "Warning couldn't use debug option for TAP" << std::endl;
432            }
433
434        /*    ioctl(_fd, SIOCGIFHWADDR, &_tap_ifr);
435            memcpy(_mac, _tap_ifr.ifr_hwaddr.sa_data, 6);*/
436              srand(time(0) * getpid());
437                _mac[0] = 0x00;
438                _mac[1] = 0x16;
439                _mac[2] = 0x3e;
440                _mac[3] = rand();
441                _mac[4] = rand();
442                _mac[5] = rand();
443        }
444
445        _link_check_counter = 1;
446        _rx_start = _rx_free = _rx_done = _rx_count = 0;
447        _tx_start = _tx_waiting = _tx_done = _tx_count = 0;
448        _dma_busy = false;
449        _link_up = false;
450
451        SC_METHOD(transition);
452        dont_initialize();
453        sensitive << p_clk.pos();
454
455        SC_METHOD(genMoore);
456        dont_initialize();
457        sensitive << p_clk.neg();
458    }
459
460    tmpl(/**/)::~VciEthernet()
461    {
462        cleanup_fifos();
463
464        if (_fd >= 0)
465            close(_fd);
466    }
467
468}}
469
470// Local Variables:
471// tab-width: 4
472// c-basic-offset: 4
473// c-file-offsets:((innamespace . 0)(inline-open . 0))
474// indent-tabs-mode: nil
475// End:
476
477// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
478
Note: See TracBrowser for help on using the repository browser.