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

Last change on this file 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.