summaryrefslogtreecommitdiff
path: root/src/dev/net
diff options
context:
space:
mode:
authorAndreas Sandberg <andreas.sandberg@arm.com>2015-12-10 10:35:18 +0000
committerAndreas Sandberg <andreas.sandberg@arm.com>2015-12-10 10:35:18 +0000
commit23c961a0fd97251ee7c760bc2ff2a011a417ad9b (patch)
tree0481b7552303373cd3722156b1cef92d813de215 /src/dev/net
parentef097ac4381ad2f14acda640ae198f04d9f7988d (diff)
downloadgem5-23c961a0fd97251ee7c760bc2ff2a011a417ad9b.tar.xz
dev: Move network devices to src/dev/net/
--HG-- rename : src/dev/Ethernet.py => src/dev/net/Ethernet.py rename : src/dev/etherbus.cc => src/dev/net/etherbus.cc rename : src/dev/etherbus.hh => src/dev/net/etherbus.hh rename : src/dev/etherdevice.cc => src/dev/net/etherdevice.cc rename : src/dev/etherdevice.hh => src/dev/net/etherdevice.hh rename : src/dev/etherdump.cc => src/dev/net/etherdump.cc rename : src/dev/etherdump.hh => src/dev/net/etherdump.hh rename : src/dev/etherint.cc => src/dev/net/etherint.cc rename : src/dev/etherint.hh => src/dev/net/etherint.hh rename : src/dev/etherlink.cc => src/dev/net/etherlink.cc rename : src/dev/etherlink.hh => src/dev/net/etherlink.hh rename : src/dev/etherobject.hh => src/dev/net/etherobject.hh rename : src/dev/etherpkt.cc => src/dev/net/etherpkt.cc rename : src/dev/etherpkt.hh => src/dev/net/etherpkt.hh rename : src/dev/ethertap.cc => src/dev/net/ethertap.cc rename : src/dev/ethertap.hh => src/dev/net/ethertap.hh rename : src/dev/i8254xGBe.cc => src/dev/net/i8254xGBe.cc rename : src/dev/i8254xGBe.hh => src/dev/net/i8254xGBe.hh rename : src/dev/i8254xGBe_defs.hh => src/dev/net/i8254xGBe_defs.hh rename : src/dev/multi_etherlink.cc => src/dev/net/multi_etherlink.cc rename : src/dev/multi_etherlink.hh => src/dev/net/multi_etherlink.hh rename : src/dev/multi_iface.cc => src/dev/net/multi_iface.cc rename : src/dev/multi_iface.hh => src/dev/net/multi_iface.hh rename : src/dev/multi_packet.cc => src/dev/net/multi_packet.cc rename : src/dev/multi_packet.hh => src/dev/net/multi_packet.hh rename : src/dev/ns_gige.cc => src/dev/net/ns_gige.cc rename : src/dev/ns_gige.hh => src/dev/net/ns_gige.hh rename : src/dev/ns_gige_reg.h => src/dev/net/ns_gige_reg.h rename : src/dev/pktfifo.cc => src/dev/net/pktfifo.cc rename : src/dev/pktfifo.hh => src/dev/net/pktfifo.hh rename : src/dev/sinic.cc => src/dev/net/sinic.cc rename : src/dev/sinic.hh => src/dev/net/sinic.hh rename : src/dev/sinicreg.hh => src/dev/net/sinicreg.hh rename : src/dev/tcp_iface.cc => src/dev/net/tcp_iface.cc rename : src/dev/tcp_iface.hh => src/dev/net/tcp_iface.hh
Diffstat (limited to 'src/dev/net')
-rw-r--r--src/dev/net/Ethernet.py245
-rw-r--r--src/dev/net/SConscript94
-rw-r--r--src/dev/net/etherbus.cc113
-rw-r--r--src/dev/net/etherbus.hh90
-rw-r--r--src/dev/net/etherdevice.cc369
-rw-r--r--src/dev/net/etherdevice.hh152
-rw-r--r--src/dev/net/etherdump.cc110
-rw-r--r--src/dev/net/etherdump.hh62
-rw-r--r--src/dev/net/etherint.cc44
-rw-r--r--src/dev/net/etherint.hh76
-rw-r--r--src/dev/net/etherlink.cc280
-rw-r--r--src/dev/net/etherlink.hh166
-rw-r--r--src/dev/net/etherobject.hh67
-rw-r--r--src/dev/net/etherpkt.cc71
-rw-r--r--src/dev/net/etherpkt.hh95
-rw-r--r--src/dev/net/ethertap.cc348
-rw-r--r--src/dev/net/ethertap.hh136
-rw-r--r--src/dev/net/i8254xGBe.cc2562
-rw-r--r--src/dev/net/i8254xGBe.hh560
-rw-r--r--src/dev/net/i8254xGBe_defs.hh854
-rw-r--r--src/dev/net/multi_etherlink.cc266
-rw-r--r--src/dev/net/multi_etherlink.hh235
-rw-r--r--src/dev/net/multi_iface.cc622
-rw-r--r--src/dev/net/multi_iface.hh492
-rw-r--r--src/dev/net/multi_packet.cc100
-rw-r--r--src/dev/net/multi_packet.hh130
-rw-r--r--src/dev/net/ns_gige.cc2483
-rw-r--r--src/dev/net/ns_gige.hh392
-rw-r--r--src/dev/net/ns_gige_reg.h401
-rw-r--r--src/dev/net/pktfifo.cc116
-rw-r--r--src/dev/net/pktfifo.hh212
-rw-r--r--src/dev/net/sinic.cc1563
-rw-r--r--src/dev/net/sinic.hh327
-rw-r--r--src/dev/net/sinicreg.hh239
-rw-r--r--src/dev/net/tcp_iface.cc158
-rw-r--r--src/dev/net/tcp_iface.hh133
36 files changed, 14363 insertions, 0 deletions
diff --git a/src/dev/net/Ethernet.py b/src/dev/net/Ethernet.py
new file mode 100644
index 000000000..9859857a0
--- /dev/null
+++ b/src/dev/net/Ethernet.py
@@ -0,0 +1,245 @@
+# Copyright (c) 2015 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Copyright (c) 2005-2007 The Regents of The University of Michigan
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: Nathan Binkert
+
+from m5.SimObject import SimObject
+from m5.params import *
+from m5.proxy import *
+from PciDevice import PciDevice
+
+class EtherObject(SimObject):
+ type = 'EtherObject'
+ abstract = True
+ cxx_header = "dev/net/etherobject.hh"
+
+class EtherLink(EtherObject):
+ type = 'EtherLink'
+ cxx_header = "dev/net/etherlink.hh"
+ int0 = SlavePort("interface 0")
+ int1 = SlavePort("interface 1")
+ delay = Param.Latency('0us', "packet transmit delay")
+ delay_var = Param.Latency('0ns', "packet transmit delay variability")
+ speed = Param.NetworkBandwidth('1Gbps', "link speed")
+ dump = Param.EtherDump(NULL, "dump object")
+
+class MultiEtherLink(EtherObject):
+ type = 'MultiEtherLink'
+ cxx_header = "dev/net/multi_etherlink.hh"
+ int0 = SlavePort("interface 0")
+ delay = Param.Latency('0us', "packet transmit delay")
+ delay_var = Param.Latency('0ns', "packet transmit delay variability")
+ speed = Param.NetworkBandwidth('1Gbps', "link speed")
+ dump = Param.EtherDump(NULL, "dump object")
+ multi_rank = Param.UInt32('0', "Rank of the this gem5 process (multi run)")
+ sync_start = Param.Latency('5200000000000t', "first multi sync barrier")
+ sync_repeat = Param.Latency('10us', "multi sync barrier repeat")
+ server_name = Param.String('localhost', "Message server name")
+ server_port = Param.UInt32('2200', "Message server port")
+
+class EtherBus(EtherObject):
+ type = 'EtherBus'
+ cxx_header = "dev/net/etherbus.hh"
+ loopback = Param.Bool(True, "send packet back to the sending interface")
+ dump = Param.EtherDump(NULL, "dump object")
+ speed = Param.NetworkBandwidth('100Mbps', "bus speed in bits per second")
+
+class EtherTap(EtherObject):
+ type = 'EtherTap'
+ cxx_header = "dev/net/ethertap.hh"
+ bufsz = Param.Int(10000, "tap buffer size")
+ dump = Param.EtherDump(NULL, "dump object")
+ port = Param.UInt16(3500, "tap port")
+
+class EtherDump(SimObject):
+ type = 'EtherDump'
+ cxx_header = "dev/net/etherdump.hh"
+ file = Param.String("dump file")
+ maxlen = Param.Int(96, "max portion of packet data to dump")
+
+class EtherDevice(PciDevice):
+ type = 'EtherDevice'
+ abstract = True
+ cxx_header = "dev/net/etherdevice.hh"
+ interface = MasterPort("Ethernet Interface")
+
+class IGbE(EtherDevice):
+ # Base class for two IGbE adapters listed above
+ type = 'IGbE'
+ cxx_header = "dev/net/i8254xGBe.hh"
+ hardware_address = Param.EthernetAddr(NextEthernetAddr,
+ "Ethernet Hardware Address")
+ rx_fifo_size = Param.MemorySize('384kB', "Size of the rx FIFO")
+ tx_fifo_size = Param.MemorySize('384kB', "Size of the tx FIFO")
+ rx_desc_cache_size = Param.Int(64,
+ "Number of enteries in the rx descriptor cache")
+ tx_desc_cache_size = Param.Int(64,
+ "Number of enteries in the rx descriptor cache")
+ VendorID = 0x8086
+ SubsystemID = 0x1008
+ SubsystemVendorID = 0x8086
+ Status = 0x0000
+ SubClassCode = 0x00
+ ClassCode = 0x02
+ ProgIF = 0x00
+ BAR0 = 0x00000000
+ BAR1 = 0x00000000
+ BAR2 = 0x00000000
+ BAR3 = 0x00000000
+ BAR4 = 0x00000000
+ BAR5 = 0x00000000
+ MaximumLatency = 0x00
+ MinimumGrant = 0xff
+ InterruptLine = 0x1e
+ InterruptPin = 0x01
+ BAR0Size = '128kB'
+ wb_delay = Param.Latency('10ns', "delay before desc writeback occurs")
+ fetch_delay = Param.Latency('10ns', "delay before desc fetch occurs")
+ fetch_comp_delay = Param.Latency('10ns', "delay after desc fetch occurs")
+ wb_comp_delay = Param.Latency('10ns', "delay after desc wb occurs")
+ tx_read_delay = Param.Latency('0ns', "delay after tx dma read")
+ rx_write_delay = Param.Latency('0ns', "delay after rx dma read")
+ phy_pid = Param.UInt16("Phy PID that corresponds to device ID")
+ phy_epid = Param.UInt16("Phy EPID that corresponds to device ID")
+
+class IGbE_e1000(IGbE):
+ # Older Intel 8254x based gigabit ethernet adapter
+ # Uses Intel e1000 driver
+ DeviceID = 0x1075
+ phy_pid = 0x02A8
+ phy_epid = 0x0380
+
+class IGbE_igb(IGbE):
+ # Newer Intel 8257x based gigabit ethernet adapter
+ # Uses Intel igb driver and in theory supports packet splitting and LRO
+ DeviceID = 0x10C9
+ phy_pid = 0x0141
+ phy_epid = 0x0CC0
+
+class EtherDevBase(EtherDevice):
+ type = 'EtherDevBase'
+ abstract = True
+ cxx_header = "dev/net/etherdevice.hh"
+
+ hardware_address = Param.EthernetAddr(NextEthernetAddr,
+ "Ethernet Hardware Address")
+
+ dma_read_delay = Param.Latency('0us', "fixed delay for dma reads")
+ dma_read_factor = Param.Latency('0us', "multiplier for dma reads")
+ dma_write_delay = Param.Latency('0us', "fixed delay for dma writes")
+ dma_write_factor = Param.Latency('0us', "multiplier for dma writes")
+
+ rx_delay = Param.Latency('1us', "Receive Delay")
+ tx_delay = Param.Latency('1us', "Transmit Delay")
+ rx_fifo_size = Param.MemorySize('512kB', "max size of rx fifo")
+ tx_fifo_size = Param.MemorySize('512kB', "max size of tx fifo")
+
+ rx_filter = Param.Bool(True, "Enable Receive Filter")
+ intr_delay = Param.Latency('10us', "Interrupt propagation delay")
+ rx_thread = Param.Bool(False, "dedicated kernel thread for transmit")
+ tx_thread = Param.Bool(False, "dedicated kernel threads for receive")
+ rss = Param.Bool(False, "Receive Side Scaling")
+
+class NSGigE(EtherDevBase):
+ type = 'NSGigE'
+ cxx_header = "dev/net/ns_gige.hh"
+
+ dma_data_free = Param.Bool(False, "DMA of Data is free")
+ dma_desc_free = Param.Bool(False, "DMA of Descriptors is free")
+ dma_no_allocate = Param.Bool(True, "Should we allocate cache on read")
+
+ VendorID = 0x100B
+ DeviceID = 0x0022
+ Status = 0x0290
+ SubClassCode = 0x00
+ ClassCode = 0x02
+ ProgIF = 0x00
+ BAR0 = 0x00000001
+ BAR1 = 0x00000000
+ BAR2 = 0x00000000
+ BAR3 = 0x00000000
+ BAR4 = 0x00000000
+ BAR5 = 0x00000000
+ MaximumLatency = 0x34
+ MinimumGrant = 0xb0
+ InterruptLine = 0x1e
+ InterruptPin = 0x01
+ BAR0Size = '256B'
+ BAR1Size = '4kB'
+
+
+
+class Sinic(EtherDevBase):
+ type = 'Sinic'
+ cxx_class = 'Sinic::Device'
+ cxx_header = "dev/net/sinic.hh"
+
+ rx_max_copy = Param.MemorySize('1514B', "rx max copy")
+ tx_max_copy = Param.MemorySize('16kB', "tx max copy")
+ rx_max_intr = Param.UInt32(10, "max rx packets per interrupt")
+ rx_fifo_threshold = Param.MemorySize('384kB', "rx fifo high threshold")
+ rx_fifo_low_mark = Param.MemorySize('128kB', "rx fifo low threshold")
+ tx_fifo_high_mark = Param.MemorySize('384kB', "tx fifo high threshold")
+ tx_fifo_threshold = Param.MemorySize('128kB', "tx fifo low threshold")
+ virtual_count = Param.UInt32(1, "Virtualized SINIC")
+ zero_copy_size = Param.UInt32(64, "Bytes to copy if below threshold")
+ zero_copy_threshold = Param.UInt32(256,
+ "Only zero copy above this threshold")
+ zero_copy = Param.Bool(False, "Zero copy receive")
+ delay_copy = Param.Bool(False, "Delayed copy transmit")
+ virtual_addr = Param.Bool(False, "Virtual addressing")
+
+ VendorID = 0x1291
+ DeviceID = 0x1293
+ Status = 0x0290
+ SubClassCode = 0x00
+ ClassCode = 0x02
+ ProgIF = 0x00
+ BAR0 = 0x00000000
+ BAR1 = 0x00000000
+ BAR2 = 0x00000000
+ BAR3 = 0x00000000
+ BAR4 = 0x00000000
+ BAR5 = 0x00000000
+ MaximumLatency = 0x34
+ MinimumGrant = 0xb0
+ InterruptLine = 0x1e
+ InterruptPin = 0x01
+ BAR0Size = '64kB'
+
+
diff --git a/src/dev/net/SConscript b/src/dev/net/SConscript
new file mode 100644
index 000000000..f529a1b2a
--- /dev/null
+++ b/src/dev/net/SConscript
@@ -0,0 +1,94 @@
+# -*- mode:python -*-
+
+# Copyright (c) 2015 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Copyright (c) 2006 The Regents of The University of Michigan
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: Steve Reinhardt
+# Gabe Black
+# Andreas Sandberg
+
+Import('*')
+
+if env['TARGET_ISA'] == 'null':
+ Return()
+
+SimObject('Ethernet.py')
+
+# Basic Ethernet infrastructure
+Source('etherbus.cc')
+Source('etherdevice.cc')
+Source('etherdump.cc')
+Source('etherint.cc')
+Source('etherlink.cc')
+Source('etherpkt.cc')
+Source('ethertap.cc')
+
+Source('pktfifo.cc')
+
+DebugFlag('Ethernet')
+DebugFlag('EthernetCksum')
+DebugFlag('EthernetDMA')
+DebugFlag('EthernetData')
+DebugFlag('EthernetDesc')
+DebugFlag('EthernetEEPROM')
+DebugFlag('EthernetIntr')
+DebugFlag('EthernetPIO')
+DebugFlag('EthernetSM')
+
+# Multi gem5
+Source('multi_packet.cc')
+Source('multi_iface.cc')
+Source('multi_etherlink.cc')
+Source('tcp_iface.cc')
+
+DebugFlag('MultiEthernet')
+DebugFlag('MultiEthernetPkt')
+
+# Ethernet controllers
+Source('i8254xGBe.cc')
+Source('ns_gige.cc')
+Source('sinic.cc')
+
+
+
+CompoundFlag('EthernetAll', [ 'Ethernet', 'EthernetPIO', 'EthernetDMA',
+ 'EthernetData' , 'EthernetDesc', 'EthernetIntr', 'EthernetSM',
+ 'EthernetCksum', 'EthernetEEPROM' ])
+
+CompoundFlag('EthernetNoData', [ 'Ethernet', 'EthernetPIO', 'EthernetDesc',
+ 'EthernetIntr', 'EthernetSM', 'EthernetCksum' ])
diff --git a/src/dev/net/etherbus.cc b/src/dev/net/etherbus.cc
new file mode 100644
index 000000000..ba5beab01
--- /dev/null
+++ b/src/dev/net/etherbus.cc
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+/* @file
+ * Device module for modelling an ethernet hub
+ */
+#include "dev/net/etherbus.hh"
+
+#include <cmath>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "base/trace.hh"
+#include "debug/Ethernet.hh"
+#include "debug/EthernetData.hh"
+#include "dev/net/etherdump.hh"
+#include "dev/net/etherint.hh"
+#include "dev/net/etherpkt.hh"
+#include "params/EtherBus.hh"
+#include "sim/core.hh"
+
+using namespace std;
+
+EtherBus::EtherBus(const Params *p)
+ : EtherObject(p), ticksPerByte(p->speed), loopback(p->loopback),
+ event(this), sender(0), dump(p->dump)
+{
+}
+
+void
+EtherBus::txDone()
+{
+ devlist_t::iterator i = devlist.begin();
+ devlist_t::iterator end = devlist.end();
+
+ DPRINTF(Ethernet, "ethernet packet received: length=%d\n", packet->length);
+ DDUMP(EthernetData, packet->data, packet->length);
+
+ while (i != end) {
+ if (loopback || *i != sender)
+ (*i)->sendPacket(packet);
+ ++i;
+ }
+
+ sender->sendDone();
+
+ if (dump)
+ dump->dump(packet);
+
+ sender = 0;
+ packet = 0;
+}
+
+EtherInt*
+EtherBus::getEthPort(const std::string &if_name, int idx)
+{
+ panic("Etherbus doesn't work\n");
+}
+
+bool
+EtherBus::send(EtherInt *sndr, EthPacketPtr &pkt)
+{
+ if (busy()) {
+ DPRINTF(Ethernet, "ethernet packet not sent, bus busy\n", curTick());
+ return false;
+ }
+
+ DPRINTF(Ethernet, "ethernet packet sent: length=%d\n", pkt->length);
+ DDUMP(EthernetData, pkt->data, pkt->length);
+
+ packet = pkt;
+ sender = sndr;
+ int delay = (int)ceil(((double)pkt->length * ticksPerByte) + 1.0);
+ DPRINTF(Ethernet, "scheduling packet: delay=%d, (rate=%f)\n",
+ delay, ticksPerByte);
+ schedule(event, curTick() + delay);
+
+ return true;
+}
+
+EtherBus *
+EtherBusParams::create()
+{
+ return new EtherBus(this);
+}
diff --git a/src/dev/net/etherbus.hh b/src/dev/net/etherbus.hh
new file mode 100644
index 000000000..7395c28d8
--- /dev/null
+++ b/src/dev/net/etherbus.hh
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+/* @file
+ * Device module for modelling an ethernet hub
+ */
+
+#ifndef __DEV_NET_ETHERBUS_HH__
+#define __DEV_NET_ETHERBUS_HH__
+
+#include "dev/net/etherobject.hh"
+#include "dev/net/etherpkt.hh"
+#include "params/EtherBus.hh"
+#include "sim/eventq.hh"
+#include "sim/sim_object.hh"
+
+class EtherDump;
+class EtherInt;
+class EtherBus : public EtherObject
+{
+ protected:
+ typedef std::list<EtherInt *> devlist_t;
+ devlist_t devlist;
+ double ticksPerByte;
+ bool loopback;
+
+ protected:
+ class DoneEvent : public Event
+ {
+ protected:
+ EtherBus *bus;
+
+ public:
+ DoneEvent(EtherBus *b) : bus(b) {}
+ virtual void process() { bus->txDone(); }
+ virtual const char *description() const
+ { return "ethernet bus completion"; }
+ };
+
+ DoneEvent event;
+ EthPacketPtr packet;
+ EtherInt *sender;
+ EtherDump *dump;
+
+ public:
+ typedef EtherBusParams Params;
+ EtherBus(const Params *p);
+ virtual ~EtherBus() {}
+
+ const Params *
+ params() const
+ {
+ return dynamic_cast<const Params *>(_params);
+ }
+
+ void txDone();
+ void reg(EtherInt *dev);
+ bool busy() const { return (bool)packet; }
+ bool send(EtherInt *sender, EthPacketPtr &packet);
+ virtual EtherInt *getEthPort(const std::string &if_name, int idx);
+};
+
+#endif // __DEV_NET_ETHERBUS_HH__
diff --git a/src/dev/net/etherdevice.cc b/src/dev/net/etherdevice.cc
new file mode 100644
index 000000000..59849502c
--- /dev/null
+++ b/src/dev/net/etherdevice.cc
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ * Lisa Hsu
+ */
+
+#include "dev/net/etherdevice.hh"
+
+#include "sim/stats.hh"
+
+void
+EtherDevice::regStats()
+{
+ txBytes
+ .name(name() + ".txBytes")
+ .desc("Bytes Transmitted")
+ .prereq(txBytes)
+ ;
+
+ rxBytes
+ .name(name() + ".rxBytes")
+ .desc("Bytes Received")
+ .prereq(rxBytes)
+ ;
+
+ txPackets
+ .name(name() + ".txPackets")
+ .desc("Number of Packets Transmitted")
+ .prereq(txBytes)
+ ;
+
+ rxPackets
+ .name(name() + ".rxPackets")
+ .desc("Number of Packets Received")
+ .prereq(rxBytes)
+ ;
+
+ txIpChecksums
+ .name(name() + ".txIpChecksums")
+ .desc("Number of tx IP Checksums done by device")
+ .precision(0)
+ .prereq(txBytes)
+ ;
+
+ rxIpChecksums
+ .name(name() + ".rxIpChecksums")
+ .desc("Number of rx IP Checksums done by device")
+ .precision(0)
+ .prereq(rxBytes)
+ ;
+
+ txTcpChecksums
+ .name(name() + ".txTcpChecksums")
+ .desc("Number of tx TCP Checksums done by device")
+ .precision(0)
+ .prereq(txBytes)
+ ;
+
+ rxTcpChecksums
+ .name(name() + ".rxTcpChecksums")
+ .desc("Number of rx TCP Checksums done by device")
+ .precision(0)
+ .prereq(rxBytes)
+ ;
+
+ txUdpChecksums
+ .name(name() + ".txUdpChecksums")
+ .desc("Number of tx UDP Checksums done by device")
+ .precision(0)
+ .prereq(txBytes)
+ ;
+
+ rxUdpChecksums
+ .name(name() + ".rxUdpChecksums")
+ .desc("Number of rx UDP Checksums done by device")
+ .precision(0)
+ .prereq(rxBytes)
+ ;
+
+ descDmaReads
+ .name(name() + ".descDMAReads")
+ .desc("Number of descriptors the device read w/ DMA")
+ .precision(0)
+ ;
+
+ descDmaWrites
+ .name(name() + ".descDMAWrites")
+ .desc("Number of descriptors the device wrote w/ DMA")
+ .precision(0)
+ ;
+
+ descDmaRdBytes
+ .name(name() + ".descDmaReadBytes")
+ .desc("number of descriptor bytes read w/ DMA")
+ .precision(0)
+ ;
+
+ descDmaWrBytes
+ .name(name() + ".descDmaWriteBytes")
+ .desc("number of descriptor bytes write w/ DMA")
+ .precision(0)
+ ;
+
+ txBandwidth
+ .name(name() + ".txBandwidth")
+ .desc("Transmit Bandwidth (bits/s)")
+ .precision(0)
+ .prereq(txBytes)
+ ;
+
+ rxBandwidth
+ .name(name() + ".rxBandwidth")
+ .desc("Receive Bandwidth (bits/s)")
+ .precision(0)
+ .prereq(rxBytes)
+ ;
+
+ totBandwidth
+ .name(name() + ".totBandwidth")
+ .desc("Total Bandwidth (bits/s)")
+ .precision(0)
+ .prereq(totBytes)
+ ;
+
+ totPackets
+ .name(name() + ".totPackets")
+ .desc("Total Packets")
+ .precision(0)
+ .prereq(totBytes)
+ ;
+
+ totBytes
+ .name(name() + ".totBytes")
+ .desc("Total Bytes")
+ .precision(0)
+ .prereq(totBytes)
+ ;
+
+ totPacketRate
+ .name(name() + ".totPPS")
+ .desc("Total Tranmission Rate (packets/s)")
+ .precision(0)
+ .prereq(totBytes)
+ ;
+
+ txPacketRate
+ .name(name() + ".txPPS")
+ .desc("Packet Tranmission Rate (packets/s)")
+ .precision(0)
+ .prereq(txBytes)
+ ;
+
+ rxPacketRate
+ .name(name() + ".rxPPS")
+ .desc("Packet Reception Rate (packets/s)")
+ .precision(0)
+ .prereq(rxBytes)
+ ;
+
+ postedSwi
+ .name(name() + ".postedSwi")
+ .desc("number of software interrupts posted to CPU")
+ .precision(0)
+ ;
+
+ totalSwi
+ .name(name() + ".totalSwi")
+ .desc("total number of Swi written to ISR")
+ .precision(0)
+ ;
+
+ coalescedSwi
+ .name(name() + ".coalescedSwi")
+ .desc("average number of Swi's coalesced into each post")
+ .precision(0)
+ ;
+
+ postedRxIdle
+ .name(name() + ".postedRxIdle")
+ .desc("number of rxIdle interrupts posted to CPU")
+ .precision(0)
+ ;
+
+ totalRxIdle
+ .name(name() + ".totalRxIdle")
+ .desc("total number of RxIdle written to ISR")
+ .precision(0)
+ ;
+
+ coalescedRxIdle
+ .name(name() + ".coalescedRxIdle")
+ .desc("average number of RxIdle's coalesced into each post")
+ .precision(0)
+ ;
+
+ postedRxOk
+ .name(name() + ".postedRxOk")
+ .desc("number of RxOk interrupts posted to CPU")
+ .precision(0)
+ ;
+
+ totalRxOk
+ .name(name() + ".totalRxOk")
+ .desc("total number of RxOk written to ISR")
+ .precision(0)
+ ;
+
+ coalescedRxOk
+ .name(name() + ".coalescedRxOk")
+ .desc("average number of RxOk's coalesced into each post")
+ .precision(0)
+ ;
+
+ postedRxDesc
+ .name(name() + ".postedRxDesc")
+ .desc("number of RxDesc interrupts posted to CPU")
+ .precision(0)
+ ;
+
+ totalRxDesc
+ .name(name() + ".totalRxDesc")
+ .desc("total number of RxDesc written to ISR")
+ .precision(0)
+ ;
+
+ coalescedRxDesc
+ .name(name() + ".coalescedRxDesc")
+ .desc("average number of RxDesc's coalesced into each post")
+ .precision(0)
+ ;
+
+ postedTxOk
+ .name(name() + ".postedTxOk")
+ .desc("number of TxOk interrupts posted to CPU")
+ .precision(0)
+ ;
+
+ totalTxOk
+ .name(name() + ".totalTxOk")
+ .desc("total number of TxOk written to ISR")
+ .precision(0)
+ ;
+
+ coalescedTxOk
+ .name(name() + ".coalescedTxOk")
+ .desc("average number of TxOk's coalesced into each post")
+ .precision(0)
+ ;
+
+ postedTxIdle
+ .name(name() + ".postedTxIdle")
+ .desc("number of TxIdle interrupts posted to CPU")
+ .precision(0)
+ ;
+
+ totalTxIdle
+ .name(name() + ".totalTxIdle")
+ .desc("total number of TxIdle written to ISR")
+ .precision(0)
+ ;
+
+ coalescedTxIdle
+ .name(name() + ".coalescedTxIdle")
+ .desc("average number of TxIdle's coalesced into each post")
+ .precision(0)
+ ;
+
+ postedTxDesc
+ .name(name() + ".postedTxDesc")
+ .desc("number of TxDesc interrupts posted to CPU")
+ .precision(0)
+ ;
+
+ totalTxDesc
+ .name(name() + ".totalTxDesc")
+ .desc("total number of TxDesc written to ISR")
+ .precision(0)
+ ;
+
+ coalescedTxDesc
+ .name(name() + ".coalescedTxDesc")
+ .desc("average number of TxDesc's coalesced into each post")
+ .precision(0)
+ ;
+
+ postedRxOrn
+ .name(name() + ".postedRxOrn")
+ .desc("number of RxOrn posted to CPU")
+ .precision(0)
+ ;
+
+ totalRxOrn
+ .name(name() + ".totalRxOrn")
+ .desc("total number of RxOrn written to ISR")
+ .precision(0)
+ ;
+
+ coalescedRxOrn
+ .name(name() + ".coalescedRxOrn")
+ .desc("average number of RxOrn's coalesced into each post")
+ .precision(0)
+ ;
+
+ coalescedTotal
+ .name(name() + ".coalescedTotal")
+ .desc("average number of interrupts coalesced into each post")
+ .precision(0)
+ ;
+
+ postedInterrupts
+ .name(name() + ".postedInterrupts")
+ .desc("number of posts to CPU")
+ .precision(0)
+ ;
+
+ droppedPackets
+ .name(name() + ".droppedPackets")
+ .desc("number of packets dropped")
+ .precision(0)
+ ;
+
+ coalescedSwi = totalSwi / postedInterrupts;
+ coalescedRxIdle = totalRxIdle / postedInterrupts;
+ coalescedRxOk = totalRxOk / postedInterrupts;
+ coalescedRxDesc = totalRxDesc / postedInterrupts;
+ coalescedTxOk = totalTxOk / postedInterrupts;
+ coalescedTxIdle = totalTxIdle / postedInterrupts;
+ coalescedTxDesc = totalTxDesc / postedInterrupts;
+ coalescedRxOrn = totalRxOrn / postedInterrupts;
+
+ coalescedTotal = (totalSwi + totalRxIdle + totalRxOk + totalRxDesc +
+ totalTxOk + totalTxIdle + totalTxDesc +
+ totalRxOrn) / postedInterrupts;
+
+ txBandwidth = txBytes * Stats::constant(8) / simSeconds;
+ rxBandwidth = rxBytes * Stats::constant(8) / simSeconds;
+ totBandwidth = txBandwidth + rxBandwidth;
+ totBytes = txBytes + rxBytes;
+ totPackets = txPackets + rxPackets;
+
+ txPacketRate = txPackets / simSeconds;
+ rxPacketRate = rxPackets / simSeconds;
+ totPacketRate = totPackets / simSeconds;
+}
diff --git a/src/dev/net/etherdevice.hh b/src/dev/net/etherdevice.hh
new file mode 100644
index 000000000..d13a1c3c3
--- /dev/null
+++ b/src/dev/net/etherdevice.hh
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2007 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Ali Saidi
+ */
+
+/**
+ * @file
+ * Base Ethernet Device declaration.
+ */
+
+#ifndef __DEV_NET_ETHERDEVICE_HH__
+#define __DEV_NET_ETHERDEVICE_HH__
+
+#include "base/statistics.hh"
+#include "dev/pci/device.hh"
+#include "params/EtherDevBase.hh"
+#include "params/EtherDevice.hh"
+#include "sim/sim_object.hh"
+
+class EtherInt;
+
+/**
+ * The base EtherObject class, allows for an accesor function to a
+ * simobj that returns the Port.
+ */
+class EtherDevice : public PciDevice
+{
+ public:
+ typedef EtherDeviceParams Params;
+ EtherDevice(const Params *params)
+ : PciDevice(params)
+ {}
+
+ const Params *
+ params() const
+ {
+ return dynamic_cast<const Params *>(_params);
+ }
+
+ public:
+ /** Additional function to return the Port of a memory object. */
+ virtual EtherInt *getEthPort(const std::string &if_name, int idx = -1) = 0;
+
+ public:
+ void regStats();
+
+ protected:
+ Stats::Scalar txBytes;
+ Stats::Scalar rxBytes;
+ Stats::Scalar txPackets;
+ Stats::Scalar rxPackets;
+ Stats::Scalar txIpChecksums;
+ Stats::Scalar rxIpChecksums;
+ Stats::Scalar txTcpChecksums;
+ Stats::Scalar rxTcpChecksums;
+ Stats::Scalar txUdpChecksums;
+ Stats::Scalar rxUdpChecksums;
+ Stats::Scalar descDmaReads;
+ Stats::Scalar descDmaWrites;
+ Stats::Scalar descDmaRdBytes;
+ Stats::Scalar descDmaWrBytes;
+ Stats::Formula totBandwidth;
+ Stats::Formula totPackets;
+ Stats::Formula totBytes;
+ Stats::Formula totPacketRate;
+ Stats::Formula txBandwidth;
+ Stats::Formula rxBandwidth;
+ Stats::Formula txPacketRate;
+ Stats::Formula rxPacketRate;
+ Stats::Scalar postedSwi;
+ Stats::Formula coalescedSwi;
+ Stats::Scalar totalSwi;
+ Stats::Scalar postedRxIdle;
+ Stats::Formula coalescedRxIdle;
+ Stats::Scalar totalRxIdle;
+ Stats::Scalar postedRxOk;
+ Stats::Formula coalescedRxOk;
+ Stats::Scalar totalRxOk;
+ Stats::Scalar postedRxDesc;
+ Stats::Formula coalescedRxDesc;
+ Stats::Scalar totalRxDesc;
+ Stats::Scalar postedTxOk;
+ Stats::Formula coalescedTxOk;
+ Stats::Scalar totalTxOk;
+ Stats::Scalar postedTxIdle;
+ Stats::Formula coalescedTxIdle;
+ Stats::Scalar totalTxIdle;
+ Stats::Scalar postedTxDesc;
+ Stats::Formula coalescedTxDesc;
+ Stats::Scalar totalTxDesc;
+ Stats::Scalar postedRxOrn;
+ Stats::Formula coalescedRxOrn;
+ Stats::Scalar totalRxOrn;
+ Stats::Formula coalescedTotal;
+ Stats::Scalar postedInterrupts;
+ Stats::Scalar droppedPackets;
+};
+
+/**
+ * Dummy class to keep the Python class hierarchy in sync with the C++
+ * object hierarchy.
+ *
+ * The Python object hierarchy includes the EtherDevBase class which
+ * is used by some ethernet devices as a way to share common
+ * configuration information in the generated param structs. Since the
+ * Python hierarchy is used to generate a SWIG interface for all C++
+ * SimObjects, we need to reflect this in the C++ object hierarchy. If
+ * we don't, SWIG might end up doing 'bad things' when it down casts
+ * ethernet objects to their base class(es).
+ */
+class EtherDevBase : public EtherDevice
+{
+ public:
+ EtherDevBase(const EtherDevBaseParams *params)
+ : EtherDevice(params)
+ {}
+
+ const EtherDevBaseParams *
+ params() const
+ {
+ return dynamic_cast<const EtherDevBaseParams *>(_params);
+ }
+
+};
+
+#endif // __DEV_NET_ETHERDEVICE_HH__
+
diff --git a/src/dev/net/etherdump.cc b/src/dev/net/etherdump.cc
new file mode 100644
index 000000000..c537fbf57
--- /dev/null
+++ b/src/dev/net/etherdump.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+/* @file
+ * Simple object for creating a simple pcap style packet trace
+ */
+#include "dev/net/etherdump.hh"
+
+#include <sys/time.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/misc.hh"
+#include "base/output.hh"
+#include "sim/core.hh"
+
+using std::string;
+
+EtherDump::EtherDump(const Params *p)
+ : SimObject(p), stream(simout.create(p->file, true)),
+ maxlen(p->maxlen)
+{
+}
+
+#define DLT_EN10MB 1 // Ethernet (10Mb)
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+#define PCAP_VERSION_MAJOR 2
+#define PCAP_VERSION_MINOR 4
+
+struct pcap_file_header {
+ uint32_t magic;
+ uint16_t version_major;
+ uint16_t version_minor;
+ int32_t thiszone; // gmt to local correction
+ uint32_t sigfigs; // accuracy of timestamps
+ uint32_t snaplen; // max length saved portion of each pkt
+ uint32_t linktype; // data link type (DLT_*)
+};
+
+struct pcap_pkthdr {
+ uint32_t seconds;
+ uint32_t microseconds;
+ uint32_t caplen; // length of portion present
+ uint32_t len; // length this packet (off wire)
+};
+
+void
+EtherDump::init()
+{
+ struct pcap_file_header hdr;
+ hdr.magic = TCPDUMP_MAGIC;
+ hdr.version_major = PCAP_VERSION_MAJOR;
+ hdr.version_minor = PCAP_VERSION_MINOR;
+
+ hdr.thiszone = 0;
+ hdr.snaplen = 1500;
+ hdr.sigfigs = 0;
+ hdr.linktype = DLT_EN10MB;
+
+ stream->write(reinterpret_cast<char *>(&hdr), sizeof(hdr));
+
+ stream->flush();
+}
+
+void
+EtherDump::dumpPacket(EthPacketPtr &packet)
+{
+ pcap_pkthdr pkthdr;
+ pkthdr.seconds = curTick() / SimClock::Int::s;
+ pkthdr.microseconds = (curTick() / SimClock::Int::us) % ULL(1000000);
+ pkthdr.caplen = std::min(packet->length, maxlen);
+ pkthdr.len = packet->length;
+ stream->write(reinterpret_cast<char *>(&pkthdr), sizeof(pkthdr));
+ stream->write(reinterpret_cast<char *>(packet->data), pkthdr.caplen);
+ stream->flush();
+}
+
+EtherDump *
+EtherDumpParams::create()
+{
+ return new EtherDump(this);
+}
diff --git a/src/dev/net/etherdump.hh b/src/dev/net/etherdump.hh
new file mode 100644
index 000000000..8e651baf3
--- /dev/null
+++ b/src/dev/net/etherdump.hh
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+/* @file
+ * Simple object for creating a simple pcap style packet trace
+ */
+
+#ifndef __DEV_NET_ETHERDUMP_HH__
+#define __DEV_NET_ETHERDUMP_HH__
+
+#include <fstream>
+
+#include "dev/net/etherpkt.hh"
+#include "params/EtherDump.hh"
+#include "sim/sim_object.hh"
+
+/*
+ * Simple object for creating a simple pcap style packet trace
+ */
+class EtherDump : public SimObject
+{
+ private:
+ std::ostream *stream;
+ const unsigned maxlen;
+ void dumpPacket(EthPacketPtr &packet);
+ void init();
+
+ public:
+ typedef EtherDumpParams Params;
+ EtherDump(const Params *p);
+
+ inline void dump(EthPacketPtr &pkt) { dumpPacket(pkt); }
+};
+
+#endif // __DEV_NET_ETHERDUMP_HH__
diff --git a/src/dev/net/etherint.cc b/src/dev/net/etherint.cc
new file mode 100644
index 000000000..b5990c7a0
--- /dev/null
+++ b/src/dev/net/etherint.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+#include "dev/net/etherint.hh"
+
+#include "base/misc.hh"
+#include "sim/sim_object.hh"
+
+void
+EtherInt::setPeer(EtherInt *p)
+{
+ if (peer && peer != p)
+ panic("You cannot change the peer once it is set.\n"
+ "Current peer=%s Desired peer=%s", peer->name(), p->name());
+
+ peer = p;
+}
diff --git a/src/dev/net/etherint.hh b/src/dev/net/etherint.hh
new file mode 100644
index 000000000..a11d02681
--- /dev/null
+++ b/src/dev/net/etherint.hh
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+/* @file
+ * Class representing the actual interface between two ethernet
+ * components.
+ */
+
+#ifndef __DEV_NET_ETHERINT_HH__
+#define __DEV_NET_ETHERINT_HH__
+
+#include <string>
+
+#include "dev/net/etherpkt.hh"
+
+/*
+ * Class representing the actual interface between two ethernet
+ * components. These components are intended to attach to another
+ * ethernet interface on one side and whatever device on the other.
+ */
+class EtherInt
+{
+ protected:
+ mutable std::string portName;
+ EtherInt *peer;
+
+ public:
+ EtherInt(const std::string &name)
+ : portName(name), peer(NULL) {}
+ virtual ~EtherInt() {}
+
+ /** Return port name (for DPRINTF). */
+ const std::string &name() const { return portName; }
+
+ void setPeer(EtherInt *p);
+ EtherInt* getPeer() { return peer; }
+
+ void recvDone() { peer->sendDone(); }
+ virtual void sendDone() = 0;
+
+ bool sendPacket(EthPacketPtr packet)
+ { return peer ? peer->recvPacket(packet) : true; }
+ virtual bool recvPacket(EthPacketPtr packet) = 0;
+
+ bool askBusy() {return peer->isBusy(); }
+ virtual bool isBusy() { return false; }
+};
+
+#endif // __DEV_NET_ETHERINT_HH__
diff --git a/src/dev/net/etherlink.cc b/src/dev/net/etherlink.cc
new file mode 100644
index 000000000..c327a0168
--- /dev/null
+++ b/src/dev/net/etherlink.cc
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ * Ron Dreslinski
+ */
+
+/* @file
+ * Device module for modelling a fixed bandwidth full duplex ethernet link
+ */
+
+#include "dev/net/etherlink.hh"
+
+#include <cmath>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "base/random.hh"
+#include "base/trace.hh"
+#include "debug/Ethernet.hh"
+#include "debug/EthernetData.hh"
+#include "dev/net/etherdump.hh"
+#include "dev/net/etherint.hh"
+#include "dev/net/etherpkt.hh"
+#include "params/EtherLink.hh"
+#include "sim/core.hh"
+#include "sim/serialize.hh"
+#include "sim/system.hh"
+
+using namespace std;
+
+EtherLink::EtherLink(const Params *p)
+ : EtherObject(p)
+{
+ link[0] = new Link(name() + ".link0", this, 0, p->speed,
+ p->delay, p->delay_var, p->dump);
+ link[1] = new Link(name() + ".link1", this, 1, p->speed,
+ p->delay, p->delay_var, p->dump);
+
+ interface[0] = new Interface(name() + ".int0", link[0], link[1]);
+ interface[1] = new Interface(name() + ".int1", link[1], link[0]);
+}
+
+
+EtherLink::~EtherLink()
+{
+ delete link[0];
+ delete link[1];
+
+ delete interface[0];
+ delete interface[1];
+}
+
+EtherInt*
+EtherLink::getEthPort(const std::string &if_name, int idx)
+{
+ Interface *i;
+ if (if_name == "int0")
+ i = interface[0];
+ else if (if_name == "int1")
+ i = interface[1];
+ else
+ return NULL;
+ if (i->getPeer())
+ panic("interface already connected to\n");
+
+ return i;
+}
+
+
+EtherLink::Interface::Interface(const string &name, Link *tx, Link *rx)
+ : EtherInt(name), txlink(tx)
+{
+ tx->setTxInt(this);
+ rx->setRxInt(this);
+}
+
+EtherLink::Link::Link(const string &name, EtherLink *p, int num,
+ double rate, Tick delay, Tick delay_var, EtherDump *d)
+ : objName(name), parent(p), number(num), txint(NULL), rxint(NULL),
+ ticksPerByte(rate), linkDelay(delay), delayVar(delay_var), dump(d),
+ doneEvent(this), txQueueEvent(this)
+{ }
+
+void
+EtherLink::serialize(CheckpointOut &cp) const
+{
+ link[0]->serialize("link0", cp);
+ link[1]->serialize("link1", cp);
+}
+
+void
+EtherLink::unserialize(CheckpointIn &cp)
+{
+ link[0]->unserialize("link0", cp);
+ link[1]->unserialize("link1", cp);
+}
+
+void
+EtherLink::Link::txComplete(EthPacketPtr packet)
+{
+ DPRINTF(Ethernet, "packet received: len=%d\n", packet->length);
+ DDUMP(EthernetData, packet->data, packet->length);
+ rxint->sendPacket(packet);
+}
+
+void
+EtherLink::Link::txDone()
+{
+ if (dump)
+ dump->dump(packet);
+
+ if (linkDelay > 0) {
+ DPRINTF(Ethernet, "packet delayed: delay=%d\n", linkDelay);
+ txQueue.emplace_back(std::make_pair(curTick() + linkDelay, packet));
+ if (!txQueueEvent.scheduled())
+ parent->schedule(txQueueEvent, txQueue.front().first);
+ } else {
+ assert(txQueue.empty());
+ txComplete(packet);
+ }
+
+ packet = 0;
+ assert(!busy());
+
+ txint->sendDone();
+}
+
+void
+EtherLink::Link::processTxQueue()
+{
+ auto cur(txQueue.front());
+ txQueue.pop_front();
+
+ // Schedule a new event to process the next packet in the queue.
+ if (!txQueue.empty()) {
+ auto next(txQueue.front());
+ assert(next.first > curTick());
+ parent->schedule(txQueueEvent, next.first);
+ }
+
+ assert(cur.first == curTick());
+ txComplete(cur.second);
+}
+
+bool
+EtherLink::Link::transmit(EthPacketPtr pkt)
+{
+ if (busy()) {
+ DPRINTF(Ethernet, "packet not sent, link busy\n");
+ return false;
+ }
+
+ DPRINTF(Ethernet, "packet sent: len=%d\n", pkt->length);
+ DDUMP(EthernetData, pkt->data, pkt->length);
+
+ packet = pkt;
+ Tick delay = (Tick)ceil(((double)pkt->length * ticksPerByte) + 1.0);
+ if (delayVar != 0)
+ delay += random_mt.random<Tick>(0, delayVar);
+
+ DPRINTF(Ethernet, "scheduling packet: delay=%d, (rate=%f)\n",
+ delay, ticksPerByte);
+ parent->schedule(doneEvent, curTick() + delay);
+
+ return true;
+}
+
+void
+EtherLink::Link::serialize(const string &base, CheckpointOut &cp) const
+{
+ bool packet_exists = packet != nullptr;
+ paramOut(cp, base + ".packet_exists", packet_exists);
+ if (packet_exists)
+ packet->serialize(base + ".packet", cp);
+
+ bool event_scheduled = doneEvent.scheduled();
+ paramOut(cp, base + ".event_scheduled", event_scheduled);
+ if (event_scheduled) {
+ Tick event_time = doneEvent.when();
+ paramOut(cp, base + ".event_time", event_time);
+ }
+
+ const size_t tx_queue_size(txQueue.size());
+ paramOut(cp, base + ".tx_queue_size", tx_queue_size);
+ unsigned idx(0);
+ for (const auto &pe : txQueue) {
+ paramOut(cp, csprintf("%s.txQueue[%i].tick", base, idx), pe.first);
+ pe.second->serialize(csprintf("%s.txQueue[%i].packet", base, idx), cp);
+
+ ++idx;
+ }
+}
+
+void
+EtherLink::Link::unserialize(const string &base, CheckpointIn &cp)
+{
+ bool packet_exists;
+ paramIn(cp, base + ".packet_exists", packet_exists);
+ if (packet_exists) {
+ packet = make_shared<EthPacketData>(16384);
+ packet->unserialize(base + ".packet", cp);
+ }
+
+ bool event_scheduled;
+ paramIn(cp, base + ".event_scheduled", event_scheduled);
+ if (event_scheduled) {
+ Tick event_time;
+ paramIn(cp, base + ".event_time", event_time);
+ parent->schedule(doneEvent, event_time);
+ }
+
+ size_t tx_queue_size;
+ if (optParamIn(cp, base + ".tx_queue_size", tx_queue_size)) {
+ for (size_t idx = 0; idx < tx_queue_size; ++idx) {
+ Tick tick;
+ EthPacketPtr delayed_packet = make_shared<EthPacketData>(16384);
+
+ paramIn(cp, csprintf("%s.txQueue[%i].tick", base, idx), tick);
+ delayed_packet->unserialize(
+ csprintf("%s.txQueue[%i].packet", base, idx), cp);
+
+ fatal_if(!txQueue.empty() && txQueue.back().first > tick,
+ "Invalid txQueue packet order in EtherLink!\n");
+ txQueue.emplace_back(std::make_pair(tick, delayed_packet));
+ }
+
+ if (!txQueue.empty())
+ parent->schedule(txQueueEvent, txQueue.front().first);
+ } else {
+ // We can't reliably convert in-flight packets from old
+ // checkpoints. In fact, gem5 hasn't been able to load these
+ // packets for at least two years before the format change.
+ warn("Old-style EtherLink serialization format detected, "
+ "in-flight packets may have been dropped.\n");
+ }
+}
+
+EtherLink *
+EtherLinkParams::create()
+{
+ return new EtherLink(this);
+}
diff --git a/src/dev/net/etherlink.hh b/src/dev/net/etherlink.hh
new file mode 100644
index 000000000..1d02dec3b
--- /dev/null
+++ b/src/dev/net/etherlink.hh
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+/* @file
+ * Device module for modelling a fixed bandwidth full duplex ethernet link
+ */
+
+#ifndef __DEV_NET_ETHERLINK_HH__
+#define __DEV_NET_ETHERLINK_HH__
+
+#include <queue>
+
+#include "base/types.hh"
+#include "dev/net/etherint.hh"
+#include "dev/net/etherobject.hh"
+#include "dev/net/etherpkt.hh"
+#include "params/EtherLink.hh"
+#include "sim/eventq.hh"
+#include "sim/sim_object.hh"
+
+class EtherDump;
+class Checkpoint;
+/*
+ * Model for a fixed bandwidth full duplex ethernet link
+ */
+class EtherLink : public EtherObject
+{
+ protected:
+ class Interface;
+
+ /*
+ * Model for a single uni-directional link
+ */
+ class Link
+ {
+ protected:
+ const std::string objName;
+
+ EtherLink *const parent;
+ const int number;
+
+ Interface *txint;
+ Interface *rxint;
+
+ const double ticksPerByte;
+ const Tick linkDelay;
+ const Tick delayVar;
+ EtherDump *const dump;
+
+ protected:
+ /*
+ * Transfer is complete
+ */
+ EthPacketPtr packet;
+ void txDone();
+ typedef EventWrapper<Link, &Link::txDone> DoneEvent;
+ friend void DoneEvent::process();
+ DoneEvent doneEvent;
+
+ /**
+ * Maintain a queue of in-flight packets. Assume that the
+ * delay is non-zero and constant (i.e., at most one packet
+ * per tick).
+ */
+ std::deque<std::pair<Tick, EthPacketPtr>> txQueue;
+
+ void processTxQueue();
+ typedef EventWrapper<Link, &Link::processTxQueue> TxQueueEvent;
+ friend void TxQueueEvent::process();
+ TxQueueEvent txQueueEvent;
+
+ void txComplete(EthPacketPtr packet);
+
+ public:
+ Link(const std::string &name, EtherLink *p, int num,
+ double rate, Tick delay, Tick delay_var, EtherDump *dump);
+ ~Link() {}
+
+ const std::string name() const { return objName; }
+
+ bool busy() const { return (bool)packet; }
+ bool transmit(EthPacketPtr packet);
+
+ void setTxInt(Interface *i) { assert(!txint); txint = i; }
+ void setRxInt(Interface *i) { assert(!rxint); rxint = i; }
+
+ void serialize(const std::string &base, CheckpointOut &cp) const;
+ void unserialize(const std::string &base, CheckpointIn &cp);
+ };
+
+ /*
+ * Interface at each end of the link
+ */
+ class Interface : public EtherInt
+ {
+ private:
+ Link *txlink;
+
+ public:
+ Interface(const std::string &name, Link *txlink, Link *rxlink);
+ bool recvPacket(EthPacketPtr packet) { return txlink->transmit(packet); }
+ void sendDone() { peer->sendDone(); }
+ bool isBusy() { return txlink->busy(); }
+ };
+
+ Link *link[2];
+ Interface *interface[2];
+
+ public:
+ typedef EtherLinkParams Params;
+ EtherLink(const Params *p);
+ virtual ~EtherLink();
+
+ const Params *
+ params() const
+ {
+ return dynamic_cast<const Params *>(_params);
+ }
+
+ EtherInt *getEthPort(const std::string &if_name, int idx) override;
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+
+};
+
+#endif // __DEV_NET_ETHERLINK_HH__
diff --git a/src/dev/net/etherobject.hh b/src/dev/net/etherobject.hh
new file mode 100644
index 000000000..55cfa97e6
--- /dev/null
+++ b/src/dev/net/etherobject.hh
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2007 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Ali Saidi
+ */
+
+/**
+ * @file
+ * Base Ethernet Object declaration.
+ */
+
+#ifndef __DEV_NET_ETHEROBJECT_HH__
+#define __DEV_NET_ETHEROBJECT_HH__
+
+#include "params/EtherObject.hh"
+#include "sim/sim_object.hh"
+
+class EtherInt;
+
+/**
+ * The base EtherObject class, allows for an accesor function to a
+ * simobj that returns the Port.
+ */
+class EtherObject : public SimObject
+{
+ public:
+ typedef EtherObjectParams Params;
+ EtherObject(const Params *params)
+ : SimObject(params) {}
+
+ const Params *
+ params() const
+ {
+ return dynamic_cast<const Params *>(_params);
+ }
+
+ public:
+ /** Additional function to return the Port of a memory object. */
+ virtual EtherInt *getEthPort(const std::string &if_name, int idx = -1) = 0;
+
+};
+
+#endif // __DEV_NET_ETHEROBJECT_HH__
diff --git a/src/dev/net/etherpkt.cc b/src/dev/net/etherpkt.cc
new file mode 100644
index 000000000..a16f572c5
--- /dev/null
+++ b/src/dev/net/etherpkt.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+#include "dev/net/etherpkt.hh"
+
+#include <iostream>
+
+#include "base/inet.hh"
+#include "base/misc.hh"
+#include "sim/serialize.hh"
+
+using namespace std;
+
+void
+EthPacketData::serialize(const string &base, CheckpointOut &cp) const
+{
+ paramOut(cp, base + ".length", length);
+ arrayParamOut(cp, base + ".data", data, length);
+}
+
+void
+EthPacketData::unserialize(const string &base, CheckpointIn &cp)
+{
+ paramIn(cp, base + ".length", length);
+ if (length)
+ arrayParamIn(cp, base + ".data", data, length);
+}
+
+void
+EthPacketData::packAddress(uint8_t *src_addr,
+ uint8_t *dst_addr,
+ unsigned &nbytes)
+{
+ Net::EthHdr *hdr = (Net::EthHdr *)data;
+ assert(hdr->src().size() == hdr->dst().size());
+ if (nbytes < hdr->src().size())
+ panic("EthPacketData::packAddress() Buffer overflow");
+
+ memcpy(dst_addr, hdr->dst().bytes(), hdr->dst().size());
+ memcpy(src_addr, hdr->src().bytes(), hdr->src().size());
+
+ nbytes = hdr->src().size();
+}
+
diff --git a/src/dev/net/etherpkt.hh b/src/dev/net/etherpkt.hh
new file mode 100644
index 000000000..4119578c3
--- /dev/null
+++ b/src/dev/net/etherpkt.hh
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ * Lisa Hsu
+ */
+
+/* @file
+ * Reference counted class containing ethernet packet data
+ */
+
+#ifndef __DEV_NET_ETHERPKT_HH__
+#define __DEV_NET_ETHERPKT_HH__
+
+#include <cassert>
+#include <iosfwd>
+#include <memory>
+
+#include "base/types.hh"
+#include "sim/serialize.hh"
+
+/*
+ * Reference counted class containing ethernet packet data
+ */
+class EthPacketData
+{
+ public:
+ /*
+ * Pointer to packet data will be deleted
+ */
+ uint8_t *data;
+
+ /*
+ * Length of the current packet
+ */
+ unsigned length;
+
+ public:
+ EthPacketData()
+ : data(NULL), length(0)
+ { }
+
+ explicit EthPacketData(unsigned size)
+ : data(new uint8_t[size]), length(0)
+ { }
+
+ ~EthPacketData() { if (data) delete [] data; }
+
+ public:
+ /**
+ * This function pulls out the MAC source and destination addresses from
+ * the packet data and stores them in the caller specified buffers.
+ *
+ * @param src_addr The buffer to store the source MAC address.
+ * @param dst_addr The buffer to store the destination MAC address.
+ * @param length This is an inout parameter. The caller stores in this
+ * the size of the address buffers. On return, this will contain the
+ * actual address size stored in the buffers. (We assume that source
+ * address size is equal to that of the destination address.)
+ */
+ void packAddress(uint8_t *src_addr, uint8_t *dst_addr, unsigned &length);
+
+ void serialize(const std::string &base, CheckpointOut &cp) const;
+ void unserialize(const std::string &base, CheckpointIn &cp);
+
+ unsigned size() const { return length; }
+};
+
+typedef std::shared_ptr<EthPacketData> EthPacketPtr;
+
+#endif // __DEV_NET_ETHERPKT_HH__
diff --git a/src/dev/net/ethertap.cc b/src/dev/net/ethertap.cc
new file mode 100644
index 000000000..e8d6a363c
--- /dev/null
+++ b/src/dev/net/ethertap.cc
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2003-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+/* @file
+ * Interface to connect a simulated ethernet device to the real world
+ */
+
+#include "dev/net/ethertap.hh"
+
+#if defined(__OpenBSD__) || defined(__APPLE__)
+#include <sys/param.h>
+
+#endif
+#include <netinet/in.h>
+#include <unistd.h>
+
+#include <deque>
+#include <string>
+
+#include "base/misc.hh"
+#include "base/pollevent.hh"
+#include "base/socket.hh"
+#include "base/trace.hh"
+#include "debug/Ethernet.hh"
+#include "debug/EthernetData.hh"
+#include "dev/net/etherdump.hh"
+#include "dev/net/etherint.hh"
+#include "dev/net/etherpkt.hh"
+
+using namespace std;
+
+/**
+ */
+class TapListener
+{
+ protected:
+ /**
+ */
+ class Event : public PollEvent
+ {
+ protected:
+ TapListener *listener;
+
+ public:
+ Event(TapListener *l, int fd, int e)
+ : PollEvent(fd, e), listener(l) {}
+
+ virtual void process(int revent) { listener->accept(); }
+ };
+
+ friend class Event;
+ Event *event;
+
+ protected:
+ ListenSocket listener;
+ EtherTap *tap;
+ int port;
+
+ public:
+ TapListener(EtherTap *t, int p)
+ : event(NULL), tap(t), port(p) {}
+ ~TapListener() { if (event) delete event; }
+
+ void accept();
+ void listen();
+};
+
+void
+TapListener::listen()
+{
+ while (!listener.listen(port, true)) {
+ DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port);
+ port++;
+ }
+
+ ccprintf(cerr, "Listening for tap connection on port %d\n", port);
+ event = new Event(this, listener.getfd(), POLLIN|POLLERR);
+ pollQueue.schedule(event);
+}
+
+void
+TapListener::accept()
+{
+ // As a consequence of being called from the PollQueue, we might
+ // have been called from a different thread. Migrate to "our"
+ // thread.
+ EventQueue::ScopedMigration migrate(tap->eventQueue());
+
+ if (!listener.islistening())
+ panic("TapListener(accept): cannot accept if we're not listening!");
+
+ int sfd = listener.accept(true);
+ if (sfd != -1)
+ tap->attach(sfd);
+}
+
+/**
+ */
+class TapEvent : public PollEvent
+{
+ protected:
+ EtherTap *tap;
+
+ public:
+ TapEvent(EtherTap *_tap, int fd, int e)
+ : PollEvent(fd, e), tap(_tap) {}
+ virtual void process(int revent) { tap->process(revent); }
+};
+
+EtherTap::EtherTap(const Params *p)
+ : EtherObject(p), event(NULL), socket(-1), buflen(p->bufsz), dump(p->dump),
+ interface(NULL), txEvent(this)
+{
+ if (ListenSocket::allDisabled())
+ fatal("All listeners are disabled! EtherTap can't work!");
+
+ buffer = new char[buflen];
+ listener = new TapListener(this, p->port);
+ listener->listen();
+ interface = new EtherTapInt(name() + ".interface", this);
+}
+
+EtherTap::~EtherTap()
+{
+ if (event)
+ delete event;
+ if (buffer)
+ delete [] buffer;
+
+ delete interface;
+ delete listener;
+}
+
+void
+EtherTap::attach(int fd)
+{
+ if (socket != -1)
+ close(fd);
+
+ buffer_offset = 0;
+ data_len = 0;
+ socket = fd;
+ DPRINTF(Ethernet, "EtherTap attached\n");
+ event = new TapEvent(this, socket, POLLIN|POLLERR);
+ pollQueue.schedule(event);
+}
+
+void
+EtherTap::detach()
+{
+ DPRINTF(Ethernet, "EtherTap detached\n");
+ delete event;
+ event = 0;
+ close(socket);
+ socket = -1;
+}
+
+bool
+EtherTap::recvPacket(EthPacketPtr packet)
+{
+ if (dump)
+ dump->dump(packet);
+
+ DPRINTF(Ethernet, "EtherTap output len=%d\n", packet->length);
+ DDUMP(EthernetData, packet->data, packet->length);
+ uint32_t len = htonl(packet->length);
+ ssize_t ret = write(socket, &len, sizeof(len));
+ if (ret != sizeof(len))
+ return false;
+ ret = write(socket, packet->data, packet->length);
+ if (ret != packet->length)
+ return false;
+
+ interface->recvDone();
+
+ return true;
+}
+
+void
+EtherTap::sendDone()
+{}
+
+void
+EtherTap::process(int revent)
+{
+ if (revent & POLLERR) {
+ detach();
+ return;
+ }
+
+ char *data = buffer + sizeof(uint32_t);
+ if (!(revent & POLLIN))
+ return;
+
+ if (buffer_offset < data_len + sizeof(uint32_t)) {
+ int len = read(socket, buffer + buffer_offset, buflen - buffer_offset);
+ if (len == 0) {
+ detach();
+ return;
+ }
+
+ buffer_offset += len;
+
+ if (data_len == 0)
+ data_len = ntohl(*(uint32_t *)buffer);
+
+ DPRINTF(Ethernet, "Received data from peer: len=%d buffer_offset=%d "
+ "data_len=%d\n", len, buffer_offset, data_len);
+ }
+
+ while (data_len != 0 && buffer_offset >= data_len + sizeof(uint32_t)) {
+ EthPacketPtr packet;
+ packet = make_shared<EthPacketData>(data_len);
+ packet->length = data_len;
+ memcpy(packet->data, data, data_len);
+
+ buffer_offset -= data_len + sizeof(uint32_t);
+ assert(buffer_offset >= 0);
+ if (buffer_offset > 0) {
+ memmove(buffer, data + data_len, buffer_offset);
+ data_len = ntohl(*(uint32_t *)buffer);
+ } else
+ data_len = 0;
+
+ DPRINTF(Ethernet, "EtherTap input len=%d\n", packet->length);
+ DDUMP(EthernetData, packet->data, packet->length);
+ if (!interface->sendPacket(packet)) {
+ DPRINTF(Ethernet, "bus busy...buffer for retransmission\n");
+ packetBuffer.push(packet);
+ if (!txEvent.scheduled())
+ schedule(txEvent, curTick() + retryTime);
+ } else if (dump) {
+ dump->dump(packet);
+ }
+ }
+}
+
+void
+EtherTap::retransmit()
+{
+ if (packetBuffer.empty())
+ return;
+
+ EthPacketPtr packet = packetBuffer.front();
+ if (interface->sendPacket(packet)) {
+ if (dump)
+ dump->dump(packet);
+ DPRINTF(Ethernet, "EtherTap retransmit\n");
+ packetBuffer.front() = NULL;
+ packetBuffer.pop();
+ }
+
+ if (!packetBuffer.empty() && !txEvent.scheduled())
+ schedule(txEvent, curTick() + retryTime);
+}
+
+EtherInt*
+EtherTap::getEthPort(const std::string &if_name, int idx)
+{
+ if (if_name == "tap") {
+ if (interface->getPeer())
+ panic("Interface already connected to\n");
+ return interface;
+ }
+ return NULL;
+}
+
+
+//=====================================================================
+
+void
+EtherTap::serialize(CheckpointOut &cp) const
+{
+ SERIALIZE_SCALAR(socket);
+ SERIALIZE_SCALAR(buflen);
+ uint8_t *buffer = (uint8_t *)this->buffer;
+ SERIALIZE_ARRAY(buffer, buflen);
+ SERIALIZE_SCALAR(buffer_offset);
+ SERIALIZE_SCALAR(data_len);
+
+ bool tapevent_present = false;
+ if (event) {
+ tapevent_present = true;
+ SERIALIZE_SCALAR(tapevent_present);
+ event->serialize(cp);
+ }
+ else {
+ SERIALIZE_SCALAR(tapevent_present);
+ }
+}
+
+void
+EtherTap::unserialize(CheckpointIn &cp)
+{
+ UNSERIALIZE_SCALAR(socket);
+ UNSERIALIZE_SCALAR(buflen);
+ uint8_t *buffer = (uint8_t *)this->buffer;
+ UNSERIALIZE_ARRAY(buffer, buflen);
+ UNSERIALIZE_SCALAR(buffer_offset);
+ UNSERIALIZE_SCALAR(data_len);
+
+ bool tapevent_present;
+ UNSERIALIZE_SCALAR(tapevent_present);
+ if (tapevent_present) {
+ event = new TapEvent(this, socket, POLLIN|POLLERR);
+
+ event->unserialize(cp);
+
+ if (event->queued()) {
+ pollQueue.schedule(event);
+ }
+ }
+}
+
+//=====================================================================
+
+EtherTap *
+EtherTapParams::create()
+{
+ return new EtherTap(this);
+}
diff --git a/src/dev/net/ethertap.hh b/src/dev/net/ethertap.hh
new file mode 100644
index 000000000..b27b80f84
--- /dev/null
+++ b/src/dev/net/ethertap.hh
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2003-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+/* @file
+ * Interface to connect a simulated ethernet device to the real world
+ */
+
+#ifndef __DEV_NET_ETHERTAP_HH__
+#define __DEV_NET_ETHERTAP_HH__
+
+#include <queue>
+#include <string>
+
+#include "base/pollevent.hh"
+#include "dev/net/etherint.hh"
+#include "dev/net/etherobject.hh"
+#include "dev/net/etherpkt.hh"
+#include "params/EtherTap.hh"
+#include "sim/eventq.hh"
+#include "sim/sim_object.hh"
+
+class TapEvent;
+class TapListener;
+class EtherTapInt;
+
+/*
+ * Interface to connect a simulated ethernet device to the real world
+ */
+class EtherTap : public EtherObject
+{
+ protected:
+ friend class TapEvent;
+ TapEvent *event;
+
+ protected:
+ friend class TapListener;
+ TapListener *listener;
+ int socket;
+ char *buffer;
+ int buflen;
+ uint32_t buffer_offset;
+ uint32_t data_len;
+
+ EtherDump *dump;
+
+ void attach(int fd);
+ void detach();
+
+ protected:
+ std::string device;
+ std::queue<EthPacketPtr> packetBuffer;
+ EtherTapInt *interface;
+
+ void process(int revent);
+ void enqueue(EthPacketData *packet);
+ void retransmit();
+
+ /*
+ */
+ class TxEvent : public Event
+ {
+ protected:
+ EtherTap *tap;
+
+ public:
+ TxEvent(EtherTap *_tap) : tap(_tap) {}
+ void process() { tap->retransmit(); }
+ virtual const char *description() const
+ { return "EtherTap retransmit"; }
+ };
+
+ friend class TxEvent;
+ TxEvent txEvent;
+
+ public:
+ typedef EtherTapParams Params;
+ EtherTap(const Params *p);
+ virtual ~EtherTap();
+
+ const Params *
+ params() const
+ {
+ return dynamic_cast<const Params *>(_params);
+ }
+
+ EtherInt *getEthPort(const std::string &if_name, int idx) override;
+
+ virtual bool recvPacket(EthPacketPtr packet);
+ virtual void sendDone();
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+};
+
+class EtherTapInt : public EtherInt
+{
+ private:
+ EtherTap *tap;
+ public:
+ EtherTapInt(const std::string &name, EtherTap *t)
+ : EtherInt(name), tap(t)
+ { }
+
+ virtual bool recvPacket(EthPacketPtr pkt) { return tap->recvPacket(pkt); }
+ virtual void sendDone() { tap->sendDone(); }
+};
+
+
+#endif // __DEV_NET_ETHERTAP_HH__
diff --git a/src/dev/net/i8254xGBe.cc b/src/dev/net/i8254xGBe.cc
new file mode 100644
index 000000000..cba773f39
--- /dev/null
+++ b/src/dev/net/i8254xGBe.cc
@@ -0,0 +1,2562 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Ali Saidi
+ */
+
+/* @file
+ * Device model for Intel's 8254x line of gigabit ethernet controllers.
+ * In particular an 82547 revision 2 (82547GI) MAC because it seems to have the
+ * fewest workarounds in the driver. It will probably work with most of the
+ * other MACs with slight modifications.
+ */
+
+#include "dev/net/i8254xGBe.hh"
+
+/*
+ * @todo really there are multiple dma engines.. we should implement them.
+ */
+
+#include <algorithm>
+#include <memory>
+
+#include "base/inet.hh"
+#include "base/trace.hh"
+#include "debug/Drain.hh"
+#include "debug/EthernetAll.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+#include "params/IGbE.hh"
+#include "sim/stats.hh"
+#include "sim/system.hh"
+
+using namespace iGbReg;
+using namespace Net;
+
+IGbE::IGbE(const Params *p)
+ : EtherDevice(p), etherInt(NULL), cpa(NULL),
+ rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size), rxTick(false),
+ txTick(false), txFifoTick(false), rxDmaPacket(false), pktOffset(0),
+ fetchDelay(p->fetch_delay), wbDelay(p->wb_delay),
+ fetchCompDelay(p->fetch_comp_delay), wbCompDelay(p->wb_comp_delay),
+ rxWriteDelay(p->rx_write_delay), txReadDelay(p->tx_read_delay),
+ rdtrEvent(this), radvEvent(this),
+ tadvEvent(this), tidvEvent(this), tickEvent(this), interEvent(this),
+ rxDescCache(this, name()+".RxDesc", p->rx_desc_cache_size),
+ txDescCache(this, name()+".TxDesc", p->tx_desc_cache_size),
+ lastInterrupt(0)
+{
+ etherInt = new IGbEInt(name() + ".int", this);
+
+ // Initialized internal registers per Intel documentation
+ // All registers intialized to 0 by per register constructor
+ regs.ctrl.fd(1);
+ regs.ctrl.lrst(1);
+ regs.ctrl.speed(2);
+ regs.ctrl.frcspd(1);
+ regs.sts.speed(3); // Say we're 1000Mbps
+ regs.sts.fd(1); // full duplex
+ regs.sts.lu(1); // link up
+ regs.eecd.fwe(1);
+ regs.eecd.ee_type(1);
+ regs.imr = 0;
+ regs.iam = 0;
+ regs.rxdctl.gran(1);
+ regs.rxdctl.wthresh(1);
+ regs.fcrth(1);
+ regs.tdwba = 0;
+ regs.rlpml = 0;
+ regs.sw_fw_sync = 0;
+
+ regs.pba.rxa(0x30);
+ regs.pba.txa(0x10);
+
+ eeOpBits = 0;
+ eeAddrBits = 0;
+ eeDataBits = 0;
+ eeOpcode = 0;
+
+ // clear all 64 16 bit words of the eeprom
+ memset(&flash, 0, EEPROM_SIZE*2);
+
+ // Set the MAC address
+ memcpy(flash, p->hardware_address.bytes(), ETH_ADDR_LEN);
+ for (int x = 0; x < ETH_ADDR_LEN/2; x++)
+ flash[x] = htobe(flash[x]);
+
+ uint16_t csum = 0;
+ for (int x = 0; x < EEPROM_SIZE; x++)
+ csum += htobe(flash[x]);
+
+
+ // 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();
+}
+
+IGbE::~IGbE()
+{
+ delete etherInt;
+}
+
+void
+IGbE::init()
+{
+ cpa = CPA::cpa();
+ PciDevice::init();
+}
+
+EtherInt*
+IGbE::getEthPort(const std::string &if_name, int idx)
+{
+
+ if (if_name == "interface") {
+ if (etherInt->getPeer())
+ panic("Port already connected to\n");
+ return etherInt;
+ }
+ return NULL;
+}
+
+Tick
+IGbE::writeConfig(PacketPtr pkt)
+{
+ int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
+ if (offset < PCI_DEVICE_SPECIFIC)
+ PciDevice::writeConfig(pkt);
+ else
+ panic("Device specific PCI config space not implemented.\n");
+
+ //
+ // Some work may need to be done here based for the pci COMMAND bits.
+ //
+
+ return configDelay;
+}
+
+// Handy macro for range-testing register access addresses
+#define IN_RANGE(val, base, len) (val >= base && val < (base + len))
+
+Tick
+IGbE::read(PacketPtr pkt)
+{
+ int bar;
+ Addr daddr;
+
+ if (!getBAR(pkt->getAddr(), bar, daddr))
+ panic("Invalid PCI memory access to unmapped memory.\n");
+
+ // Only Memory register BAR is allowed
+ assert(bar == 0);
+
+ // Only 32bit accesses allowed
+ assert(pkt->getSize() == 4);
+
+ DPRINTF(Ethernet, "Read device register %#X\n", daddr);
+
+ //
+ // Handle read of register here
+ //
+
+
+ switch (daddr) {
+ case REG_CTRL:
+ pkt->set<uint32_t>(regs.ctrl());
+ break;
+ case REG_STATUS:
+ pkt->set<uint32_t>(regs.sts());
+ break;
+ case REG_EECD:
+ pkt->set<uint32_t>(regs.eecd());
+ break;
+ case REG_EERD:
+ pkt->set<uint32_t>(regs.eerd());
+ break;
+ case REG_CTRL_EXT:
+ pkt->set<uint32_t>(regs.ctrl_ext());
+ break;
+ case REG_MDIC:
+ pkt->set<uint32_t>(regs.mdic());
+ break;
+ case REG_ICR:
+ DPRINTF(Ethernet, "Reading ICR. ICR=%#x IMR=%#x IAM=%#x IAME=%d\n",
+ regs.icr(), regs.imr, regs.iam, regs.ctrl_ext.iame());
+ pkt->set<uint32_t>(regs.icr());
+ if (regs.icr.int_assert() || regs.imr == 0) {
+ regs.icr = regs.icr() & ~mask(30);
+ DPRINTF(Ethernet, "Cleared ICR. ICR=%#x\n", regs.icr());
+ }
+ if (regs.ctrl_ext.iame() && regs.icr.int_assert())
+ regs.imr &= ~regs.iam;
+ chkInterrupt();
+ break;
+ case REG_EICR:
+ // This is only useful for MSI, but the driver reads it every time
+ // Just don't do anything
+ pkt->set<uint32_t>(0);
+ break;
+ case REG_ITR:
+ pkt->set<uint32_t>(regs.itr());
+ break;
+ case REG_RCTL:
+ pkt->set<uint32_t>(regs.rctl());
+ break;
+ case REG_FCTTV:
+ pkt->set<uint32_t>(regs.fcttv());
+ break;
+ case REG_TCTL:
+ pkt->set<uint32_t>(regs.tctl());
+ break;
+ case REG_PBA:
+ pkt->set<uint32_t>(regs.pba());
+ break;
+ case REG_WUC:
+ case REG_LEDCTL:
+ pkt->set<uint32_t>(0); // We don't care, so just return 0
+ break;
+ case REG_FCRTL:
+ pkt->set<uint32_t>(regs.fcrtl());
+ break;
+ case REG_FCRTH:
+ pkt->set<uint32_t>(regs.fcrth());
+ break;
+ case REG_RDBAL:
+ pkt->set<uint32_t>(regs.rdba.rdbal());
+ break;
+ case REG_RDBAH:
+ pkt->set<uint32_t>(regs.rdba.rdbah());
+ break;
+ case REG_RDLEN:
+ pkt->set<uint32_t>(regs.rdlen());
+ break;
+ case REG_SRRCTL:
+ pkt->set<uint32_t>(regs.srrctl());
+ break;
+ case REG_RDH:
+ pkt->set<uint32_t>(regs.rdh());
+ break;
+ case REG_RDT:
+ pkt->set<uint32_t>(regs.rdt());
+ break;
+ case REG_RDTR:
+ pkt->set<uint32_t>(regs.rdtr());
+ if (regs.rdtr.fpd()) {
+ rxDescCache.writeback(0);
+ DPRINTF(EthernetIntr,
+ "Posting interrupt because of RDTR.FPD write\n");
+ postInterrupt(IT_RXT);
+ regs.rdtr.fpd(0);
+ }
+ break;
+ case REG_RXDCTL:
+ pkt->set<uint32_t>(regs.rxdctl());
+ break;
+ case REG_RADV:
+ pkt->set<uint32_t>(regs.radv());
+ break;
+ case REG_TDBAL:
+ pkt->set<uint32_t>(regs.tdba.tdbal());
+ break;
+ case REG_TDBAH:
+ pkt->set<uint32_t>(regs.tdba.tdbah());
+ break;
+ case REG_TDLEN:
+ pkt->set<uint32_t>(regs.tdlen());
+ break;
+ case REG_TDH:
+ pkt->set<uint32_t>(regs.tdh());
+ break;
+ case REG_TXDCA_CTL:
+ pkt->set<uint32_t>(regs.txdca_ctl());
+ break;
+ case REG_TDT:
+ pkt->set<uint32_t>(regs.tdt());
+ break;
+ case REG_TIDV:
+ pkt->set<uint32_t>(regs.tidv());
+ break;
+ case REG_TXDCTL:
+ pkt->set<uint32_t>(regs.txdctl());
+ break;
+ case REG_TADV:
+ pkt->set<uint32_t>(regs.tadv());
+ break;
+ case REG_TDWBAL:
+ pkt->set<uint32_t>(regs.tdwba & mask(32));
+ break;
+ case REG_TDWBAH:
+ pkt->set<uint32_t>(regs.tdwba >> 32);
+ break;
+ case REG_RXCSUM:
+ pkt->set<uint32_t>(regs.rxcsum());
+ break;
+ case REG_RLPML:
+ pkt->set<uint32_t>(regs.rlpml);
+ break;
+ case REG_RFCTL:
+ pkt->set<uint32_t>(regs.rfctl());
+ break;
+ case REG_MANC:
+ pkt->set<uint32_t>(regs.manc());
+ break;
+ case REG_SWSM:
+ pkt->set<uint32_t>(regs.swsm());
+ regs.swsm.smbi(1);
+ break;
+ case REG_FWSM:
+ pkt->set<uint32_t>(regs.fwsm());
+ break;
+ case REG_SWFWSYNC:
+ pkt->set<uint32_t>(regs.sw_fw_sync);
+ break;
+ default:
+ if (!IN_RANGE(daddr, REG_VFTA, VLAN_FILTER_TABLE_SIZE*4) &&
+ !IN_RANGE(daddr, REG_RAL, RCV_ADDRESS_TABLE_SIZE*8) &&
+ !IN_RANGE(daddr, REG_MTA, MULTICAST_TABLE_SIZE*4) &&
+ !IN_RANGE(daddr, REG_CRCERRS, STATS_REGS_SIZE))
+ panic("Read request to unknown register number: %#x\n", daddr);
+ else
+ pkt->set<uint32_t>(0);
+ };
+
+ pkt->makeAtomicResponse();
+ return pioDelay;
+}
+
+Tick
+IGbE::write(PacketPtr pkt)
+{
+ int bar;
+ Addr daddr;
+
+
+ if (!getBAR(pkt->getAddr(), bar, daddr))
+ panic("Invalid PCI memory access to unmapped memory.\n");
+
+ // Only Memory register BAR is allowed
+ assert(bar == 0);
+
+ // Only 32bit accesses allowed
+ assert(pkt->getSize() == sizeof(uint32_t));
+
+ DPRINTF(Ethernet, "Wrote device register %#X value %#X\n",
+ daddr, pkt->get<uint32_t>());
+
+ //
+ // Handle write of register here
+ //
+ uint32_t val = pkt->get<uint32_t>();
+
+ Regs::RCTL oldrctl;
+ Regs::TCTL oldtctl;
+
+ switch (daddr) {
+ case REG_CTRL:
+ regs.ctrl = val;
+ if (regs.ctrl.tfce())
+ warn("TX Flow control enabled, should implement\n");
+ if (regs.ctrl.rfce())
+ warn("RX Flow control enabled, should implement\n");
+ break;
+ case REG_CTRL_EXT:
+ regs.ctrl_ext = val;
+ break;
+ case REG_STATUS:
+ regs.sts = val;
+ break;
+ case REG_EECD:
+ int oldClk;
+ oldClk = regs.eecd.sk();
+ regs.eecd = val;
+ // See if this is a eeprom access and emulate accordingly
+ if (!oldClk && regs.eecd.sk()) {
+ if (eeOpBits < 8) {
+ eeOpcode = eeOpcode << 1 | regs.eecd.din();
+ eeOpBits++;
+ } else if (eeAddrBits < 8 && eeOpcode == EEPROM_READ_OPCODE_SPI) {
+ eeAddr = eeAddr << 1 | regs.eecd.din();
+ eeAddrBits++;
+ } else if (eeDataBits < 16 && eeOpcode == EEPROM_READ_OPCODE_SPI) {
+ assert(eeAddr>>1 < EEPROM_SIZE);
+ DPRINTF(EthernetEEPROM, "EEPROM bit read: %d word: %#X\n",
+ flash[eeAddr>>1] >> eeDataBits & 0x1,
+ flash[eeAddr>>1]);
+ regs.eecd.dout((flash[eeAddr>>1] >> (15-eeDataBits)) & 0x1);
+ eeDataBits++;
+ } else if (eeDataBits < 8 && eeOpcode == EEPROM_RDSR_OPCODE_SPI) {
+ regs.eecd.dout(0);
+ eeDataBits++;
+ } else
+ panic("What's going on with eeprom interface? opcode:"
+ " %#x:%d addr: %#x:%d, data: %d\n", (uint32_t)eeOpcode,
+ (uint32_t)eeOpBits, (uint32_t)eeAddr,
+ (uint32_t)eeAddrBits, (uint32_t)eeDataBits);
+
+ // Reset everything for the next command
+ if ((eeDataBits == 16 && eeOpcode == EEPROM_READ_OPCODE_SPI) ||
+ (eeDataBits == 8 && eeOpcode == EEPROM_RDSR_OPCODE_SPI)) {
+ eeOpBits = 0;
+ eeAddrBits = 0;
+ eeDataBits = 0;
+ eeOpcode = 0;
+ eeAddr = 0;
+ }
+
+ DPRINTF(EthernetEEPROM, "EEPROM: opcode: %#X:%d addr: %#X:%d\n",
+ (uint32_t)eeOpcode, (uint32_t) eeOpBits,
+ (uint32_t)eeAddr>>1, (uint32_t)eeAddrBits);
+ if (eeOpBits == 8 && !(eeOpcode == EEPROM_READ_OPCODE_SPI ||
+ eeOpcode == EEPROM_RDSR_OPCODE_SPI ))
+ panic("Unknown eeprom opcode: %#X:%d\n", (uint32_t)eeOpcode,
+ (uint32_t)eeOpBits);
+
+
+ }
+ // If driver requests eeprom access, immediately give it to it
+ regs.eecd.ee_gnt(regs.eecd.ee_req());
+ break;
+ case REG_EERD:
+ regs.eerd = val;
+ if (regs.eerd.start()) {
+ regs.eerd.done(1);
+ assert(regs.eerd.addr() < EEPROM_SIZE);
+ regs.eerd.data(flash[regs.eerd.addr()]);
+ regs.eerd.start(0);
+ DPRINTF(EthernetEEPROM, "EEPROM: read addr: %#X data %#x\n",
+ regs.eerd.addr(), regs.eerd.data());
+ }
+ break;
+ case REG_MDIC:
+ regs.mdic = val;
+ if (regs.mdic.i())
+ panic("No support for interrupt on mdic complete\n");
+ if (regs.mdic.phyadd() != 1)
+ panic("No support for reading anything but phy\n");
+ DPRINTF(Ethernet, "%s phy address %x\n",
+ regs.mdic.op() == 1 ? "Writing" : "Reading",
+ regs.mdic.regadd());
+ switch (regs.mdic.regadd()) {
+ case PHY_PSTATUS:
+ regs.mdic.data(0x796D); // link up
+ break;
+ case PHY_PID:
+ regs.mdic.data(params()->phy_pid);
+ break;
+ case PHY_EPID:
+ regs.mdic.data(params()->phy_epid);
+ break;
+ case PHY_GSTATUS:
+ regs.mdic.data(0x7C00);
+ break;
+ case PHY_EPSTATUS:
+ regs.mdic.data(0x3000);
+ break;
+ case PHY_AGC:
+ regs.mdic.data(0x180); // some random length
+ break;
+ default:
+ regs.mdic.data(0);
+ }
+ regs.mdic.r(1);
+ break;
+ case REG_ICR:
+ DPRINTF(Ethernet, "Writing ICR. ICR=%#x IMR=%#x IAM=%#x IAME=%d\n",
+ regs.icr(), regs.imr, regs.iam, regs.ctrl_ext.iame());
+ if (regs.ctrl_ext.iame())
+ regs.imr &= ~regs.iam;
+ regs.icr = ~bits(val,30,0) & regs.icr();
+ chkInterrupt();
+ break;
+ case REG_ITR:
+ regs.itr = val;
+ break;
+ case REG_ICS:
+ DPRINTF(EthernetIntr, "Posting interrupt because of ICS write\n");
+ postInterrupt((IntTypes)val);
+ break;
+ case REG_IMS:
+ regs.imr |= val;
+ chkInterrupt();
+ break;
+ case REG_IMC:
+ regs.imr &= ~val;
+ chkInterrupt();
+ break;
+ case REG_IAM:
+ regs.iam = val;
+ break;
+ case REG_RCTL:
+ oldrctl = regs.rctl;
+ regs.rctl = val;
+ if (regs.rctl.rst()) {
+ rxDescCache.reset();
+ DPRINTF(EthernetSM, "RXS: Got RESET!\n");
+ rxFifo.clear();
+ regs.rctl.rst(0);
+ }
+ if (regs.rctl.en())
+ rxTick = true;
+ restartClock();
+ break;
+ case REG_FCTTV:
+ regs.fcttv = val;
+ break;
+ case REG_TCTL:
+ regs.tctl = val;
+ oldtctl = regs.tctl;
+ regs.tctl = val;
+ if (regs.tctl.en())
+ txTick = true;
+ restartClock();
+ if (regs.tctl.en() && !oldtctl.en()) {
+ txDescCache.reset();
+ }
+ break;
+ case REG_PBA:
+ regs.pba.rxa(val);
+ regs.pba.txa(64 - regs.pba.rxa());
+ break;
+ case REG_WUC:
+ case REG_LEDCTL:
+ case REG_FCAL:
+ case REG_FCAH:
+ case REG_FCT:
+ case REG_VET:
+ case REG_AIFS:
+ case REG_TIPG:
+ ; // We don't care, so don't store anything
+ break;
+ case REG_IVAR0:
+ warn("Writing to IVAR0, ignoring...\n");
+ break;
+ case REG_FCRTL:
+ regs.fcrtl = val;
+ break;
+ case REG_FCRTH:
+ regs.fcrth = val;
+ break;
+ case REG_RDBAL:
+ regs.rdba.rdbal( val & ~mask(4));
+ rxDescCache.areaChanged();
+ break;
+ case REG_RDBAH:
+ regs.rdba.rdbah(val);
+ rxDescCache.areaChanged();
+ break;
+ case REG_RDLEN:
+ regs.rdlen = val & ~mask(7);
+ rxDescCache.areaChanged();
+ break;
+ case REG_SRRCTL:
+ regs.srrctl = val;
+ break;
+ case REG_RDH:
+ regs.rdh = val;
+ rxDescCache.areaChanged();
+ break;
+ case REG_RDT:
+ regs.rdt = val;
+ DPRINTF(EthernetSM, "RXS: RDT Updated.\n");
+ if (drainState() == DrainState::Running) {
+ DPRINTF(EthernetSM, "RXS: RDT Fetching Descriptors!\n");
+ rxDescCache.fetchDescriptors();
+ } else {
+ DPRINTF(EthernetSM, "RXS: RDT NOT Fetching Desc b/c draining!\n");
+ }
+ break;
+ case REG_RDTR:
+ regs.rdtr = val;
+ break;
+ case REG_RADV:
+ regs.radv = val;
+ break;
+ case REG_RXDCTL:
+ regs.rxdctl = val;
+ break;
+ case REG_TDBAL:
+ regs.tdba.tdbal( val & ~mask(4));
+ txDescCache.areaChanged();
+ break;
+ case REG_TDBAH:
+ regs.tdba.tdbah(val);
+ txDescCache.areaChanged();
+ break;
+ case REG_TDLEN:
+ regs.tdlen = val & ~mask(7);
+ txDescCache.areaChanged();
+ break;
+ case REG_TDH:
+ regs.tdh = val;
+ txDescCache.areaChanged();
+ break;
+ case REG_TXDCA_CTL:
+ regs.txdca_ctl = val;
+ if (regs.txdca_ctl.enabled())
+ panic("No support for DCA\n");
+ break;
+ case REG_TDT:
+ regs.tdt = val;
+ DPRINTF(EthernetSM, "TXS: TX Tail pointer updated\n");
+ if (drainState() == DrainState::Running) {
+ DPRINTF(EthernetSM, "TXS: TDT Fetching Descriptors!\n");
+ txDescCache.fetchDescriptors();
+ } else {
+ DPRINTF(EthernetSM, "TXS: TDT NOT Fetching Desc b/c draining!\n");
+ }
+ break;
+ case REG_TIDV:
+ regs.tidv = val;
+ break;
+ case REG_TXDCTL:
+ regs.txdctl = val;
+ break;
+ case REG_TADV:
+ regs.tadv = val;
+ break;
+ case REG_TDWBAL:
+ regs.tdwba &= ~mask(32);
+ regs.tdwba |= val;
+ txDescCache.completionWriteback(regs.tdwba & ~mask(1),
+ regs.tdwba & mask(1));
+ break;
+ case REG_TDWBAH:
+ regs.tdwba &= mask(32);
+ regs.tdwba |= (uint64_t)val << 32;
+ txDescCache.completionWriteback(regs.tdwba & ~mask(1),
+ regs.tdwba & mask(1));
+ break;
+ case REG_RXCSUM:
+ regs.rxcsum = val;
+ break;
+ case REG_RLPML:
+ regs.rlpml = val;
+ break;
+ case REG_RFCTL:
+ regs.rfctl = val;
+ if (regs.rfctl.exsten())
+ panic("Extended RX descriptors not implemented\n");
+ break;
+ case REG_MANC:
+ regs.manc = val;
+ break;
+ case REG_SWSM:
+ regs.swsm = val;
+ if (regs.fwsm.eep_fw_semaphore())
+ regs.swsm.swesmbi(0);
+ break;
+ case REG_SWFWSYNC:
+ regs.sw_fw_sync = val;
+ break;
+ default:
+ if (!IN_RANGE(daddr, REG_VFTA, VLAN_FILTER_TABLE_SIZE*4) &&
+ !IN_RANGE(daddr, REG_RAL, RCV_ADDRESS_TABLE_SIZE*8) &&
+ !IN_RANGE(daddr, REG_MTA, MULTICAST_TABLE_SIZE*4))
+ panic("Write request to unknown register number: %#x\n", daddr);
+ };
+
+ pkt->makeAtomicResponse();
+ return pioDelay;
+}
+
+void
+IGbE::postInterrupt(IntTypes t, bool now)
+{
+ assert(t);
+
+ // Interrupt is already pending
+ if (t & regs.icr() && !now)
+ return;
+
+ regs.icr = regs.icr() | t;
+
+ Tick itr_interval = SimClock::Int::ns * 256 * regs.itr.interval();
+ DPRINTF(EthernetIntr,
+ "EINT: postInterrupt() curTick(): %d itr: %d interval: %d\n",
+ curTick(), regs.itr.interval(), itr_interval);
+
+ if (regs.itr.interval() == 0 || now ||
+ lastInterrupt + itr_interval <= curTick()) {
+ if (interEvent.scheduled()) {
+ deschedule(interEvent);
+ }
+ cpuPostInt();
+ } else {
+ Tick int_time = lastInterrupt + itr_interval;
+ assert(int_time > 0);
+ DPRINTF(EthernetIntr, "EINT: Scheduling timer interrupt for tick %d\n",
+ int_time);
+ if (!interEvent.scheduled()) {
+ schedule(interEvent, int_time);
+ }
+ }
+}
+
+void
+IGbE::delayIntEvent()
+{
+ cpuPostInt();
+}
+
+
+void
+IGbE::cpuPostInt()
+{
+
+ postedInterrupts++;
+
+ if (!(regs.icr() & regs.imr)) {
+ DPRINTF(Ethernet, "Interrupt Masked. Not Posting\n");
+ return;
+ }
+
+ DPRINTF(Ethernet, "Posting Interrupt\n");
+
+
+ if (interEvent.scheduled()) {
+ deschedule(interEvent);
+ }
+
+ if (rdtrEvent.scheduled()) {
+ regs.icr.rxt0(1);
+ deschedule(rdtrEvent);
+ }
+ if (radvEvent.scheduled()) {
+ regs.icr.rxt0(1);
+ deschedule(radvEvent);
+ }
+ if (tadvEvent.scheduled()) {
+ regs.icr.txdw(1);
+ deschedule(tadvEvent);
+ }
+ if (tidvEvent.scheduled()) {
+ regs.icr.txdw(1);
+ deschedule(tidvEvent);
+ }
+
+ regs.icr.int_assert(1);
+ DPRINTF(EthernetIntr, "EINT: Posting interrupt to CPU now. Vector %#x\n",
+ regs.icr());
+
+ intrPost();
+
+ lastInterrupt = curTick();
+}
+
+void
+IGbE::cpuClearInt()
+{
+ if (regs.icr.int_assert()) {
+ regs.icr.int_assert(0);
+ DPRINTF(EthernetIntr,
+ "EINT: Clearing interrupt to CPU now. Vector %#x\n",
+ regs.icr());
+ intrClear();
+ }
+}
+
+void
+IGbE::chkInterrupt()
+{
+ DPRINTF(Ethernet, "Checking interrupts icr: %#x imr: %#x\n", regs.icr(),
+ regs.imr);
+ // Check if we need to clear the cpu interrupt
+ if (!(regs.icr() & regs.imr)) {
+ DPRINTF(Ethernet, "Mask cleaned all interrupts\n");
+ if (interEvent.scheduled())
+ deschedule(interEvent);
+ if (regs.icr.int_assert())
+ cpuClearInt();
+ }
+ DPRINTF(Ethernet, "ITR = %#X itr.interval = %#X\n",
+ regs.itr(), regs.itr.interval());
+
+ if (regs.icr() & regs.imr) {
+ if (regs.itr.interval() == 0) {
+ cpuPostInt();
+ } else {
+ DPRINTF(Ethernet,
+ "Possibly scheduling interrupt because of imr write\n");
+ if (!interEvent.scheduled()) {
+ Tick t = curTick() + SimClock::Int::ns * 256 * regs.itr.interval();
+ DPRINTF(Ethernet, "Scheduling for %d\n", t);
+ schedule(interEvent, t);
+ }
+ }
+ }
+}
+
+
+///////////////////////////// IGbE::DescCache //////////////////////////////
+
+template<class T>
+IGbE::DescCache<T>::DescCache(IGbE *i, const std::string n, int s)
+ : igbe(i), _name(n), cachePnt(0), size(s), curFetching(0),
+ wbOut(0), moreToWb(false), wbAlignment(0), pktPtr(NULL),
+ wbDelayEvent(this), fetchDelayEvent(this), fetchEvent(this),
+ wbEvent(this)
+{
+ fetchBuf = new T[size];
+ wbBuf = new T[size];
+}
+
+template<class T>
+IGbE::DescCache<T>::~DescCache()
+{
+ reset();
+ delete[] fetchBuf;
+ delete[] wbBuf;
+}
+
+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->drainState() != DrainState::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->drainState() != DrainState::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(CheckpointOut &cp) const
+{
+ 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(cp, 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(cp, 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(CheckpointIn &cp)
+{
+ 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, 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, 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 //////////////////////////////
+
+IGbE::RxDescCache::RxDescCache(IGbE *i, const std::string n, int s)
+ : DescCache<RxDesc>(i, n, s), pktDone(false), splitCount(0),
+ 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
+IGbE::RxDescCache::pktSplitDone()
+{
+ splitCount++;
+ DPRINTF(EthernetDesc,
+ "Part of split packet done: splitcount now %d\n", splitCount);
+ assert(splitCount <= 2);
+ if (splitCount != 2)
+ return;
+ splitCount = 0;
+ DPRINTF(EthernetDesc,
+ "Part of split packet done: calling pktComplete()\n");
+ pktComplete();
+}
+
+int
+IGbE::RxDescCache::writePacket(EthPacketPtr packet, int pkt_offset)
+{
+ assert(unusedCache.size());
+ //if (!unusedCache.size())
+ // return false;
+
+ pktPtr = packet;
+ pktDone = false;
+ unsigned buf_len, hdr_len;
+
+ RxDesc *desc = unusedCache.front();
+ switch (igbe->regs.srrctl.desctype()) {
+ case RXDT_LEGACY:
+ assert(pkt_offset == 0);
+ bytesCopied = packet->length;
+ DPRINTF(EthernetDesc, "Packet Length: %d Desc Size: %d\n",
+ packet->length, igbe->regs.rctl.descSize());
+ assert(packet->length < igbe->regs.rctl.descSize());
+ igbe->dmaWrite(pciToDma(desc->legacy.buf),
+ packet->length, &pktEvent, packet->data,
+ igbe->rxWriteDelay);
+ break;
+ case RXDT_ADV_ONEBUF:
+ assert(pkt_offset == 0);
+ bytesCopied = packet->length;
+ buf_len = igbe->regs.rctl.lpe() ? igbe->regs.srrctl.bufLen() :
+ igbe->regs.rctl.descSize();
+ DPRINTF(EthernetDesc, "Packet Length: %d srrctl: %#x Desc Size: %d\n",
+ packet->length, igbe->regs.srrctl(), buf_len);
+ assert(packet->length < buf_len);
+ igbe->dmaWrite(pciToDma(desc->adv_read.pkt),
+ packet->length, &pktEvent, packet->data,
+ igbe->rxWriteDelay);
+ desc->adv_wb.header_len = htole(0);
+ desc->adv_wb.sph = htole(0);
+ desc->adv_wb.pkt_len = htole((uint16_t)(pktPtr->length));
+ break;
+ case RXDT_ADV_SPLIT_A:
+ int split_point;
+
+ buf_len = igbe->regs.rctl.lpe() ? igbe->regs.srrctl.bufLen() :
+ igbe->regs.rctl.descSize();
+ hdr_len = igbe->regs.rctl.lpe() ? igbe->regs.srrctl.hdrLen() : 0;
+ DPRINTF(EthernetDesc,
+ "lpe: %d Packet Length: %d offset: %d srrctl: %#x "
+ "hdr addr: %#x Hdr Size: %d desc addr: %#x Desc Size: %d\n",
+ igbe->regs.rctl.lpe(), packet->length, pkt_offset,
+ igbe->regs.srrctl(), desc->adv_read.hdr, hdr_len,
+ desc->adv_read.pkt, buf_len);
+
+ split_point = hsplit(pktPtr);
+
+ if (packet->length <= hdr_len) {
+ bytesCopied = packet->length;
+ assert(pkt_offset == 0);
+ DPRINTF(EthernetDesc, "Hdr split: Entire packet in header\n");
+ igbe->dmaWrite(pciToDma(desc->adv_read.hdr),
+ packet->length, &pktEvent, packet->data,
+ igbe->rxWriteDelay);
+ desc->adv_wb.header_len = htole((uint16_t)packet->length);
+ desc->adv_wb.sph = htole(0);
+ desc->adv_wb.pkt_len = htole(0);
+ } else if (split_point) {
+ if (pkt_offset) {
+ // we are only copying some data, header/data has already been
+ // copied
+ int max_to_copy =
+ std::min(packet->length - pkt_offset, buf_len);
+ bytesCopied += max_to_copy;
+ DPRINTF(EthernetDesc,
+ "Hdr split: Continuing data buffer copy\n");
+ igbe->dmaWrite(pciToDma(desc->adv_read.pkt),
+ max_to_copy, &pktEvent,
+ packet->data + pkt_offset, igbe->rxWriteDelay);
+ desc->adv_wb.header_len = htole(0);
+ desc->adv_wb.pkt_len = htole((uint16_t)max_to_copy);
+ desc->adv_wb.sph = htole(0);
+ } else {
+ int max_to_copy =
+ std::min(packet->length - split_point, buf_len);
+ bytesCopied += max_to_copy + split_point;
+
+ DPRINTF(EthernetDesc, "Hdr split: splitting at %d\n",
+ split_point);
+ igbe->dmaWrite(pciToDma(desc->adv_read.hdr),
+ split_point, &pktHdrEvent,
+ packet->data, igbe->rxWriteDelay);
+ igbe->dmaWrite(pciToDma(desc->adv_read.pkt),
+ max_to_copy, &pktDataEvent,
+ packet->data + split_point, igbe->rxWriteDelay);
+ desc->adv_wb.header_len = htole(split_point);
+ desc->adv_wb.sph = 1;
+ desc->adv_wb.pkt_len = htole((uint16_t)(max_to_copy));
+ }
+ } else {
+ panic("Header split not fitting within header buffer or "
+ "undecodable packet not fitting in header unsupported\n");
+ }
+ break;
+ default:
+ panic("Unimplemnted RX receive buffer type: %d\n",
+ igbe->regs.srrctl.desctype());
+ }
+ return bytesCopied;
+
+}
+
+void
+IGbE::RxDescCache::pktComplete()
+{
+ assert(unusedCache.size());
+ 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,
+ htole((uint16_t)(pktPtr->length + crcfixup)),
+ (uint16_t)(pktPtr->length + crcfixup));
+
+ // no support for anything but starting at 0
+ assert(igbe->regs.rxcsum.pcss() == 0);
+
+ DPRINTF(EthernetDesc, "Packet written to memory updating Descriptor\n");
+
+ uint16_t status = RXDS_DD;
+ uint8_t err = 0;
+ uint16_t ext_err = 0;
+ uint16_t csum = 0;
+ uint16_t ptype = 0;
+ uint16_t ip_id = 0;
+
+ assert(bytesCopied <= pktPtr->length);
+ if (bytesCopied == pktPtr->length)
+ status |= RXDS_EOP;
+
+ IpPtr ip(pktPtr);
+
+ if (ip) {
+ DPRINTF(EthernetDesc, "Proccesing Ip packet with Id=%d\n", ip->id());
+ ptype |= RXDP_IPV4;
+ ip_id = ip->id();
+
+ if (igbe->regs.rxcsum.ipofld()) {
+ DPRINTF(EthernetDesc, "Checking IP checksum\n");
+ status |= RXDS_IPCS;
+ csum = htole(cksum(ip));
+ igbe->rxIpChecksums++;
+ if (cksum(ip) != 0) {
+ err |= RXDE_IPE;
+ ext_err |= RXDEE_IPE;
+ DPRINTF(EthernetDesc, "Checksum is bad!!\n");
+ }
+ }
+ TcpPtr tcp(ip);
+ if (tcp && igbe->regs.rxcsum.tuofld()) {
+ DPRINTF(EthernetDesc, "Checking TCP checksum\n");
+ status |= RXDS_TCPCS;
+ ptype |= RXDP_TCP;
+ csum = htole(cksum(tcp));
+ igbe->rxTcpChecksums++;
+ if (cksum(tcp) != 0) {
+ DPRINTF(EthernetDesc, "Checksum is bad!!\n");
+ err |= RXDE_TCPE;
+ ext_err |= RXDEE_TCPE;
+ }
+ }
+
+ UdpPtr udp(ip);
+ if (udp && igbe->regs.rxcsum.tuofld()) {
+ DPRINTF(EthernetDesc, "Checking UDP checksum\n");
+ status |= RXDS_UDPCS;
+ ptype |= RXDP_UDP;
+ csum = htole(cksum(udp));
+ igbe->rxUdpChecksums++;
+ if (cksum(udp) != 0) {
+ DPRINTF(EthernetDesc, "Checksum is bad!!\n");
+ ext_err |= RXDEE_TCPE;
+ err |= RXDE_TCPE;
+ }
+ }
+ } else { // if ip
+ DPRINTF(EthernetSM, "Proccesing Non-Ip packet\n");
+ }
+
+ switch (igbe->regs.srrctl.desctype()) {
+ case RXDT_LEGACY:
+ desc->legacy.len = htole((uint16_t)(pktPtr->length + crcfixup));
+ desc->legacy.status = htole(status);
+ desc->legacy.errors = htole(err);
+ // No vlan support at this point... just set it to 0
+ desc->legacy.vlan = 0;
+ break;
+ case RXDT_ADV_SPLIT_A:
+ case RXDT_ADV_ONEBUF:
+ desc->adv_wb.rss_type = htole(0);
+ desc->adv_wb.pkt_type = htole(ptype);
+ if (igbe->regs.rxcsum.pcsd()) {
+ // no rss support right now
+ desc->adv_wb.rss_hash = htole(0);
+ } else {
+ desc->adv_wb.id = htole(ip_id);
+ desc->adv_wb.csum = htole(csum);
+ }
+ desc->adv_wb.status = htole(status);
+ desc->adv_wb.errors = htole(ext_err);
+ // no vlan support
+ desc->adv_wb.vlan_tag = htole(0);
+ break;
+ default:
+ panic("Unimplemnted RX receive buffer type %d\n",
+ igbe->regs.srrctl.desctype());
+ }
+
+ DPRINTF(EthernetDesc, "Descriptor complete w0: %#x w1: %#x\n",
+ desc->adv_read.pkt, desc->adv_read.hdr);
+
+ if (bytesCopied == pktPtr->length) {
+ DPRINTF(EthernetDesc,
+ "Packet completely written to descriptor buffers\n");
+ // Deal with the rx timer interrupts
+ if (igbe->regs.rdtr.delay()) {
+ Tick delay = igbe->regs.rdtr.delay() * igbe->intClock();
+ DPRINTF(EthernetSM, "RXS: Scheduling DTR for %d\n", delay);
+ igbe->reschedule(igbe->rdtrEvent, curTick() + delay);
+ }
+
+ if (igbe->regs.radv.idv()) {
+ Tick delay = igbe->regs.radv.idv() * igbe->intClock();
+ DPRINTF(EthernetSM, "RXS: Scheduling ADV for %d\n", delay);
+ if (!igbe->radvEvent.scheduled()) {
+ igbe->schedule(igbe->radvEvent, curTick() + delay);
+ }
+ }
+
+ // if neither radv or rdtr, maybe itr is set...
+ if (!igbe->regs.rdtr.delay() && !igbe->regs.radv.idv()) {
+ DPRINTF(EthernetSM,
+ "RXS: Receive interrupt delay disabled, posting IT_RXT\n");
+ igbe->postInterrupt(IT_RXT);
+ }
+
+ // If the packet is small enough, interrupt appropriately
+ // I wonder if this is delayed or not?!
+ if (pktPtr->length <= igbe->regs.rsrpd.idv()) {
+ DPRINTF(EthernetSM,
+ "RXS: Posting IT_SRPD beacuse small packet received\n");
+ igbe->postInterrupt(IT_SRPD);
+ }
+ bytesCopied = 0;
+ }
+
+ pktPtr = NULL;
+ igbe->checkDrain();
+ 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);
+}
+
+void
+IGbE::RxDescCache::enableSm()
+{
+ if (igbe->drainState() != DrainState::Draining) {
+ igbe->rxTick = true;
+ igbe->restartClock();
+ }
+}
+
+bool
+IGbE::RxDescCache::packetDone()
+{
+ if (pktDone) {
+ pktDone = false;
+ return true;
+ }
+ return false;
+}
+
+bool
+IGbE::RxDescCache::hasOutstandingEvents()
+{
+ return pktEvent.scheduled() || wbEvent.scheduled() ||
+ fetchEvent.scheduled() || pktHdrEvent.scheduled() ||
+ pktDataEvent.scheduled();
+
+}
+
+void
+IGbE::RxDescCache::serialize(CheckpointOut &cp) const
+{
+ DescCache<RxDesc>::serialize(cp);
+ SERIALIZE_SCALAR(pktDone);
+ SERIALIZE_SCALAR(splitCount);
+ SERIALIZE_SCALAR(bytesCopied);
+}
+
+void
+IGbE::RxDescCache::unserialize(CheckpointIn &cp)
+{
+ DescCache<RxDesc>::unserialize(cp);
+ UNSERIALIZE_SCALAR(pktDone);
+ UNSERIALIZE_SCALAR(splitCount);
+ UNSERIALIZE_SCALAR(bytesCopied);
+}
+
+
+///////////////////////////// IGbE::TxDescCache //////////////////////////////
+
+IGbE::TxDescCache::TxDescCache(IGbE *i, const std::string n, int s)
+ : DescCache<TxDesc>(i,n, s), pktDone(false), isTcp(false),
+ pktWaiting(false), pktMultiDesc(false),
+ completionAddress(0), completionEnabled(false),
+ useTso(false), tsoHeaderLen(0), tsoMss(0), tsoTotalLen(0), tsoUsedLen(0),
+ tsoPrevSeq(0), tsoPktPayloadBytes(0), tsoLoadedHeader(false),
+ tsoPktHasHeader(false), tsoDescBytesUsed(0), tsoCopyBytes(0), tsoPkts(0),
+ 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
+IGbE::TxDescCache::processContextDesc()
+{
+ assert(unusedCache.size());
+ TxDesc *desc;
+
+ DPRINTF(EthernetDesc, "Checking and processing context descriptors\n");
+
+ while (!useTso && unusedCache.size() &&
+ TxdOp::isContext(unusedCache.front())) {
+ DPRINTF(EthernetDesc, "Got context descriptor type...\n");
+
+ desc = unusedCache.front();
+ DPRINTF(EthernetDesc, "Descriptor upper: %#x lower: %#X\n",
+ desc->d1, desc->d2);
+
+
+ // is this going to be a tcp or udp packet?
+ isTcp = TxdOp::tcp(desc) ? true : false;
+
+ // setup all the TSO variables, they'll be ignored if we don't use
+ // tso for this connection
+ tsoHeaderLen = TxdOp::hdrlen(desc);
+ tsoMss = TxdOp::mss(desc);
+
+ if (TxdOp::isType(desc, TxdOp::TXD_CNXT) && TxdOp::tse(desc)) {
+ DPRINTF(EthernetDesc, "TCP offload enabled for packet hdrlen: "
+ "%d mss: %d paylen %d\n", TxdOp::hdrlen(desc),
+ TxdOp::mss(desc), TxdOp::getLen(desc));
+ useTso = true;
+ tsoTotalLen = TxdOp::getLen(desc);
+ tsoLoadedHeader = false;
+ tsoDescBytesUsed = 0;
+ tsoUsedLen = 0;
+ tsoPrevSeq = 0;
+ tsoPktHasHeader = false;
+ tsoPkts = 0;
+ tsoCopyBytes = 0;
+ }
+
+ TxdOp::setDd(desc);
+ unusedCache.pop_front();
+ igbe->anDq("TXS", annUnusedCacheQ);
+ usedCache.push_back(desc);
+ igbe->anQ("TXS", annUsedCacheQ);
+ }
+
+ if (!unusedCache.size())
+ return;
+
+ desc = unusedCache.front();
+ if (!useTso && TxdOp::isType(desc, TxdOp::TXD_ADVDATA) &&
+ TxdOp::tse(desc)) {
+ DPRINTF(EthernetDesc, "TCP offload(adv) enabled for packet "
+ "hdrlen: %d mss: %d paylen %d\n",
+ tsoHeaderLen, tsoMss, TxdOp::getTsoLen(desc));
+ useTso = true;
+ tsoTotalLen = TxdOp::getTsoLen(desc);
+ tsoLoadedHeader = false;
+ tsoDescBytesUsed = 0;
+ tsoUsedLen = 0;
+ tsoPrevSeq = 0;
+ tsoPktHasHeader = false;
+ tsoPkts = 0;
+ }
+
+ if (useTso && !tsoLoadedHeader) {
+ // we need to fetch a header
+ DPRINTF(EthernetDesc, "Starting DMA of TSO header\n");
+ assert(TxdOp::isData(desc) && TxdOp::getLen(desc) >= tsoHeaderLen);
+ pktWaiting = true;
+ assert(tsoHeaderLen <= 256);
+ igbe->dmaRead(pciToDma(TxdOp::getBuf(desc)),
+ tsoHeaderLen, &headerEvent, tsoHeader, 0);
+ }
+}
+
+void
+IGbE::TxDescCache::headerComplete()
+{
+ DPRINTF(EthernetDesc, "TSO: Fetching TSO header complete\n");
+ pktWaiting = false;
+
+ assert(unusedCache.size());
+ TxDesc *desc = unusedCache.front();
+ DPRINTF(EthernetDesc, "TSO: len: %d tsoHeaderLen: %d\n",
+ TxdOp::getLen(desc), tsoHeaderLen);
+
+ if (TxdOp::getLen(desc) == tsoHeaderLen) {
+ tsoDescBytesUsed = 0;
+ tsoLoadedHeader = true;
+ unusedCache.pop_front();
+ usedCache.push_back(desc);
+ } else {
+ DPRINTF(EthernetDesc, "TSO: header part of larger payload\n");
+ tsoDescBytesUsed = tsoHeaderLen;
+ tsoLoadedHeader = true;
+ }
+ enableSm();
+ igbe->checkDrain();
+}
+
+unsigned
+IGbE::TxDescCache::getPacketSize(EthPacketPtr p)
+{
+ if (!unusedCache.size())
+ return 0;
+
+ DPRINTF(EthernetDesc, "Starting processing of descriptor\n");
+
+ assert(!useTso || tsoLoadedHeader);
+ TxDesc *desc = unusedCache.front();
+
+ if (useTso) {
+ DPRINTF(EthernetDesc, "getPacket(): TxDescriptor data "
+ "d1: %#llx d2: %#llx\n", desc->d1, desc->d2);
+ DPRINTF(EthernetDesc, "TSO: use: %d hdrlen: %d mss: %d total: %d "
+ "used: %d loaded hdr: %d\n", useTso, tsoHeaderLen, tsoMss,
+ tsoTotalLen, tsoUsedLen, tsoLoadedHeader);
+
+ if (tsoPktHasHeader)
+ tsoCopyBytes = std::min((tsoMss + tsoHeaderLen) - p->length,
+ TxdOp::getLen(desc) - tsoDescBytesUsed);
+ else
+ tsoCopyBytes = std::min(tsoMss,
+ TxdOp::getLen(desc) - tsoDescBytesUsed);
+ unsigned pkt_size =
+ tsoCopyBytes + (tsoPktHasHeader ? 0 : tsoHeaderLen);
+
+ DPRINTF(EthernetDesc, "TSO: descBytesUsed: %d copyBytes: %d "
+ "this descLen: %d\n",
+ tsoDescBytesUsed, tsoCopyBytes, TxdOp::getLen(desc));
+ DPRINTF(EthernetDesc, "TSO: pktHasHeader: %d\n", tsoPktHasHeader);
+ DPRINTF(EthernetDesc, "TSO: Next packet is %d bytes\n", pkt_size);
+ return pkt_size;
+ }
+
+ DPRINTF(EthernetDesc, "Next TX packet is %d bytes\n",
+ TxdOp::getLen(unusedCache.front()));
+ return TxdOp::getLen(desc);
+}
+
+void
+IGbE::TxDescCache::getPacketData(EthPacketPtr p)
+{
+ assert(unusedCache.size());
+
+ TxDesc *desc;
+ desc = unusedCache.front();
+
+ DPRINTF(EthernetDesc, "getPacketData(): TxDescriptor data "
+ "d1: %#llx d2: %#llx\n", desc->d1, desc->d2);
+ assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) &&
+ TxdOp::getLen(desc));
+
+ pktPtr = p;
+
+ pktWaiting = true;
+
+ DPRINTF(EthernetDesc, "Starting DMA of packet at offset %d\n", p->length);
+
+ if (useTso) {
+ assert(tsoLoadedHeader);
+ if (!tsoPktHasHeader) {
+ DPRINTF(EthernetDesc,
+ "Loading TSO header (%d bytes) into start of packet\n",
+ tsoHeaderLen);
+ memcpy(p->data, &tsoHeader,tsoHeaderLen);
+ p->length +=tsoHeaderLen;
+ tsoPktHasHeader = true;
+ }
+ }
+
+ if (useTso) {
+ DPRINTF(EthernetDesc,
+ "Starting DMA of packet at offset %d length: %d\n",
+ p->length, tsoCopyBytes);
+ igbe->dmaRead(pciToDma(TxdOp::getBuf(desc))
+ + tsoDescBytesUsed,
+ tsoCopyBytes, &pktEvent, p->data + p->length,
+ igbe->txReadDelay);
+ tsoDescBytesUsed += tsoCopyBytes;
+ assert(tsoDescBytesUsed <= TxdOp::getLen(desc));
+ } else {
+ igbe->dmaRead(pciToDma(TxdOp::getBuf(desc)),
+ TxdOp::getLen(desc), &pktEvent, p->data + p->length,
+ igbe->txReadDelay);
+ }
+}
+
+void
+IGbE::TxDescCache::pktComplete()
+{
+
+ TxDesc *desc;
+ assert(unusedCache.size());
+ assert(pktPtr);
+
+ igbe->anBegin("TXS", "Update Desc");
+
+ DPRINTF(EthernetDesc, "DMA of packet complete\n");
+
+
+ desc = unusedCache.front();
+ assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) &&
+ TxdOp::getLen(desc));
+
+ DPRINTF(EthernetDesc, "TxDescriptor data d1: %#llx d2: %#llx\n",
+ desc->d1, desc->d2);
+
+ // Set the length of the data in the EtherPacket
+ if (useTso) {
+ DPRINTF(EthernetDesc, "TSO: use: %d hdrlen: %d mss: %d total: %d "
+ "used: %d loaded hdr: %d\n", useTso, tsoHeaderLen, tsoMss,
+ tsoTotalLen, tsoUsedLen, tsoLoadedHeader);
+ pktPtr->length += tsoCopyBytes;
+ tsoUsedLen += tsoCopyBytes;
+ DPRINTF(EthernetDesc, "TSO: descBytesUsed: %d copyBytes: %d\n",
+ tsoDescBytesUsed, tsoCopyBytes);
+ } else
+ pktPtr->length += TxdOp::getLen(desc);
+
+
+
+ if ((!TxdOp::eop(desc) && !useTso) ||
+ (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;
+ pktDone = true;
+ pktWaiting = false;
+ pktMultiDesc = true;
+
+ DPRINTF(EthernetDesc, "Partial Packet Descriptor of %d bytes Done\n",
+ pktPtr->length);
+ pktPtr = NULL;
+
+ enableSm();
+ igbe->checkDrain();
+ return;
+ }
+
+
+ pktMultiDesc = false;
+ // no support for vlans
+ assert(!TxdOp::vle(desc));
+
+ // we only support single packet descriptors at this point
+ if (!useTso)
+ assert(TxdOp::eop(desc));
+
+ // set that this packet is done
+ if (TxdOp::rs(desc))
+ TxdOp::setDd(desc);
+
+ DPRINTF(EthernetDesc, "TxDescriptor data d1: %#llx d2: %#llx\n",
+ desc->d1, desc->d2);
+
+ if (useTso) {
+ IpPtr ip(pktPtr);
+ if (ip) {
+ DPRINTF(EthernetDesc, "TSO: Modifying IP header. Id + %d\n",
+ tsoPkts);
+ ip->id(ip->id() + tsoPkts++);
+ ip->len(pktPtr->length - EthPtr(pktPtr)->size());
+
+ TcpPtr tcp(ip);
+ if (tcp) {
+ DPRINTF(EthernetDesc,
+ "TSO: Modifying TCP header. old seq %d + %d\n",
+ tcp->seq(), tsoPrevSeq);
+ tcp->seq(tcp->seq() + tsoPrevSeq);
+ if (tsoUsedLen != tsoTotalLen)
+ tcp->flags(tcp->flags() & ~9); // clear fin & psh
+ }
+ UdpPtr udp(ip);
+ if (udp) {
+ DPRINTF(EthernetDesc, "TSO: Modifying UDP header.\n");
+ udp->len(pktPtr->length - EthPtr(pktPtr)->size());
+ }
+ }
+ tsoPrevSeq = tsoUsedLen;
+ }
+
+ if (DTRACE(EthernetDesc)) {
+ IpPtr ip(pktPtr);
+ if (ip)
+ DPRINTF(EthernetDesc, "Proccesing Ip packet with Id=%d\n",
+ ip->id());
+ else
+ DPRINTF(EthernetSM, "Proccesing Non-Ip packet\n");
+ }
+
+ // Checksums are only ofloaded for new descriptor types
+ if (TxdOp::isData(desc) && ( TxdOp::ixsm(desc) || TxdOp::txsm(desc)) ) {
+ DPRINTF(EthernetDesc, "Calculating checksums for packet\n");
+ IpPtr ip(pktPtr);
+ assert(ip);
+ if (TxdOp::ixsm(desc)) {
+ ip->sum(0);
+ ip->sum(cksum(ip));
+ igbe->txIpChecksums++;
+ DPRINTF(EthernetDesc, "Calculated IP checksum\n");
+ }
+ if (TxdOp::txsm(desc)) {
+ TcpPtr tcp(ip);
+ UdpPtr udp(ip);
+ if (tcp) {
+ tcp->sum(0);
+ tcp->sum(cksum(tcp));
+ igbe->txTcpChecksums++;
+ DPRINTF(EthernetDesc, "Calculated TCP checksum\n");
+ } else if (udp) {
+ assert(udp);
+ udp->sum(0);
+ udp->sum(cksum(udp));
+ igbe->txUdpChecksums++;
+ DPRINTF(EthernetDesc, "Calculated UDP checksum\n");
+ } else {
+ panic("Told to checksum, but don't know how\n");
+ }
+ }
+ }
+
+ if (TxdOp::ide(desc)) {
+ // Deal with the rx timer interrupts
+ DPRINTF(EthernetDesc, "Descriptor had IDE set\n");
+ if (igbe->regs.tidv.idv()) {
+ Tick delay = igbe->regs.tidv.idv() * igbe->intClock();
+ DPRINTF(EthernetDesc, "setting tidv\n");
+ igbe->reschedule(igbe->tidvEvent, curTick() + delay, true);
+ }
+
+ if (igbe->regs.tadv.idv() && igbe->regs.tidv.idv()) {
+ Tick delay = igbe->regs.tadv.idv() * igbe->intClock();
+ DPRINTF(EthernetDesc, "setting tadv\n");
+ if (!igbe->tadvEvent.scheduled()) {
+ igbe->schedule(igbe->tadvEvent, curTick() + delay);
+ }
+ }
+ }
+
+
+ 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;
+ }
+
+ if (useTso && tsoUsedLen == tsoTotalLen)
+ useTso = false;
+
+
+ DPRINTF(EthernetDesc,
+ "------Packet of %d bytes ready for transmission-------\n",
+ pktPtr->length);
+ pktDone = true;
+ pktWaiting = false;
+ pktPtr = NULL;
+ 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);
+ }
+
+ enableSm();
+ igbe->checkDrain();
+}
+
+void
+IGbE::TxDescCache::actionAfterWb()
+{
+ DPRINTF(EthernetDesc, "actionAfterWb() completionEnabled: %d\n",
+ completionEnabled);
+ igbe->postInterrupt(iGbReg::IT_TXDW);
+ if (completionEnabled) {
+ descEnd = igbe->regs.tdh();
+ DPRINTF(EthernetDesc,
+ "Completion writing back value: %d to addr: %#x\n", descEnd,
+ completionAddress);
+ igbe->dmaWrite(pciToDma(mbits(completionAddress, 63, 2)),
+ sizeof(descEnd), &nullEvent, (uint8_t*)&descEnd, 0);
+ }
+}
+
+void
+IGbE::TxDescCache::serialize(CheckpointOut &cp) const
+{
+ DescCache<TxDesc>::serialize(cp);
+
+ SERIALIZE_SCALAR(pktDone);
+ SERIALIZE_SCALAR(isTcp);
+ SERIALIZE_SCALAR(pktWaiting);
+ SERIALIZE_SCALAR(pktMultiDesc);
+
+ SERIALIZE_SCALAR(useTso);
+ SERIALIZE_SCALAR(tsoHeaderLen);
+ SERIALIZE_SCALAR(tsoMss);
+ SERIALIZE_SCALAR(tsoTotalLen);
+ SERIALIZE_SCALAR(tsoUsedLen);
+ SERIALIZE_SCALAR(tsoPrevSeq);;
+ SERIALIZE_SCALAR(tsoPktPayloadBytes);
+ SERIALIZE_SCALAR(tsoLoadedHeader);
+ SERIALIZE_SCALAR(tsoPktHasHeader);
+ SERIALIZE_ARRAY(tsoHeader, 256);
+ SERIALIZE_SCALAR(tsoDescBytesUsed);
+ SERIALIZE_SCALAR(tsoCopyBytes);
+ SERIALIZE_SCALAR(tsoPkts);
+
+ SERIALIZE_SCALAR(completionAddress);
+ SERIALIZE_SCALAR(completionEnabled);
+ SERIALIZE_SCALAR(descEnd);
+}
+
+void
+IGbE::TxDescCache::unserialize(CheckpointIn &cp)
+{
+ DescCache<TxDesc>::unserialize(cp);
+
+ UNSERIALIZE_SCALAR(pktDone);
+ UNSERIALIZE_SCALAR(isTcp);
+ UNSERIALIZE_SCALAR(pktWaiting);
+ UNSERIALIZE_SCALAR(pktMultiDesc);
+
+ UNSERIALIZE_SCALAR(useTso);
+ UNSERIALIZE_SCALAR(tsoHeaderLen);
+ UNSERIALIZE_SCALAR(tsoMss);
+ UNSERIALIZE_SCALAR(tsoTotalLen);
+ UNSERIALIZE_SCALAR(tsoUsedLen);
+ UNSERIALIZE_SCALAR(tsoPrevSeq);;
+ UNSERIALIZE_SCALAR(tsoPktPayloadBytes);
+ UNSERIALIZE_SCALAR(tsoLoadedHeader);
+ UNSERIALIZE_SCALAR(tsoPktHasHeader);
+ UNSERIALIZE_ARRAY(tsoHeader, 256);
+ UNSERIALIZE_SCALAR(tsoDescBytesUsed);
+ UNSERIALIZE_SCALAR(tsoCopyBytes);
+ UNSERIALIZE_SCALAR(tsoPkts);
+
+ UNSERIALIZE_SCALAR(completionAddress);
+ UNSERIALIZE_SCALAR(completionEnabled);
+ UNSERIALIZE_SCALAR(descEnd);
+}
+
+bool
+IGbE::TxDescCache::packetAvailable()
+{
+ if (pktDone) {
+ pktDone = false;
+ return true;
+ }
+ return false;
+}
+
+void
+IGbE::TxDescCache::enableSm()
+{
+ if (igbe->drainState() != DrainState::Draining) {
+ igbe->txTick = true;
+ igbe->restartClock();
+ }
+}
+
+bool
+IGbE::TxDescCache::hasOutstandingEvents()
+{
+ return pktEvent.scheduled() || wbEvent.scheduled() ||
+ fetchEvent.scheduled();
+}
+
+
+///////////////////////////////////// IGbE /////////////////////////////////
+
+void
+IGbE::restartClock()
+{
+ if (!tickEvent.scheduled() && (rxTick || txTick || txFifoTick) &&
+ drainState() == DrainState::Running)
+ schedule(tickEvent, clockEdge(Cycles(1)));
+}
+
+DrainState
+IGbE::drain()
+{
+ unsigned int count(0);
+ if (rxDescCache.hasOutstandingEvents() ||
+ txDescCache.hasOutstandingEvents()) {
+ count++;
+ }
+
+ txFifoTick = false;
+ txTick = false;
+ rxTick = false;
+
+ if (tickEvent.scheduled())
+ deschedule(tickEvent);
+
+ if (count) {
+ DPRINTF(Drain, "IGbE not drained\n");
+ return DrainState::Draining;
+ } else
+ return DrainState::Drained;
+}
+
+void
+IGbE::drainResume()
+{
+ Drainable::drainResume();
+
+ txFifoTick = true;
+ txTick = true;
+ rxTick = true;
+
+ restartClock();
+ DPRINTF(EthernetSM, "resuming from drain");
+}
+
+void
+IGbE::checkDrain()
+{
+ if (drainState() != DrainState::Draining)
+ return;
+
+ txFifoTick = false;
+ txTick = false;
+ rxTick = false;
+ if (!rxDescCache.hasOutstandingEvents() &&
+ !txDescCache.hasOutstandingEvents()) {
+ DPRINTF(Drain, "IGbE done draining, processing drain event\n");
+ signalDrainDone();
+ }
+}
+
+void
+IGbE::txStateMachine()
+{
+ if (!regs.tctl.en()) {
+ txTick = false;
+ DPRINTF(EthernetSM, "TXS: TX disabled, stopping ticking\n");
+ return;
+ }
+
+ // If we have a packet available and it's length is not 0 (meaning it's not
+ // a multidescriptor packet) put it in the fifo, otherwise an the next
+ // iteration we'll get the rest of the data
+ if (txPacket && txDescCache.packetAvailable()
+ && !txDescCache.packetMultiDesc() && txPacket->length) {
+ anQ("TXS", "TX FIFO Q");
+ DPRINTF(EthernetSM, "TXS: packet placed in TX FIFO\n");
+#ifndef NDEBUG
+ bool success =
+#endif
+ txFifo.push(txPacket);
+ txFifoTick = true && drainState() != DrainState::Draining;
+ assert(success);
+ txPacket = NULL;
+ anBegin("TXS", "Desc Writeback");
+ txDescCache.writeback((cacheBlockSize()-1)>>4);
+ return;
+ }
+
+ // Only support descriptor granularity
+ if (regs.txdctl.lwthresh() &&
+ txDescCache.descLeft() < (regs.txdctl.lwthresh() * 8)) {
+ DPRINTF(EthernetSM, "TXS: LWTHRESH caused posting of TXDLOW\n");
+ postInterrupt(IT_TXDLOW);
+ }
+
+ if (!txPacket) {
+ txPacket = std::make_shared<EthPacketData>(16384);
+ }
+
+ 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");
+ txTick = false;
+ return;
+ }
+
+
+ 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();
+ if (txDescCache.packetWaiting()) {
+ DPRINTF(EthernetSM,
+ "TXS: Fetching TSO header, stopping ticking\n");
+ txTick = false;
+ return;
+ }
+
+ unsigned 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 "
+ "beginning DMA of next packet\n", size);
+ txFifo.reserve(size);
+ txDescCache.getPacketData(txPacket);
+ } 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;
+ }
+
+
+ return;
+ }
+ DPRINTF(EthernetSM, "TXS: Nothing to do, stopping ticking\n");
+ txTick = false;
+}
+
+bool
+IGbE::ethRxPkt(EthPacketPtr pkt)
+{
+ rxBytes += pkt->length;
+ 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;
+ }
+
+ // restart the state machines if they are stopped
+ rxTick = true && drainState() != DrainState::Draining;
+ if ((rxTick || txTick) && !tickEvent.scheduled()) {
+ DPRINTF(EthernetSM,
+ "RXS: received packet into fifo, starting ticking\n");
+ restartClock();
+ }
+
+ 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;
+}
+
+
+void
+IGbE::rxStateMachine()
+{
+ if (!regs.rctl.en()) {
+ rxTick = false;
+ DPRINTF(EthernetSM, "RXS: RX disabled, stopping ticking\n");
+ return;
+ }
+
+ // If the packet is done check for interrupts/descriptors/etc
+ if (rxDescCache.packetDone()) {
+ rxDmaPacket = false;
+ DPRINTF(EthernetSM, "RXS: Packet completed DMA to memory\n");
+ int descLeft = rxDescCache.descLeft();
+ DPRINTF(EthernetSM, "RXS: descLeft: %d rdmts: %d rdlen: %d\n",
+ descLeft, regs.rctl.rdmts(), regs.rdlen());
+ switch (regs.rctl.rdmts()) {
+ case 2: if (descLeft > .125 * regs.rdlen()) break;
+ case 1: if (descLeft > .250 * regs.rdlen()) break;
+ case 0: if (descLeft > .500 * regs.rdlen()) break;
+ DPRINTF(Ethernet, "RXS: Interrupting (RXDMT) "
+ "because of descriptors left\n");
+ postInterrupt(IT_RXDMT);
+ break;
+ }
+
+ if (rxFifo.empty())
+ 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");
+ rxTick = false;
+ }
+
+ // only support descriptor granulaties
+ assert(regs.rxdctl.gran());
+
+ 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
+ rxDescCache.writeback((cacheBlockSize()-1)>>4);
+ }
+
+ 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;
+ }
+ return;
+ }
+
+ if (rxDmaPacket) {
+ DPRINTF(EthernetSM,
+ "RXS: stopping ticking until packet DMA completes\n");
+ rxTick = false;
+ return;
+ }
+
+ 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())) {
+ anQ("TXQ", "WireQ");
+ if (DTRACE(EthernetSM)) {
+ IpPtr ip(txFifo.front());
+ if (ip)
+ DPRINTF(EthernetSM, "Transmitting Ip packet with Id=%d\n",
+ ip->id());
+ 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());
+
+ txBytes += txFifo.front()->length;
+ txPackets++;
+ txFifoTick = false;
+
+ txFifo.pop();
+ } else {
+ // We'll get woken up when the packet ethTxDone() gets called
+ txFifoTick = false;
+ }
+}
+
+void
+IGbE::tick()
+{
+ DPRINTF(EthernetSM, "IGbE: -------------- Cycle --------------\n");
+
+ if (rxTick)
+ rxStateMachine();
+
+ if (txTick)
+ txStateMachine();
+
+ if (txFifoTick)
+ txWire();
+
+
+ if (rxTick || txTick || txFifoTick)
+ schedule(tickEvent, curTick() + clockPeriod());
+}
+
+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
+ txFifoTick = true && drainState() != DrainState::Draining;
+ if (txDescCache.descLeft() != 0 && drainState() != DrainState::Draining)
+ txTick = true;
+
+ restartClock();
+ txWire();
+ DPRINTF(EthernetSM, "TxFIFO: Transmission complete\n");
+}
+
+void
+IGbE::serialize(CheckpointOut &cp) const
+{
+ PciDevice::serialize(cp);
+
+ regs.serialize(cp);
+ SERIALIZE_SCALAR(eeOpBits);
+ SERIALIZE_SCALAR(eeAddrBits);
+ SERIALIZE_SCALAR(eeDataBits);
+ SERIALIZE_SCALAR(eeOpcode);
+ SERIALIZE_SCALAR(eeAddr);
+ SERIALIZE_SCALAR(lastInterrupt);
+ SERIALIZE_ARRAY(flash,iGbReg::EEPROM_SIZE);
+
+ rxFifo.serialize("rxfifo", cp);
+ txFifo.serialize("txfifo", cp);
+
+ bool txPktExists = txPacket != nullptr;
+ SERIALIZE_SCALAR(txPktExists);
+ if (txPktExists)
+ txPacket->serialize("txpacket", cp);
+
+ Tick rdtr_time = 0, radv_time = 0, tidv_time = 0, tadv_time = 0,
+ inter_time = 0;
+
+ if (rdtrEvent.scheduled())
+ rdtr_time = rdtrEvent.when();
+ SERIALIZE_SCALAR(rdtr_time);
+
+ if (radvEvent.scheduled())
+ radv_time = radvEvent.when();
+ SERIALIZE_SCALAR(radv_time);
+
+ if (tidvEvent.scheduled())
+ tidv_time = tidvEvent.when();
+ SERIALIZE_SCALAR(tidv_time);
+
+ if (tadvEvent.scheduled())
+ tadv_time = tadvEvent.when();
+ SERIALIZE_SCALAR(tadv_time);
+
+ if (interEvent.scheduled())
+ inter_time = interEvent.when();
+ SERIALIZE_SCALAR(inter_time);
+
+ SERIALIZE_SCALAR(pktOffset);
+
+ txDescCache.serializeSection(cp, "TxDescCache");
+ rxDescCache.serializeSection(cp, "RxDescCache");
+}
+
+void
+IGbE::unserialize(CheckpointIn &cp)
+{
+ PciDevice::unserialize(cp);
+
+ regs.unserialize(cp);
+ UNSERIALIZE_SCALAR(eeOpBits);
+ UNSERIALIZE_SCALAR(eeAddrBits);
+ UNSERIALIZE_SCALAR(eeDataBits);
+ UNSERIALIZE_SCALAR(eeOpcode);
+ UNSERIALIZE_SCALAR(eeAddr);
+ UNSERIALIZE_SCALAR(lastInterrupt);
+ UNSERIALIZE_ARRAY(flash,iGbReg::EEPROM_SIZE);
+
+ rxFifo.unserialize("rxfifo", cp);
+ txFifo.unserialize("txfifo", cp);
+
+ bool txPktExists;
+ UNSERIALIZE_SCALAR(txPktExists);
+ if (txPktExists) {
+ txPacket = std::make_shared<EthPacketData>(16384);
+ txPacket->unserialize("txpacket", cp);
+ }
+
+ rxTick = true;
+ txTick = true;
+ txFifoTick = true;
+
+ Tick rdtr_time, radv_time, tidv_time, tadv_time, inter_time;
+ UNSERIALIZE_SCALAR(rdtr_time);
+ UNSERIALIZE_SCALAR(radv_time);
+ UNSERIALIZE_SCALAR(tidv_time);
+ UNSERIALIZE_SCALAR(tadv_time);
+ UNSERIALIZE_SCALAR(inter_time);
+
+ if (rdtr_time)
+ schedule(rdtrEvent, rdtr_time);
+
+ if (radv_time)
+ schedule(radvEvent, radv_time);
+
+ if (tidv_time)
+ schedule(tidvEvent, tidv_time);
+
+ if (tadv_time)
+ schedule(tadvEvent, tadv_time);
+
+ if (inter_time)
+ schedule(interEvent, inter_time);
+
+ UNSERIALIZE_SCALAR(pktOffset);
+
+ txDescCache.unserializeSection(cp, "TxDescCache");
+ rxDescCache.unserializeSection(cp, "RxDescCache");
+}
+
+IGbE *
+IGbEParams::create()
+{
+ return new IGbE(this);
+}
diff --git a/src/dev/net/i8254xGBe.hh b/src/dev/net/i8254xGBe.hh
new file mode 100644
index 000000000..e35744459
--- /dev/null
+++ b/src/dev/net/i8254xGBe.hh
@@ -0,0 +1,560 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Ali Saidi
+ */
+
+/* @file
+ * Device model for Intel's 8254x line of gigabit ethernet controllers.
+ */
+
+#ifndef __DEV_NET_I8254XGBE_HH__
+#define __DEV_NET_I8254XGBE_HH__
+
+#include <deque>
+#include <string>
+
+#include "base/cp_annotate.hh"
+#include "base/inet.hh"
+#include "debug/EthernetDesc.hh"
+#include "debug/EthernetIntr.hh"
+#include "dev/net/etherdevice.hh"
+#include "dev/net/etherint.hh"
+#include "dev/net/etherpkt.hh"
+#include "dev/net/i8254xGBe_defs.hh"
+#include "dev/net/pktfifo.hh"
+#include "dev/pci/device.hh"
+#include "params/IGbE.hh"
+#include "sim/eventq.hh"
+
+class IGbEInt;
+
+class IGbE : public EtherDevice
+{
+ private:
+ IGbEInt *etherInt;
+ CPA *cpa;
+
+ // device registers
+ iGbReg::Regs regs;
+
+ // eeprom data, status and control bits
+ int eeOpBits, eeAddrBits, eeDataBits;
+ uint8_t eeOpcode, eeAddr;
+ uint16_t flash[iGbReg::EEPROM_SIZE];
+
+ // packet fifos
+ PacketFifo rxFifo;
+ PacketFifo txFifo;
+
+ // Packet that we are currently putting into the txFifo
+ EthPacketPtr txPacket;
+
+ // Should to Rx/Tx State machine tick?
+ bool rxTick;
+ bool txTick;
+ bool txFifoTick;
+
+ bool rxDmaPacket;
+
+ // Number of bytes copied from current RX packet
+ unsigned pktOffset;
+
+ // Delays in managaging descriptors
+ Tick fetchDelay, wbDelay;
+ Tick fetchCompDelay, wbCompDelay;
+ Tick rxWriteDelay, txReadDelay;
+
+ // Event and function to deal with RDTR timer expiring
+ void rdtrProcess() {
+ rxDescCache.writeback(0);
+ DPRINTF(EthernetIntr,
+ "Posting RXT interrupt because RDTR timer expired\n");
+ postInterrupt(iGbReg::IT_RXT);
+ }
+
+ //friend class EventWrapper<IGbE, &IGbE::rdtrProcess>;
+ EventWrapper<IGbE, &IGbE::rdtrProcess> rdtrEvent;
+
+ // Event and function to deal with RADV timer expiring
+ void radvProcess() {
+ rxDescCache.writeback(0);
+ DPRINTF(EthernetIntr,
+ "Posting RXT interrupt because RADV timer expired\n");
+ postInterrupt(iGbReg::IT_RXT);
+ }
+
+ //friend class EventWrapper<IGbE, &IGbE::radvProcess>;
+ EventWrapper<IGbE, &IGbE::radvProcess> radvEvent;
+
+ // Event and function to deal with TADV timer expiring
+ void tadvProcess() {
+ txDescCache.writeback(0);
+ DPRINTF(EthernetIntr,
+ "Posting TXDW interrupt because TADV timer expired\n");
+ postInterrupt(iGbReg::IT_TXDW);
+ }
+
+ //friend class EventWrapper<IGbE, &IGbE::tadvProcess>;
+ EventWrapper<IGbE, &IGbE::tadvProcess> tadvEvent;
+
+ // Event and function to deal with TIDV timer expiring
+ void tidvProcess() {
+ txDescCache.writeback(0);
+ DPRINTF(EthernetIntr,
+ "Posting TXDW interrupt because TIDV timer expired\n");
+ postInterrupt(iGbReg::IT_TXDW);
+ }
+ //friend class EventWrapper<IGbE, &IGbE::tidvProcess>;
+ EventWrapper<IGbE, &IGbE::tidvProcess> tidvEvent;
+
+ // Main event to tick the device
+ void tick();
+ //friend class EventWrapper<IGbE, &IGbE::tick>;
+ EventWrapper<IGbE, &IGbE::tick> tickEvent;
+
+
+ uint64_t macAddr;
+
+ void rxStateMachine();
+ void txStateMachine();
+ void txWire();
+
+ /** Write an interrupt into the interrupt pending register and check mask
+ * and interrupt limit timer before sending interrupt to CPU
+ * @param t the type of interrupt we are posting
+ * @param now should we ignore the interrupt limiting timer
+ */
+ void postInterrupt(iGbReg::IntTypes t, bool now = false);
+
+ /** Check and see if changes to the mask register have caused an interrupt
+ * to need to be sent or perhaps removed an interrupt cause.
+ */
+ void chkInterrupt();
+
+ /** Send an interrupt to the cpu
+ */
+ void delayIntEvent();
+ void cpuPostInt();
+ // Event to moderate interrupts
+ EventWrapper<IGbE, &IGbE::delayIntEvent> interEvent;
+
+ /** Clear the interupt line to the cpu
+ */
+ void cpuClearInt();
+
+ Tick intClock() { return SimClock::Int::ns * 1024; }
+
+ /** This function is used to restart the clock so it can handle things like
+ * draining and resume in one place. */
+ void restartClock();
+
+ /** Check if all the draining things that need to occur have occured and
+ * handle the drain event if so.
+ */
+ void checkDrain();
+
+ void anBegin(std::string sm, std::string st, int flags = CPA::FL_NONE) {
+ if (cpa)
+ cpa->hwBegin((CPA::flags)flags, sys, macAddr, sm, st);
+ }
+
+ void anQ(std::string sm, std::string q) {
+ if (cpa)
+ cpa->hwQ(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
+ }
+
+ void anDq(std::string sm, std::string q) {
+ if (cpa)
+ cpa->hwDq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
+ }
+
+ void anPq(std::string sm, std::string q, int num = 1) {
+ if (cpa)
+ cpa->hwPq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr, NULL, num);
+ }
+
+ void anRq(std::string sm, std::string q, int num = 1) {
+ if (cpa)
+ cpa->hwRq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr, NULL, num);
+ }
+
+ void anWe(std::string sm, std::string q) {
+ if (cpa)
+ cpa->hwWe(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
+ }
+
+ void anWf(std::string sm, std::string q) {
+ if (cpa)
+ cpa->hwWf(CPA::FL_NONE, sys, macAddr, sm, q, macAddr);
+ }
+
+
+ template<class T>
+ class DescCache : public Serializable
+ {
+ protected:
+ virtual Addr descBase() const = 0;
+ virtual long descHead() const = 0;
+ virtual long descTail() const = 0;
+ virtual long descLen() const = 0;
+ virtual void updateHead(long h) = 0;
+ virtual void enableSm() = 0;
+ virtual void actionAfterWb() {}
+ virtual void fetchAfterWb() = 0;
+
+ typedef std::deque<T *> CacheType;
+ CacheType usedCache;
+ CacheType unusedCache;
+
+ T *fetchBuf;
+ T *wbBuf;
+
+ // Pointer to the device we cache for
+ IGbE *igbe;
+
+ // Name of this descriptor cache
+ std::string _name;
+
+ // How far we've cached
+ int cachePnt;
+
+ // The size of the descriptor cache
+ int size;
+
+ // How many descriptors we are currently fetching
+ int curFetching;
+
+ // How many descriptors we are currently writing back
+ int wbOut;
+
+ // if the we wrote back to the end of the descriptor ring and are going
+ // to have to wrap and write more
+ bool moreToWb;
+
+ // What the alignment is of the next descriptor writeback
+ Addr wbAlignment;
+
+ /** The packet that is currently being dmad to memory if any */
+ EthPacketPtr pktPtr;
+
+ /** Shortcut for DMA address translation */
+ Addr pciToDma(Addr a) { return igbe->pciToDma(a); }
+
+ public:
+ /** Annotate sm*/
+ std::string annSmFetch, annSmWb, annUnusedDescQ, annUsedCacheQ,
+ annUsedDescQ, annUnusedCacheQ, annDescQ;
+
+ DescCache(IGbE *i, const std::string n, int s);
+ virtual ~DescCache();
+
+ std::string name() { return _name; }
+
+ /** If the address/len/head change when we've got descriptors that are
+ * dirty that is very bad. This function checks that we don't and if we
+ * do panics.
+ */
+ void areaChanged();
+
+ void writeback(Addr aMask);
+ void writeback1();
+ EventWrapper<DescCache, &DescCache::writeback1> wbDelayEvent;
+
+ /** Fetch a chunk of descriptors into the descriptor cache.
+ * Calls fetchComplete when the memory system returns the data
+ */
+ void fetchDescriptors();
+ void fetchDescriptors1();
+ EventWrapper<DescCache, &DescCache::fetchDescriptors1> fetchDelayEvent;
+
+ /** Called by event when dma to read descriptors is completed
+ */
+ void fetchComplete();
+ EventWrapper<DescCache, &DescCache::fetchComplete> fetchEvent;
+
+ /** Called by event when dma to writeback descriptors is completed
+ */
+ void wbComplete();
+ EventWrapper<DescCache, &DescCache::wbComplete> wbEvent;
+
+ /* Return the number of descriptors left in the ring, so the device has
+ * a way to figure out if it needs to interrupt.
+ */
+ unsigned
+ descLeft() const
+ {
+ unsigned left = unusedCache.size();
+ if (cachePnt > descTail())
+ left += (descLen() - cachePnt + descTail());
+ else
+ left += (descTail() - cachePnt);
+
+ return left;
+ }
+
+ /* Return the number of descriptors used and not written back.
+ */
+ unsigned descUsed() const { return usedCache.size(); }
+
+ /* Return the number of cache unused descriptors we have. */
+ unsigned descUnused() const { return unusedCache.size(); }
+
+ /* Get into a state where the descriptor address/head/etc colud be
+ * changed */
+ void reset();
+
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+
+ virtual bool hasOutstandingEvents() {
+ return wbEvent.scheduled() || fetchEvent.scheduled();
+ }
+
+ };
+
+
+ class RxDescCache : public DescCache<iGbReg::RxDesc>
+ {
+ protected:
+ Addr descBase() const override { return igbe->regs.rdba(); }
+ long descHead() const override { return igbe->regs.rdh(); }
+ long descLen() const override { return igbe->regs.rdlen() >> 4; }
+ long descTail() const override { return igbe->regs.rdt(); }
+ void updateHead(long h) override { igbe->regs.rdh(h); }
+ void enableSm() override;
+ void fetchAfterWb() override {
+ if (!igbe->rxTick && igbe->drainState() == DrainState::Running)
+ fetchDescriptors();
+ }
+
+ bool pktDone;
+
+ /** Variable to head with header/data completion events */
+ int splitCount;
+
+ /** Bytes of packet that have been copied, so we know when to
+ set EOP */
+ unsigned bytesCopied;
+
+ public:
+ RxDescCache(IGbE *i, std::string n, int s);
+
+ /** Write the given packet into the buffer(s) pointed to by the
+ * descriptor and update the book keeping. Should only be called when
+ * there are no dma's pending.
+ * @param packet ethernet packet to write
+ * @param pkt_offset bytes already copied from the packet to memory
+ * @return pkt_offset + number of bytes copied during this call
+ */
+ int writePacket(EthPacketPtr packet, int pkt_offset);
+
+ /** Called by event when dma to write packet is completed
+ */
+ void pktComplete();
+
+ /** Check if the dma on the packet has completed and RX state machine
+ * can continue
+ */
+ bool packetDone();
+
+ EventWrapper<RxDescCache, &RxDescCache::pktComplete> pktEvent;
+
+ // Event to handle issuing header and data write at the same time
+ // and only callking pktComplete() when both are completed
+ void pktSplitDone();
+ EventWrapper<RxDescCache, &RxDescCache::pktSplitDone> pktHdrEvent;
+ EventWrapper<RxDescCache, &RxDescCache::pktSplitDone> pktDataEvent;
+
+ bool hasOutstandingEvents() override;
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+ };
+ friend class RxDescCache;
+
+ RxDescCache rxDescCache;
+
+ class TxDescCache : public DescCache<iGbReg::TxDesc>
+ {
+ protected:
+ Addr descBase() const override { return igbe->regs.tdba(); }
+ long descHead() const override { return igbe->regs.tdh(); }
+ long descTail() const override { return igbe->regs.tdt(); }
+ long descLen() const override { return igbe->regs.tdlen() >> 4; }
+ void updateHead(long h) override { igbe->regs.tdh(h); }
+ void enableSm() override;
+ void actionAfterWb() override;
+ void fetchAfterWb() override {
+ if (!igbe->txTick && igbe->drainState() == DrainState::Running)
+ fetchDescriptors();
+ }
+
+
+
+ bool pktDone;
+ bool isTcp;
+ bool pktWaiting;
+ bool pktMultiDesc;
+ Addr completionAddress;
+ bool completionEnabled;
+ uint32_t descEnd;
+
+
+ // tso variables
+ bool useTso;
+ Addr tsoHeaderLen;
+ Addr tsoMss;
+ Addr tsoTotalLen;
+ Addr tsoUsedLen;
+ Addr tsoPrevSeq;
+ Addr tsoPktPayloadBytes;
+ bool tsoLoadedHeader;
+ bool tsoPktHasHeader;
+ uint8_t tsoHeader[256];
+ Addr tsoDescBytesUsed;
+ Addr tsoCopyBytes;
+ int tsoPkts;
+
+ public:
+ TxDescCache(IGbE *i, std::string n, int s);
+
+ /** Tell the cache to DMA a packet from main memory into its buffer and
+ * return the size the of the packet to reserve space in tx fifo.
+ * @return size of the packet
+ */
+ unsigned getPacketSize(EthPacketPtr p);
+ void getPacketData(EthPacketPtr p);
+ void processContextDesc();
+
+ /** Return the number of dsecriptors in a cache block for threshold
+ * operations.
+ */
+ unsigned
+ descInBlock(unsigned num_desc)
+ {
+ return num_desc / igbe->cacheBlockSize() / sizeof(iGbReg::TxDesc);
+ }
+
+ /** Ask if the packet has been transfered so the state machine can give
+ * it to the fifo.
+ * @return packet available in descriptor cache
+ */
+ bool packetAvailable();
+
+ /** Ask if we are still waiting for the packet to be transfered.
+ * @return packet still in transit.
+ */
+ bool packetWaiting() { return pktWaiting; }
+
+ /** Ask if this packet is composed of multiple descriptors
+ * so even if we've got data, we need to wait for more before
+ * we can send it out.
+ * @return packet can't be sent out because it's a multi-descriptor
+ * packet
+ */
+ bool packetMultiDesc() { return pktMultiDesc;}
+
+ /** Called by event when dma to write packet is completed
+ */
+ void pktComplete();
+ EventWrapper<TxDescCache, &TxDescCache::pktComplete> pktEvent;
+
+ void headerComplete();
+ EventWrapper<TxDescCache, &TxDescCache::headerComplete> headerEvent;
+
+
+ void completionWriteback(Addr a, bool enabled) {
+ DPRINTF(EthernetDesc,
+ "Completion writeback Addr: %#x enabled: %d\n",
+ a, enabled);
+ completionAddress = a;
+ completionEnabled = enabled;
+ }
+
+ bool hasOutstandingEvents() override;
+
+ void nullCallback() {
+ DPRINTF(EthernetDesc, "Completion writeback complete\n");
+ }
+ EventWrapper<TxDescCache, &TxDescCache::nullCallback> nullEvent;
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+ };
+
+ friend class TxDescCache;
+
+ TxDescCache txDescCache;
+
+ public:
+ typedef IGbEParams Params;
+ const Params *
+ params() const {
+ return dynamic_cast<const Params *>(_params);
+ }
+
+ IGbE(const Params *params);
+ ~IGbE();
+ void init() override;
+
+ EtherInt *getEthPort(const std::string &if_name, int idx) override;
+
+ Tick lastInterrupt;
+
+ Tick read(PacketPtr pkt) override;
+ Tick write(PacketPtr pkt) override;
+
+ Tick writeConfig(PacketPtr pkt) override;
+
+ bool ethRxPkt(EthPacketPtr packet);
+ void ethTxDone();
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+
+ DrainState drain() override;
+ void drainResume() override;
+
+};
+
+class IGbEInt : public EtherInt
+{
+ private:
+ IGbE *dev;
+
+ public:
+ IGbEInt(const std::string &name, IGbE *d)
+ : EtherInt(name), dev(d)
+ { }
+
+ virtual bool recvPacket(EthPacketPtr pkt) { return dev->ethRxPkt(pkt); }
+ virtual void sendDone() { dev->ethTxDone(); }
+};
+
+#endif //__DEV_NET_I8254XGBE_HH__
diff --git a/src/dev/net/i8254xGBe_defs.hh b/src/dev/net/i8254xGBe_defs.hh
new file mode 100644
index 000000000..79a9413da
--- /dev/null
+++ b/src/dev/net/i8254xGBe_defs.hh
@@ -0,0 +1,854 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Ali Saidi
+ */
+
+/* @file
+ * Register and structure descriptions for Intel's 8254x line of gigabit ethernet controllers.
+ */
+#include "base/bitfield.hh"
+
+namespace iGbReg {
+
+
+// Registers used by the Intel GbE NIC
+const uint32_t REG_CTRL = 0x00000;
+const uint32_t REG_STATUS = 0x00008;
+const uint32_t REG_EECD = 0x00010;
+const uint32_t REG_EERD = 0x00014;
+const uint32_t REG_CTRL_EXT = 0x00018;
+const uint32_t REG_MDIC = 0x00020;
+const uint32_t REG_FCAL = 0x00028;
+const uint32_t REG_FCAH = 0x0002C;
+const uint32_t REG_FCT = 0x00030;
+const uint32_t REG_VET = 0x00038;
+const uint32_t REG_PBA = 0x01000;
+const uint32_t REG_ICR = 0x000C0;
+const uint32_t REG_ITR = 0x000C4;
+const uint32_t REG_ICS = 0x000C8;
+const uint32_t REG_IMS = 0x000D0;
+const uint32_t REG_IMC = 0x000D8;
+const uint32_t REG_IAM = 0x000E0;
+const uint32_t REG_RCTL = 0x00100;
+const uint32_t REG_FCTTV = 0x00170;
+const uint32_t REG_TIPG = 0x00410;
+const uint32_t REG_AIFS = 0x00458;
+const uint32_t REG_LEDCTL = 0x00e00;
+const uint32_t REG_EICR = 0x01580;
+const uint32_t REG_IVAR0 = 0x01700;
+const uint32_t REG_FCRTL = 0x02160;
+const uint32_t REG_FCRTH = 0x02168;
+const uint32_t REG_RDBAL = 0x02800;
+const uint32_t REG_RDBAH = 0x02804;
+const uint32_t REG_RDLEN = 0x02808;
+const uint32_t REG_SRRCTL = 0x0280C;
+const uint32_t REG_RDH = 0x02810;
+const uint32_t REG_RDT = 0x02818;
+const uint32_t REG_RDTR = 0x02820;
+const uint32_t REG_RXDCTL = 0x02828;
+const uint32_t REG_RADV = 0x0282C;
+const uint32_t REG_TCTL = 0x00400;
+const uint32_t REG_TDBAL = 0x03800;
+const uint32_t REG_TDBAH = 0x03804;
+const uint32_t REG_TDLEN = 0x03808;
+const uint32_t REG_TDH = 0x03810;
+const uint32_t REG_TXDCA_CTL = 0x03814;
+const uint32_t REG_TDT = 0x03818;
+const uint32_t REG_TIDV = 0x03820;
+const uint32_t REG_TXDCTL = 0x03828;
+const uint32_t REG_TADV = 0x0382C;
+const uint32_t REG_TDWBAL = 0x03838;
+const uint32_t REG_TDWBAH = 0x0383C;
+const uint32_t REG_CRCERRS = 0x04000;
+const uint32_t REG_RXCSUM = 0x05000;
+const uint32_t REG_RLPML = 0x05004;
+const uint32_t REG_RFCTL = 0x05008;
+const uint32_t REG_MTA = 0x05200;
+const uint32_t REG_RAL = 0x05400;
+const uint32_t REG_RAH = 0x05404;
+const uint32_t REG_VFTA = 0x05600;
+
+const uint32_t REG_WUC = 0x05800;
+const uint32_t REG_MANC = 0x05820;
+const uint32_t REG_SWSM = 0x05B50;
+const uint32_t REG_FWSM = 0x05B54;
+const uint32_t REG_SWFWSYNC = 0x05B5C;
+
+const uint8_t EEPROM_READ_OPCODE_SPI = 0x03;
+const uint8_t EEPROM_RDSR_OPCODE_SPI = 0x05;
+const uint8_t EEPROM_SIZE = 64;
+const uint16_t EEPROM_CSUM = 0xBABA;
+
+const uint8_t VLAN_FILTER_TABLE_SIZE = 128;
+const uint8_t RCV_ADDRESS_TABLE_SIZE = 24;
+const uint8_t MULTICAST_TABLE_SIZE = 128;
+const uint32_t STATS_REGS_SIZE = 0x228;
+
+
+// Registers in that are accessed in the PHY
+const uint8_t PHY_PSTATUS = 0x1;
+const uint8_t PHY_PID = 0x2;
+const uint8_t PHY_EPID = 0x3;
+const uint8_t PHY_GSTATUS = 10;
+const uint8_t PHY_EPSTATUS = 15;
+const uint8_t PHY_AGC = 18;
+
+// Receive Descriptor Status Flags
+const uint16_t RXDS_DYNINT = 0x800;
+const uint16_t RXDS_UDPV = 0x400;
+const uint16_t RXDS_CRCV = 0x100;
+const uint16_t RXDS_PIF = 0x080;
+const uint16_t RXDS_IPCS = 0x040;
+const uint16_t RXDS_TCPCS = 0x020;
+const uint16_t RXDS_UDPCS = 0x010;
+const uint16_t RXDS_VP = 0x008;
+const uint16_t RXDS_IXSM = 0x004;
+const uint16_t RXDS_EOP = 0x002;
+const uint16_t RXDS_DD = 0x001;
+
+// Receive Descriptor Error Flags
+const uint8_t RXDE_RXE = 0x80;
+const uint8_t RXDE_IPE = 0x40;
+const uint8_t RXDE_TCPE = 0x20;
+const uint8_t RXDE_SEQ = 0x04;
+const uint8_t RXDE_SE = 0x02;
+const uint8_t RXDE_CE = 0x01;
+
+// Receive Descriptor Extended Error Flags
+const uint16_t RXDEE_HBO = 0x008;
+const uint16_t RXDEE_CE = 0x010;
+const uint16_t RXDEE_LE = 0x020;
+const uint16_t RXDEE_PE = 0x080;
+const uint16_t RXDEE_OSE = 0x100;
+const uint16_t RXDEE_USE = 0x200;
+const uint16_t RXDEE_TCPE = 0x400;
+const uint16_t RXDEE_IPE = 0x800;
+
+
+// Receive Descriptor Types
+const uint8_t RXDT_LEGACY = 0x00;
+const uint8_t RXDT_ADV_ONEBUF = 0x01;
+const uint8_t RXDT_ADV_SPLIT_A = 0x05;
+
+// Receive Descriptor Packet Types
+const uint16_t RXDP_IPV4 = 0x001;
+const uint16_t RXDP_IPV4E = 0x002;
+const uint16_t RXDP_IPV6 = 0x004;
+const uint16_t RXDP_IPV6E = 0x008;
+const uint16_t RXDP_TCP = 0x010;
+const uint16_t RXDP_UDP = 0x020;
+const uint16_t RXDP_SCTP = 0x040;
+const uint16_t RXDP_NFS = 0x080;
+
+// Interrupt types
+enum IntTypes
+{
+ IT_NONE = 0x00000, //dummy value
+ IT_TXDW = 0x00001,
+ IT_TXQE = 0x00002,
+ IT_LSC = 0x00004,
+ IT_RXSEQ = 0x00008,
+ IT_RXDMT = 0x00010,
+ IT_RXO = 0x00040,
+ IT_RXT = 0x00080,
+ IT_MADC = 0x00200,
+ IT_RXCFG = 0x00400,
+ IT_GPI0 = 0x02000,
+ IT_GPI1 = 0x04000,
+ IT_TXDLOW = 0x08000,
+ IT_SRPD = 0x10000,
+ IT_ACK = 0x20000
+};
+
+// Receive Descriptor struct
+struct RxDesc {
+ union {
+ struct {
+ Addr buf;
+ uint16_t len;
+ uint16_t csum;
+ uint8_t status;
+ uint8_t errors;
+ uint16_t vlan;
+ } legacy;
+ struct {
+ Addr pkt;
+ Addr hdr;
+ } adv_read;
+ struct {
+ uint16_t rss_type:4;
+ uint16_t pkt_type:12;
+ uint16_t __reserved1:5;
+ uint16_t header_len:10;
+ uint16_t sph:1;
+ union {
+ struct {
+ uint16_t id;
+ uint16_t csum;
+ };
+ uint32_t rss_hash;
+ };
+ uint32_t status:20;
+ uint32_t errors:12;
+ uint16_t pkt_len;
+ uint16_t vlan_tag;
+ } adv_wb ;
+ };
+};
+
+struct TxDesc {
+ uint64_t d1;
+ uint64_t d2;
+};
+
+namespace TxdOp {
+const uint8_t TXD_CNXT = 0x0;
+const uint8_t TXD_DATA = 0x1;
+const uint8_t TXD_ADVCNXT = 0x2;
+const uint8_t TXD_ADVDATA = 0x3;
+
+inline bool isLegacy(TxDesc *d) { return !bits(d->d2,29,29); }
+inline uint8_t getType(TxDesc *d) { return bits(d->d2, 23,20); }
+inline bool isType(TxDesc *d, uint8_t type) { return getType(d) == type; }
+inline bool isTypes(TxDesc *d, uint8_t t1, uint8_t t2) { return isType(d, t1) || isType(d, t2); }
+inline bool isAdvDesc(TxDesc *d) { return !isLegacy(d) && isTypes(d, TXD_ADVDATA,TXD_ADVCNXT); }
+inline bool isContext(TxDesc *d) { return !isLegacy(d) && isTypes(d,TXD_CNXT, TXD_ADVCNXT); }
+inline bool isData(TxDesc *d) { return !isLegacy(d) && isTypes(d, TXD_DATA, TXD_ADVDATA); }
+
+inline Addr getBuf(TxDesc *d) { assert(isLegacy(d) || isData(d)); return d->d1; }
+inline Addr getLen(TxDesc *d) { if (isLegacy(d)) return bits(d->d2,15,0); else return bits(d->d2, 19,0); }
+inline void setDd(TxDesc *d) { replaceBits(d->d2, 35, 32, ULL(1)); }
+
+inline bool ide(TxDesc *d) { return bits(d->d2, 31,31) && (getType(d) == TXD_DATA || isLegacy(d)); }
+inline bool vle(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 30,30); }
+inline bool rs(TxDesc *d) { return bits(d->d2, 27,27); }
+inline bool ic(TxDesc *d) { assert(isLegacy(d) || isData(d)); return isLegacy(d) && bits(d->d2, 26,26); }
+inline bool tse(TxDesc *d) {
+ if (isTypes(d, TXD_CNXT, TXD_DATA))
+ return bits(d->d2, 26,26);
+ if (isType(d, TXD_ADVDATA))
+ return bits(d->d2, 31, 31);
+ return false;
+}
+
+inline bool ifcs(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 25,25); }
+inline bool eop(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 24,24); }
+inline bool ip(TxDesc *d) { assert(isContext(d)); return bits(d->d2, 25,25); }
+inline bool tcp(TxDesc *d) { assert(isContext(d)); return bits(d->d2, 24,24); }
+
+inline uint8_t getCso(TxDesc *d) { assert(isLegacy(d)); return bits(d->d2, 23,16); }
+inline uint8_t getCss(TxDesc *d) { assert(isLegacy(d)); return bits(d->d2, 47,40); }
+
+inline bool ixsm(TxDesc *d) { return isData(d) && bits(d->d2, 40,40); }
+inline bool txsm(TxDesc *d) { return isData(d) && bits(d->d2, 41,41); }
+
+inline int tucse(TxDesc *d) { assert(isContext(d)); return bits(d->d1,63,48); }
+inline int tucso(TxDesc *d) { assert(isContext(d)); return bits(d->d1,47,40); }
+inline int tucss(TxDesc *d) { assert(isContext(d)); return bits(d->d1,39,32); }
+inline int ipcse(TxDesc *d) { assert(isContext(d)); return bits(d->d1,31,16); }
+inline int ipcso(TxDesc *d) { assert(isContext(d)); return bits(d->d1,15,8); }
+inline int ipcss(TxDesc *d) { assert(isContext(d)); return bits(d->d1,7,0); }
+inline int mss(TxDesc *d) { assert(isContext(d)); return bits(d->d2,63,48); }
+inline int hdrlen(TxDesc *d) {
+ assert(isContext(d));
+ if (!isAdvDesc(d))
+ return bits(d->d2,47,40);
+ return bits(d->d2, 47,40) + bits(d->d1, 8,0) + bits(d->d1, 15, 9);
+}
+
+inline int getTsoLen(TxDesc *d) { assert(isType(d, TXD_ADVDATA)); return bits(d->d2, 63,46); }
+inline int utcmd(TxDesc *d) { assert(isContext(d)); return bits(d->d2,24,31); }
+} // namespace TxdOp
+
+
+#define ADD_FIELD32(NAME, OFFSET, BITS) \
+ inline uint32_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
+ inline void NAME(uint32_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
+
+#define ADD_FIELD64(NAME, OFFSET, BITS) \
+ inline uint64_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
+ inline void NAME(uint64_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
+
+struct Regs : public Serializable {
+ template<class T>
+ struct Reg {
+ T _data;
+ T operator()() { return _data; }
+ const Reg<T> &operator=(T d) { _data = d; return *this;}
+ bool operator==(T d) { return d == _data; }
+ void operator()(T d) { _data = d; }
+ Reg() { _data = 0; }
+ void serialize(CheckpointOut &cp) const
+ {
+ SERIALIZE_SCALAR(_data);
+ }
+ void unserialize(CheckpointIn &cp)
+ {
+ UNSERIALIZE_SCALAR(_data);
+ }
+ };
+
+ struct CTRL : public Reg<uint32_t> { // 0x0000 CTRL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(fd,0,1); // full duplex
+ ADD_FIELD32(bem,1,1); // big endian mode
+ ADD_FIELD32(pcipr,2,1); // PCI priority
+ ADD_FIELD32(lrst,3,1); // link reset
+ ADD_FIELD32(tme,4,1); // test mode enable
+ ADD_FIELD32(asde,5,1); // Auto-speed detection
+ ADD_FIELD32(slu,6,1); // Set link up
+ ADD_FIELD32(ilos,7,1); // invert los-of-signal
+ ADD_FIELD32(speed,8,2); // speed selection bits
+ ADD_FIELD32(be32,10,1); // big endian mode 32
+ ADD_FIELD32(frcspd,11,1); // force speed
+ ADD_FIELD32(frcdpx,12,1); // force duplex
+ ADD_FIELD32(duden,13,1); // dock/undock enable
+ ADD_FIELD32(dudpol,14,1); // dock/undock polarity
+ ADD_FIELD32(fphyrst,15,1); // force phy reset
+ ADD_FIELD32(extlen,16,1); // external link status enable
+ ADD_FIELD32(rsvd,17,1); // reserved
+ ADD_FIELD32(sdp0d,18,1); // software controlled pin data
+ ADD_FIELD32(sdp1d,19,1); // software controlled pin data
+ ADD_FIELD32(sdp2d,20,1); // software controlled pin data
+ ADD_FIELD32(sdp3d,21,1); // software controlled pin data
+ ADD_FIELD32(sdp0i,22,1); // software controlled pin dir
+ ADD_FIELD32(sdp1i,23,1); // software controlled pin dir
+ ADD_FIELD32(sdp2i,24,1); // software controlled pin dir
+ ADD_FIELD32(sdp3i,25,1); // software controlled pin dir
+ ADD_FIELD32(rst,26,1); // reset
+ ADD_FIELD32(rfce,27,1); // receive flow control enable
+ ADD_FIELD32(tfce,28,1); // transmit flow control enable
+ ADD_FIELD32(rte,29,1); // routing tag enable
+ ADD_FIELD32(vme,30,1); // vlan enable
+ ADD_FIELD32(phyrst,31,1); // phy reset
+ };
+ CTRL ctrl;
+
+ struct STATUS : public Reg<uint32_t> { // 0x0008 STATUS Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(fd,0,1); // full duplex
+ ADD_FIELD32(lu,1,1); // link up
+ ADD_FIELD32(func,2,2); // function id
+ ADD_FIELD32(txoff,4,1); // transmission paused
+ ADD_FIELD32(tbimode,5,1); // tbi mode
+ ADD_FIELD32(speed,6,2); // link speed
+ ADD_FIELD32(asdv,8,2); // auto speed detection value
+ ADD_FIELD32(mtxckok,10,1); // mtx clock running ok
+ ADD_FIELD32(pci66,11,1); // In 66Mhz pci slot
+ ADD_FIELD32(bus64,12,1); // in 64 bit slot
+ ADD_FIELD32(pcix,13,1); // Pci mode
+ ADD_FIELD32(pcixspd,14,2); // pci x speed
+ };
+ STATUS sts;
+
+ struct EECD : public Reg<uint32_t> { // 0x0010 EECD Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(sk,0,1); // clack input to the eeprom
+ ADD_FIELD32(cs,1,1); // chip select to eeprom
+ ADD_FIELD32(din,2,1); // data input to eeprom
+ ADD_FIELD32(dout,3,1); // data output bit
+ ADD_FIELD32(fwe,4,2); // flash write enable
+ ADD_FIELD32(ee_req,6,1); // request eeprom access
+ ADD_FIELD32(ee_gnt,7,1); // grant eeprom access
+ ADD_FIELD32(ee_pres,8,1); // eeprom present
+ ADD_FIELD32(ee_size,9,1); // eeprom size
+ ADD_FIELD32(ee_sz1,10,1); // eeprom size
+ ADD_FIELD32(rsvd,11,2); // reserved
+ ADD_FIELD32(ee_type,13,1); // type of eeprom
+ } ;
+ EECD eecd;
+
+ struct EERD : public Reg<uint32_t> { // 0x0014 EERD Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(start,0,1); // start read
+ ADD_FIELD32(done,1,1); // done read
+ ADD_FIELD32(addr,2,14); // address
+ ADD_FIELD32(data,16,16); // data
+ };
+ EERD eerd;
+
+ struct CTRL_EXT : public Reg<uint32_t> { // 0x0018 CTRL_EXT Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(gpi_en,0,4); // enable interrupts from gpio
+ ADD_FIELD32(phyint,5,1); // reads the phy internal int status
+ ADD_FIELD32(sdp2_data,6,1); // data from gpio sdp
+ ADD_FIELD32(spd3_data,7,1); // data frmo gpio sdp
+ ADD_FIELD32(spd2_iodir,10,1); // direction of sdp2
+ ADD_FIELD32(spd3_iodir,11,1); // direction of sdp2
+ ADD_FIELD32(asdchk,12,1); // initiate auto-speed-detection
+ ADD_FIELD32(eerst,13,1); // reset the eeprom
+ ADD_FIELD32(spd_byps,15,1); // bypass speed select
+ ADD_FIELD32(ro_dis,17,1); // disable relaxed memory ordering
+ ADD_FIELD32(vreg,21,1); // power down the voltage regulator
+ ADD_FIELD32(link_mode,22,2); // interface to talk to the link
+ ADD_FIELD32(iame, 27,1); // interrupt acknowledge auto-mask ??
+ ADD_FIELD32(drv_loaded, 28,1);// driver is loaded and incharge of device
+ ADD_FIELD32(timer_clr, 29,1); // clear interrupt timers after IMS clear ??
+ };
+ CTRL_EXT ctrl_ext;
+
+ struct MDIC : public Reg<uint32_t> { // 0x0020 MDIC Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(data,0,16); // data
+ ADD_FIELD32(regadd,16,5); // register address
+ ADD_FIELD32(phyadd,21,5); // phy addresses
+ ADD_FIELD32(op,26,2); // opcode
+ ADD_FIELD32(r,28,1); // ready
+ ADD_FIELD32(i,29,1); // interrupt
+ ADD_FIELD32(e,30,1); // error
+ };
+ MDIC mdic;
+
+ struct ICR : public Reg<uint32_t> { // 0x00C0 ICR Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(txdw,0,1) // tx descr witten back
+ ADD_FIELD32(txqe,1,1) // tx queue empty
+ ADD_FIELD32(lsc,2,1) // link status change
+ ADD_FIELD32(rxseq,3,1) // rcv sequence error
+ ADD_FIELD32(rxdmt0,4,1) // rcv descriptor min thresh
+ ADD_FIELD32(rsvd1,5,1) // reserved
+ ADD_FIELD32(rxo,6,1) // receive overrunn
+ ADD_FIELD32(rxt0,7,1) // receiver timer interrupt
+ ADD_FIELD32(mdac,9,1) // mdi/o access complete
+ ADD_FIELD32(rxcfg,10,1) // recv /c/ ordered sets
+ ADD_FIELD32(phyint,12,1) // phy interrupt
+ ADD_FIELD32(gpi1,13,1) // gpi int 1
+ ADD_FIELD32(gpi2,14,1) // gpi int 2
+ ADD_FIELD32(txdlow,15,1) // transmit desc low thresh
+ ADD_FIELD32(srpd,16,1) // small receive packet detected
+ ADD_FIELD32(ack,17,1); // receive ack frame
+ ADD_FIELD32(int_assert, 31,1); // interrupt caused a system interrupt
+ };
+ ICR icr;
+
+ uint32_t imr; // register that contains the current interrupt mask
+
+ struct ITR : public Reg<uint32_t> { // 0x00C4 ITR Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(interval, 0,16); // minimum inter-interrutp inteval
+ // specified in 256ns interrupts
+ };
+ ITR itr;
+
+ // When CTRL_EXT.IAME and the ICR.INT_ASSERT is 1 an ICR read or write
+ // causes the IAM register contents to be written into the IMC
+ // automatically clearing all interrupts that have a bit in the IAM set
+ uint32_t iam;
+
+ struct RCTL : public Reg<uint32_t> { // 0x0100 RCTL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(rst,0,1); // Reset
+ ADD_FIELD32(en,1,1); // Enable
+ ADD_FIELD32(sbp,2,1); // Store bad packets
+ ADD_FIELD32(upe,3,1); // Unicast Promiscuous enabled
+ ADD_FIELD32(mpe,4,1); // Multicast promiscuous enabled
+ ADD_FIELD32(lpe,5,1); // long packet reception enabled
+ ADD_FIELD32(lbm,6,2); //
+ ADD_FIELD32(rdmts,8,2); //
+ ADD_FIELD32(mo,12,2); //
+ ADD_FIELD32(mdr,14,1); //
+ ADD_FIELD32(bam,15,1); //
+ ADD_FIELD32(bsize,16,2); //
+ ADD_FIELD32(vfe,18,1); //
+ ADD_FIELD32(cfien,19,1); //
+ ADD_FIELD32(cfi,20,1); //
+ ADD_FIELD32(dpf,22,1); // discard pause frames
+ ADD_FIELD32(pmcf,23,1); // pass mac control frames
+ ADD_FIELD32(bsex,25,1); // buffer size extension
+ ADD_FIELD32(secrc,26,1); // strip ethernet crc from incoming packet
+ unsigned descSize()
+ {
+ switch(bsize()) {
+ case 0: return bsex() == 0 ? 2048 : 0;
+ case 1: return bsex() == 0 ? 1024 : 16384;
+ case 2: return bsex() == 0 ? 512 : 8192;
+ case 3: return bsex() == 0 ? 256 : 4096;
+ default:
+ return 0;
+ }
+ }
+ };
+ RCTL rctl;
+
+ struct FCTTV : public Reg<uint32_t> { // 0x0170 FCTTV
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(ttv,0,16); // Transmit Timer Value
+ };
+ FCTTV fcttv;
+
+ struct TCTL : public Reg<uint32_t> { // 0x0400 TCTL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(rst,0,1); // Reset
+ ADD_FIELD32(en,1,1); // Enable
+ ADD_FIELD32(bce,2,1); // busy check enable
+ ADD_FIELD32(psp,3,1); // pad short packets
+ ADD_FIELD32(ct,4,8); // collision threshold
+ ADD_FIELD32(cold,12,10); // collision distance
+ ADD_FIELD32(swxoff,22,1); // software xoff transmission
+ ADD_FIELD32(pbe,23,1); // packet burst enable
+ ADD_FIELD32(rtlc,24,1); // retransmit late collisions
+ ADD_FIELD32(nrtu,25,1); // on underrun no TX
+ ADD_FIELD32(mulr,26,1); // multiple request
+ };
+ TCTL tctl;
+
+ struct PBA : public Reg<uint32_t> { // 0x1000 PBA Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(rxa,0,16);
+ ADD_FIELD32(txa,16,16);
+ };
+ PBA pba;
+
+ struct FCRTL : public Reg<uint32_t> { // 0x2160 FCRTL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(rtl,3,28); // make this bigger than the spec so we can have
+ // a larger buffer
+ ADD_FIELD32(xone, 31,1);
+ };
+ FCRTL fcrtl;
+
+ struct FCRTH : public Reg<uint32_t> { // 0x2168 FCRTL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(rth,3,13); // make this bigger than the spec so we can have
+ //a larger buffer
+ ADD_FIELD32(xfce, 31,1);
+ };
+ FCRTH fcrth;
+
+ struct RDBA : public Reg<uint64_t> { // 0x2800 RDBA Register
+ using Reg<uint64_t>::operator=;
+ ADD_FIELD64(rdbal,0,32); // base address of rx descriptor ring
+ ADD_FIELD64(rdbah,32,32); // base address of rx descriptor ring
+ };
+ RDBA rdba;
+
+ struct RDLEN : public Reg<uint32_t> { // 0x2808 RDLEN Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(len,7,13); // number of bytes in the descriptor buffer
+ };
+ RDLEN rdlen;
+
+ struct SRRCTL : public Reg<uint32_t> { // 0x280C SRRCTL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(pktlen, 0, 8);
+ ADD_FIELD32(hdrlen, 8, 8); // guess based on header, not documented
+ ADD_FIELD32(desctype, 25,3); // type of descriptor 000 legacy, 001 adv,
+ //101 hdr split
+ unsigned bufLen() { return pktlen() << 10; }
+ unsigned hdrLen() { return hdrlen() << 6; }
+ };
+ SRRCTL srrctl;
+
+ struct RDH : public Reg<uint32_t> { // 0x2810 RDH Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(rdh,0,16); // head of the descriptor ring
+ };
+ RDH rdh;
+
+ struct RDT : public Reg<uint32_t> { // 0x2818 RDT Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(rdt,0,16); // tail of the descriptor ring
+ };
+ RDT rdt;
+
+ struct RDTR : public Reg<uint32_t> { // 0x2820 RDTR Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(delay,0,16); // receive delay timer
+ ADD_FIELD32(fpd, 31,1); // flush partial descriptor block ??
+ };
+ RDTR rdtr;
+
+ struct RXDCTL : public Reg<uint32_t> { // 0x2828 RXDCTL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(pthresh,0,6); // prefetch threshold, less that this
+ // consider prefetch
+ ADD_FIELD32(hthresh,8,6); // number of descriptors in host mem to
+ // consider prefetch
+ ADD_FIELD32(wthresh,16,6); // writeback threshold
+ ADD_FIELD32(gran,24,1); // granularity 0 = desc, 1 = cacheline
+ };
+ RXDCTL rxdctl;
+
+ struct RADV : public Reg<uint32_t> { // 0x282C RADV Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(idv,0,16); // absolute interrupt delay
+ };
+ RADV radv;
+
+ struct RSRPD : public Reg<uint32_t> { // 0x2C00 RSRPD Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(idv,0,12); // size to interrutp on small packets
+ };
+ RSRPD rsrpd;
+
+ struct TDBA : public Reg<uint64_t> { // 0x3800 TDBAL Register
+ using Reg<uint64_t>::operator=;
+ ADD_FIELD64(tdbal,0,32); // base address of transmit descriptor ring
+ ADD_FIELD64(tdbah,32,32); // base address of transmit descriptor ring
+ };
+ TDBA tdba;
+
+ struct TDLEN : public Reg<uint32_t> { // 0x3808 TDLEN Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(len,7,13); // number of bytes in the descriptor buffer
+ };
+ TDLEN tdlen;
+
+ struct TDH : public Reg<uint32_t> { // 0x3810 TDH Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(tdh,0,16); // head of the descriptor ring
+ };
+ TDH tdh;
+
+ struct TXDCA_CTL : public Reg<uint32_t> { // 0x3814 TXDCA_CTL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(cpu_mask, 0, 5);
+ ADD_FIELD32(enabled, 5,1);
+ ADD_FIELD32(relax_ordering, 6, 1);
+ };
+ TXDCA_CTL txdca_ctl;
+
+ struct TDT : public Reg<uint32_t> { // 0x3818 TDT Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(tdt,0,16); // tail of the descriptor ring
+ };
+ TDT tdt;
+
+ struct TIDV : public Reg<uint32_t> { // 0x3820 TIDV Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(idv,0,16); // interrupt delay
+ };
+ TIDV tidv;
+
+ struct TXDCTL : public Reg<uint32_t> { // 0x3828 TXDCTL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(pthresh, 0,6); // if number of descriptors control has is
+ // below this number, a prefetch is considered
+ ADD_FIELD32(hthresh,8,8); // number of valid descriptors is host memory
+ // before a prefetch is considered
+ ADD_FIELD32(wthresh,16,6); // number of descriptors to keep until
+ // writeback is considered
+ ADD_FIELD32(gran, 24,1); // granulatiry of above values (0 = cacheline,
+ // 1 == desscriptor)
+ ADD_FIELD32(lwthresh,25,7); // xmit descriptor low thresh, interrupt
+ // below this level
+ };
+ TXDCTL txdctl;
+
+ struct TADV : public Reg<uint32_t> { // 0x382C TADV Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(idv,0,16); // absolute interrupt delay
+ };
+ TADV tadv;
+/*
+ struct TDWBA : public Reg<uint64_t> { // 0x3838 TDWBA Register
+ using Reg<uint64_t>::operator=;
+ ADD_FIELD64(en,0,1); // enable transmit description ring address writeback
+ ADD_FIELD64(tdwbal,2,32); // base address of transmit descriptor ring address writeback
+ ADD_FIELD64(tdwbah,32,32); // base address of transmit descriptor ring
+ };
+ TDWBA tdwba;*/
+ uint64_t tdwba;
+
+ struct RXCSUM : public Reg<uint32_t> { // 0x5000 RXCSUM Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(pcss,0,8);
+ ADD_FIELD32(ipofld,8,1);
+ ADD_FIELD32(tuofld,9,1);
+ ADD_FIELD32(pcsd, 13,1);
+ };
+ RXCSUM rxcsum;
+
+ uint32_t rlpml; // 0x5004 RLPML probably maximum accepted packet size
+
+ struct RFCTL : public Reg<uint32_t> { // 0x5008 RFCTL Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(iscsi_dis,0,1);
+ ADD_FIELD32(iscsi_dwc,1,5);
+ ADD_FIELD32(nfsw_dis,6,1);
+ ADD_FIELD32(nfsr_dis,7,1);
+ ADD_FIELD32(nfs_ver,8,2);
+ ADD_FIELD32(ipv6_dis,10,1);
+ ADD_FIELD32(ipv6xsum_dis,11,1);
+ ADD_FIELD32(ackdis,13,1);
+ ADD_FIELD32(ipfrsp_dis,14,1);
+ ADD_FIELD32(exsten,15,1);
+ };
+ RFCTL rfctl;
+
+ struct MANC : public Reg<uint32_t> { // 0x5820 MANC Register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(smbus,0,1); // SMBus enabled #####
+ ADD_FIELD32(asf,1,1); // ASF enabled #####
+ ADD_FIELD32(ronforce,2,1); // reset of force
+ ADD_FIELD32(rsvd,3,5); // reserved
+ ADD_FIELD32(rmcp1,8,1); // rcmp1 filtering
+ ADD_FIELD32(rmcp2,9,1); // rcmp2 filtering
+ ADD_FIELD32(ipv4,10,1); // enable ipv4
+ ADD_FIELD32(ipv6,11,1); // enable ipv6
+ ADD_FIELD32(snap,12,1); // accept snap
+ ADD_FIELD32(arp,13,1); // filter arp #####
+ ADD_FIELD32(neighbor,14,1); // neighbor discovery
+ ADD_FIELD32(arp_resp,15,1); // arp response
+ ADD_FIELD32(tcorst,16,1); // tco reset happened
+ ADD_FIELD32(rcvtco,17,1); // receive tco enabled ######
+ ADD_FIELD32(blkphyrst,18,1);// block phy resets ########
+ ADD_FIELD32(rcvall,19,1); // receive all
+ ADD_FIELD32(macaddrfltr,20,1); // mac address filtering ######
+ ADD_FIELD32(mng2host,21,1); // mng2 host packets #######
+ ADD_FIELD32(ipaddrfltr,22,1); // ip address filtering
+ ADD_FIELD32(xsumfilter,23,1); // checksum filtering
+ ADD_FIELD32(brfilter,24,1); // broadcast filtering
+ ADD_FIELD32(smbreq,25,1); // smb request
+ ADD_FIELD32(smbgnt,26,1); // smb grant
+ ADD_FIELD32(smbclkin,27,1); // smbclkin
+ ADD_FIELD32(smbdatain,28,1); // smbdatain
+ ADD_FIELD32(smbdataout,29,1); // smb data out
+ ADD_FIELD32(smbclkout,30,1); // smb clock out
+ };
+ MANC manc;
+
+ struct SWSM : public Reg<uint32_t> { // 0x5B50 SWSM register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(smbi,0,1); // Semaphone bit
+ ADD_FIELD32(swesmbi, 1,1); // Software eeporm semaphore
+ ADD_FIELD32(wmng, 2,1); // Wake MNG clock
+ ADD_FIELD32(reserved, 3, 29);
+ };
+ SWSM swsm;
+
+ struct FWSM : public Reg<uint32_t> { // 0x5B54 FWSM register
+ using Reg<uint32_t>::operator=;
+ ADD_FIELD32(eep_fw_semaphore,0,1);
+ ADD_FIELD32(fw_mode, 1,3);
+ ADD_FIELD32(ide, 4,1);
+ ADD_FIELD32(sol, 5,1);
+ ADD_FIELD32(eep_roload, 6,1);
+ ADD_FIELD32(reserved, 7,8);
+ ADD_FIELD32(fw_val_bit, 15, 1);
+ ADD_FIELD32(reset_cnt, 16, 3);
+ ADD_FIELD32(ext_err_ind, 19, 6);
+ ADD_FIELD32(reserved2, 25, 7);
+ };
+ FWSM fwsm;
+
+ uint32_t sw_fw_sync;
+
+ void serialize(CheckpointOut &cp) const override
+ {
+ paramOut(cp, "ctrl", ctrl._data);
+ paramOut(cp, "sts", sts._data);
+ paramOut(cp, "eecd", eecd._data);
+ paramOut(cp, "eerd", eerd._data);
+ paramOut(cp, "ctrl_ext", ctrl_ext._data);
+ paramOut(cp, "mdic", mdic._data);
+ paramOut(cp, "icr", icr._data);
+ SERIALIZE_SCALAR(imr);
+ paramOut(cp, "itr", itr._data);
+ SERIALIZE_SCALAR(iam);
+ paramOut(cp, "rctl", rctl._data);
+ paramOut(cp, "fcttv", fcttv._data);
+ paramOut(cp, "tctl", tctl._data);
+ paramOut(cp, "pba", pba._data);
+ paramOut(cp, "fcrtl", fcrtl._data);
+ paramOut(cp, "fcrth", fcrth._data);
+ paramOut(cp, "rdba", rdba._data);
+ paramOut(cp, "rdlen", rdlen._data);
+ paramOut(cp, "srrctl", srrctl._data);
+ paramOut(cp, "rdh", rdh._data);
+ paramOut(cp, "rdt", rdt._data);
+ paramOut(cp, "rdtr", rdtr._data);
+ paramOut(cp, "rxdctl", rxdctl._data);
+ paramOut(cp, "radv", radv._data);
+ paramOut(cp, "rsrpd", rsrpd._data);
+ paramOut(cp, "tdba", tdba._data);
+ paramOut(cp, "tdlen", tdlen._data);
+ paramOut(cp, "tdh", tdh._data);
+ paramOut(cp, "txdca_ctl", txdca_ctl._data);
+ paramOut(cp, "tdt", tdt._data);
+ paramOut(cp, "tidv", tidv._data);
+ paramOut(cp, "txdctl", txdctl._data);
+ paramOut(cp, "tadv", tadv._data);
+ //paramOut(cp, "tdwba", tdwba._data);
+ SERIALIZE_SCALAR(tdwba);
+ paramOut(cp, "rxcsum", rxcsum._data);
+ SERIALIZE_SCALAR(rlpml);
+ paramOut(cp, "rfctl", rfctl._data);
+ paramOut(cp, "manc", manc._data);
+ paramOut(cp, "swsm", swsm._data);
+ paramOut(cp, "fwsm", fwsm._data);
+ SERIALIZE_SCALAR(sw_fw_sync);
+ }
+
+ void unserialize(CheckpointIn &cp) override
+ {
+ paramIn(cp, "ctrl", ctrl._data);
+ paramIn(cp, "sts", sts._data);
+ paramIn(cp, "eecd", eecd._data);
+ paramIn(cp, "eerd", eerd._data);
+ paramIn(cp, "ctrl_ext", ctrl_ext._data);
+ paramIn(cp, "mdic", mdic._data);
+ paramIn(cp, "icr", icr._data);
+ UNSERIALIZE_SCALAR(imr);
+ paramIn(cp, "itr", itr._data);
+ UNSERIALIZE_SCALAR(iam);
+ paramIn(cp, "rctl", rctl._data);
+ paramIn(cp, "fcttv", fcttv._data);
+ paramIn(cp, "tctl", tctl._data);
+ paramIn(cp, "pba", pba._data);
+ paramIn(cp, "fcrtl", fcrtl._data);
+ paramIn(cp, "fcrth", fcrth._data);
+ paramIn(cp, "rdba", rdba._data);
+ paramIn(cp, "rdlen", rdlen._data);
+ paramIn(cp, "srrctl", srrctl._data);
+ paramIn(cp, "rdh", rdh._data);
+ paramIn(cp, "rdt", rdt._data);
+ paramIn(cp, "rdtr", rdtr._data);
+ paramIn(cp, "rxdctl", rxdctl._data);
+ paramIn(cp, "radv", radv._data);
+ paramIn(cp, "rsrpd", rsrpd._data);
+ paramIn(cp, "tdba", tdba._data);
+ paramIn(cp, "tdlen", tdlen._data);
+ paramIn(cp, "tdh", tdh._data);
+ paramIn(cp, "txdca_ctl", txdca_ctl._data);
+ paramIn(cp, "tdt", tdt._data);
+ paramIn(cp, "tidv", tidv._data);
+ paramIn(cp, "txdctl", txdctl._data);
+ paramIn(cp, "tadv", tadv._data);
+ UNSERIALIZE_SCALAR(tdwba);
+ //paramIn(cp, "tdwba", tdwba._data);
+ paramIn(cp, "rxcsum", rxcsum._data);
+ UNSERIALIZE_SCALAR(rlpml);
+ paramIn(cp, "rfctl", rfctl._data);
+ paramIn(cp, "manc", manc._data);
+ paramIn(cp, "swsm", swsm._data);
+ paramIn(cp, "fwsm", fwsm._data);
+ UNSERIALIZE_SCALAR(sw_fw_sync);
+ }
+};
+} // namespace iGbReg
diff --git a/src/dev/net/multi_etherlink.cc b/src/dev/net/multi_etherlink.cc
new file mode 100644
index 000000000..cf4300ddf
--- /dev/null
+++ b/src/dev/net/multi_etherlink.cc
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabor Dozsa
+ */
+
+/* @file
+ * Device module for a full duplex ethernet link for multi gem5 simulations.
+ */
+
+#include "dev/net/multi_etherlink.hh"
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cmath>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "base/random.hh"
+#include "base/trace.hh"
+#include "debug/EthernetData.hh"
+#include "debug/MultiEthernet.hh"
+#include "debug/MultiEthernetPkt.hh"
+#include "dev/net/etherdump.hh"
+#include "dev/net/etherint.hh"
+#include "dev/net/etherlink.hh"
+#include "dev/net/etherobject.hh"
+#include "dev/net/etherpkt.hh"
+#include "dev/net/multi_iface.hh"
+#include "dev/net/tcp_iface.hh"
+#include "params/EtherLink.hh"
+#include "sim/core.hh"
+#include "sim/serialize.hh"
+#include "sim/system.hh"
+
+using namespace std;
+
+MultiEtherLink::MultiEtherLink(const Params *p)
+ : EtherObject(p)
+{
+ DPRINTF(MultiEthernet,"MultiEtherLink::MultiEtherLink() "
+ "link delay:%llu\n", p->delay);
+
+ txLink = new TxLink(name() + ".link0", this, p->speed, p->delay_var,
+ p->dump);
+ rxLink = new RxLink(name() + ".link1", this, p->delay, p->dump);
+
+ // create the multi (TCP) interface to talk to the peer gem5 processes.
+ multiIface = new TCPIface(p->server_name, p->server_port, p->multi_rank,
+ p->sync_start, p->sync_repeat, this);
+
+ localIface = new LocalIface(name() + ".int0", txLink, rxLink, multiIface);
+}
+
+MultiEtherLink::~MultiEtherLink()
+{
+ delete txLink;
+ delete rxLink;
+ delete localIface;
+ delete multiIface;
+}
+
+EtherInt*
+MultiEtherLink::getEthPort(const std::string &if_name, int idx)
+{
+ if (if_name != "int0") {
+ return nullptr;
+ } else {
+ panic_if(localIface->getPeer(), "interface already connected to");
+ }
+ return localIface;
+}
+
+void MultiEtherLink::memWriteback()
+{
+ DPRINTF(MultiEthernet,"MultiEtherLink::memWriteback() called\n");
+ multiIface->drainDone();
+}
+
+void
+MultiEtherLink::serialize(CheckpointOut &cp) const
+{
+ multiIface->serialize("multiIface", cp);
+ txLink->serialize("txLink", cp);
+ rxLink->serialize("rxLink", cp);
+}
+
+void
+MultiEtherLink::unserialize(CheckpointIn &cp)
+{
+ multiIface->unserialize("multiIface", cp);
+ txLink->unserialize("txLink", cp);
+ rxLink->unserialize("rxLink", cp);
+}
+
+void
+MultiEtherLink::init()
+{
+ DPRINTF(MultiEthernet,"MultiEtherLink::init() called\n");
+ multiIface->initRandom();
+}
+
+void
+MultiEtherLink::startup()
+{
+ DPRINTF(MultiEthernet,"MultiEtherLink::startup() called\n");
+ multiIface->startPeriodicSync();
+}
+
+void
+MultiEtherLink::RxLink::setMultiInt(MultiIface *m)
+{
+ assert(!multiIface);
+ multiIface = m;
+ // Spawn a new receiver thread that will process messages
+ // coming in from peer gem5 processes.
+ // The receive thread will also schedule a (receive) doneEvent
+ // for each incoming data packet.
+ multiIface->spawnRecvThread(&doneEvent, linkDelay);
+}
+
+void
+MultiEtherLink::RxLink::rxDone()
+{
+ assert(!busy());
+
+ // retrieve the packet that triggered the receive done event
+ packet = multiIface->packetIn();
+
+ if (dump)
+ dump->dump(packet);
+
+ DPRINTF(MultiEthernetPkt, "MultiEtherLink::MultiLink::rxDone() "
+ "packet received: len=%d\n", packet->length);
+ DDUMP(EthernetData, packet->data, packet->length);
+
+ localIface->sendPacket(packet);
+
+ packet = nullptr;
+}
+
+void
+MultiEtherLink::TxLink::txDone()
+{
+ if (dump)
+ dump->dump(packet);
+
+ packet = nullptr;
+ assert(!busy());
+
+ localIface->sendDone();
+}
+
+bool
+MultiEtherLink::TxLink::transmit(EthPacketPtr pkt)
+{
+ if (busy()) {
+ DPRINTF(MultiEthernet, "packet not sent, link busy\n");
+ return false;
+ }
+
+ packet = pkt;
+ Tick delay = (Tick)ceil(((double)pkt->length * ticksPerByte) + 1.0);
+ if (delayVar != 0)
+ delay += random_mt.random<Tick>(0, delayVar);
+
+ // send the packet to the peers
+ assert(multiIface);
+ multiIface->packetOut(pkt, delay);
+
+ // schedule the send done event
+ parent->schedule(doneEvent, curTick() + delay);
+
+ return true;
+}
+
+void
+MultiEtherLink::Link::serialize(const string &base, CheckpointOut &cp) const
+{
+ bool packet_exists = (packet != nullptr);
+ paramOut(cp, base + ".packet_exists", packet_exists);
+ if (packet_exists)
+ packet->serialize(base + ".packet", cp);
+
+ bool event_scheduled = event->scheduled();
+ paramOut(cp, base + ".event_scheduled", event_scheduled);
+ if (event_scheduled) {
+ Tick event_time = event->when();
+ paramOut(cp, base + ".event_time", event_time);
+ }
+}
+
+void
+MultiEtherLink::Link::unserialize(const string &base, CheckpointIn &cp)
+{
+ bool packet_exists;
+ paramIn(cp, base + ".packet_exists", packet_exists);
+ if (packet_exists) {
+ packet = make_shared<EthPacketData>(16384);
+ packet->unserialize(base + ".packet", cp);
+ }
+
+ bool event_scheduled;
+ paramIn(cp, base + ".event_scheduled", event_scheduled);
+ if (event_scheduled) {
+ Tick event_time;
+ paramIn(cp, base + ".event_time", event_time);
+ parent->schedule(*event, event_time);
+ }
+}
+
+MultiEtherLink::LocalIface::LocalIface(const std::string &name,
+ TxLink *tx,
+ RxLink *rx,
+ MultiIface *m) :
+ EtherInt(name), txLink(tx)
+{
+ tx->setLocalInt(this);
+ rx->setLocalInt(this);
+ tx->setMultiInt(m);
+ rx->setMultiInt(m);
+}
+
+MultiEtherLink *
+MultiEtherLinkParams::create()
+{
+ return new MultiEtherLink(this);
+}
+
+
diff --git a/src/dev/net/multi_etherlink.hh b/src/dev/net/multi_etherlink.hh
new file mode 100644
index 000000000..0a3e39bd7
--- /dev/null
+++ b/src/dev/net/multi_etherlink.hh
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabor Dozsa
+ */
+
+/* @file
+ * Device module for a full duplex ethernet link for multi gem5 simulations.
+ *
+ * See comments in dev/multi_iface.hh for a generic description of multi
+ * gem5 simulations.
+ *
+ * This class is meant to be a drop in replacement for the EtherLink class for
+ * multi gem5 runs.
+ *
+ */
+#ifndef __DEV_NET_MULTIETHERLINK_HH__
+#define __DEV_NET_MULTIETHERLINK_HH__
+
+#include <iostream>
+
+#include "dev/net/etherlink.hh"
+#include "params/MultiEtherLink.hh"
+
+class MultiIface;
+class EthPacketData;
+
+/**
+ * Model for a fixed bandwidth full duplex ethernet link.
+ */
+class MultiEtherLink : public EtherObject
+{
+ protected:
+ class LocalIface;
+
+ /**
+ * Model base class for a single uni-directional link.
+ *
+ * The link will encapsulate and transfer Ethernet packets to/from
+ * the message server.
+ */
+ class Link
+ {
+ protected:
+ std::string objName;
+ MultiEtherLink *parent;
+ LocalIface *localIface;
+ EtherDump *dump;
+ MultiIface *multiIface;
+ Event *event;
+ EthPacketPtr packet;
+
+ public:
+ Link(const std::string &name, MultiEtherLink *p,
+ EtherDump *d, Event *e) :
+ objName(name), parent(p), localIface(nullptr), dump(d),
+ multiIface(nullptr), event(e) {}
+
+ ~Link() {}
+
+ const std::string name() const { return objName; }
+ bool busy() const { return (bool)packet; }
+ void setLocalInt(LocalIface *i) { assert(!localIface); localIface=i; }
+
+ void serialize(const std::string &base, CheckpointOut &cp) const;
+ void unserialize(const std::string &base, CheckpointIn &cp);
+ };
+
+ /**
+ * Model for a send link.
+ */
+ class TxLink : public Link
+ {
+ protected:
+ /**
+ * Per byte send delay
+ */
+ double ticksPerByte;
+ /**
+ * Random component of the send delay
+ */
+ Tick delayVar;
+
+ /**
+ * Send done callback. Called from doneEvent.
+ */
+ void txDone();
+ typedef EventWrapper<TxLink, &TxLink::txDone> DoneEvent;
+ friend void DoneEvent::process();
+ DoneEvent doneEvent;
+
+ public:
+ TxLink(const std::string &name, MultiEtherLink *p,
+ double invBW, Tick delay_var, EtherDump *d) :
+ Link(name, p, d, &doneEvent), ticksPerByte(invBW),
+ delayVar(delay_var), doneEvent(this) {}
+ ~TxLink() {}
+
+ /**
+ * Register the multi interface to be used to talk to the
+ * peer gem5 processes.
+ */
+ void setMultiInt(MultiIface *m) { assert(!multiIface); multiIface=m; }
+
+ /**
+ * Initiate sending of a packet via this link.
+ *
+ * @param packet Ethernet packet to send
+ */
+ bool transmit(EthPacketPtr packet);
+ };
+
+ /**
+ * Model for a receive link.
+ */
+ class RxLink : public Link
+ {
+ protected:
+
+ /**
+ * Transmission delay for the simulated Ethernet link.
+ */
+ Tick linkDelay;
+
+ /**
+ * Receive done callback method. Called from doneEvent.
+ */
+ void rxDone() ;
+ typedef EventWrapper<RxLink, &RxLink::rxDone> DoneEvent;
+ friend void DoneEvent::process();
+ DoneEvent doneEvent;
+
+ public:
+
+ RxLink(const std::string &name, MultiEtherLink *p,
+ Tick delay, EtherDump *d) :
+ Link(name, p, d, &doneEvent),
+ linkDelay(delay), doneEvent(this) {}
+ ~RxLink() {}
+
+ /**
+ * Register our multi interface to talk to the peer gem5 processes.
+ */
+ void setMultiInt(MultiIface *m);
+ };
+
+ /**
+ * Interface to the local simulated system
+ */
+ class LocalIface : public EtherInt
+ {
+ private:
+ TxLink *txLink;
+
+ public:
+ LocalIface(const std::string &name, TxLink *tx, RxLink *rx,
+ MultiIface *m);
+
+ bool recvPacket(EthPacketPtr pkt) { return txLink->transmit(pkt); }
+ void sendDone() { peer->sendDone(); }
+ bool isBusy() { return txLink->busy(); }
+ };
+
+
+ protected:
+ /**
+ * Interface to talk to the peer gem5 processes.
+ */
+ MultiIface *multiIface;
+ /**
+ * Send link
+ */
+ TxLink *txLink;
+ /**
+ * Receive link
+ */
+ RxLink *rxLink;
+ LocalIface *localIface;
+
+ public:
+ typedef MultiEtherLinkParams Params;
+ MultiEtherLink(const Params *p);
+ ~MultiEtherLink();
+
+ const Params *
+ params() const
+ {
+ return dynamic_cast<const Params *>(_params);
+ }
+
+ virtual EtherInt *getEthPort(const std::string &if_name,
+ int idx) override;
+
+ void memWriteback() override;
+ void init() override;
+ void startup() override;
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+};
+
+#endif // __DEV_NET_MULTIETHERLINK_HH__
diff --git a/src/dev/net/multi_iface.cc b/src/dev/net/multi_iface.cc
new file mode 100644
index 000000000..15f69f2ac
--- /dev/null
+++ b/src/dev/net/multi_iface.cc
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabor Dozsa
+ */
+
+/* @file
+ * The interface class for multi gem5 simulations.
+ */
+
+#include "dev/net/multi_iface.hh"
+
+#include <queue>
+#include <thread>
+
+#include "base/random.hh"
+#include "base/trace.hh"
+#include "debug/MultiEthernet.hh"
+#include "debug/MultiEthernetPkt.hh"
+#include "dev/net/etherpkt.hh"
+#include "sim/sim_exit.hh"
+#include "sim/sim_object.hh"
+
+
+MultiIface::Sync *MultiIface::sync = nullptr;
+MultiIface::SyncEvent *MultiIface::syncEvent = nullptr;
+unsigned MultiIface::recvThreadsNum = 0;
+MultiIface *MultiIface::master = nullptr;
+
+bool
+MultiIface::Sync::run(SyncTrigger t, Tick sync_tick)
+{
+ std::unique_lock<std::mutex> sync_lock(lock);
+
+ trigger = t;
+ if (trigger != SyncTrigger::periodic) {
+ DPRINTF(MultiEthernet,"MultiIface::Sync::run() trigger:%d\n",
+ (unsigned)trigger);
+ }
+
+ switch (state) {
+ case SyncState::asyncCkpt:
+ switch (trigger) {
+ case SyncTrigger::ckpt:
+ assert(MultiIface::syncEvent->interrupted == false);
+ state = SyncState::busy;
+ break;
+ case SyncTrigger::periodic:
+ if (waitNum == 0) {
+ // So all recv threads got an async checkpoint request already
+ // and a simExit is scheduled at the end of the current tick
+ // (i.e. it is a periodic sync scheduled at the same tick as
+ // the simExit).
+ state = SyncState::idle;
+ DPRINTF(MultiEthernet,"MultiIface::Sync::run() interrupted "
+ "due to async ckpt scheduled\n");
+ return false;
+ } else {
+ // we still need to wait for some receiver thread to get the
+ // aysnc ckpt request. We are going to proceed as 'interrupted'
+ // periodic sync.
+ state = SyncState::interrupted;
+ DPRINTF(MultiEthernet,"MultiIface::Sync::run() interrupted "
+ "due to ckpt request is coming in\n");
+ }
+ break;
+ case SyncTrigger::atomic:
+ assert(trigger != SyncTrigger::atomic);
+ }
+ break;
+ case SyncState::idle:
+ state = SyncState::busy;
+ break;
+ // Only one sync can be active at any time
+ case SyncState::interrupted:
+ case SyncState::busy:
+ assert(state != SyncState::interrupted);
+ assert(state != SyncState::busy);
+ break;
+ }
+ // Kick-off the sync unless we are in the middle of an interrupted
+ // periodic sync
+ if (state != SyncState::interrupted) {
+ assert(waitNum == 0);
+ waitNum = MultiIface::recvThreadsNum;
+ // initiate the global synchronisation
+ assert(MultiIface::master != nullptr);
+ MultiIface::master->syncRaw(triggerToMsg[(unsigned)trigger], sync_tick);
+ }
+ // now wait until all receiver threads complete the synchronisation
+ auto lf = [this]{ return waitNum == 0; };
+ cv.wait(sync_lock, lf);
+
+ // we are done
+ assert(state == SyncState::busy || state == SyncState::interrupted);
+ bool ret = (state != SyncState::interrupted);
+ state = SyncState::idle;
+ return ret;
+}
+
+void
+MultiIface::Sync::progress(MsgType msg)
+{
+ std::unique_lock<std::mutex> sync_lock(lock);
+
+ switch (msg) {
+ case MsgType::cmdAtomicSyncAck:
+ assert(state == SyncState::busy && trigger == SyncTrigger::atomic);
+ break;
+ case MsgType::cmdPeriodicSyncAck:
+ assert(state == SyncState::busy && trigger == SyncTrigger::periodic);
+ break;
+ case MsgType::cmdCkptSyncAck:
+ assert(state == SyncState::busy && trigger == SyncTrigger::ckpt);
+ break;
+ case MsgType::cmdCkptSyncReq:
+ switch (state) {
+ case SyncState::busy:
+ if (trigger == SyncTrigger::ckpt) {
+ // We are already in a checkpoint sync but got another ckpt
+ // sync request. This may happen if two (or more) peer gem5
+ // processes try to start a ckpt nearly at the same time.
+ // Incrementing waitNum here (before decrementing it below)
+ // effectively results in ignoring this new ckpt sync request.
+ waitNum++;
+ break;
+ }
+ assert (waitNum == recvThreadsNum);
+ state = SyncState::interrupted;
+ // we need to fall over here to handle "recvThreadsNum == 1" case
+ case SyncState::interrupted:
+ assert(trigger == SyncTrigger::periodic);
+ assert(waitNum >= 1);
+ if (waitNum == 1) {
+ exitSimLoop("checkpoint");
+ }
+ break;
+ case SyncState::idle:
+ // There is no on-going sync so we got an async ckpt request. If we
+ // are the only receiver thread then we need to schedule the
+ // checkpoint. Otherwise, only change the state to 'asyncCkpt' and
+ // let the last receiver thread to schedule the checkpoint at the
+ // 'asyncCkpt' case.
+ // Note that a periodic or resume sync may start later and that can
+ // trigger a state change to 'interrupted' (so the checkpoint may
+ // get scheduled at 'interrupted' case finally).
+ assert(waitNum == 0);
+ state = SyncState::asyncCkpt;
+ waitNum = MultiIface::recvThreadsNum;
+ // we need to fall over here to handle "recvThreadsNum == 1" case
+ case SyncState::asyncCkpt:
+ assert(waitNum >= 1);
+ if (waitNum == 1)
+ exitSimLoop("checkpoint");
+ break;
+ default:
+ panic("Unexpected state for checkpoint request message");
+ break;
+ }
+ break;
+ default:
+ panic("Unknown msg type");
+ break;
+ }
+ waitNum--;
+ assert(state != SyncState::idle);
+ // Notify the simultaion thread if there is an on-going sync.
+ if (state != SyncState::asyncCkpt) {
+ sync_lock.unlock();
+ cv.notify_one();
+ }
+}
+
+void MultiIface::SyncEvent::start(Tick start, Tick interval)
+{
+ assert(!scheduled());
+ if (interval == 0)
+ panic("Multi synchronisation period must be greater than zero");
+ repeat = interval;
+ schedule(start);
+}
+
+void
+MultiIface::SyncEvent::adjust(Tick start_tick, Tick repeat_tick)
+{
+ // The new multi interface may require earlier start of the
+ // synchronisation.
+ assert(scheduled() == true);
+ if (start_tick < when())
+ reschedule(start_tick);
+ // The new multi interface may require more frequent synchronisation.
+ if (repeat == 0)
+ panic("Multi synchronisation period must be greater than zero");
+ if (repeat < repeat_tick)
+ repeat = repeat_tick;
+}
+
+void
+MultiIface::SyncEvent::process()
+{
+ /*
+ * Note that this is a global event so this process method will be called
+ * by only exactly one thread.
+ */
+ // if we are draining the system then we must not start a periodic sync (as
+ // it is not sure that all peer gem5 will reach this tick before taking
+ // the checkpoint).
+ if (isDraining == true) {
+ assert(interrupted == false);
+ interrupted = true;
+ DPRINTF(MultiEthernet,"MultiIface::SyncEvent::process() interrupted "
+ "due to draining\n");
+ return;
+ }
+ if (interrupted == false)
+ scheduledAt = curTick();
+ /*
+ * We hold the eventq lock at this point but the receiver thread may
+ * need the lock to schedule new recv events while waiting for the
+ * multi sync to complete.
+ * Note that the other simulation threads also release their eventq
+ * locks while waiting for us due to the global event semantics.
+ */
+ curEventQueue()->unlock();
+ // we do a global sync here
+ interrupted = !MultiIface::sync->run(SyncTrigger::periodic, scheduledAt);
+ // Global sync completed or got interrupted.
+ // we are expected to exit with the eventq lock held
+ curEventQueue()->lock();
+ // schedule the next global sync event if this one completed. Otherwise
+ // (i.e. this one was interrupted by a checkpoint request), we will
+ // reschedule this one after the draining is complete.
+ if (!interrupted)
+ schedule(scheduledAt + repeat);
+}
+
+void MultiIface::SyncEvent::resume()
+{
+ Tick sync_tick;
+ assert(!scheduled());
+ if (interrupted) {
+ assert(curTick() >= scheduledAt);
+ // We have to complete the interrupted periodic sync asap.
+ // Note that this sync might be interrupted now again with a checkpoint
+ // request from a peer gem5...
+ sync_tick = curTick();
+ schedule(sync_tick);
+ } else {
+ // So we completed the last periodic sync, let's find out the tick for
+ // next one
+ assert(curTick() > scheduledAt);
+ sync_tick = scheduledAt + repeat;
+ if (sync_tick < curTick())
+ panic("Cannot resume periodic synchronisation");
+ schedule(sync_tick);
+ }
+ DPRINTF(MultiEthernet,
+ "MultiIface::SyncEvent periodic sync resumed at %lld "
+ "(curTick:%lld)\n", sync_tick, curTick());
+}
+
+void MultiIface::SyncEvent::serialize(const std::string &base,
+ CheckpointOut &cp) const
+{
+ // Save the periodic multi sync schedule information
+ paramOut(cp, base + ".periodicSyncRepeat", repeat);
+ paramOut(cp, base + ".periodicSyncInterrupted", interrupted);
+ paramOut(cp, base + ".periodicSyncAt", scheduledAt);
+}
+
+void MultiIface::SyncEvent::unserialize(const std::string &base,
+ CheckpointIn &cp)
+{
+ paramIn(cp, base + ".periodicSyncRepeat", repeat);
+ paramIn(cp, base + ".periodicSyncInterrupted", interrupted);
+ paramIn(cp, base + ".periodicSyncAt", scheduledAt);
+}
+
+MultiIface::MultiIface(unsigned multi_rank,
+ Tick sync_start,
+ Tick sync_repeat,
+ EventManager *em) :
+ syncStart(sync_start), syncRepeat(sync_repeat),
+ recvThread(nullptr), eventManager(em), recvDone(nullptr),
+ scheduledRecvPacket(nullptr), linkDelay(0), rank(multi_rank)
+{
+ DPRINTF(MultiEthernet, "MultiIface() ctor rank:%d\n",multi_rank);
+ if (master == nullptr) {
+ assert(sync == nullptr);
+ assert(syncEvent == nullptr);
+ sync = new Sync();
+ syncEvent = new SyncEvent();
+ master = this;
+ }
+}
+
+MultiIface::~MultiIface()
+{
+ assert(recvThread);
+ delete recvThread;
+ if (this == master) {
+ assert(syncEvent);
+ delete syncEvent;
+ assert(sync);
+ delete sync;
+ }
+}
+
+void
+MultiIface::packetOut(EthPacketPtr pkt, Tick send_delay)
+{
+ MultiHeaderPkt::Header header_pkt;
+ unsigned address_length = MultiHeaderPkt::maxAddressLength();
+
+ // Prepare a multi header packet for the Ethernet packet we want to
+ // send out.
+ header_pkt.msgType = MsgType::dataDescriptor;
+ header_pkt.sendTick = curTick();
+ header_pkt.sendDelay = send_delay;
+
+ // Store also the source and destination addresses.
+ pkt->packAddress(header_pkt.srcAddress, header_pkt.dstAddress,
+ address_length);
+
+ header_pkt.dataPacketLength = pkt->size();
+
+ // Send out the multi hedare packet followed by the Ethernet packet.
+ sendRaw(&header_pkt, sizeof(header_pkt), header_pkt.dstAddress);
+ sendRaw(pkt->data, pkt->size(), header_pkt.dstAddress);
+ DPRINTF(MultiEthernetPkt,
+ "MultiIface::sendDataPacket() done size:%d send_delay:%llu "
+ "src:0x%02x%02x%02x%02x%02x%02x "
+ "dst:0x%02x%02x%02x%02x%02x%02x\n",
+ pkt->size(), send_delay,
+ header_pkt.srcAddress[0], header_pkt.srcAddress[1],
+ header_pkt.srcAddress[2], header_pkt.srcAddress[3],
+ header_pkt.srcAddress[4], header_pkt.srcAddress[5],
+ header_pkt.dstAddress[0], header_pkt.dstAddress[1],
+ header_pkt.dstAddress[2], header_pkt.dstAddress[3],
+ header_pkt.dstAddress[4], header_pkt.dstAddress[5]);
+}
+
+bool
+MultiIface::recvHeader(MultiHeaderPkt::Header &header_pkt)
+{
+ // Blocking receive of an incoming multi header packet.
+ return recvRaw((void *)&header_pkt, sizeof(header_pkt));
+}
+
+void
+MultiIface::recvData(const MultiHeaderPkt::Header &header_pkt)
+{
+ // We are here beacuse a header packet has been received implying
+ // that an Ethernet (data) packet is coming in next.
+ assert(header_pkt.msgType == MsgType::dataDescriptor);
+ // Allocate storage for the incoming Ethernet packet.
+ EthPacketPtr new_packet(new EthPacketData(header_pkt.dataPacketLength));
+ // Now execute the blocking receive and store the incoming data directly
+ // in the new EthPacketData object.
+ if (! recvRaw((void *)(new_packet->data), header_pkt.dataPacketLength))
+ panic("Missing data packet");
+
+ new_packet->length = header_pkt.dataPacketLength;
+ // Grab the event queue lock to schedule a new receive event for the
+ // data packet.
+ curEventQueue()->lock();
+ // Compute the receive tick. It includes the send delay and the
+ // simulated link delay.
+ Tick recv_tick = header_pkt.sendTick + header_pkt.sendDelay + linkDelay;
+ DPRINTF(MultiEthernetPkt, "MultiIface::recvThread() packet receive, "
+ "send_tick:%llu send_delay:%llu link_delay:%llu recv_tick:%llu\n",
+ header_pkt.sendTick, header_pkt.sendDelay, linkDelay, recv_tick);
+
+ if (recv_tick <= curTick()) {
+ panic("Simulators out of sync - missed packet receive by %llu ticks",
+ curTick() - recv_tick);
+ }
+ // Now we are about to schedule a recvDone event for the new data packet.
+ // We use the same recvDone object for all incoming data packets. If
+ // that is already scheduled - i.e. a receive event for a previous
+ // data packet is already pending - then we have to check whether the
+ // receive tick for the new packet is earlier than that of the currently
+ // pending event. Packets may arrive out-of-order with respect to
+ // simulated receive time. If that is the case, we need to re-schedule the
+ // recvDone event for the new packet. Otherwise, we save the packet
+ // pointer and the recv tick for the new packet in the recvQueue. See
+ // the implementation of the packetIn() method for comments on how this
+ // information is retrieved from the recvQueue by the simulation thread.
+ if (!recvDone->scheduled()) {
+ assert(recvQueue.size() == 0);
+ assert(scheduledRecvPacket == nullptr);
+ scheduledRecvPacket = new_packet;
+ eventManager->schedule(recvDone, recv_tick);
+ } else if (recvDone->when() > recv_tick) {
+ recvQueue.emplace(scheduledRecvPacket, recvDone->when());
+ eventManager->reschedule(recvDone, recv_tick);
+ scheduledRecvPacket = new_packet;
+ } else {
+ recvQueue.emplace(new_packet, recv_tick);
+ }
+ curEventQueue()->unlock();
+}
+
+void
+MultiIface::recvThreadFunc()
+{
+ EthPacketPtr new_packet;
+ MultiHeaderPkt::Header header;
+
+ // The new receiver thread shares the event queue with the simulation
+ // thread (associated with the simulated Ethernet link).
+ curEventQueue(eventManager->eventQueue());
+ // Main loop to wait for and process any incoming message.
+ for (;;) {
+ // recvHeader() blocks until the next multi header packet comes in.
+ if (!recvHeader(header)) {
+ // We lost connection to the peer gem5 processes most likely
+ // because one of them called m5 exit. So we stop here.
+ exit_message("info", 0, "Message server closed connection, "
+ "simulation is exiting");
+ }
+ // We got a valid multi header packet, let's process it
+ if (header.msgType == MsgType::dataDescriptor) {
+ recvData(header);
+ } else {
+ // everything else must be synchronisation related command
+ sync->progress(header.msgType);
+ }
+ }
+}
+
+EthPacketPtr
+MultiIface::packetIn()
+{
+ // We are called within the process() method of the recvDone event. We
+ // return the packet that triggered the current receive event.
+ // If there is further packets in the recvQueue, we also have to schedule
+ // the recvEvent for the next packet with the smallest receive tick.
+ // The priority queue container ensures that smallest receive tick is
+ // always on the top of the queue.
+ assert(scheduledRecvPacket != nullptr);
+ EthPacketPtr next_packet = scheduledRecvPacket;
+
+ if (! recvQueue.empty()) {
+ eventManager->schedule(recvDone, recvQueue.top().second);
+ scheduledRecvPacket = recvQueue.top().first;
+ recvQueue.pop();
+ } else {
+ scheduledRecvPacket = nullptr;
+ }
+
+ return next_packet;
+}
+
+void
+MultiIface::spawnRecvThread(Event *recv_done, Tick link_delay)
+{
+ assert(recvThread == nullptr);
+ // all receive thread must be spawned before simulation starts
+ assert(eventManager->eventQueue()->getCurTick() == 0);
+
+ recvDone = recv_done;
+ linkDelay = link_delay;
+
+ recvThread = new std::thread(&MultiIface::recvThreadFunc, this);
+
+ recvThreadsNum++;
+}
+
+DrainState
+MultiIface::drain()
+{
+ DPRINTF(MultiEthernet,"MultiIFace::drain() called\n");
+
+ // This can be called multiple times in the same drain cycle.
+ if (master == this) {
+ syncEvent->isDraining = true;
+ }
+
+ return DrainState::Drained;
+}
+
+void MultiIface::drainDone() {
+ if (master == this) {
+ assert(syncEvent->isDraining == true);
+ syncEvent->isDraining = false;
+ // We need to resume the interrupted periodic sync here now that the
+ // draining is done. If the last periodic sync completed before the
+ // checkpoint then the next one is already scheduled.
+ if (syncEvent->interrupted)
+ syncEvent->resume();
+ }
+}
+
+void MultiIface::serialize(const std::string &base, CheckpointOut &cp) const
+{
+ // Drain the multi interface before the checkpoint is taken. We cannot call
+ // this as part of the normal drain cycle because this multi sync has to be
+ // called exactly once after the system is fully drained.
+ // Note that every peer will take a checkpoint but they may take it at
+ // different ticks.
+ // This sync request may interrupt an on-going periodic sync in some peers.
+ sync->run(SyncTrigger::ckpt, curTick());
+
+ // Save the periodic multi sync status
+ syncEvent->serialize(base, cp);
+
+ unsigned n_rx_packets = recvQueue.size();
+ if (scheduledRecvPacket != nullptr)
+ n_rx_packets++;
+
+ paramOut(cp, base + ".nRxPackets", n_rx_packets);
+
+ if (n_rx_packets > 0) {
+ assert(recvDone->scheduled());
+ scheduledRecvPacket->serialize(base + ".rxPacket[0]", cp);
+ }
+
+ for (unsigned i=1; i < n_rx_packets; i++) {
+ const RecvInfo recv_info = recvQueue.impl().at(i-1);
+ recv_info.first->serialize(base + csprintf(".rxPacket[%d]", i), cp);
+ Tick rx_tick = recv_info.second;
+ paramOut(cp, base + csprintf(".rxTick[%d]", i), rx_tick);
+ }
+}
+
+void MultiIface::unserialize(const std::string &base, CheckpointIn &cp)
+{
+ assert(recvQueue.size() == 0);
+ assert(scheduledRecvPacket == nullptr);
+ assert(recvDone->scheduled() == false);
+
+ // restore periodic sync info
+ syncEvent->unserialize(base, cp);
+
+ unsigned n_rx_packets;
+ paramIn(cp, base + ".nRxPackets", n_rx_packets);
+
+ if (n_rx_packets > 0) {
+ scheduledRecvPacket = std::make_shared<EthPacketData>(16384);
+ scheduledRecvPacket->unserialize(base + ".rxPacket[0]", cp);
+ // Note: receive event will be scheduled when the link is unserialized
+ }
+
+ for (unsigned i=1; i < n_rx_packets; i++) {
+ EthPacketPtr rx_packet = std::make_shared<EthPacketData>(16384);
+ rx_packet->unserialize(base + csprintf(".rxPacket[%d]", i), cp);
+ Tick rx_tick = 0;
+ paramIn(cp, base + csprintf(".rxTick[%d]", i), rx_tick);
+ assert(rx_tick > 0);
+ recvQueue.emplace(rx_packet,rx_tick);
+ }
+}
+
+void MultiIface::initRandom()
+{
+ // Initialize the seed for random generator to avoid the same sequence
+ // in all gem5 peer processes
+ assert(master != nullptr);
+ if (this == master)
+ random_mt.init(5489 * (rank+1) + 257);
+}
+
+void MultiIface::startPeriodicSync()
+{
+ DPRINTF(MultiEthernet, "MultiIface:::initPeriodicSync started\n");
+ // Do a global sync here to ensure that peer gem5 processes are around
+ // (actually this may not be needed...)
+ sync->run(SyncTrigger::atomic, curTick());
+
+ // Start the periodic sync if it is a fresh simulation from scratch
+ if (curTick() == 0) {
+ if (this == master) {
+ syncEvent->start(syncStart, syncRepeat);
+ inform("Multi synchronisation activated: start at %lld, "
+ "repeat at every %lld ticks.\n",
+ syncStart, syncRepeat);
+ } else {
+ // In case another multiIface object requires different schedule
+ // for periodic sync than the master does.
+ syncEvent->adjust(syncStart, syncRepeat);
+ }
+ } else {
+ // Schedule the next periodic sync if resuming from a checkpoint
+ if (this == master)
+ syncEvent->resume();
+ }
+ DPRINTF(MultiEthernet, "MultiIface::initPeriodicSync done\n");
+}
diff --git a/src/dev/net/multi_iface.hh b/src/dev/net/multi_iface.hh
new file mode 100644
index 000000000..f8ce2abf7
--- /dev/null
+++ b/src/dev/net/multi_iface.hh
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabor Dozsa
+ */
+
+/* @file
+ * The interface class for multi gem5 simulations.
+ *
+ * Multi gem5 is an extension to gem5 to enable parallel simulation of a
+ * distributed system (e.g. simulation of a pool of machines
+ * connected by Ethernet links). A multi gem5 run consists of seperate gem5
+ * processes running in parallel. Each gem5 process executes
+ * the simulation of a component of the simulated distributed system.
+ * (An example component can be a multi-core board with an Ethernet NIC.)
+ * The MultiIface class below provides services to transfer data and
+ * control messages among the gem5 processes. The main such services are
+ * as follows.
+ *
+ * 1. Send a data packet coming from a simulated Ethernet link. The packet
+ * will be transferred to (all) the target(s) gem5 processes. The send
+ * operation is always performed by the simulation thread, i.e. the gem5
+ * thread that is processing the event queue associated with the simulated
+ * Ethernet link.
+ *
+ * 2. Spawn a receiver thread to process messages coming in from the
+ * from other gem5 processes. Each simulated Ethernet link has its own
+ * associated receiver thread. The receiver thread saves the incoming packet
+ * and schedule an appropriate receive event in the event queue.
+ *
+ * 3. Schedule a global barrier event periodically to keep the gem5
+ * processes in sync.
+ * Periodic barrier event to keep peer gem5 processes in sync. The basic idea
+ * is that no gem5 process can go ahead further than the simulated link
+ * transmission delay to ensure that a corresponding receive event can always
+ * be scheduled for any message coming in from a peer gem5 process.
+ *
+ *
+ *
+ * This interface is an abstract class (sendRaw() and recvRaw()
+ * methods are pure virtual). It can work with various low level
+ * send/receive service implementations (e.g. TCP/IP, MPI,...). A TCP
+ * stream socket version is implemented in dev/src/tcp_iface.[hh,cc].
+ */
+#ifndef __DEV_NET_MULTI_IFACE_HH__
+#define __DEV_NET_MULTI_IFACE_HH__
+
+#include <array>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <utility>
+
+#include "dev/net/etherpkt.hh"
+#include "dev/net/multi_packet.hh"
+#include "sim/core.hh"
+#include "sim/drain.hh"
+#include "sim/global_event.hh"
+
+class EventManager;
+
+/**
+ * The interface class to talk to peer gem5 processes.
+ */
+class MultiIface : public Drainable
+{
+ public:
+ /*!
+ * The possible reasons a multi sync among gem5 peers is needed for.
+ */
+ enum
+ class SyncTrigger {
+ periodic, /*!< Regular periodic sync. This can be interrupted by a
+ checkpoint sync request */
+ ckpt, /*!< sync before taking a checkpoint */
+ atomic /*!< sync that cannot be interrupted (e.g. sync at startup) */
+ };
+
+ private:
+ typedef MultiHeaderPkt::MsgType MsgType;
+
+ /** Sync State-Machine
+ \dot
+ digraph Sync {
+ node [shape=box, fontsize=10];
+ idle -> busy
+ [ label="new trigger\n by run()" fontsize=8 ];
+ busy -> busy
+ [ label="new message by progress():\n(msg == SyncAck &&\nwaitNum > 1) || \n(msg==CkptSyncReq &&\ntrigger == ckpt)" fontsize=8 ];
+ busy -> idle
+ [ label="new message by progress():\n(msg == SyncAck &&\nwaitNum == 1)" fontsize=8 ];
+ busy -> interrupted
+ [ label="new message by progress():\n(msg == CkptSyncReq &&\ntrigger == periodic)" fontsize=8 ];
+ idle -> asyncCkpt
+ [ label="new message by progress():\nmsg == CkptSyncReq" fontsize=8 ];
+ asyncCkpt -> asyncCkpt
+ [ label="new message by progress():\nmsg == CkptSyncReq" fontsize=8 ];
+ asyncCkpt -> busy
+ [ label="new trigger by run():\ntrigger == ckpt" fontsize=8 ];
+ asyncCkpt -> idle
+ [ label="new trigger by run():\n(trigger == periodic &&\nwaitNum == 0) " fontsize=8 ];
+ asyncCkpt -> interrupted
+ [ label="new trigger by run():\n(trigger == periodic &&\nwaitNum > 0) " fontsize=8 ];
+ interrupted -> interrupted
+ [ label="new message by progress():\n(msg == CkptSyncReq &&\nwaitNum > 1)" fontsize=8 ];
+ interrupted -> idle
+ [ label="new message by progress():\n(msg == CkptSyncReq &&\nwaitNum == 1)" fontsize=8 ];
+ }
+ \enddot
+ */
+ /** @class Sync
+ * This class implements global sync operations among gem5 peer processes.
+ *
+ * @note This class is used as a singleton object (shared by all MultiIface
+ * objects).
+ */
+ class Sync
+ {
+ private:
+ /*!
+ * Internal state of the sync singleton object.
+ */
+ enum class SyncState {
+ busy, /*!< There is an on-going sync. */
+ interrupted, /*!< An on-going periodic sync was interrupted. */
+ asyncCkpt, /*!< A checkpoint (sim_exit) is already scheduled */
+ idle /*!< There is no active sync. */
+ };
+ /**
+ * The lock to protect access to the MultiSync object.
+ */
+ std::mutex lock;
+ /**
+ * Condition variable for the simulation thread to wait on
+ * until all receiver threads completes the current global
+ * synchronisation.
+ */
+ std::condition_variable cv;
+ /**
+ * Number of receiver threads that not yet completed the current global
+ * synchronisation.
+ */
+ unsigned waitNum;
+ /**
+ * The trigger for the most recent sync.
+ */
+ SyncTrigger trigger;
+ /**
+ * Map sync triggers to request messages.
+ */
+ std::array<MsgType, 3> triggerToMsg = {{
+ MsgType::cmdPeriodicSyncReq,
+ MsgType::cmdCkptSyncReq,
+ MsgType::cmdAtomicSyncReq
+ }};
+
+ /**
+ * Current sync state.
+ */
+ SyncState state;
+
+ public:
+ /**
+ * Core method to perform a full multi sync.
+ *
+ * @param t Sync trigger.
+ * @param sync_tick The tick the sync was expected to happen at.
+ * @return true if the sync completed, false if it was interrupted.
+ *
+ * @note In case of an interrupted periodic sync, sync_tick can be less
+ * than curTick() when we resume (i.e. re-run) it
+ */
+ bool run(SyncTrigger t, Tick sync_tick);
+ /**
+ * Callback when the receiver thread gets a sync message.
+ */
+ void progress(MsgType m);
+
+ Sync() : waitNum(0), state(SyncState::idle) {}
+ ~Sync() {}
+ };
+
+
+ /**
+ * The global event to schedule peridic multi sync. It is used as a
+ * singleton object.
+ *
+ * The periodic synchronisation works as follows.
+ * 1. A MultisyncEvent is scheduled as a global event when startup() is
+ * called.
+ * 2. The progress() method of the MultisyncEvent initiates a new barrier
+ * for each simulated Ethernet links.
+ * 3. Simulation thread(s) then waits until all receiver threads
+ * completes the ongoing barrier. The global sync event is done.
+ */
+ class SyncEvent : public GlobalSyncEvent
+ {
+ public:
+ /**
+ * Flag to indicate that the most recent periodic sync was interrupted
+ * (by a checkpoint request).
+ */
+ bool interrupted;
+ /**
+ * The tick when the most recent periodic synchronisation was scheduled
+ * at.
+ */
+ Tick scheduledAt;
+ /**
+ * Flag to indicate an on-going drain cycle.
+ */
+ bool isDraining;
+
+ public:
+ /**
+ * Only the firstly instanstiated MultiIface object will
+ * call this constructor.
+ */
+ SyncEvent() : GlobalSyncEvent(Default_Pri, 0), interrupted(false),
+ scheduledAt(0), isDraining(false) {}
+
+ ~SyncEvent() { assert (scheduled() == false); }
+ /**
+ * Schedule the first periodic sync event.
+ *
+ * @param start Start tick for multi synchronisation
+ * @param repeat Frequency of multi synchronisation
+ *
+ */
+ void start(Tick start, Tick repeat);
+ /**
+ * Reschedule (if necessary) the periodic sync event.
+ *
+ * @param start Start tick for multi synchronisation
+ * @param repeat Frequency of multi synchronisation
+ *
+ * @note Useful if we have multiple MultiIface objects with
+ * different 'start' and 'repeat' values for global sync.
+ */
+ void adjust(Tick start, Tick repeat);
+ /**
+ * This is a global event so process() will be called by each
+ * simulation threads. (See further comments in the .cc file.)
+ */
+ void process() override;
+ /**
+ * Schedule periodic sync when resuming from a checkpoint.
+ */
+ void resume();
+
+ void serialize(const std::string &base, CheckpointOut &cp) const;
+ void unserialize(const std::string &base, CheckpointIn &cp);
+ };
+
+ /**
+ * The receive thread needs to store the packet pointer and the computed
+ * receive tick for each incoming data packet. This information is used
+ * by the simulation thread when it processes the corresponding receive
+ * event. (See more comments at the implemetation of the recvThreadFunc()
+ * and RecvPacketIn() methods.)
+ */
+ typedef std::pair<EthPacketPtr, Tick> RecvInfo;
+
+ /**
+ * Comparison predicate for RecvInfo, needed by the recvQueue.
+ */
+ struct RecvInfoCompare {
+ bool operator()(const RecvInfo &lhs, const RecvInfo &rhs)
+ {
+ return lhs.second > rhs.second;
+ }
+ };
+
+ /**
+ * Customized priority queue used to store incoming data packets info by
+ * the receiver thread. We need to expose the underlying container to
+ * enable iterator access for serializing.
+ */
+ class RecvQueue : public std::priority_queue<RecvInfo,
+ std::vector<RecvInfo>,
+ RecvInfoCompare>
+ {
+ public:
+ std::vector<RecvInfo> &impl() { return c; }
+ const std::vector<RecvInfo> &impl() const { return c; }
+ };
+
+ /*
+ * The priority queue to store RecvInfo items ordered by receive ticks.
+ */
+ RecvQueue recvQueue;
+ /**
+ * The singleton Sync object to perform multi synchronisation.
+ */
+ static Sync *sync;
+ /**
+ * The singleton SyncEvent object to schedule periodic multi sync.
+ */
+ static SyncEvent *syncEvent;
+ /**
+ * Tick to schedule the first multi sync event.
+ * This is just as optimization : we do not need any multi sync
+ * event until the simulated NIC is brought up by the OS.
+ */
+ Tick syncStart;
+ /**
+ * Frequency of multi sync events in ticks.
+ */
+ Tick syncRepeat;
+ /**
+ * Receiver thread pointer.
+ * Each MultiIface object must have exactly one receiver thread.
+ */
+ std::thread *recvThread;
+ /**
+ * The event manager associated with the MultiIface object.
+ */
+ EventManager *eventManager;
+
+ /**
+ * The receive done event for the simulated Ethernet link.
+ * It is scheduled by the receiver thread for each incoming data
+ * packet.
+ */
+ Event *recvDone;
+
+ /**
+ * The packet that belongs to the currently scheduled recvDone event.
+ */
+ EthPacketPtr scheduledRecvPacket;
+
+ /**
+ * The link delay in ticks for the simulated Ethernet link.
+ */
+ Tick linkDelay;
+
+ /**
+ * The rank of this process among the gem5 peers.
+ */
+ unsigned rank;
+ /**
+ * Total number of receiver threads (in this gem5 process).
+ * During the simulation it should be constant and equal to the
+ * number of MultiIface objects (i.e. simulated Ethernet
+ * links).
+ */
+ static unsigned recvThreadsNum;
+ /**
+ * The very first MultiIface object created becomes the master. We need
+ * a master to co-ordinate the global synchronisation.
+ */
+ static MultiIface *master;
+
+ protected:
+ /**
+ * Low level generic send routine.
+ * @param buf buffer that holds the data to send out
+ * @param length number of bytes to send
+ * @param dest_addr address of the target (simulated NIC). This may be
+ * used by a subclass for optimization (e.g. optimize broadcast)
+ */
+ virtual void sendRaw(void *buf,
+ unsigned length,
+ const MultiHeaderPkt::AddressType dest_addr) = 0;
+ /**
+ * Low level generic receive routine.
+ * @param buf the buffer to store the incoming message
+ * @param length buffer size (in bytes)
+ */
+ virtual bool recvRaw(void *buf, unsigned length) = 0;
+ /**
+ * Low level request for synchronisation among gem5 processes. Only one
+ * MultiIface object needs to call this (in each gem5 process) to trigger
+ * a multi sync.
+ *
+ * @param sync_req Sync request command.
+ * @param sync_tick The tick when sync is expected to happen in the sender.
+ */
+ virtual void syncRaw(MsgType sync_req, Tick sync_tick) = 0;
+ /**
+ * The function executed by a receiver thread.
+ */
+ void recvThreadFunc();
+ /**
+ * Receive a multi header packet. Called by the receiver thread.
+ * @param header the structure to store the incoming header packet.
+ * @return false if any error occured during the receive, true otherwise
+ *
+ * A header packet can carry a control command (e.g. 'barrier leave') or
+ * information about a data packet that is following the header packet
+ * back to back.
+ */
+ bool recvHeader(MultiHeaderPkt::Header &header);
+ /**
+ * Receive a data packet. Called by the receiver thread.
+ * @param data_header The packet descriptor for the expected incoming data
+ * packet.
+ */
+ void recvData(const MultiHeaderPkt::Header &data_header);
+
+ public:
+
+ /**
+ * ctor
+ * @param multi_rank Rank of this gem5 process within the multi run
+ * @param sync_start Start tick for multi synchronisation
+ * @param sync_repeat Frequency for multi synchronisation
+ * @param em The event manager associated with the simulated Ethernet link
+ */
+ MultiIface(unsigned multi_rank,
+ Tick sync_start,
+ Tick sync_repeat,
+ EventManager *em);
+
+ virtual ~MultiIface();
+ /**
+ * Send out an Ethernet packet.
+ * @param pkt The Ethernet packet to send.
+ * @param send_delay The delay in ticks for the send completion event.
+ */
+ void packetOut(EthPacketPtr pkt, Tick send_delay);
+ /**
+ * Fetch the next packet from the receive queue.
+ */
+ EthPacketPtr packetIn();
+
+ /**
+ * spawn the receiver thread.
+ * @param recv_done The receive done event associated with the simulated
+ * Ethernet link.
+ * @param link_delay The link delay for the simulated Ethernet link.
+ */
+ void spawnRecvThread(Event *recv_done,
+ Tick link_delay);
+ /**
+ * Initialize the random number generator with a different seed in each
+ * peer gem5 process.
+ */
+ void initRandom();
+
+ DrainState drain() override;
+
+ /**
+ * Callback when draining is complete.
+ */
+ void drainDone();
+
+ /**
+ * Initialize the periodic synchronisation among peer gem5 processes.
+ */
+ void startPeriodicSync();
+
+ void serialize(const std::string &base, CheckpointOut &cp) const;
+ void unserialize(const std::string &base, CheckpointIn &cp);
+
+};
+
+
+#endif // __DEV_NET_MULTI_IFACE_HH__
diff --git a/src/dev/net/multi_packet.cc b/src/dev/net/multi_packet.cc
new file mode 100644
index 000000000..85f76b0c4
--- /dev/null
+++ b/src/dev/net/multi_packet.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabor Dozsa
+ */
+
+/* @file
+ * MultiHeaderPkt class to encapsulate multi-gem5 header packets
+ *
+ */
+
+#include "dev/net/multi_packet.hh"
+
+#include <cstdint>
+#include <cstring>
+
+#include "base/inet.hh"
+
+unsigned
+MultiHeaderPkt::maxAddressLength()
+{
+ return sizeof(AddressType);
+}
+
+void
+MultiHeaderPkt::clearAddress(AddressType &addr)
+{
+ std::memset(addr, 0, sizeof(addr));
+}
+
+bool
+MultiHeaderPkt::isAddressEqual(const AddressType &addr1,
+ const AddressType &addr2)
+{
+ return (std::memcmp(addr1, addr2, sizeof(addr1)) == 0);
+}
+
+bool
+MultiHeaderPkt::isAddressLess(const AddressType &addr1,
+ const AddressType &addr2)
+{
+ return (std::memcmp(addr1, addr2, sizeof(addr1)) < 0);
+}
+
+void
+MultiHeaderPkt::copyAddress(AddressType &dest, const AddressType &src)
+{
+ std::memcpy(dest, src, sizeof(dest));
+}
+
+bool
+MultiHeaderPkt::isBroadcastAddress(const AddressType &addr)
+{
+ return ((Net::EthAddr *)&addr)->broadcast();
+}
+
+bool
+MultiHeaderPkt::isMulticastAddress(const AddressType &addr)
+{
+ return ((Net::EthAddr *)&addr)->multicast();
+}
+
+bool
+MultiHeaderPkt::isUnicastAddress(const AddressType &addr)
+{
+ return ((Net::EthAddr *)&addr)->unicast();
+}
diff --git a/src/dev/net/multi_packet.hh b/src/dev/net/multi_packet.hh
new file mode 100644
index 000000000..3d8e85dfa
--- /dev/null
+++ b/src/dev/net/multi_packet.hh
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabor Dozsa
+ */
+
+/* @file
+ * Header packet class for multi gem5 runs.
+ *
+ * For a high level description about multi gem5 see comments in
+ * header file multi_iface.hh.
+ *
+ * The MultiHeaderPkt class defines the format of message headers
+ * sent among gem5 processes during a multi gem5 simulation. A header packet
+ * can either carry the description of data packet (i.e. a simulated Ethernet
+ * packet) or a synchronisation related control command. In case of
+ * data packet description, the corresponding data packet always follows
+ * the header packet back-to-back.
+ */
+#ifndef __DEV_NET_MULTI_PACKET_HH__
+#define __DEV_NET_MULTI_PACKET_HH__
+
+#include <cstring>
+
+#include "base/types.hh"
+
+class MultiHeaderPkt
+{
+ private:
+ MultiHeaderPkt() {}
+ ~MultiHeaderPkt() {}
+
+ public:
+ /**
+ * Simply type to help with calculating space requirements for
+ * the corresponding header field.
+ */
+ typedef uint8_t AddressType[6];
+
+ /**
+ * The msg type defines what informarion a multi header packet carries.
+ */
+ enum class MsgType
+ {
+ dataDescriptor,
+ cmdPeriodicSyncReq,
+ cmdPeriodicSyncAck,
+ cmdCkptSyncReq,
+ cmdCkptSyncAck,
+ cmdAtomicSyncReq,
+ cmdAtomicSyncAck,
+ unknown
+ };
+
+ struct Header
+ {
+ /**
+ * The msg type field is valid for all header packets. In case of
+ * a synchronisation control command this is the only valid field.
+ */
+ MsgType msgType;
+ Tick sendTick;
+ Tick sendDelay;
+ /**
+ * Actual length of the simulated Ethernet packet.
+ */
+ unsigned dataPacketLength;
+ /**
+ * Source MAC address.
+ */
+ AddressType srcAddress;
+ /**
+ * Destination MAC address.
+ */
+ AddressType dstAddress;
+ };
+
+ static unsigned maxAddressLength();
+
+ /**
+ * Static functions for manipulating and comparing MAC addresses.
+ */
+ static void clearAddress(AddressType &addr);
+ static bool isAddressEqual(const AddressType &addr1,
+ const AddressType &addr2);
+ static bool isAddressLess(const AddressType &addr1,
+ const AddressType &addr2);
+
+ static void copyAddress(AddressType &dest,
+ const AddressType &src);
+
+ static bool isUnicastAddress(const AddressType &addr);
+ static bool isMulticastAddress(const AddressType &addr);
+ static bool isBroadcastAddress(const AddressType &addr);
+};
+
+#endif // __DEV_NET_MULTI_PACKET_HH__
diff --git a/src/dev/net/ns_gige.cc b/src/dev/net/ns_gige.cc
new file mode 100644
index 000000000..a1dc23b50
--- /dev/null
+++ b/src/dev/net/ns_gige.cc
@@ -0,0 +1,2483 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ * Lisa Hsu
+ */
+
+/** @file
+ * Device module for modelling the National Semiconductor
+ * DP83820 ethernet controller. Does not support priority queueing
+ */
+
+#include "dev/net/ns_gige.hh"
+
+#include <deque>
+#include <memory>
+#include <string>
+
+#include "base/debug.hh"
+#include "base/inet.hh"
+#include "base/types.hh"
+#include "config/the_isa.hh"
+#include "debug/EthernetAll.hh"
+#include "dev/net/etherlink.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+#include "params/NSGigE.hh"
+#include "sim/system.hh"
+
+// clang complains about std::set being overloaded with Packet::set if
+// we open up the entire namespace std
+using std::make_shared;
+using std::min;
+using std::ostream;
+using std::string;
+
+const char *NsRxStateStrings[] =
+{
+ "rxIdle",
+ "rxDescRefr",
+ "rxDescRead",
+ "rxFifoBlock",
+ "rxFragWrite",
+ "rxDescWrite",
+ "rxAdvance"
+};
+
+const char *NsTxStateStrings[] =
+{
+ "txIdle",
+ "txDescRefr",
+ "txDescRead",
+ "txFifoBlock",
+ "txFragRead",
+ "txDescWrite",
+ "txAdvance"
+};
+
+const char *NsDmaState[] =
+{
+ "dmaIdle",
+ "dmaReading",
+ "dmaWriting",
+ "dmaReadWaiting",
+ "dmaWriteWaiting"
+};
+
+using namespace Net;
+using namespace TheISA;
+
+///////////////////////////////////////////////////////////////////////
+//
+// NSGigE PCI Device
+//
+NSGigE::NSGigE(Params *p)
+ : EtherDevBase(p), ioEnable(false),
+ txFifo(p->tx_fifo_size), rxFifo(p->rx_fifo_size),
+ txPacket(0), rxPacket(0), txPacketBufPtr(NULL), rxPacketBufPtr(NULL),
+ txXferLen(0), rxXferLen(0), rxDmaFree(false), txDmaFree(false),
+ txState(txIdle), txEnable(false), CTDD(false), txHalt(false),
+ txFragPtr(0), txDescCnt(0), txDmaState(dmaIdle), rxState(rxIdle),
+ rxEnable(false), CRDD(false), rxPktBytes(0), rxHalt(false),
+ rxFragPtr(0), rxDescCnt(0), rxDmaState(dmaIdle), extstsEnable(false),
+ eepromState(eepromStart), eepromClk(false), eepromBitsToRx(0),
+ eepromOpcode(0), eepromAddress(0), eepromData(0),
+ dmaReadDelay(p->dma_read_delay), dmaWriteDelay(p->dma_write_delay),
+ dmaReadFactor(p->dma_read_factor), dmaWriteFactor(p->dma_write_factor),
+ rxDmaData(NULL), rxDmaAddr(0), rxDmaLen(0),
+ txDmaData(NULL), txDmaAddr(0), txDmaLen(0),
+ rxDmaReadEvent(this), rxDmaWriteEvent(this),
+ txDmaReadEvent(this), txDmaWriteEvent(this),
+ dmaDescFree(p->dma_desc_free), dmaDataFree(p->dma_data_free),
+ txDelay(p->tx_delay), rxDelay(p->rx_delay),
+ rxKickTick(0), rxKickEvent(this), txKickTick(0), txKickEvent(this),
+ txEvent(this), rxFilterEnable(p->rx_filter),
+ acceptBroadcast(false), acceptMulticast(false), acceptUnicast(false),
+ acceptPerfect(false), acceptArp(false), multicastHashEnable(false),
+ intrDelay(p->intr_delay), intrTick(0), cpuPendingIntr(false),
+ intrEvent(0), interface(0)
+{
+
+
+ interface = new NSGigEInt(name() + ".int0", this);
+
+ regsReset();
+ memcpy(&rom.perfectMatch, p->hardware_address.bytes(), ETH_ADDR_LEN);
+
+ memset(&rxDesc32, 0, sizeof(rxDesc32));
+ memset(&txDesc32, 0, sizeof(txDesc32));
+ memset(&rxDesc64, 0, sizeof(rxDesc64));
+ memset(&txDesc64, 0, sizeof(txDesc64));
+}
+
+NSGigE::~NSGigE()
+{
+ delete interface;
+}
+
+/**
+ * This is to write to the PCI general configuration registers
+ */
+Tick
+NSGigE::writeConfig(PacketPtr pkt)
+{
+ int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
+ if (offset < PCI_DEVICE_SPECIFIC)
+ PciDevice::writeConfig(pkt);
+ else
+ panic("Device specific PCI config space not implemented!\n");
+
+ switch (offset) {
+ // seems to work fine without all these PCI settings, but i
+ // put in the IO to double check, an assertion will fail if we
+ // need to properly implement it
+ case PCI_COMMAND:
+ if (config.data[offset] & PCI_CMD_IOSE)
+ ioEnable = true;
+ else
+ ioEnable = false;
+ break;
+ }
+
+ return configDelay;
+}
+
+EtherInt*
+NSGigE::getEthPort(const std::string &if_name, int idx)
+{
+ if (if_name == "interface") {
+ if (interface->getPeer())
+ panic("interface already connected to\n");
+ return interface;
+ }
+ return NULL;
+}
+
+/**
+ * This reads the device registers, which are detailed in the NS83820
+ * spec sheet
+ */
+Tick
+NSGigE::read(PacketPtr pkt)
+{
+ assert(ioEnable);
+
+ //The mask is to give you only the offset into the device register file
+ Addr daddr = pkt->getAddr() & 0xfff;
+ DPRINTF(EthernetPIO, "read da=%#x pa=%#x size=%d\n",
+ daddr, pkt->getAddr(), pkt->getSize());
+
+
+ // there are some reserved registers, you can see ns_gige_reg.h and
+ // the spec sheet for details
+ if (daddr > LAST && daddr <= RESERVED) {
+ panic("Accessing reserved register");
+ } else if (daddr > RESERVED && daddr <= 0x3FC) {
+ return readConfig(pkt);
+ } else if (daddr >= MIB_START && daddr <= MIB_END) {
+ // don't implement all the MIB's. hopefully the kernel
+ // doesn't actually DEPEND upon their values
+ // MIB are just hardware stats keepers
+ pkt->set<uint32_t>(0);
+ pkt->makeAtomicResponse();
+ return pioDelay;
+ } else if (daddr > 0x3FC)
+ panic("Something is messed up!\n");
+
+ assert(pkt->getSize() == sizeof(uint32_t));
+ uint32_t &reg = *pkt->getPtr<uint32_t>();
+ uint16_t rfaddr;
+
+ switch (daddr) {
+ case CR:
+ reg = regs.command;
+ //these are supposed to be cleared on a read
+ reg &= ~(CR_RXD | CR_TXD | CR_TXR | CR_RXR);
+ break;
+
+ case CFGR:
+ reg = regs.config;
+ break;
+
+ case MEAR:
+ reg = regs.mear;
+ break;
+
+ case PTSCR:
+ reg = regs.ptscr;
+ break;
+
+ case ISR:
+ reg = regs.isr;
+ devIntrClear(ISR_ALL);
+ break;
+
+ case IMR:
+ reg = regs.imr;
+ break;
+
+ case IER:
+ reg = regs.ier;
+ break;
+
+ case IHR:
+ reg = regs.ihr;
+ break;
+
+ case TXDP:
+ reg = regs.txdp;
+ break;
+
+ case TXDP_HI:
+ reg = regs.txdp_hi;
+ break;
+
+ case TX_CFG:
+ reg = regs.txcfg;
+ break;
+
+ case GPIOR:
+ reg = regs.gpior;
+ break;
+
+ case RXDP:
+ reg = regs.rxdp;
+ break;
+
+ case RXDP_HI:
+ reg = regs.rxdp_hi;
+ break;
+
+ case RX_CFG:
+ reg = regs.rxcfg;
+ break;
+
+ case PQCR:
+ reg = regs.pqcr;
+ break;
+
+ case WCSR:
+ reg = regs.wcsr;
+ break;
+
+ case PCR:
+ reg = regs.pcr;
+ break;
+
+ // see the spec sheet for how RFCR and RFDR work
+ // basically, you write to RFCR to tell the machine
+ // what you want to do next, then you act upon RFDR,
+ // and the device will be prepared b/c of what you
+ // wrote to RFCR
+ case RFCR:
+ reg = regs.rfcr;
+ break;
+
+ case RFDR:
+ rfaddr = (uint16_t)(regs.rfcr & RFCR_RFADDR);
+ switch (rfaddr) {
+ // Read from perfect match ROM octets
+ case 0x000:
+ reg = rom.perfectMatch[1];
+ reg = reg << 8;
+ reg += rom.perfectMatch[0];
+ break;
+ case 0x002:
+ reg = rom.perfectMatch[3] << 8;
+ reg += rom.perfectMatch[2];
+ break;
+ case 0x004:
+ reg = rom.perfectMatch[5] << 8;
+ reg += rom.perfectMatch[4];
+ break;
+ default:
+ // Read filter hash table
+ if (rfaddr >= FHASH_ADDR &&
+ rfaddr < FHASH_ADDR + FHASH_SIZE) {
+
+ // Only word-aligned reads supported
+ if (rfaddr % 2)
+ panic("unaligned read from filter hash table!");
+
+ reg = rom.filterHash[rfaddr - FHASH_ADDR + 1] << 8;
+ reg += rom.filterHash[rfaddr - FHASH_ADDR];
+ break;
+ }
+
+ panic("reading RFDR for something other than pattern"
+ " matching or hashing! %#x\n", rfaddr);
+ }
+ break;
+
+ case SRR:
+ reg = regs.srr;
+ break;
+
+ case MIBC:
+ reg = regs.mibc;
+ reg &= ~(MIBC_MIBS | MIBC_ACLR);
+ break;
+
+ case VRCR:
+ reg = regs.vrcr;
+ break;
+
+ case VTCR:
+ reg = regs.vtcr;
+ break;
+
+ case VDR:
+ reg = regs.vdr;
+ break;
+
+ case CCSR:
+ reg = regs.ccsr;
+ break;
+
+ case TBICR:
+ reg = regs.tbicr;
+ break;
+
+ case TBISR:
+ reg = regs.tbisr;
+ break;
+
+ case TANAR:
+ reg = regs.tanar;
+ break;
+
+ case TANLPAR:
+ reg = regs.tanlpar;
+ break;
+
+ case TANER:
+ reg = regs.taner;
+ break;
+
+ case TESR:
+ reg = regs.tesr;
+ break;
+
+ case M5REG:
+ reg = 0;
+ if (params()->rx_thread)
+ reg |= M5REG_RX_THREAD;
+ if (params()->tx_thread)
+ reg |= M5REG_TX_THREAD;
+ if (params()->rss)
+ reg |= M5REG_RSS;
+ break;
+
+ default:
+ panic("reading unimplemented register: addr=%#x", daddr);
+ }
+
+ DPRINTF(EthernetPIO, "read from %#x: data=%d data=%#x\n",
+ daddr, reg, reg);
+
+ pkt->makeAtomicResponse();
+ return pioDelay;
+}
+
+Tick
+NSGigE::write(PacketPtr pkt)
+{
+ assert(ioEnable);
+
+ Addr daddr = pkt->getAddr() & 0xfff;
+ DPRINTF(EthernetPIO, "write da=%#x pa=%#x size=%d\n",
+ daddr, pkt->getAddr(), pkt->getSize());
+
+ if (daddr > LAST && daddr <= RESERVED) {
+ panic("Accessing reserved register");
+ } else if (daddr > RESERVED && daddr <= 0x3FC) {
+ return writeConfig(pkt);
+ } else if (daddr > 0x3FC)
+ panic("Something is messed up!\n");
+
+ if (pkt->getSize() == sizeof(uint32_t)) {
+ uint32_t reg = pkt->get<uint32_t>();
+ uint16_t rfaddr;
+
+ DPRINTF(EthernetPIO, "write data=%d data=%#x\n", reg, reg);
+
+ switch (daddr) {
+ case CR:
+ regs.command = reg;
+ if (reg & CR_TXD) {
+ txEnable = false;
+ } else if (reg & CR_TXE) {
+ txEnable = true;
+
+ // the kernel is enabling the transmit machine
+ if (txState == txIdle)
+ txKick();
+ }
+
+ if (reg & CR_RXD) {
+ rxEnable = false;
+ } else if (reg & CR_RXE) {
+ rxEnable = true;
+
+ if (rxState == rxIdle)
+ rxKick();
+ }
+
+ if (reg & CR_TXR)
+ txReset();
+
+ if (reg & CR_RXR)
+ rxReset();
+
+ if (reg & CR_SWI)
+ devIntrPost(ISR_SWI);
+
+ if (reg & CR_RST) {
+ txReset();
+ rxReset();
+
+ regsReset();
+ }
+ break;
+
+ case CFGR:
+ if (reg & CFGR_LNKSTS ||
+ reg & CFGR_SPDSTS ||
+ reg & CFGR_DUPSTS ||
+ reg & CFGR_RESERVED ||
+ reg & CFGR_T64ADDR ||
+ reg & CFGR_PCI64_DET) {
+ // First clear all writable bits
+ regs.config &= CFGR_LNKSTS | CFGR_SPDSTS | CFGR_DUPSTS |
+ CFGR_RESERVED | CFGR_T64ADDR |
+ CFGR_PCI64_DET;
+ // Now set the appropriate writable bits
+ regs.config |= reg & ~(CFGR_LNKSTS | CFGR_SPDSTS | CFGR_DUPSTS |
+ CFGR_RESERVED | CFGR_T64ADDR |
+ CFGR_PCI64_DET);
+ }
+
+// all these #if 0's are because i don't THINK the kernel needs to
+// have these implemented. if there is a problem relating to one of
+// these, you may need to add functionality in.
+
+// grouped together and #if 0'ed to avoid empty if body and make clang happy
+#if 0
+ if (reg & CFGR_TBI_EN) ;
+ if (reg & CFGR_MODE_1000) ;
+
+ if (reg & CFGR_PINT_DUPSTS ||
+ reg & CFGR_PINT_LNKSTS ||
+ reg & CFGR_PINT_SPDSTS)
+ ;
+
+ if (reg & CFGR_TMRTEST) ;
+ if (reg & CFGR_MRM_DIS) ;
+ if (reg & CFGR_MWI_DIS) ;
+
+ if (reg & CFGR_DATA64_EN) ;
+ if (reg & CFGR_M64ADDR) ;
+ if (reg & CFGR_PHY_RST) ;
+ if (reg & CFGR_PHY_DIS) ;
+
+ if (reg & CFGR_REQALG) ;
+ if (reg & CFGR_SB) ;
+ if (reg & CFGR_POW) ;
+ if (reg & CFGR_EXD) ;
+ if (reg & CFGR_PESEL) ;
+ if (reg & CFGR_BROM_DIS) ;
+ if (reg & CFGR_EXT_125) ;
+ if (reg & CFGR_BEM) ;
+
+ if (reg & CFGR_T64ADDR) ;
+ // panic("CFGR_T64ADDR is read only register!\n");
+#endif
+ if (reg & CFGR_AUTO_1000)
+ panic("CFGR_AUTO_1000 not implemented!\n");
+
+ if (reg & CFGR_PCI64_DET)
+ panic("CFGR_PCI64_DET is read only register!\n");
+
+ if (reg & CFGR_EXTSTS_EN)
+ extstsEnable = true;
+ else
+ extstsEnable = false;
+ break;
+
+ case MEAR:
+ // Clear writable bits
+ regs.mear &= MEAR_EEDO;
+ // Set appropriate writable bits
+ regs.mear |= reg & ~MEAR_EEDO;
+
+ // FreeBSD uses the EEPROM to read PMATCH (for the MAC address)
+ // even though it could get it through RFDR
+ if (reg & MEAR_EESEL) {
+ // Rising edge of clock
+ if (reg & MEAR_EECLK && !eepromClk)
+ eepromKick();
+ }
+ else {
+ eepromState = eepromStart;
+ regs.mear &= ~MEAR_EEDI;
+ }
+
+ eepromClk = reg & MEAR_EECLK;
+
+ // since phy is completely faked, MEAR_MD* don't matter
+
+// grouped together and #if 0'ed to avoid empty if body and make clang happy
+#if 0
+ if (reg & MEAR_MDIO) ;
+ if (reg & MEAR_MDDIR) ;
+ if (reg & MEAR_MDC) ;
+#endif
+ break;
+
+ case PTSCR:
+ regs.ptscr = reg & ~(PTSCR_RBIST_RDONLY);
+ // these control BISTs for various parts of chip - we
+ // don't care or do just fake that the BIST is done
+ if (reg & PTSCR_RBIST_EN)
+ regs.ptscr |= PTSCR_RBIST_DONE;
+ if (reg & PTSCR_EEBIST_EN)
+ regs.ptscr &= ~PTSCR_EEBIST_EN;
+ if (reg & PTSCR_EELOAD_EN)
+ regs.ptscr &= ~PTSCR_EELOAD_EN;
+ break;
+
+ case ISR: /* writing to the ISR has no effect */
+ panic("ISR is a read only register!\n");
+
+ case IMR:
+ regs.imr = reg;
+ devIntrChangeMask();
+ break;
+
+ case IER:
+ regs.ier = reg;
+ break;
+
+ case IHR:
+ regs.ihr = reg;
+ /* not going to implement real interrupt holdoff */
+ break;
+
+ case TXDP:
+ regs.txdp = (reg & 0xFFFFFFFC);
+ assert(txState == txIdle);
+ CTDD = false;
+ break;
+
+ case TXDP_HI:
+ regs.txdp_hi = reg;
+ break;
+
+ case TX_CFG:
+ regs.txcfg = reg;
+#if 0
+ if (reg & TX_CFG_CSI) ;
+ if (reg & TX_CFG_HBI) ;
+ if (reg & TX_CFG_MLB) ;
+ if (reg & TX_CFG_ATP) ;
+ if (reg & TX_CFG_ECRETRY) {
+ /*
+ * this could easily be implemented, but considering
+ * the network is just a fake pipe, wouldn't make
+ * sense to do this
+ */
+ }
+
+ if (reg & TX_CFG_BRST_DIS) ;
+#endif
+
+#if 0
+ /* we handle our own DMA, ignore the kernel's exhortations */
+ if (reg & TX_CFG_MXDMA) ;
+#endif
+
+ // also, we currently don't care about fill/drain
+ // thresholds though this may change in the future with
+ // more realistic networks or a driver which changes it
+ // according to feedback
+
+ break;
+
+ case GPIOR:
+ // Only write writable bits
+ regs.gpior &= GPIOR_UNUSED | GPIOR_GP5_IN | GPIOR_GP4_IN
+ | GPIOR_GP3_IN | GPIOR_GP2_IN | GPIOR_GP1_IN;
+ regs.gpior |= reg & ~(GPIOR_UNUSED | GPIOR_GP5_IN | GPIOR_GP4_IN
+ | GPIOR_GP3_IN | GPIOR_GP2_IN | GPIOR_GP1_IN);
+ /* these just control general purpose i/o pins, don't matter */
+ break;
+
+ case RXDP:
+ regs.rxdp = reg;
+ CRDD = false;
+ break;
+
+ case RXDP_HI:
+ regs.rxdp_hi = reg;
+ break;
+
+ case RX_CFG:
+ regs.rxcfg = reg;
+#if 0
+ if (reg & RX_CFG_AEP) ;
+ if (reg & RX_CFG_ARP) ;
+ if (reg & RX_CFG_STRIPCRC) ;
+ if (reg & RX_CFG_RX_RD) ;
+ if (reg & RX_CFG_ALP) ;
+ if (reg & RX_CFG_AIRL) ;
+
+ /* we handle our own DMA, ignore what kernel says about it */
+ if (reg & RX_CFG_MXDMA) ;
+
+ //also, we currently don't care about fill/drain thresholds
+ //though this may change in the future with more realistic
+ //networks or a driver which changes it according to feedback
+ if (reg & (RX_CFG_DRTH | RX_CFG_DRTH0)) ;
+#endif
+ break;
+
+ case PQCR:
+ /* there is no priority queueing used in the linux 2.6 driver */
+ regs.pqcr = reg;
+ break;
+
+ case WCSR:
+ /* not going to implement wake on LAN */
+ regs.wcsr = reg;
+ break;
+
+ case PCR:
+ /* not going to implement pause control */
+ regs.pcr = reg;
+ break;
+
+ case RFCR:
+ regs.rfcr = reg;
+
+ rxFilterEnable = (reg & RFCR_RFEN) ? true : false;
+ acceptBroadcast = (reg & RFCR_AAB) ? true : false;
+ acceptMulticast = (reg & RFCR_AAM) ? true : false;
+ acceptUnicast = (reg & RFCR_AAU) ? true : false;
+ acceptPerfect = (reg & RFCR_APM) ? true : false;
+ acceptArp = (reg & RFCR_AARP) ? true : false;
+ multicastHashEnable = (reg & RFCR_MHEN) ? true : false;
+
+#if 0
+ if (reg & RFCR_APAT)
+ panic("RFCR_APAT not implemented!\n");
+#endif
+ if (reg & RFCR_UHEN)
+ panic("Unicast hash filtering not used by drivers!\n");
+
+ if (reg & RFCR_ULM)
+ panic("RFCR_ULM not implemented!\n");
+
+ break;
+
+ case RFDR:
+ rfaddr = (uint16_t)(regs.rfcr & RFCR_RFADDR);
+ switch (rfaddr) {
+ case 0x000:
+ rom.perfectMatch[0] = (uint8_t)reg;
+ rom.perfectMatch[1] = (uint8_t)(reg >> 8);
+ break;
+ case 0x002:
+ rom.perfectMatch[2] = (uint8_t)reg;
+ rom.perfectMatch[3] = (uint8_t)(reg >> 8);
+ break;
+ case 0x004:
+ rom.perfectMatch[4] = (uint8_t)reg;
+ rom.perfectMatch[5] = (uint8_t)(reg >> 8);
+ break;
+ default:
+
+ if (rfaddr >= FHASH_ADDR &&
+ rfaddr < FHASH_ADDR + FHASH_SIZE) {
+
+ // Only word-aligned writes supported
+ if (rfaddr % 2)
+ panic("unaligned write to filter hash table!");
+
+ rom.filterHash[rfaddr - FHASH_ADDR] = (uint8_t)reg;
+ rom.filterHash[rfaddr - FHASH_ADDR + 1]
+ = (uint8_t)(reg >> 8);
+ break;
+ }
+ panic("writing RFDR for something other than pattern matching "
+ "or hashing! %#x\n", rfaddr);
+ }
+
+ case BRAR:
+ regs.brar = reg;
+ break;
+
+ case BRDR:
+ panic("the driver never uses BRDR, something is wrong!\n");
+
+ case SRR:
+ panic("SRR is read only register!\n");
+
+ case MIBC:
+ panic("the driver never uses MIBC, something is wrong!\n");
+
+ case VRCR:
+ regs.vrcr = reg;
+ break;
+
+ case VTCR:
+ regs.vtcr = reg;
+ break;
+
+ case VDR:
+ panic("the driver never uses VDR, something is wrong!\n");
+
+ case CCSR:
+ /* not going to implement clockrun stuff */
+ regs.ccsr = reg;
+ break;
+
+ case TBICR:
+ regs.tbicr = reg;
+ if (reg & TBICR_MR_LOOPBACK)
+ panic("TBICR_MR_LOOPBACK never used, something wrong!\n");
+
+ if (reg & TBICR_MR_AN_ENABLE) {
+ regs.tanlpar = regs.tanar;
+ regs.tbisr |= (TBISR_MR_AN_COMPLETE | TBISR_MR_LINK_STATUS);
+ }
+
+#if 0
+ if (reg & TBICR_MR_RESTART_AN) ;
+#endif
+
+ break;
+
+ case TBISR:
+ panic("TBISR is read only register!\n");
+
+ case TANAR:
+ // Only write the writable bits
+ regs.tanar &= TANAR_RF1 | TANAR_RF2 | TANAR_UNUSED;
+ regs.tanar |= reg & ~(TANAR_RF1 | TANAR_RF2 | TANAR_UNUSED);
+
+ // Pause capability unimplemented
+#if 0
+ if (reg & TANAR_PS2) ;
+ if (reg & TANAR_PS1) ;
+#endif
+
+ break;
+
+ case TANLPAR:
+ panic("this should only be written to by the fake phy!\n");
+
+ case TANER:
+ panic("TANER is read only register!\n");
+
+ case TESR:
+ regs.tesr = reg;
+ break;
+
+ default:
+ panic("invalid register access daddr=%#x", daddr);
+ }
+ } else {
+ panic("Invalid Request Size");
+ }
+ pkt->makeAtomicResponse();
+ return pioDelay;
+}
+
+void
+NSGigE::devIntrPost(uint32_t interrupts)
+{
+ if (interrupts & ISR_RESERVE)
+ panic("Cannot set a reserved interrupt");
+
+ if (interrupts & ISR_NOIMPL)
+ warn("interrupt not implemented %#x\n", interrupts);
+
+ interrupts &= ISR_IMPL;
+ regs.isr |= interrupts;
+
+ if (interrupts & regs.imr) {
+ if (interrupts & ISR_SWI) {
+ totalSwi++;
+ }
+ if (interrupts & ISR_RXIDLE) {
+ totalRxIdle++;
+ }
+ if (interrupts & ISR_RXOK) {
+ totalRxOk++;
+ }
+ if (interrupts & ISR_RXDESC) {
+ totalRxDesc++;
+ }
+ if (interrupts & ISR_TXOK) {
+ totalTxOk++;
+ }
+ if (interrupts & ISR_TXIDLE) {
+ totalTxIdle++;
+ }
+ if (interrupts & ISR_TXDESC) {
+ totalTxDesc++;
+ }
+ if (interrupts & ISR_RXORN) {
+ totalRxOrn++;
+ }
+ }
+
+ DPRINTF(EthernetIntr,
+ "interrupt written to ISR: intr=%#x isr=%#x imr=%#x\n",
+ interrupts, regs.isr, regs.imr);
+
+ if ((regs.isr & regs.imr)) {
+ Tick when = curTick();
+ if ((regs.isr & regs.imr & ISR_NODELAY) == 0)
+ when += intrDelay;
+ postedInterrupts++;
+ cpuIntrPost(when);
+ }
+}
+
+/* writing this interrupt counting stats inside this means that this function
+ is now limited to being used to clear all interrupts upon the kernel
+ reading isr and servicing. just telling you in case you were thinking
+ of expanding use.
+*/
+void
+NSGigE::devIntrClear(uint32_t interrupts)
+{
+ if (interrupts & ISR_RESERVE)
+ panic("Cannot clear a reserved interrupt");
+
+ if (regs.isr & regs.imr & ISR_SWI) {
+ postedSwi++;
+ }
+ if (regs.isr & regs.imr & ISR_RXIDLE) {
+ postedRxIdle++;
+ }
+ if (regs.isr & regs.imr & ISR_RXOK) {
+ postedRxOk++;
+ }
+ if (regs.isr & regs.imr & ISR_RXDESC) {
+ postedRxDesc++;
+ }
+ if (regs.isr & regs.imr & ISR_TXOK) {
+ postedTxOk++;
+ }
+ if (regs.isr & regs.imr & ISR_TXIDLE) {
+ postedTxIdle++;
+ }
+ if (regs.isr & regs.imr & ISR_TXDESC) {
+ postedTxDesc++;
+ }
+ if (regs.isr & regs.imr & ISR_RXORN) {
+ postedRxOrn++;
+ }
+
+ interrupts &= ~ISR_NOIMPL;
+ regs.isr &= ~interrupts;
+
+ DPRINTF(EthernetIntr,
+ "interrupt cleared from ISR: intr=%x isr=%x imr=%x\n",
+ interrupts, regs.isr, regs.imr);
+
+ if (!(regs.isr & regs.imr))
+ cpuIntrClear();
+}
+
+void
+NSGigE::devIntrChangeMask()
+{
+ DPRINTF(EthernetIntr, "interrupt mask changed: isr=%x imr=%x masked=%x\n",
+ regs.isr, regs.imr, regs.isr & regs.imr);
+
+ if (regs.isr & regs.imr)
+ cpuIntrPost(curTick());
+ else
+ cpuIntrClear();
+}
+
+void
+NSGigE::cpuIntrPost(Tick when)
+{
+ // If the interrupt you want to post is later than an interrupt
+ // already scheduled, just let it post in the coming one and don't
+ // schedule another.
+ // HOWEVER, must be sure that the scheduled intrTick is in the
+ // future (this was formerly the source of a bug)
+ /**
+ * @todo this warning should be removed and the intrTick code should
+ * be fixed.
+ */
+ assert(when >= curTick());
+ assert(intrTick >= curTick() || intrTick == 0);
+ if (when > intrTick && intrTick != 0) {
+ DPRINTF(EthernetIntr, "don't need to schedule event...intrTick=%d\n",
+ intrTick);
+ return;
+ }
+
+ intrTick = when;
+ if (intrTick < curTick()) {
+ Debug::breakpoint();
+ intrTick = curTick();
+ }
+
+ DPRINTF(EthernetIntr, "going to schedule an interrupt for intrTick=%d\n",
+ intrTick);
+
+ if (intrEvent)
+ intrEvent->squash();
+ intrEvent = new IntrEvent(this, true);
+ schedule(intrEvent, intrTick);
+}
+
+void
+NSGigE::cpuInterrupt()
+{
+ assert(intrTick == curTick());
+
+ // Whether or not there's a pending interrupt, we don't care about
+ // it anymore
+ intrEvent = 0;
+ intrTick = 0;
+
+ // Don't send an interrupt if there's already one
+ if (cpuPendingIntr) {
+ DPRINTF(EthernetIntr,
+ "would send an interrupt now, but there's already pending\n");
+ } else {
+ // Send interrupt
+ cpuPendingIntr = true;
+
+ DPRINTF(EthernetIntr, "posting interrupt\n");
+ intrPost();
+ }
+}
+
+void
+NSGigE::cpuIntrClear()
+{
+ if (!cpuPendingIntr)
+ return;
+
+ if (intrEvent) {
+ intrEvent->squash();
+ intrEvent = 0;
+ }
+
+ intrTick = 0;
+
+ cpuPendingIntr = false;
+
+ DPRINTF(EthernetIntr, "clearing interrupt\n");
+ intrClear();
+}
+
+bool
+NSGigE::cpuIntrPending() const
+{ return cpuPendingIntr; }
+
+void
+NSGigE::txReset()
+{
+
+ DPRINTF(Ethernet, "transmit reset\n");
+
+ CTDD = false;
+ txEnable = false;;
+ txFragPtr = 0;
+ assert(txDescCnt == 0);
+ txFifo.clear();
+ txState = txIdle;
+ assert(txDmaState == dmaIdle);
+}
+
+void
+NSGigE::rxReset()
+{
+ DPRINTF(Ethernet, "receive reset\n");
+
+ CRDD = false;
+ assert(rxPktBytes == 0);
+ rxEnable = false;
+ rxFragPtr = 0;
+ assert(rxDescCnt == 0);
+ assert(rxDmaState == dmaIdle);
+ rxFifo.clear();
+ rxState = rxIdle;
+}
+
+void
+NSGigE::regsReset()
+{
+ memset(&regs, 0, sizeof(regs));
+ regs.config = (CFGR_LNKSTS | CFGR_TBI_EN | CFGR_MODE_1000);
+ regs.mear = 0x12;
+ regs.txcfg = 0x120; // set drain threshold to 1024 bytes and
+ // fill threshold to 32 bytes
+ regs.rxcfg = 0x4; // set drain threshold to 16 bytes
+ regs.srr = 0x0103; // set the silicon revision to rev B or 0x103
+ regs.mibc = MIBC_FRZ;
+ regs.vdr = 0x81; // set the vlan tag type to 802.1q
+ regs.tesr = 0xc000; // TBI capable of both full and half duplex
+ regs.brar = 0xffffffff;
+
+ extstsEnable = false;
+ acceptBroadcast = false;
+ acceptMulticast = false;
+ acceptUnicast = false;
+ acceptPerfect = false;
+ acceptArp = false;
+}
+
+bool
+NSGigE::doRxDmaRead()
+{
+ assert(rxDmaState == dmaIdle || rxDmaState == dmaReadWaiting);
+ rxDmaState = dmaReading;
+
+ if (dmaPending() || drainState() != DrainState::Running)
+ rxDmaState = dmaReadWaiting;
+ else
+ dmaRead(rxDmaAddr, rxDmaLen, &rxDmaReadEvent, (uint8_t*)rxDmaData);
+
+ return true;
+}
+
+void
+NSGigE::rxDmaReadDone()
+{
+ assert(rxDmaState == dmaReading);
+ rxDmaState = dmaIdle;
+
+ DPRINTF(EthernetDMA, "rx dma read paddr=%#x len=%d\n",
+ rxDmaAddr, rxDmaLen);
+ DDUMP(EthernetDMA, rxDmaData, rxDmaLen);
+
+ // If the transmit state machine has a pending DMA, let it go first
+ if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting)
+ txKick();
+
+ rxKick();
+}
+
+bool
+NSGigE::doRxDmaWrite()
+{
+ assert(rxDmaState == dmaIdle || rxDmaState == dmaWriteWaiting);
+ rxDmaState = dmaWriting;
+
+ if (dmaPending() || drainState() != DrainState::Running)
+ rxDmaState = dmaWriteWaiting;
+ else
+ dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaWriteEvent, (uint8_t*)rxDmaData);
+ return true;
+}
+
+void
+NSGigE::rxDmaWriteDone()
+{
+ assert(rxDmaState == dmaWriting);
+ rxDmaState = dmaIdle;
+
+ DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n",
+ rxDmaAddr, rxDmaLen);
+ DDUMP(EthernetDMA, rxDmaData, rxDmaLen);
+
+ // If the transmit state machine has a pending DMA, let it go first
+ if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting)
+ txKick();
+
+ rxKick();
+}
+
+void
+NSGigE::rxKick()
+{
+ bool is64bit = (bool)(regs.config & CFGR_M64ADDR);
+
+ DPRINTF(EthernetSM,
+ "receive kick rxState=%s (rxBuf.size=%d) %d-bit\n",
+ NsRxStateStrings[rxState], rxFifo.size(), is64bit ? 64 : 32);
+
+ Addr link, bufptr;
+ uint32_t &cmdsts = is64bit ? rxDesc64.cmdsts : rxDesc32.cmdsts;
+ uint32_t &extsts = is64bit ? rxDesc64.extsts : rxDesc32.extsts;
+
+ next:
+ if (rxKickTick > curTick()) {
+ DPRINTF(EthernetSM, "receive kick exiting, can't run till %d\n",
+ rxKickTick);
+
+ goto exit;
+ }
+
+ // Go to the next state machine clock tick.
+ rxKickTick = clockEdge(Cycles(1));
+
+ switch(rxDmaState) {
+ case dmaReadWaiting:
+ if (doRxDmaRead())
+ goto exit;
+ break;
+ case dmaWriteWaiting:
+ if (doRxDmaWrite())
+ goto exit;
+ break;
+ default:
+ break;
+ }
+
+ link = is64bit ? (Addr)rxDesc64.link : (Addr)rxDesc32.link;
+ bufptr = is64bit ? (Addr)rxDesc64.bufptr : (Addr)rxDesc32.bufptr;
+
+ // see state machine from spec for details
+ // the way this works is, if you finish work on one state and can
+ // go directly to another, you do that through jumping to the
+ // label "next". however, if you have intermediate work, like DMA
+ // so that you can't go to the next state yet, you go to exit and
+ // exit the loop. however, when the DMA is done it will trigger
+ // an event and come back to this loop.
+ switch (rxState) {
+ case rxIdle:
+ if (!rxEnable) {
+ DPRINTF(EthernetSM, "Receive Disabled! Nothing to do.\n");
+ goto exit;
+ }
+
+ if (CRDD) {
+ rxState = rxDescRefr;
+
+ rxDmaAddr = regs.rxdp & 0x3fffffff;
+ rxDmaData =
+ is64bit ? (void *)&rxDesc64.link : (void *)&rxDesc32.link;
+ rxDmaLen = is64bit ? sizeof(rxDesc64.link) : sizeof(rxDesc32.link);
+ rxDmaFree = dmaDescFree;
+
+ descDmaReads++;
+ descDmaRdBytes += rxDmaLen;
+
+ if (doRxDmaRead())
+ goto exit;
+ } else {
+ rxState = rxDescRead;
+
+ rxDmaAddr = regs.rxdp & 0x3fffffff;
+ rxDmaData = is64bit ? (void *)&rxDesc64 : (void *)&rxDesc32;
+ rxDmaLen = is64bit ? sizeof(rxDesc64) : sizeof(rxDesc32);
+ rxDmaFree = dmaDescFree;
+
+ descDmaReads++;
+ descDmaRdBytes += rxDmaLen;
+
+ if (doRxDmaRead())
+ goto exit;
+ }
+ break;
+
+ case rxDescRefr:
+ if (rxDmaState != dmaIdle)
+ goto exit;
+
+ rxState = rxAdvance;
+ break;
+
+ case rxDescRead:
+ if (rxDmaState != dmaIdle)
+ goto exit;
+
+ DPRINTF(EthernetDesc, "rxDesc: addr=%08x read descriptor\n",
+ regs.rxdp & 0x3fffffff);
+ DPRINTF(EthernetDesc,
+ "rxDesc: link=%#x bufptr=%#x cmdsts=%08x extsts=%08x\n",
+ link, bufptr, cmdsts, extsts);
+
+ if (cmdsts & CMDSTS_OWN) {
+ devIntrPost(ISR_RXIDLE);
+ rxState = rxIdle;
+ goto exit;
+ } else {
+ rxState = rxFifoBlock;
+ rxFragPtr = bufptr;
+ rxDescCnt = cmdsts & CMDSTS_LEN_MASK;
+ }
+ break;
+
+ case rxFifoBlock:
+ if (!rxPacket) {
+ /**
+ * @todo in reality, we should be able to start processing
+ * the packet as it arrives, and not have to wait for the
+ * full packet ot be in the receive fifo.
+ */
+ if (rxFifo.empty())
+ goto exit;
+
+ DPRINTF(EthernetSM, "****processing receive of new packet****\n");
+
+ // If we don't have a packet, grab a new one from the fifo.
+ rxPacket = rxFifo.front();
+ rxPktBytes = rxPacket->length;
+ rxPacketBufPtr = rxPacket->data;
+
+#if TRACING_ON
+ if (DTRACE(Ethernet)) {
+ IpPtr ip(rxPacket);
+ if (ip) {
+ DPRINTF(Ethernet, "ID is %d\n", ip->id());
+ TcpPtr tcp(ip);
+ if (tcp) {
+ DPRINTF(Ethernet,
+ "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n",
+ tcp->sport(), tcp->dport(), tcp->seq(),
+ tcp->ack());
+ }
+ }
+ }
+#endif
+
+ // sanity check - i think the driver behaves like this
+ assert(rxDescCnt >= rxPktBytes);
+ rxFifo.pop();
+ }
+
+
+ // dont' need the && rxDescCnt > 0 if driver sanity check
+ // above holds
+ if (rxPktBytes > 0) {
+ rxState = rxFragWrite;
+ // don't need min<>(rxPktBytes,rxDescCnt) if above sanity
+ // check holds
+ rxXferLen = rxPktBytes;
+
+ rxDmaAddr = rxFragPtr & 0x3fffffff;
+ rxDmaData = rxPacketBufPtr;
+ rxDmaLen = rxXferLen;
+ rxDmaFree = dmaDataFree;
+
+ if (doRxDmaWrite())
+ goto exit;
+
+ } else {
+ rxState = rxDescWrite;
+
+ //if (rxPktBytes == 0) { /* packet is done */
+ assert(rxPktBytes == 0);
+ DPRINTF(EthernetSM, "done with receiving packet\n");
+
+ cmdsts |= CMDSTS_OWN;
+ cmdsts &= ~CMDSTS_MORE;
+ cmdsts |= CMDSTS_OK;
+ cmdsts &= 0xffff0000;
+ cmdsts += rxPacket->length; //i.e. set CMDSTS_SIZE
+
+#if 0
+ /*
+ * all the driver uses these are for its own stats keeping
+ * which we don't care about, aren't necessary for
+ * functionality and doing this would just slow us down.
+ * if they end up using this in a later version for
+ * functional purposes, just undef
+ */
+ if (rxFilterEnable) {
+ cmdsts &= ~CMDSTS_DEST_MASK;
+ const EthAddr &dst = rxFifoFront()->dst();
+ if (dst->unicast())
+ cmdsts |= CMDSTS_DEST_SELF;
+ if (dst->multicast())
+ cmdsts |= CMDSTS_DEST_MULTI;
+ if (dst->broadcast())
+ cmdsts |= CMDSTS_DEST_MASK;
+ }
+#endif
+
+ IpPtr ip(rxPacket);
+ if (extstsEnable && ip) {
+ extsts |= EXTSTS_IPPKT;
+ rxIpChecksums++;
+ if (cksum(ip) != 0) {
+ DPRINTF(EthernetCksum, "Rx IP Checksum Error\n");
+ extsts |= EXTSTS_IPERR;
+ }
+ TcpPtr tcp(ip);
+ UdpPtr udp(ip);
+ if (tcp) {
+ extsts |= EXTSTS_TCPPKT;
+ rxTcpChecksums++;
+ if (cksum(tcp) != 0) {
+ DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n");
+ extsts |= EXTSTS_TCPERR;
+
+ }
+ } else if (udp) {
+ extsts |= EXTSTS_UDPPKT;
+ rxUdpChecksums++;
+ if (cksum(udp) != 0) {
+ DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n");
+ extsts |= EXTSTS_UDPERR;
+ }
+ }
+ }
+ rxPacket = 0;
+
+ /*
+ * the driver seems to always receive into desc buffers
+ * of size 1514, so you never have a pkt that is split
+ * into multiple descriptors on the receive side, so
+ * i don't implement that case, hence the assert above.
+ */
+
+ DPRINTF(EthernetDesc,
+ "rxDesc: addr=%08x writeback cmdsts extsts\n",
+ regs.rxdp & 0x3fffffff);
+ DPRINTF(EthernetDesc,
+ "rxDesc: link=%#x bufptr=%#x cmdsts=%08x extsts=%08x\n",
+ link, bufptr, cmdsts, extsts);
+
+ rxDmaAddr = regs.rxdp & 0x3fffffff;
+ rxDmaData = &cmdsts;
+ if (is64bit) {
+ rxDmaAddr += offsetof(ns_desc64, cmdsts);
+ rxDmaLen = sizeof(rxDesc64.cmdsts) + sizeof(rxDesc64.extsts);
+ } else {
+ rxDmaAddr += offsetof(ns_desc32, cmdsts);
+ rxDmaLen = sizeof(rxDesc32.cmdsts) + sizeof(rxDesc32.extsts);
+ }
+ rxDmaFree = dmaDescFree;
+
+ descDmaWrites++;
+ descDmaWrBytes += rxDmaLen;
+
+ if (doRxDmaWrite())
+ goto exit;
+ }
+ break;
+
+ case rxFragWrite:
+ if (rxDmaState != dmaIdle)
+ goto exit;
+
+ rxPacketBufPtr += rxXferLen;
+ rxFragPtr += rxXferLen;
+ rxPktBytes -= rxXferLen;
+
+ rxState = rxFifoBlock;
+ break;
+
+ case rxDescWrite:
+ if (rxDmaState != dmaIdle)
+ goto exit;
+
+ assert(cmdsts & CMDSTS_OWN);
+
+ assert(rxPacket == 0);
+ devIntrPost(ISR_RXOK);
+
+ if (cmdsts & CMDSTS_INTR)
+ devIntrPost(ISR_RXDESC);
+
+ if (!rxEnable) {
+ DPRINTF(EthernetSM, "Halting the RX state machine\n");
+ rxState = rxIdle;
+ goto exit;
+ } else
+ rxState = rxAdvance;
+ break;
+
+ case rxAdvance:
+ if (link == 0) {
+ devIntrPost(ISR_RXIDLE);
+ rxState = rxIdle;
+ CRDD = true;
+ goto exit;
+ } else {
+ if (rxDmaState != dmaIdle)
+ goto exit;
+ rxState = rxDescRead;
+ regs.rxdp = link;
+ CRDD = false;
+
+ rxDmaAddr = regs.rxdp & 0x3fffffff;
+ rxDmaData = is64bit ? (void *)&rxDesc64 : (void *)&rxDesc32;
+ rxDmaLen = is64bit ? sizeof(rxDesc64) : sizeof(rxDesc32);
+ rxDmaFree = dmaDescFree;
+
+ if (doRxDmaRead())
+ goto exit;
+ }
+ break;
+
+ default:
+ panic("Invalid rxState!");
+ }
+
+ DPRINTF(EthernetSM, "entering next rxState=%s\n",
+ NsRxStateStrings[rxState]);
+ goto next;
+
+ exit:
+ /**
+ * @todo do we want to schedule a future kick?
+ */
+ DPRINTF(EthernetSM, "rx state machine exited rxState=%s\n",
+ NsRxStateStrings[rxState]);
+
+ if (!rxKickEvent.scheduled())
+ schedule(rxKickEvent, rxKickTick);
+}
+
+void
+NSGigE::transmit()
+{
+ if (txFifo.empty()) {
+ DPRINTF(Ethernet, "nothing to transmit\n");
+ return;
+ }
+
+ DPRINTF(Ethernet, "Attempt Pkt Transmit: txFifo length=%d\n",
+ txFifo.size());
+ if (interface->sendPacket(txFifo.front())) {
+#if TRACING_ON
+ if (DTRACE(Ethernet)) {
+ IpPtr ip(txFifo.front());
+ if (ip) {
+ DPRINTF(Ethernet, "ID is %d\n", ip->id());
+ TcpPtr tcp(ip);
+ if (tcp) {
+ DPRINTF(Ethernet,
+ "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n",
+ tcp->sport(), tcp->dport(), tcp->seq(),
+ tcp->ack());
+ }
+ }
+ }
+#endif
+
+ DDUMP(EthernetData, txFifo.front()->data, txFifo.front()->length);
+ txBytes += txFifo.front()->length;
+ txPackets++;
+
+ DPRINTF(Ethernet, "Successful Xmit! now txFifoAvail is %d\n",
+ txFifo.avail());
+ txFifo.pop();
+
+ /*
+ * normally do a writeback of the descriptor here, and ONLY
+ * after that is done, send this interrupt. but since our
+ * stuff never actually fails, just do this interrupt here,
+ * otherwise the code has to stray from this nice format.
+ * besides, it's functionally the same.
+ */
+ devIntrPost(ISR_TXOK);
+ }
+
+ if (!txFifo.empty() && !txEvent.scheduled()) {
+ DPRINTF(Ethernet, "reschedule transmit\n");
+ schedule(txEvent, curTick() + retryTime);
+ }
+}
+
+bool
+NSGigE::doTxDmaRead()
+{
+ assert(txDmaState == dmaIdle || txDmaState == dmaReadWaiting);
+ txDmaState = dmaReading;
+
+ if (dmaPending() || drainState() != DrainState::Running)
+ txDmaState = dmaReadWaiting;
+ else
+ dmaRead(txDmaAddr, txDmaLen, &txDmaReadEvent, (uint8_t*)txDmaData);
+
+ return true;
+}
+
+void
+NSGigE::txDmaReadDone()
+{
+ assert(txDmaState == dmaReading);
+ txDmaState = dmaIdle;
+
+ DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n",
+ txDmaAddr, txDmaLen);
+ DDUMP(EthernetDMA, txDmaData, txDmaLen);
+
+ // If the receive state machine has a pending DMA, let it go first
+ if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting)
+ rxKick();
+
+ txKick();
+}
+
+bool
+NSGigE::doTxDmaWrite()
+{
+ assert(txDmaState == dmaIdle || txDmaState == dmaWriteWaiting);
+ txDmaState = dmaWriting;
+
+ if (dmaPending() || drainState() != DrainState::Running)
+ txDmaState = dmaWriteWaiting;
+ else
+ dmaWrite(txDmaAddr, txDmaLen, &txDmaWriteEvent, (uint8_t*)txDmaData);
+ return true;
+}
+
+void
+NSGigE::txDmaWriteDone()
+{
+ assert(txDmaState == dmaWriting);
+ txDmaState = dmaIdle;
+
+ DPRINTF(EthernetDMA, "tx dma write paddr=%#x len=%d\n",
+ txDmaAddr, txDmaLen);
+ DDUMP(EthernetDMA, txDmaData, txDmaLen);
+
+ // If the receive state machine has a pending DMA, let it go first
+ if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting)
+ rxKick();
+
+ txKick();
+}
+
+void
+NSGigE::txKick()
+{
+ bool is64bit = (bool)(regs.config & CFGR_M64ADDR);
+
+ DPRINTF(EthernetSM, "transmit kick txState=%s %d-bit\n",
+ NsTxStateStrings[txState], is64bit ? 64 : 32);
+
+ Addr link, bufptr;
+ uint32_t &cmdsts = is64bit ? txDesc64.cmdsts : txDesc32.cmdsts;
+ uint32_t &extsts = is64bit ? txDesc64.extsts : txDesc32.extsts;
+
+ next:
+ if (txKickTick > curTick()) {
+ DPRINTF(EthernetSM, "transmit kick exiting, can't run till %d\n",
+ txKickTick);
+ goto exit;
+ }
+
+ // Go to the next state machine clock tick.
+ txKickTick = clockEdge(Cycles(1));
+
+ switch(txDmaState) {
+ case dmaReadWaiting:
+ if (doTxDmaRead())
+ goto exit;
+ break;
+ case dmaWriteWaiting:
+ if (doTxDmaWrite())
+ goto exit;
+ break;
+ default:
+ break;
+ }
+
+ link = is64bit ? (Addr)txDesc64.link : (Addr)txDesc32.link;
+ bufptr = is64bit ? (Addr)txDesc64.bufptr : (Addr)txDesc32.bufptr;
+ switch (txState) {
+ case txIdle:
+ if (!txEnable) {
+ DPRINTF(EthernetSM, "Transmit disabled. Nothing to do.\n");
+ goto exit;
+ }
+
+ if (CTDD) {
+ txState = txDescRefr;
+
+ txDmaAddr = regs.txdp & 0x3fffffff;
+ txDmaData =
+ is64bit ? (void *)&txDesc64.link : (void *)&txDesc32.link;
+ txDmaLen = is64bit ? sizeof(txDesc64.link) : sizeof(txDesc32.link);
+ txDmaFree = dmaDescFree;
+
+ descDmaReads++;
+ descDmaRdBytes += txDmaLen;
+
+ if (doTxDmaRead())
+ goto exit;
+
+ } else {
+ txState = txDescRead;
+
+ txDmaAddr = regs.txdp & 0x3fffffff;
+ txDmaData = is64bit ? (void *)&txDesc64 : (void *)&txDesc32;
+ txDmaLen = is64bit ? sizeof(txDesc64) : sizeof(txDesc32);
+ txDmaFree = dmaDescFree;
+
+ descDmaReads++;
+ descDmaRdBytes += txDmaLen;
+
+ if (doTxDmaRead())
+ goto exit;
+ }
+ break;
+
+ case txDescRefr:
+ if (txDmaState != dmaIdle)
+ goto exit;
+
+ txState = txAdvance;
+ break;
+
+ case txDescRead:
+ if (txDmaState != dmaIdle)
+ goto exit;
+
+ DPRINTF(EthernetDesc, "txDesc: addr=%08x read descriptor\n",
+ regs.txdp & 0x3fffffff);
+ DPRINTF(EthernetDesc,
+ "txDesc: link=%#x bufptr=%#x cmdsts=%#08x extsts=%#08x\n",
+ link, bufptr, cmdsts, extsts);
+
+ if (cmdsts & CMDSTS_OWN) {
+ txState = txFifoBlock;
+ txFragPtr = bufptr;
+ txDescCnt = cmdsts & CMDSTS_LEN_MASK;
+ } else {
+ devIntrPost(ISR_TXIDLE);
+ txState = txIdle;
+ goto exit;
+ }
+ break;
+
+ case txFifoBlock:
+ if (!txPacket) {
+ DPRINTF(EthernetSM, "****starting the tx of a new packet****\n");
+ txPacket = make_shared<EthPacketData>(16384);
+ txPacketBufPtr = txPacket->data;
+ }
+
+ if (txDescCnt == 0) {
+ DPRINTF(EthernetSM, "the txDescCnt == 0, done with descriptor\n");
+ if (cmdsts & CMDSTS_MORE) {
+ DPRINTF(EthernetSM, "there are more descriptors to come\n");
+ txState = txDescWrite;
+
+ cmdsts &= ~CMDSTS_OWN;
+
+ txDmaAddr = regs.txdp & 0x3fffffff;
+ txDmaData = &cmdsts;
+ if (is64bit) {
+ txDmaAddr += offsetof(ns_desc64, cmdsts);
+ txDmaLen = sizeof(txDesc64.cmdsts);
+ } else {
+ txDmaAddr += offsetof(ns_desc32, cmdsts);
+ txDmaLen = sizeof(txDesc32.cmdsts);
+ }
+ txDmaFree = dmaDescFree;
+
+ if (doTxDmaWrite())
+ goto exit;
+
+ } else { /* this packet is totally done */
+ DPRINTF(EthernetSM, "This packet is done, let's wrap it up\n");
+ /* deal with the the packet that just finished */
+ if ((regs.vtcr & VTCR_PPCHK) && extstsEnable) {
+ IpPtr ip(txPacket);
+ if (extsts & EXTSTS_UDPPKT) {
+ UdpPtr udp(ip);
+ if (udp) {
+ udp->sum(0);
+ udp->sum(cksum(udp));
+ txUdpChecksums++;
+ } else {
+ Debug::breakpoint();
+ warn_once("UDPPKT set, but not UDP!\n");
+ }
+ } else if (extsts & EXTSTS_TCPPKT) {
+ TcpPtr tcp(ip);
+ if (tcp) {
+ tcp->sum(0);
+ tcp->sum(cksum(tcp));
+ txTcpChecksums++;
+ } else {
+ Debug::breakpoint();
+ warn_once("TCPPKT set, but not UDP!\n");
+ }
+ }
+ if (extsts & EXTSTS_IPPKT) {
+ if (ip) {
+ ip->sum(0);
+ ip->sum(cksum(ip));
+ txIpChecksums++;
+ } else {
+ Debug::breakpoint();
+ warn_once("IPPKT set, but not UDP!\n");
+ }
+ }
+ }
+
+ txPacket->length = txPacketBufPtr - txPacket->data;
+ // this is just because the receive can't handle a
+ // packet bigger want to make sure
+ if (txPacket->length > 1514)
+ panic("transmit packet too large, %s > 1514\n",
+ txPacket->length);
+
+#ifndef NDEBUG
+ bool success =
+#endif
+ txFifo.push(txPacket);
+ assert(success);
+
+ /*
+ * this following section is not tqo spec, but
+ * functionally shouldn't be any different. normally,
+ * the chip will wait til the transmit has occurred
+ * before writing back the descriptor because it has
+ * to wait to see that it was successfully transmitted
+ * to decide whether to set CMDSTS_OK or not.
+ * however, in the simulator since it is always
+ * successfully transmitted, and writing it exactly to
+ * spec would complicate the code, we just do it here
+ */
+
+ cmdsts &= ~CMDSTS_OWN;
+ cmdsts |= CMDSTS_OK;
+
+ DPRINTF(EthernetDesc,
+ "txDesc writeback: cmdsts=%08x extsts=%08x\n",
+ cmdsts, extsts);
+
+ txDmaFree = dmaDescFree;
+ txDmaAddr = regs.txdp & 0x3fffffff;
+ txDmaData = &cmdsts;
+ if (is64bit) {
+ txDmaAddr += offsetof(ns_desc64, cmdsts);
+ txDmaLen =
+ sizeof(txDesc64.cmdsts) + sizeof(txDesc64.extsts);
+ } else {
+ txDmaAddr += offsetof(ns_desc32, cmdsts);
+ txDmaLen =
+ sizeof(txDesc32.cmdsts) + sizeof(txDesc32.extsts);
+ }
+
+ descDmaWrites++;
+ descDmaWrBytes += txDmaLen;
+
+ transmit();
+ txPacket = 0;
+
+ if (!txEnable) {
+ DPRINTF(EthernetSM, "halting TX state machine\n");
+ txState = txIdle;
+ goto exit;
+ } else
+ txState = txAdvance;
+
+ if (doTxDmaWrite())
+ goto exit;
+ }
+ } else {
+ DPRINTF(EthernetSM, "this descriptor isn't done yet\n");
+ if (!txFifo.full()) {
+ txState = txFragRead;
+
+ /*
+ * The number of bytes transferred is either whatever
+ * is left in the descriptor (txDescCnt), or if there
+ * is not enough room in the fifo, just whatever room
+ * is left in the fifo
+ */
+ txXferLen = min<uint32_t>(txDescCnt, txFifo.avail());
+
+ txDmaAddr = txFragPtr & 0x3fffffff;
+ txDmaData = txPacketBufPtr;
+ txDmaLen = txXferLen;
+ txDmaFree = dmaDataFree;
+
+ if (doTxDmaRead())
+ goto exit;
+ } else {
+ txState = txFifoBlock;
+ transmit();
+
+ goto exit;
+ }
+
+ }
+ break;
+
+ case txFragRead:
+ if (txDmaState != dmaIdle)
+ goto exit;
+
+ txPacketBufPtr += txXferLen;
+ txFragPtr += txXferLen;
+ txDescCnt -= txXferLen;
+ txFifo.reserve(txXferLen);
+
+ txState = txFifoBlock;
+ break;
+
+ case txDescWrite:
+ if (txDmaState != dmaIdle)
+ goto exit;
+
+ if (cmdsts & CMDSTS_INTR)
+ devIntrPost(ISR_TXDESC);
+
+ if (!txEnable) {
+ DPRINTF(EthernetSM, "halting TX state machine\n");
+ txState = txIdle;
+ goto exit;
+ } else
+ txState = txAdvance;
+ break;
+
+ case txAdvance:
+ if (link == 0) {
+ devIntrPost(ISR_TXIDLE);
+ txState = txIdle;
+ goto exit;
+ } else {
+ if (txDmaState != dmaIdle)
+ goto exit;
+ txState = txDescRead;
+ regs.txdp = link;
+ CTDD = false;
+
+ txDmaAddr = link & 0x3fffffff;
+ txDmaData = is64bit ? (void *)&txDesc64 : (void *)&txDesc32;
+ txDmaLen = is64bit ? sizeof(txDesc64) : sizeof(txDesc32);
+ txDmaFree = dmaDescFree;
+
+ if (doTxDmaRead())
+ goto exit;
+ }
+ break;
+
+ default:
+ panic("invalid state");
+ }
+
+ DPRINTF(EthernetSM, "entering next txState=%s\n",
+ NsTxStateStrings[txState]);
+ goto next;
+
+ exit:
+ /**
+ * @todo do we want to schedule a future kick?
+ */
+ DPRINTF(EthernetSM, "tx state machine exited txState=%s\n",
+ NsTxStateStrings[txState]);
+
+ if (!txKickEvent.scheduled())
+ schedule(txKickEvent, txKickTick);
+}
+
+/**
+ * Advance the EEPROM state machine
+ * Called on rising edge of EEPROM clock bit in MEAR
+ */
+void
+NSGigE::eepromKick()
+{
+ switch (eepromState) {
+
+ case eepromStart:
+
+ // Wait for start bit
+ if (regs.mear & MEAR_EEDI) {
+ // Set up to get 2 opcode bits
+ eepromState = eepromGetOpcode;
+ eepromBitsToRx = 2;
+ eepromOpcode = 0;
+ }
+ break;
+
+ case eepromGetOpcode:
+ eepromOpcode <<= 1;
+ eepromOpcode += (regs.mear & MEAR_EEDI) ? 1 : 0;
+ --eepromBitsToRx;
+
+ // Done getting opcode
+ if (eepromBitsToRx == 0) {
+ if (eepromOpcode != EEPROM_READ)
+ panic("only EEPROM reads are implemented!");
+
+ // Set up to get address
+ eepromState = eepromGetAddress;
+ eepromBitsToRx = 6;
+ eepromAddress = 0;
+ }
+ break;
+
+ case eepromGetAddress:
+ eepromAddress <<= 1;
+ eepromAddress += (regs.mear & MEAR_EEDI) ? 1 : 0;
+ --eepromBitsToRx;
+
+ // Done getting address
+ if (eepromBitsToRx == 0) {
+
+ if (eepromAddress >= EEPROM_SIZE)
+ panic("EEPROM read access out of range!");
+
+ switch (eepromAddress) {
+
+ case EEPROM_PMATCH2_ADDR:
+ eepromData = rom.perfectMatch[5];
+ eepromData <<= 8;
+ eepromData += rom.perfectMatch[4];
+ break;
+
+ case EEPROM_PMATCH1_ADDR:
+ eepromData = rom.perfectMatch[3];
+ eepromData <<= 8;
+ eepromData += rom.perfectMatch[2];
+ break;
+
+ case EEPROM_PMATCH0_ADDR:
+ eepromData = rom.perfectMatch[1];
+ eepromData <<= 8;
+ eepromData += rom.perfectMatch[0];
+ break;
+
+ default:
+ panic("FreeBSD driver only uses EEPROM to read PMATCH!");
+ }
+ // Set up to read data
+ eepromState = eepromRead;
+ eepromBitsToRx = 16;
+
+ // Clear data in bit
+ regs.mear &= ~MEAR_EEDI;
+ }
+ break;
+
+ case eepromRead:
+ // Clear Data Out bit
+ regs.mear &= ~MEAR_EEDO;
+ // Set bit to value of current EEPROM bit
+ regs.mear |= (eepromData & 0x8000) ? MEAR_EEDO : 0x0;
+
+ eepromData <<= 1;
+ --eepromBitsToRx;
+
+ // All done
+ if (eepromBitsToRx == 0) {
+ eepromState = eepromStart;
+ }
+ break;
+
+ default:
+ panic("invalid EEPROM state");
+ }
+
+}
+
+void
+NSGigE::transferDone()
+{
+ if (txFifo.empty()) {
+ DPRINTF(Ethernet, "transfer complete: txFifo empty...nothing to do\n");
+ return;
+ }
+
+ DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n");
+
+ reschedule(txEvent, clockEdge(Cycles(1)), true);
+}
+
+bool
+NSGigE::rxFilter(const EthPacketPtr &packet)
+{
+ EthPtr eth = packet;
+ bool drop = true;
+ string type;
+
+ const EthAddr &dst = eth->dst();
+ if (dst.unicast()) {
+ // If we're accepting all unicast addresses
+ if (acceptUnicast)
+ drop = false;
+
+ // If we make a perfect match
+ if (acceptPerfect && dst == rom.perfectMatch)
+ drop = false;
+
+ if (acceptArp && eth->type() == ETH_TYPE_ARP)
+ drop = false;
+
+ } else if (dst.broadcast()) {
+ // if we're accepting broadcasts
+ if (acceptBroadcast)
+ drop = false;
+
+ } else if (dst.multicast()) {
+ // if we're accepting all multicasts
+ if (acceptMulticast)
+ drop = false;
+
+ // Multicast hashing faked - all packets accepted
+ if (multicastHashEnable)
+ drop = false;
+ }
+
+ if (drop) {
+ DPRINTF(Ethernet, "rxFilter drop\n");
+ DDUMP(EthernetData, packet->data, packet->length);
+ }
+
+ return drop;
+}
+
+bool
+NSGigE::recvPacket(EthPacketPtr packet)
+{
+ rxBytes += packet->length;
+ rxPackets++;
+
+ DPRINTF(Ethernet, "Receiving packet from wire, rxFifoAvail=%d\n",
+ rxFifo.avail());
+
+ if (!rxEnable) {
+ DPRINTF(Ethernet, "receive disabled...packet dropped\n");
+ return true;
+ }
+
+ if (!rxFilterEnable) {
+ DPRINTF(Ethernet,
+ "receive packet filtering disabled . . . packet dropped\n");
+ return true;
+ }
+
+ if (rxFilter(packet)) {
+ DPRINTF(Ethernet, "packet filtered...dropped\n");
+ return true;
+ }
+
+ if (rxFifo.avail() < packet->length) {
+#if TRACING_ON
+ IpPtr ip(packet);
+ TcpPtr tcp(ip);
+ if (ip) {
+ DPRINTF(Ethernet,
+ "packet won't fit in receive buffer...pkt ID %d dropped\n",
+ ip->id());
+ if (tcp) {
+ DPRINTF(Ethernet, "Seq=%d\n", tcp->seq());
+ }
+ }
+#endif
+ droppedPackets++;
+ devIntrPost(ISR_RXORN);
+ return false;
+ }
+
+ rxFifo.push(packet);
+
+ rxKick();
+ return true;
+}
+
+
+void
+NSGigE::drainResume()
+{
+ Drainable::drainResume();
+
+ // During drain we could have left the state machines in a waiting state and
+ // they wouldn't get out until some other event occured to kick them.
+ // This way they'll get out immediately
+ txKick();
+ rxKick();
+}
+
+
+//=====================================================================
+//
+//
+void
+NSGigE::serialize(CheckpointOut &cp) const
+{
+ // Serialize the PciDevice base class
+ PciDevice::serialize(cp);
+
+ /*
+ * Finalize any DMA events now.
+ */
+ // @todo will mem system save pending dma?
+
+ /*
+ * Serialize the device registers
+ */
+ SERIALIZE_SCALAR(regs.command);
+ SERIALIZE_SCALAR(regs.config);
+ SERIALIZE_SCALAR(regs.mear);
+ SERIALIZE_SCALAR(regs.ptscr);
+ SERIALIZE_SCALAR(regs.isr);
+ SERIALIZE_SCALAR(regs.imr);
+ SERIALIZE_SCALAR(regs.ier);
+ SERIALIZE_SCALAR(regs.ihr);
+ SERIALIZE_SCALAR(regs.txdp);
+ SERIALIZE_SCALAR(regs.txdp_hi);
+ SERIALIZE_SCALAR(regs.txcfg);
+ SERIALIZE_SCALAR(regs.gpior);
+ SERIALIZE_SCALAR(regs.rxdp);
+ SERIALIZE_SCALAR(regs.rxdp_hi);
+ SERIALIZE_SCALAR(regs.rxcfg);
+ SERIALIZE_SCALAR(regs.pqcr);
+ SERIALIZE_SCALAR(regs.wcsr);
+ SERIALIZE_SCALAR(regs.pcr);
+ SERIALIZE_SCALAR(regs.rfcr);
+ SERIALIZE_SCALAR(regs.rfdr);
+ SERIALIZE_SCALAR(regs.brar);
+ SERIALIZE_SCALAR(regs.brdr);
+ SERIALIZE_SCALAR(regs.srr);
+ SERIALIZE_SCALAR(regs.mibc);
+ SERIALIZE_SCALAR(regs.vrcr);
+ SERIALIZE_SCALAR(regs.vtcr);
+ SERIALIZE_SCALAR(regs.vdr);
+ SERIALIZE_SCALAR(regs.ccsr);
+ SERIALIZE_SCALAR(regs.tbicr);
+ SERIALIZE_SCALAR(regs.tbisr);
+ SERIALIZE_SCALAR(regs.tanar);
+ SERIALIZE_SCALAR(regs.tanlpar);
+ SERIALIZE_SCALAR(regs.taner);
+ SERIALIZE_SCALAR(regs.tesr);
+
+ SERIALIZE_ARRAY(rom.perfectMatch, ETH_ADDR_LEN);
+ SERIALIZE_ARRAY(rom.filterHash, FHASH_SIZE);
+
+ SERIALIZE_SCALAR(ioEnable);
+
+ /*
+ * Serialize the data Fifos
+ */
+ rxFifo.serialize("rxFifo", cp);
+ txFifo.serialize("txFifo", cp);
+
+ /*
+ * Serialize the various helper variables
+ */
+ bool txPacketExists = txPacket != nullptr;
+ SERIALIZE_SCALAR(txPacketExists);
+ if (txPacketExists) {
+ txPacket->length = txPacketBufPtr - txPacket->data;
+ txPacket->serialize("txPacket", cp);
+ uint32_t txPktBufPtr = (uint32_t) (txPacketBufPtr - txPacket->data);
+ SERIALIZE_SCALAR(txPktBufPtr);
+ }
+
+ bool rxPacketExists = rxPacket != nullptr;
+ SERIALIZE_SCALAR(rxPacketExists);
+ if (rxPacketExists) {
+ rxPacket->serialize("rxPacket", cp);
+ uint32_t rxPktBufPtr = (uint32_t) (rxPacketBufPtr - rxPacket->data);
+ SERIALIZE_SCALAR(rxPktBufPtr);
+ }
+
+ SERIALIZE_SCALAR(txXferLen);
+ SERIALIZE_SCALAR(rxXferLen);
+
+ /*
+ * Serialize Cached Descriptors
+ */
+ SERIALIZE_SCALAR(rxDesc64.link);
+ SERIALIZE_SCALAR(rxDesc64.bufptr);
+ SERIALIZE_SCALAR(rxDesc64.cmdsts);
+ SERIALIZE_SCALAR(rxDesc64.extsts);
+ SERIALIZE_SCALAR(txDesc64.link);
+ SERIALIZE_SCALAR(txDesc64.bufptr);
+ SERIALIZE_SCALAR(txDesc64.cmdsts);
+ SERIALIZE_SCALAR(txDesc64.extsts);
+ SERIALIZE_SCALAR(rxDesc32.link);
+ SERIALIZE_SCALAR(rxDesc32.bufptr);
+ SERIALIZE_SCALAR(rxDesc32.cmdsts);
+ SERIALIZE_SCALAR(rxDesc32.extsts);
+ SERIALIZE_SCALAR(txDesc32.link);
+ SERIALIZE_SCALAR(txDesc32.bufptr);
+ SERIALIZE_SCALAR(txDesc32.cmdsts);
+ SERIALIZE_SCALAR(txDesc32.extsts);
+ SERIALIZE_SCALAR(extstsEnable);
+
+ /*
+ * Serialize tx state machine
+ */
+ int txState = this->txState;
+ SERIALIZE_SCALAR(txState);
+ SERIALIZE_SCALAR(txEnable);
+ SERIALIZE_SCALAR(CTDD);
+ SERIALIZE_SCALAR(txFragPtr);
+ SERIALIZE_SCALAR(txDescCnt);
+ int txDmaState = this->txDmaState;
+ SERIALIZE_SCALAR(txDmaState);
+ SERIALIZE_SCALAR(txKickTick);
+
+ /*
+ * Serialize rx state machine
+ */
+ int rxState = this->rxState;
+ SERIALIZE_SCALAR(rxState);
+ SERIALIZE_SCALAR(rxEnable);
+ SERIALIZE_SCALAR(CRDD);
+ SERIALIZE_SCALAR(rxPktBytes);
+ SERIALIZE_SCALAR(rxFragPtr);
+ SERIALIZE_SCALAR(rxDescCnt);
+ int rxDmaState = this->rxDmaState;
+ SERIALIZE_SCALAR(rxDmaState);
+ SERIALIZE_SCALAR(rxKickTick);
+
+ /*
+ * Serialize EEPROM state machine
+ */
+ int eepromState = this->eepromState;
+ SERIALIZE_SCALAR(eepromState);
+ SERIALIZE_SCALAR(eepromClk);
+ SERIALIZE_SCALAR(eepromBitsToRx);
+ SERIALIZE_SCALAR(eepromOpcode);
+ SERIALIZE_SCALAR(eepromAddress);
+ SERIALIZE_SCALAR(eepromData);
+
+ /*
+ * If there's a pending transmit, store the time so we can
+ * reschedule it later
+ */
+ Tick transmitTick = txEvent.scheduled() ? txEvent.when() - curTick() : 0;
+ SERIALIZE_SCALAR(transmitTick);
+
+ /*
+ * receive address filter settings
+ */
+ SERIALIZE_SCALAR(rxFilterEnable);
+ SERIALIZE_SCALAR(acceptBroadcast);
+ SERIALIZE_SCALAR(acceptMulticast);
+ SERIALIZE_SCALAR(acceptUnicast);
+ SERIALIZE_SCALAR(acceptPerfect);
+ SERIALIZE_SCALAR(acceptArp);
+ SERIALIZE_SCALAR(multicastHashEnable);
+
+ /*
+ * Keep track of pending interrupt status.
+ */
+ SERIALIZE_SCALAR(intrTick);
+ SERIALIZE_SCALAR(cpuPendingIntr);
+ Tick intrEventTick = 0;
+ if (intrEvent)
+ intrEventTick = intrEvent->when();
+ SERIALIZE_SCALAR(intrEventTick);
+
+}
+
+void
+NSGigE::unserialize(CheckpointIn &cp)
+{
+ // Unserialize the PciDevice base class
+ PciDevice::unserialize(cp);
+
+ UNSERIALIZE_SCALAR(regs.command);
+ UNSERIALIZE_SCALAR(regs.config);
+ UNSERIALIZE_SCALAR(regs.mear);
+ UNSERIALIZE_SCALAR(regs.ptscr);
+ UNSERIALIZE_SCALAR(regs.isr);
+ UNSERIALIZE_SCALAR(regs.imr);
+ UNSERIALIZE_SCALAR(regs.ier);
+ UNSERIALIZE_SCALAR(regs.ihr);
+ UNSERIALIZE_SCALAR(regs.txdp);
+ UNSERIALIZE_SCALAR(regs.txdp_hi);
+ UNSERIALIZE_SCALAR(regs.txcfg);
+ UNSERIALIZE_SCALAR(regs.gpior);
+ UNSERIALIZE_SCALAR(regs.rxdp);
+ UNSERIALIZE_SCALAR(regs.rxdp_hi);
+ UNSERIALIZE_SCALAR(regs.rxcfg);
+ UNSERIALIZE_SCALAR(regs.pqcr);
+ UNSERIALIZE_SCALAR(regs.wcsr);
+ UNSERIALIZE_SCALAR(regs.pcr);
+ UNSERIALIZE_SCALAR(regs.rfcr);
+ UNSERIALIZE_SCALAR(regs.rfdr);
+ UNSERIALIZE_SCALAR(regs.brar);
+ UNSERIALIZE_SCALAR(regs.brdr);
+ UNSERIALIZE_SCALAR(regs.srr);
+ UNSERIALIZE_SCALAR(regs.mibc);
+ UNSERIALIZE_SCALAR(regs.vrcr);
+ UNSERIALIZE_SCALAR(regs.vtcr);
+ UNSERIALIZE_SCALAR(regs.vdr);
+ UNSERIALIZE_SCALAR(regs.ccsr);
+ UNSERIALIZE_SCALAR(regs.tbicr);
+ UNSERIALIZE_SCALAR(regs.tbisr);
+ UNSERIALIZE_SCALAR(regs.tanar);
+ UNSERIALIZE_SCALAR(regs.tanlpar);
+ UNSERIALIZE_SCALAR(regs.taner);
+ UNSERIALIZE_SCALAR(regs.tesr);
+
+ UNSERIALIZE_ARRAY(rom.perfectMatch, ETH_ADDR_LEN);
+ UNSERIALIZE_ARRAY(rom.filterHash, FHASH_SIZE);
+
+ UNSERIALIZE_SCALAR(ioEnable);
+
+ /*
+ * unserialize the data fifos
+ */
+ rxFifo.unserialize("rxFifo", cp);
+ txFifo.unserialize("txFifo", cp);
+
+ /*
+ * unserialize the various helper variables
+ */
+ bool txPacketExists;
+ UNSERIALIZE_SCALAR(txPacketExists);
+ if (txPacketExists) {
+ txPacket = make_shared<EthPacketData>(16384);
+ txPacket->unserialize("txPacket", cp);
+ uint32_t txPktBufPtr;
+ UNSERIALIZE_SCALAR(txPktBufPtr);
+ txPacketBufPtr = (uint8_t *) txPacket->data + txPktBufPtr;
+ } else
+ txPacket = 0;
+
+ bool rxPacketExists;
+ UNSERIALIZE_SCALAR(rxPacketExists);
+ rxPacket = 0;
+ if (rxPacketExists) {
+ rxPacket = make_shared<EthPacketData>(16384);
+ rxPacket->unserialize("rxPacket", cp);
+ uint32_t rxPktBufPtr;
+ UNSERIALIZE_SCALAR(rxPktBufPtr);
+ rxPacketBufPtr = (uint8_t *) rxPacket->data + rxPktBufPtr;
+ } else
+ rxPacket = 0;
+
+ UNSERIALIZE_SCALAR(txXferLen);
+ UNSERIALIZE_SCALAR(rxXferLen);
+
+ /*
+ * Unserialize Cached Descriptors
+ */
+ UNSERIALIZE_SCALAR(rxDesc64.link);
+ UNSERIALIZE_SCALAR(rxDesc64.bufptr);
+ UNSERIALIZE_SCALAR(rxDesc64.cmdsts);
+ UNSERIALIZE_SCALAR(rxDesc64.extsts);
+ UNSERIALIZE_SCALAR(txDesc64.link);
+ UNSERIALIZE_SCALAR(txDesc64.bufptr);
+ UNSERIALIZE_SCALAR(txDesc64.cmdsts);
+ UNSERIALIZE_SCALAR(txDesc64.extsts);
+ UNSERIALIZE_SCALAR(rxDesc32.link);
+ UNSERIALIZE_SCALAR(rxDesc32.bufptr);
+ UNSERIALIZE_SCALAR(rxDesc32.cmdsts);
+ UNSERIALIZE_SCALAR(rxDesc32.extsts);
+ UNSERIALIZE_SCALAR(txDesc32.link);
+ UNSERIALIZE_SCALAR(txDesc32.bufptr);
+ UNSERIALIZE_SCALAR(txDesc32.cmdsts);
+ UNSERIALIZE_SCALAR(txDesc32.extsts);
+ UNSERIALIZE_SCALAR(extstsEnable);
+
+ /*
+ * unserialize tx state machine
+ */
+ int txState;
+ UNSERIALIZE_SCALAR(txState);
+ this->txState = (TxState) txState;
+ UNSERIALIZE_SCALAR(txEnable);
+ UNSERIALIZE_SCALAR(CTDD);
+ UNSERIALIZE_SCALAR(txFragPtr);
+ UNSERIALIZE_SCALAR(txDescCnt);
+ int txDmaState;
+ UNSERIALIZE_SCALAR(txDmaState);
+ this->txDmaState = (DmaState) txDmaState;
+ UNSERIALIZE_SCALAR(txKickTick);
+ if (txKickTick)
+ schedule(txKickEvent, txKickTick);
+
+ /*
+ * unserialize rx state machine
+ */
+ int rxState;
+ UNSERIALIZE_SCALAR(rxState);
+ this->rxState = (RxState) rxState;
+ UNSERIALIZE_SCALAR(rxEnable);
+ UNSERIALIZE_SCALAR(CRDD);
+ UNSERIALIZE_SCALAR(rxPktBytes);
+ UNSERIALIZE_SCALAR(rxFragPtr);
+ UNSERIALIZE_SCALAR(rxDescCnt);
+ int rxDmaState;
+ UNSERIALIZE_SCALAR(rxDmaState);
+ this->rxDmaState = (DmaState) rxDmaState;
+ UNSERIALIZE_SCALAR(rxKickTick);
+ if (rxKickTick)
+ schedule(rxKickEvent, rxKickTick);
+
+ /*
+ * Unserialize EEPROM state machine
+ */
+ int eepromState;
+ UNSERIALIZE_SCALAR(eepromState);
+ this->eepromState = (EEPROMState) eepromState;
+ UNSERIALIZE_SCALAR(eepromClk);
+ UNSERIALIZE_SCALAR(eepromBitsToRx);
+ UNSERIALIZE_SCALAR(eepromOpcode);
+ UNSERIALIZE_SCALAR(eepromAddress);
+ UNSERIALIZE_SCALAR(eepromData);
+
+ /*
+ * If there's a pending transmit, reschedule it now
+ */
+ Tick transmitTick;
+ UNSERIALIZE_SCALAR(transmitTick);
+ if (transmitTick)
+ schedule(txEvent, curTick() + transmitTick);
+
+ /*
+ * unserialize receive address filter settings
+ */
+ UNSERIALIZE_SCALAR(rxFilterEnable);
+ UNSERIALIZE_SCALAR(acceptBroadcast);
+ UNSERIALIZE_SCALAR(acceptMulticast);
+ UNSERIALIZE_SCALAR(acceptUnicast);
+ UNSERIALIZE_SCALAR(acceptPerfect);
+ UNSERIALIZE_SCALAR(acceptArp);
+ UNSERIALIZE_SCALAR(multicastHashEnable);
+
+ /*
+ * Keep track of pending interrupt status.
+ */
+ UNSERIALIZE_SCALAR(intrTick);
+ UNSERIALIZE_SCALAR(cpuPendingIntr);
+ Tick intrEventTick;
+ UNSERIALIZE_SCALAR(intrEventTick);
+ if (intrEventTick) {
+ intrEvent = new IntrEvent(this, true);
+ schedule(intrEvent, intrEventTick);
+ }
+}
+
+NSGigE *
+NSGigEParams::create()
+{
+ return new NSGigE(this);
+}
diff --git a/src/dev/net/ns_gige.hh b/src/dev/net/ns_gige.hh
new file mode 100644
index 000000000..096b2b69e
--- /dev/null
+++ b/src/dev/net/ns_gige.hh
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ * Lisa Hsu
+ */
+
+/** @file
+ * Device module for modelling the National Semiconductor
+ * DP83820 ethernet controller
+ */
+
+#ifndef __DEV_NET_NS_GIGE_HH__
+#define __DEV_NET_NS_GIGE_HH__
+
+#include "base/inet.hh"
+#include "dev/io_device.hh"
+#include "dev/net/etherdevice.hh"
+#include "dev/net/etherint.hh"
+#include "dev/net/etherpkt.hh"
+#include "dev/net/ns_gige_reg.h"
+#include "dev/net/pktfifo.hh"
+#include "params/NSGigE.hh"
+#include "sim/eventq.hh"
+
+// Hash filtering constants
+const uint16_t FHASH_ADDR = 0x100;
+const uint16_t FHASH_SIZE = 0x100;
+
+// EEPROM constants
+const uint8_t EEPROM_READ = 0x2;
+const uint8_t EEPROM_SIZE = 64; // Size in words of NSC93C46 EEPROM
+const uint8_t EEPROM_PMATCH2_ADDR = 0xA; // EEPROM Address of PMATCH word 2
+const uint8_t EEPROM_PMATCH1_ADDR = 0xB; // EEPROM Address of PMATCH word 1
+const uint8_t EEPROM_PMATCH0_ADDR = 0xC; // EEPROM Address of PMATCH word 0
+
+/**
+ * Ethernet device registers
+ */
+struct dp_regs {
+ uint32_t command;
+ uint32_t config;
+ uint32_t mear;
+ uint32_t ptscr;
+ uint32_t isr;
+ uint32_t imr;
+ uint32_t ier;
+ uint32_t ihr;
+ uint32_t txdp;
+ uint32_t txdp_hi;
+ uint32_t txcfg;
+ uint32_t gpior;
+ uint32_t rxdp;
+ uint32_t rxdp_hi;
+ uint32_t rxcfg;
+ uint32_t pqcr;
+ uint32_t wcsr;
+ uint32_t pcr;
+ uint32_t rfcr;
+ uint32_t rfdr;
+ uint32_t brar;
+ uint32_t brdr;
+ uint32_t srr;
+ uint32_t mibc;
+ uint32_t vrcr;
+ uint32_t vtcr;
+ uint32_t vdr;
+ uint32_t ccsr;
+ uint32_t tbicr;
+ uint32_t tbisr;
+ uint32_t tanar;
+ uint32_t tanlpar;
+ uint32_t taner;
+ uint32_t tesr;
+};
+
+struct dp_rom {
+ /**
+ * for perfect match memory.
+ * the linux driver doesn't use any other ROM
+ */
+ uint8_t perfectMatch[ETH_ADDR_LEN];
+
+ /**
+ * for hash table memory.
+ * used by the freebsd driver
+ */
+ uint8_t filterHash[FHASH_SIZE];
+};
+
+class NSGigEInt;
+class Packet;
+
+/**
+ * NS DP83820 Ethernet device model
+ */
+class NSGigE : public EtherDevBase
+{
+ public:
+ /** Transmit State Machine states */
+ enum TxState
+ {
+ txIdle,
+ txDescRefr,
+ txDescRead,
+ txFifoBlock,
+ txFragRead,
+ txDescWrite,
+ txAdvance
+ };
+
+ /** Receive State Machine States */
+ enum RxState
+ {
+ rxIdle,
+ rxDescRefr,
+ rxDescRead,
+ rxFifoBlock,
+ rxFragWrite,
+ rxDescWrite,
+ rxAdvance
+ };
+
+ enum DmaState
+ {
+ dmaIdle,
+ dmaReading,
+ dmaWriting,
+ dmaReadWaiting,
+ dmaWriteWaiting
+ };
+
+ /** EEPROM State Machine States */
+ enum EEPROMState
+ {
+ eepromStart,
+ eepromGetOpcode,
+ eepromGetAddress,
+ eepromRead
+ };
+
+ protected:
+ /** device register file */
+ dp_regs regs;
+ dp_rom rom;
+
+ /** pci settings */
+ bool ioEnable;
+#if 0
+ bool memEnable;
+ bool bmEnable;
+#endif
+
+ /*** BASIC STRUCTURES FOR TX/RX ***/
+ /* Data FIFOs */
+ PacketFifo txFifo;
+ PacketFifo rxFifo;
+
+ /** various helper vars */
+ EthPacketPtr txPacket;
+ EthPacketPtr rxPacket;
+ uint8_t *txPacketBufPtr;
+ uint8_t *rxPacketBufPtr;
+ uint32_t txXferLen;
+ uint32_t rxXferLen;
+ bool rxDmaFree;
+ bool txDmaFree;
+
+ /** DescCaches */
+ ns_desc32 txDesc32;
+ ns_desc32 rxDesc32;
+ ns_desc64 txDesc64;
+ ns_desc64 rxDesc64;
+
+ /* tx State Machine */
+ TxState txState;
+ bool txEnable;
+
+ /** Current Transmit Descriptor Done */
+ bool CTDD;
+ /** halt the tx state machine after next packet */
+ bool txHalt;
+ /** ptr to the next byte in the current fragment */
+ Addr txFragPtr;
+ /** count of bytes remaining in the current descriptor */
+ uint32_t txDescCnt;
+ DmaState txDmaState;
+
+ /** rx State Machine */
+ RxState rxState;
+ bool rxEnable;
+
+ /** Current Receive Descriptor Done */
+ bool CRDD;
+ /** num of bytes in the current packet being drained from rxDataFifo */
+ uint32_t rxPktBytes;
+ /** halt the rx state machine after current packet */
+ bool rxHalt;
+ /** ptr to the next byte in current fragment */
+ Addr rxFragPtr;
+ /** count of bytes remaining in the current descriptor */
+ uint32_t rxDescCnt;
+ DmaState rxDmaState;
+
+ bool extstsEnable;
+
+ /** EEPROM State Machine */
+ EEPROMState eepromState;
+ bool eepromClk;
+ uint8_t eepromBitsToRx;
+ uint8_t eepromOpcode;
+ uint8_t eepromAddress;
+ uint16_t eepromData;
+
+ protected:
+ Tick dmaReadDelay;
+ Tick dmaWriteDelay;
+
+ Tick dmaReadFactor;
+ Tick dmaWriteFactor;
+
+ void *rxDmaData;
+ Addr rxDmaAddr;
+ int rxDmaLen;
+ bool doRxDmaRead();
+ bool doRxDmaWrite();
+
+ void *txDmaData;
+ Addr txDmaAddr;
+ int txDmaLen;
+ bool doTxDmaRead();
+ bool doTxDmaWrite();
+
+ void rxDmaReadDone();
+ friend class EventWrapper<NSGigE, &NSGigE::rxDmaReadDone>;
+ EventWrapper<NSGigE, &NSGigE::rxDmaReadDone> rxDmaReadEvent;
+
+ void rxDmaWriteDone();
+ friend class EventWrapper<NSGigE, &NSGigE::rxDmaWriteDone>;
+ EventWrapper<NSGigE, &NSGigE::rxDmaWriteDone> rxDmaWriteEvent;
+
+ void txDmaReadDone();
+ friend class EventWrapper<NSGigE, &NSGigE::txDmaReadDone>;
+ EventWrapper<NSGigE, &NSGigE::txDmaReadDone> txDmaReadEvent;
+
+ void txDmaWriteDone();
+ friend class EventWrapper<NSGigE, &NSGigE::txDmaWriteDone>;
+ EventWrapper<NSGigE, &NSGigE::txDmaWriteDone> txDmaWriteEvent;
+
+ bool dmaDescFree;
+ bool dmaDataFree;
+
+ protected:
+ Tick txDelay;
+ Tick rxDelay;
+
+ void txReset();
+ void rxReset();
+ void regsReset();
+
+ void rxKick();
+ Tick rxKickTick;
+ typedef EventWrapper<NSGigE, &NSGigE::rxKick> RxKickEvent;
+ friend void RxKickEvent::process();
+ RxKickEvent rxKickEvent;
+
+ void txKick();
+ Tick txKickTick;
+ typedef EventWrapper<NSGigE, &NSGigE::txKick> TxKickEvent;
+ friend void TxKickEvent::process();
+ TxKickEvent txKickEvent;
+
+ void eepromKick();
+
+ /**
+ * Retransmit event
+ */
+ void transmit();
+ void txEventTransmit()
+ {
+ transmit();
+ if (txState == txFifoBlock)
+ txKick();
+ }
+ typedef EventWrapper<NSGigE, &NSGigE::txEventTransmit> TxEvent;
+ friend void TxEvent::process();
+ TxEvent txEvent;
+
+ void txDump() const;
+ void rxDump() const;
+
+ /**
+ * receive address filter
+ */
+ bool rxFilterEnable;
+ bool rxFilter(const EthPacketPtr &packet);
+ bool acceptBroadcast;
+ bool acceptMulticast;
+ bool acceptUnicast;
+ bool acceptPerfect;
+ bool acceptArp;
+ bool multicastHashEnable;
+
+ /**
+ * Interrupt management
+ */
+ void devIntrPost(uint32_t interrupts);
+ void devIntrClear(uint32_t interrupts);
+ void devIntrChangeMask();
+
+ Tick intrDelay;
+ Tick intrTick;
+ bool cpuPendingIntr;
+ void cpuIntrPost(Tick when);
+ void cpuInterrupt();
+ void cpuIntrClear();
+
+ typedef EventWrapper<NSGigE, &NSGigE::cpuInterrupt> IntrEvent;
+ friend void IntrEvent::process();
+ IntrEvent *intrEvent;
+ NSGigEInt *interface;
+
+ public:
+ typedef NSGigEParams Params;
+ const Params *params() const {
+ return dynamic_cast<const Params *>(_params);
+ }
+
+ NSGigE(Params *params);
+ ~NSGigE();
+
+ EtherInt *getEthPort(const std::string &if_name, int idx) override;
+
+ Tick writeConfig(PacketPtr pkt) override;
+
+ Tick read(PacketPtr pkt) override;
+ Tick write(PacketPtr pkt) override;
+
+ bool cpuIntrPending() const;
+ void cpuIntrAck() { cpuIntrClear(); }
+
+ bool recvPacket(EthPacketPtr packet);
+ void transferDone();
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+
+ void drainResume() override;
+};
+
+/*
+ * Ethernet Interface for an Ethernet Device
+ */
+class NSGigEInt : public EtherInt
+{
+ private:
+ NSGigE *dev;
+
+ public:
+ NSGigEInt(const std::string &name, NSGigE *d)
+ : EtherInt(name), dev(d)
+ { }
+
+ virtual bool recvPacket(EthPacketPtr pkt) { return dev->recvPacket(pkt); }
+ virtual void sendDone() { dev->transferDone(); }
+};
+
+#endif // __DEV_NET_NS_GIGE_HH__
diff --git a/src/dev/net/ns_gige_reg.h b/src/dev/net/ns_gige_reg.h
new file mode 100644
index 000000000..c37c06aed
--- /dev/null
+++ b/src/dev/net/ns_gige_reg.h
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Lisa Hsu
+ */
+
+/** @file
+ * Ethernet device register definitions for the National
+ * Semiconductor DP83820 Ethernet controller
+ */
+
+#ifndef __DEV_NS_GIGE_REG_H__
+#define __DEV_NS_GIGE_REG_H__
+
+/* Device Register Address Map */
+enum DeviceRegisterAddress {
+ CR = 0x00,
+ CFGR = 0x04,
+ MEAR = 0x08,
+ PTSCR = 0x0c,
+ ISR = 0x10,
+ IMR = 0x14,
+ IER = 0x18,
+ IHR = 0x1c,
+ TXDP = 0x20,
+ TXDP_HI = 0x24,
+ TX_CFG = 0x28,
+ GPIOR = 0x2c,
+ RXDP = 0x30,
+ RXDP_HI = 0x34,
+ RX_CFG = 0x38,
+ PQCR = 0x3c,
+ WCSR = 0x40,
+ PCR = 0x44,
+ RFCR = 0x48,
+ RFDR = 0x4c,
+ BRAR = 0x50,
+ BRDR = 0x54,
+ SRR = 0x58,
+ MIBC = 0x5c,
+ MIB_START = 0x60,
+ MIB_END = 0x88,
+ VRCR = 0xbc,
+ VTCR = 0xc0,
+ VDR = 0xc4,
+ CCSR = 0xcc,
+ TBICR = 0xe0,
+ TBISR = 0xe4,
+ TANAR = 0xe8,
+ TANLPAR = 0xec,
+ TANER = 0xf0,
+ TESR = 0xf4,
+ M5REG = 0xf8,
+ LAST = 0xf8,
+ RESERVED = 0xfc
+};
+
+/* Chip Command Register */
+enum ChipCommandRegister {
+ CR_TXE = 0x00000001,
+ CR_TXD = 0x00000002,
+ CR_RXE = 0x00000004,
+ CR_RXD = 0x00000008,
+ CR_TXR = 0x00000010,
+ CR_RXR = 0x00000020,
+ CR_SWI = 0x00000080,
+ CR_RST = 0x00000100
+};
+
+/* configuration register */
+enum ConfigurationRegisters {
+ CFGR_LNKSTS = 0x80000000,
+ CFGR_SPDSTS = 0x60000000,
+ CFGR_SPDSTS1 = 0x40000000,
+ CFGR_SPDSTS0 = 0x20000000,
+ CFGR_DUPSTS = 0x10000000,
+ CFGR_TBI_EN = 0x01000000,
+ CFGR_RESERVED = 0x0e000000,
+ CFGR_MODE_1000 = 0x00400000,
+ CFGR_AUTO_1000 = 0x00200000,
+ CFGR_PINT_CTL = 0x001c0000,
+ CFGR_PINT_DUPSTS = 0x00100000,
+ CFGR_PINT_LNKSTS = 0x00080000,
+ CFGR_PINT_SPDSTS = 0x00040000,
+ CFGR_TMRTEST = 0x00020000,
+ CFGR_MRM_DIS = 0x00010000,
+ CFGR_MWI_DIS = 0x00008000,
+ CFGR_T64ADDR = 0x00004000,
+ CFGR_PCI64_DET = 0x00002000,
+ CFGR_DATA64_EN = 0x00001000,
+ CFGR_M64ADDR = 0x00000800,
+ CFGR_PHY_RST = 0x00000400,
+ CFGR_PHY_DIS = 0x00000200,
+ CFGR_EXTSTS_EN = 0x00000100,
+ CFGR_REQALG = 0x00000080,
+ CFGR_SB = 0x00000040,
+ CFGR_POW = 0x00000020,
+ CFGR_EXD = 0x00000010,
+ CFGR_PESEL = 0x00000008,
+ CFGR_BROM_DIS = 0x00000004,
+ CFGR_EXT_125 = 0x00000002,
+ CFGR_BEM = 0x00000001
+};
+
+/* EEPROM access register */
+enum EEPROMAccessRegister {
+ MEAR_EEDI = 0x00000001,
+ MEAR_EEDO = 0x00000002,
+ MEAR_EECLK = 0x00000004,
+ MEAR_EESEL = 0x00000008,
+ MEAR_MDIO = 0x00000010,
+ MEAR_MDDIR = 0x00000020,
+ MEAR_MDC = 0x00000040,
+};
+
+/* PCI test control register */
+enum PCITestControlRegister {
+ PTSCR_EEBIST_FAIL = 0x00000001,
+ PTSCR_EEBIST_EN = 0x00000002,
+ PTSCR_EELOAD_EN = 0x00000004,
+ PTSCR_RBIST_FAIL = 0x000001b8,
+ PTSCR_RBIST_DONE = 0x00000200,
+ PTSCR_RBIST_EN = 0x00000400,
+ PTSCR_RBIST_RST = 0x00002000,
+ PTSCR_RBIST_RDONLY = 0x000003f9
+};
+
+/* interrupt status register */
+enum InterruptStatusRegister {
+ ISR_RESERVE = 0x80000000,
+ ISR_TXDESC3 = 0x40000000,
+ ISR_TXDESC2 = 0x20000000,
+ ISR_TXDESC1 = 0x10000000,
+ ISR_TXDESC0 = 0x08000000,
+ ISR_RXDESC3 = 0x04000000,
+ ISR_RXDESC2 = 0x02000000,
+ ISR_RXDESC1 = 0x01000000,
+ ISR_RXDESC0 = 0x00800000,
+ ISR_TXRCMP = 0x00400000,
+ ISR_RXRCMP = 0x00200000,
+ ISR_DPERR = 0x00100000,
+ ISR_SSERR = 0x00080000,
+ ISR_RMABT = 0x00040000,
+ ISR_RTAB = 0x00020000,
+ ISR_RXSOVR = 0x00010000,
+ ISR_HIBINT = 0x00008000,
+ ISR_PHY = 0x00004000,
+ ISR_PME = 0x00002000,
+ ISR_SWI = 0x00001000,
+ ISR_MIB = 0x00000800,
+ ISR_TXURN = 0x00000400,
+ ISR_TXIDLE = 0x00000200,
+ ISR_TXERR = 0x00000100,
+ ISR_TXDESC = 0x00000080,
+ ISR_TXOK = 0x00000040,
+ ISR_RXORN = 0x00000020,
+ ISR_RXIDLE = 0x00000010,
+ ISR_RXEARLY = 0x00000008,
+ ISR_RXERR = 0x00000004,
+ ISR_RXDESC = 0x00000002,
+ ISR_RXOK = 0x00000001,
+ ISR_ALL = 0x7FFFFFFF,
+ ISR_DELAY = (ISR_TXIDLE|ISR_TXDESC|ISR_TXOK|
+ ISR_RXIDLE|ISR_RXDESC|ISR_RXOK),
+ ISR_NODELAY = (ISR_ALL & ~ISR_DELAY),
+ ISR_IMPL = (ISR_SWI|ISR_TXIDLE|ISR_TXDESC|ISR_TXOK|ISR_RXORN|
+ ISR_RXIDLE|ISR_RXDESC|ISR_RXOK),
+ ISR_NOIMPL = (ISR_ALL & ~ISR_IMPL)
+};
+
+/* transmit configuration register */
+enum TransmitConfigurationRegister {
+ TX_CFG_CSI = 0x80000000,
+ TX_CFG_HBI = 0x40000000,
+ TX_CFG_MLB = 0x20000000,
+ TX_CFG_ATP = 0x10000000,
+ TX_CFG_ECRETRY = 0x00800000,
+ TX_CFG_BRST_DIS = 0x00080000,
+ TX_CFG_MXDMA1024 = 0x00000000,
+ TX_CFG_MXDMA512 = 0x00700000,
+ TX_CFG_MXDMA256 = 0x00600000,
+ TX_CFG_MXDMA128 = 0x00500000,
+ TX_CFG_MXDMA64 = 0x00400000,
+ TX_CFG_MXDMA32 = 0x00300000,
+ TX_CFG_MXDMA16 = 0x00200000,
+ TX_CFG_MXDMA8 = 0x00100000,
+ TX_CFG_MXDMA = 0x00700000,
+
+ TX_CFG_FLTH_MASK = 0x0000ff00,
+ TX_CFG_DRTH_MASK = 0x000000ff
+};
+
+/*general purpose I/O control register */
+enum GeneralPurposeIOControlRegister {
+ GPIOR_UNUSED = 0xffff8000,
+ GPIOR_GP5_IN = 0x00004000,
+ GPIOR_GP4_IN = 0x00002000,
+ GPIOR_GP3_IN = 0x00001000,
+ GPIOR_GP2_IN = 0x00000800,
+ GPIOR_GP1_IN = 0x00000400,
+ GPIOR_GP5_OE = 0x00000200,
+ GPIOR_GP4_OE = 0x00000100,
+ GPIOR_GP3_OE = 0x00000080,
+ GPIOR_GP2_OE = 0x00000040,
+ GPIOR_GP1_OE = 0x00000020,
+ GPIOR_GP5_OUT = 0x00000010,
+ GPIOR_GP4_OUT = 0x00000008,
+ GPIOR_GP3_OUT = 0x00000004,
+ GPIOR_GP2_OUT = 0x00000002,
+ GPIOR_GP1_OUT = 0x00000001
+};
+
+/* receive configuration register */
+enum ReceiveConfigurationRegister {
+ RX_CFG_AEP = 0x80000000,
+ RX_CFG_ARP = 0x40000000,
+ RX_CFG_STRIPCRC = 0x20000000,
+ RX_CFG_RX_FD = 0x10000000,
+ RX_CFG_ALP = 0x08000000,
+ RX_CFG_AIRL = 0x04000000,
+ RX_CFG_MXDMA512 = 0x00700000,
+ RX_CFG_MXDMA = 0x00700000,
+ RX_CFG_DRTH = 0x0000003e,
+ RX_CFG_DRTH0 = 0x00000002
+};
+
+/* pause control status register */
+enum PauseControlStatusRegister {
+ PCR_PSEN = (1 << 31),
+ PCR_PS_MCAST = (1 << 30),
+ PCR_PS_DA = (1 << 29),
+ PCR_STHI_8 = (3 << 23),
+ PCR_STLO_4 = (1 << 23),
+ PCR_FFHI_8K = (3 << 21),
+ PCR_FFLO_4K = (1 << 21),
+ PCR_PAUSE_CNT = 0xFFFE
+};
+
+/*receive filter/match control register */
+enum ReceiveFilterMatchControlRegister {
+ RFCR_RFEN = 0x80000000,
+ RFCR_AAB = 0x40000000,
+ RFCR_AAM = 0x20000000,
+ RFCR_AAU = 0x10000000,
+ RFCR_APM = 0x08000000,
+ RFCR_APAT = 0x07800000,
+ RFCR_APAT3 = 0x04000000,
+ RFCR_APAT2 = 0x02000000,
+ RFCR_APAT1 = 0x01000000,
+ RFCR_APAT0 = 0x00800000,
+ RFCR_AARP = 0x00400000,
+ RFCR_MHEN = 0x00200000,
+ RFCR_UHEN = 0x00100000,
+ RFCR_ULM = 0x00080000,
+ RFCR_RFADDR = 0x000003ff
+};
+
+/* receive filter/match data register */
+enum ReceiveFilterMatchDataRegister {
+ RFDR_BMASK = 0x00030000,
+ RFDR_RFDATA0 = 0x000000ff,
+ RFDR_RFDATA1 = 0x0000ff00
+};
+
+/* management information base control register */
+enum ManagementInformationBaseControlRegister {
+ MIBC_MIBS = 0x00000008,
+ MIBC_ACLR = 0x00000004,
+ MIBC_FRZ = 0x00000002,
+ MIBC_WRN = 0x00000001
+};
+
+/* VLAN/IP receive control register */
+enum VLANIPReceiveControlRegister {
+ VRCR_RUDPE = 0x00000080,
+ VRCR_RTCPE = 0x00000040,
+ VRCR_RIPE = 0x00000020,
+ VRCR_IPEN = 0x00000010,
+ VRCR_DUTF = 0x00000008,
+ VRCR_DVTF = 0x00000004,
+ VRCR_VTREN = 0x00000002,
+ VRCR_VTDEN = 0x00000001
+};
+
+/* VLAN/IP transmit control register */
+enum VLANIPTransmitControlRegister {
+ VTCR_PPCHK = 0x00000008,
+ VTCR_GCHK = 0x00000004,
+ VTCR_VPPTI = 0x00000002,
+ VTCR_VGTI = 0x00000001
+};
+
+/* Clockrun Control/Status Register */
+enum ClockrunControlStatusRegister {
+ CCSR_CLKRUN_EN = 0x00000001
+};
+
+/* TBI control register */
+enum TBIControlRegister {
+ TBICR_MR_LOOPBACK = 0x00004000,
+ TBICR_MR_AN_ENABLE = 0x00001000,
+ TBICR_MR_RESTART_AN = 0x00000200
+};
+
+/* TBI status register */
+enum TBIStatusRegister {
+ TBISR_MR_LINK_STATUS = 0x00000020,
+ TBISR_MR_AN_COMPLETE = 0x00000004
+};
+
+/* TBI auto-negotiation advertisement register */
+enum TBIAutoNegotiationAdvertisementRegister {
+ TANAR_NP = 0x00008000,
+ TANAR_RF2 = 0x00002000,
+ TANAR_RF1 = 0x00001000,
+ TANAR_PS2 = 0x00000100,
+ TANAR_PS1 = 0x00000080,
+ TANAR_HALF_DUP = 0x00000040,
+ TANAR_FULL_DUP = 0x00000020,
+ TANAR_UNUSED = 0x00000E1F
+};
+
+/* M5 control register */
+enum M5ControlRegister {
+ M5REG_RESERVED = 0xfffffffc,
+ M5REG_RSS = 0x00000004,
+ M5REG_RX_THREAD = 0x00000002,
+ M5REG_TX_THREAD = 0x00000001
+};
+
+struct ns_desc32 {
+ uint32_t link; /* link field to next descriptor in linked list */
+ uint32_t bufptr; /* pointer to the first fragment or buffer */
+ uint32_t cmdsts; /* command/status field */
+ uint32_t extsts; /* extended status field for VLAN and IP info */
+};
+
+struct ns_desc64 {
+ uint64_t link; /* link field to next descriptor in linked list */
+ uint64_t bufptr; /* pointer to the first fragment or buffer */
+ uint32_t cmdsts; /* command/status field */
+ uint32_t extsts; /* extended status field for VLAN and IP info */
+};
+
+/* cmdsts flags for descriptors */
+enum CMDSTSFlatsForDescriptors {
+ CMDSTS_OWN = 0x80000000,
+ CMDSTS_MORE = 0x40000000,
+ CMDSTS_INTR = 0x20000000,
+ CMDSTS_ERR = 0x10000000,
+ CMDSTS_OK = 0x08000000,
+ CMDSTS_LEN_MASK = 0x0000ffff,
+
+ CMDSTS_DEST_MASK = 0x01800000,
+ CMDSTS_DEST_SELF = 0x00800000,
+ CMDSTS_DEST_MULTI = 0x01000000
+};
+
+/* extended flags for descriptors */
+enum ExtendedFlagsForDescriptors {
+ EXTSTS_UDPERR = 0x00400000,
+ EXTSTS_UDPPKT = 0x00200000,
+ EXTSTS_TCPERR = 0x00100000,
+ EXTSTS_TCPPKT = 0x00080000,
+ EXTSTS_IPERR = 0x00040000,
+ EXTSTS_IPPKT = 0x00020000
+};
+
+/* speed status */
+static inline int
+SPDSTS_POLARITY(int lnksts)
+{
+ return (CFGR_SPDSTS1 | CFGR_SPDSTS0 | CFGR_DUPSTS |
+ (lnksts ? CFGR_LNKSTS : 0));
+}
+
+#endif /* __DEV_NS_GIGE_REG_H__ */
diff --git a/src/dev/net/pktfifo.cc b/src/dev/net/pktfifo.cc
new file mode 100644
index 000000000..af4dbf412
--- /dev/null
+++ b/src/dev/net/pktfifo.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+#include "dev/net/pktfifo.hh"
+
+#include "base/misc.hh"
+
+using namespace std;
+
+bool
+PacketFifo::copyout(void *dest, unsigned offset, unsigned len)
+{
+ char *data = (char *)dest;
+ if (offset + len >= size())
+ return false;
+
+ iterator i = fifo.begin();
+ iterator end = fifo.end();
+ while (len > 0) {
+ EthPacketPtr &pkt = i->packet;
+ while (offset >= pkt->length) {
+ offset -= pkt->length;
+ ++i;
+ }
+
+ if (i == end)
+ panic("invalid fifo");
+
+ unsigned size = min(pkt->length - offset, len);
+ memcpy(data, pkt->data, size);
+ offset = 0;
+ len -= size;
+ data += size;
+ ++i;
+ }
+
+ return true;
+}
+
+
+void
+PacketFifoEntry::serialize(const string &base, CheckpointOut &cp) const
+{
+ packet->serialize(base + ".packet", cp);
+ paramOut(cp, base + ".slack", slack);
+ paramOut(cp, base + ".number", number);
+ paramOut(cp, base + ".priv", priv);
+}
+
+void
+PacketFifoEntry::unserialize(const string &base, CheckpointIn &cp)
+{
+ packet = make_shared<EthPacketData>(16384);
+ packet->unserialize(base + ".packet", cp);
+ paramIn(cp, base + ".slack", slack);
+ paramIn(cp, base + ".number", number);
+ paramIn(cp, base + ".priv", priv);
+}
+
+void
+PacketFifo::serialize(const string &base, CheckpointOut &cp) const
+{
+ paramOut(cp, base + ".size", _size);
+ paramOut(cp, base + ".maxsize", _maxsize);
+ paramOut(cp, base + ".reserved", _reserved);
+ paramOut(cp, base + ".packets", fifo.size());
+
+ int i = 0;
+ for (const auto &entry : fifo)
+ entry.serialize(csprintf("%s.entry%d", base, i++), cp);
+}
+
+void
+PacketFifo::unserialize(const string &base, CheckpointIn &cp)
+{
+ paramIn(cp, base + ".size", _size);
+// paramIn(cp, base + ".maxsize", _maxsize);
+ paramIn(cp, base + ".reserved", _reserved);
+ int fifosize;
+ paramIn(cp, base + ".packets", fifosize);
+
+ fifo.clear();
+
+ for (int i = 0; i < fifosize; ++i) {
+ PacketFifoEntry entry;
+ entry.unserialize(csprintf("%s.entry%d", base, i), cp);
+ fifo.push_back(entry);
+ }
+}
diff --git a/src/dev/net/pktfifo.hh b/src/dev/net/pktfifo.hh
new file mode 100644
index 000000000..5ef75423c
--- /dev/null
+++ b/src/dev/net/pktfifo.hh
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+#ifndef __DEV_NET_PKTFIFO_HH__
+#define __DEV_NET_PKTFIFO_HH__
+
+#include <iosfwd>
+#include <list>
+#include <string>
+
+#include "base/misc.hh"
+#include "dev/net/etherpkt.hh"
+#include "sim/serialize.hh"
+
+class Checkpoint;
+
+struct PacketFifoEntry
+{
+ EthPacketPtr packet;
+ uint64_t number;
+ unsigned slack;
+ int priv;
+
+ PacketFifoEntry()
+ {
+ clear();
+ }
+
+ PacketFifoEntry(const PacketFifoEntry &s)
+ : packet(s.packet), number(s.number), slack(s.slack), priv(s.priv)
+ {
+ }
+
+ PacketFifoEntry(EthPacketPtr p, uint64_t n)
+ : packet(p), number(n), slack(0), priv(-1)
+ {
+ }
+
+ void clear()
+ {
+ packet = NULL;
+ number = 0;
+ slack = 0;
+ priv = -1;
+ }
+
+ void serialize(const std::string &base, CheckpointOut &cp) const;
+ void unserialize(const std::string &base, CheckpointIn &cp);
+};
+
+class PacketFifo
+{
+ public:
+
+ typedef std::list<PacketFifoEntry> fifo_list;
+ typedef fifo_list::iterator iterator;
+ typedef fifo_list::const_iterator const_iterator;
+
+ protected:
+ std::list<PacketFifoEntry> fifo;
+ uint64_t _counter;
+ unsigned _maxsize;
+ unsigned _size;
+ unsigned _reserved;
+
+ public:
+ explicit PacketFifo(int max)
+ : _counter(0), _maxsize(max), _size(0), _reserved(0) {}
+ virtual ~PacketFifo() {}
+
+ unsigned packets() const { return fifo.size(); }
+ unsigned maxsize() const { return _maxsize; }
+ unsigned size() const { return _size; }
+ unsigned reserved() const { return _reserved; }
+ unsigned avail() const { return _maxsize - _size - _reserved; }
+ bool empty() const { return size() <= 0; }
+ bool full() const { return avail() <= 0; }
+
+ unsigned
+ reserve(unsigned len = 0)
+ {
+ _reserved += len;
+ assert(avail() >= 0);
+ return _reserved;
+ }
+
+ iterator begin() { return fifo.begin(); }
+ iterator end() { return fifo.end(); }
+
+ const_iterator begin() const { return fifo.begin(); }
+ const_iterator end() const { return fifo.end(); }
+
+ EthPacketPtr front() { return fifo.begin()->packet; }
+
+ bool push(EthPacketPtr ptr)
+ {
+ assert(ptr->length);
+ assert(_reserved <= ptr->length);
+ if (avail() < ptr->length - _reserved)
+ return false;
+
+ _size += ptr->length;
+
+ PacketFifoEntry entry;
+ entry.packet = ptr;
+ entry.number = _counter++;
+ fifo.push_back(entry);
+ _reserved = 0;
+ return true;
+ }
+
+ void pop()
+ {
+ if (empty())
+ return;
+
+ iterator entry = fifo.begin();
+ _size -= entry->packet->length;
+ _size -= entry->slack;
+ entry->packet = NULL;
+ fifo.pop_front();
+ }
+
+ void clear()
+ {
+ for (iterator i = begin(); i != end(); ++i)
+ i->clear();
+ fifo.clear();
+ _size = 0;
+ _reserved = 0;
+ }
+
+ void remove(iterator i)
+ {
+ if (i != fifo.begin()) {
+ iterator prev = i;
+ --prev;
+ assert(prev != fifo.end());
+ prev->slack += i->packet->length;
+ prev->slack += i->slack;
+ } else {
+ _size -= i->packet->length;
+ _size -= i->slack;
+ }
+
+ i->clear();
+ fifo.erase(i);
+ }
+
+ bool copyout(void *dest, unsigned offset, unsigned len);
+
+ int countPacketsBefore(const_iterator i) const
+ {
+ if (i == fifo.end())
+ return 0;
+ return i->number - fifo.begin()->number;
+ }
+
+ int countPacketsAfter(const_iterator i) const
+ {
+ auto end = fifo.end();
+ if (i == end)
+ return 0;
+ return (--end)->number - i->number;
+ }
+
+ void check() const
+ {
+ unsigned total = 0;
+ for (auto i = begin(); i != end(); ++i)
+ total += i->packet->length + i->slack;
+
+ if (total != _size)
+ panic("total (%d) is not == to size (%d)\n", total, _size);
+ }
+
+/**
+ * Serialization stuff
+ */
+ public:
+ void serialize(const std::string &base, CheckpointOut &cp) const;
+ void unserialize(const std::string &base, CheckpointIn &cp);
+};
+
+#endif // __DEV_NET_PKTFIFO_HH__
diff --git a/src/dev/net/sinic.cc b/src/dev/net/sinic.cc
new file mode 100644
index 000000000..d0adb1016
--- /dev/null
+++ b/src/dev/net/sinic.cc
@@ -0,0 +1,1563 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+#include "dev/net/sinic.hh"
+
+#include <deque>
+#include <limits>
+#include <string>
+
+#ifdef SINIC_VTOPHYS
+#include "arch/vtophys.hh"
+
+#endif
+#include "base/compiler.hh"
+#include "base/debug.hh"
+#include "base/inet.hh"
+#include "base/types.hh"
+#include "config/the_isa.hh"
+#include "debug/EthernetAll.hh"
+#include "dev/net/etherlink.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+#include "sim/eventq.hh"
+#include "sim/stats.hh"
+
+using namespace std;
+using namespace Net;
+using namespace TheISA;
+
+namespace Sinic {
+
+const char *RxStateStrings[] =
+{
+ "rxIdle",
+ "rxFifoBlock",
+ "rxBeginCopy",
+ "rxCopy",
+ "rxCopyDone"
+};
+
+const char *TxStateStrings[] =
+{
+ "txIdle",
+ "txFifoBlock",
+ "txBeginCopy",
+ "txCopy",
+ "txCopyDone"
+};
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// Sinic PCI Device
+//
+Base::Base(const Params *p)
+ : EtherDevBase(p), rxEnable(false), txEnable(false),
+ intrDelay(p->intr_delay), intrTick(0), cpuIntrEnable(false),
+ cpuPendingIntr(false), intrEvent(0), interface(NULL)
+{
+}
+
+Device::Device(const Params *p)
+ : Base(p), rxUnique(0), txUnique(0),
+ virtualRegs(p->virtual_count < 1 ? 1 : p->virtual_count),
+ rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size),
+ rxKickTick(0), txKickTick(0),
+ txEvent(this), rxDmaEvent(this), txDmaEvent(this),
+ dmaReadDelay(p->dma_read_delay), dmaReadFactor(p->dma_read_factor),
+ dmaWriteDelay(p->dma_write_delay), dmaWriteFactor(p->dma_write_factor)
+{
+ interface = new Interface(name() + ".int0", this);
+ reset();
+
+}
+
+Device::~Device()
+{}
+
+void
+Device::regStats()
+{
+ Base::regStats();
+
+ _maxVnicDistance = 0;
+
+ maxVnicDistance
+ .name(name() + ".maxVnicDistance")
+ .desc("maximum vnic distance")
+ ;
+
+ totalVnicDistance
+ .name(name() + ".totalVnicDistance")
+ .desc("total vnic distance")
+ ;
+ numVnicDistance
+ .name(name() + ".numVnicDistance")
+ .desc("number of vnic distance measurements")
+ ;
+
+ avgVnicDistance
+ .name(name() + ".avgVnicDistance")
+ .desc("average vnic distance")
+ ;
+
+ avgVnicDistance = totalVnicDistance / numVnicDistance;
+}
+
+void
+Device::resetStats()
+{
+ Base::resetStats();
+
+ _maxVnicDistance = 0;
+}
+
+EtherInt*
+Device::getEthPort(const std::string &if_name, int idx)
+{
+ if (if_name == "interface") {
+ if (interface->getPeer())
+ panic("interface already connected to\n");
+
+ return interface;
+ }
+ return NULL;
+}
+
+
+void
+Device::prepareIO(ContextID cpu, int index)
+{
+ int size = virtualRegs.size();
+ if (index > size)
+ panic("Trying to access a vnic that doesn't exist %d > %d\n",
+ index, size);
+}
+
+//add stats for head of line blocking
+//add stats for average fifo length
+//add stats for average number of vnics busy
+
+void
+Device::prepareRead(ContextID cpu, int index)
+{
+ using namespace Regs;
+ prepareIO(cpu, index);
+
+ VirtualReg &vnic = virtualRegs[index];
+
+ // update rx registers
+ uint64_t rxdone = vnic.RxDone;
+ rxdone = set_RxDone_Packets(rxdone, rxFifo.countPacketsAfter(rxFifoPtr));
+ rxdone = set_RxDone_Empty(rxdone, rxFifo.empty());
+ rxdone = set_RxDone_High(rxdone, rxFifo.size() > regs.RxFifoHigh);
+ rxdone = set_RxDone_NotHigh(rxdone, rxLow);
+ regs.RxData = vnic.RxData;
+ regs.RxDone = rxdone;
+ regs.RxWait = rxdone;
+
+ // update tx regsiters
+ uint64_t txdone = vnic.TxDone;
+ txdone = set_TxDone_Packets(txdone, txFifo.packets());
+ txdone = set_TxDone_Full(txdone, txFifo.avail() < regs.TxMaxCopy);
+ txdone = set_TxDone_Low(txdone, txFifo.size() < regs.TxFifoLow);
+ regs.TxData = vnic.TxData;
+ regs.TxDone = txdone;
+ regs.TxWait = txdone;
+
+ int head = 0xffff;
+
+ if (!rxFifo.empty()) {
+ int vnic = rxFifo.begin()->priv;
+ if (vnic != -1 && virtualRegs[vnic].rxPacketOffset > 0)
+ head = vnic;
+ }
+
+ regs.RxStatus = set_RxStatus_Head(regs.RxStatus, head);
+ regs.RxStatus = set_RxStatus_Busy(regs.RxStatus, rxBusyCount);
+ regs.RxStatus = set_RxStatus_Mapped(regs.RxStatus, rxMappedCount);
+ regs.RxStatus = set_RxStatus_Dirty(regs.RxStatus, rxDirtyCount);
+}
+
+void
+Device::prepareWrite(ContextID cpu, int index)
+{
+ prepareIO(cpu, index);
+}
+
+/**
+ * I/O read of device register
+ */
+Tick
+Device::read(PacketPtr pkt)
+{
+ assert(config.command & PCI_CMD_MSE);
+ assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]);
+
+ ContextID cpu = pkt->req->contextId();
+ Addr daddr = pkt->getAddr() - BARAddrs[0];
+ Addr index = daddr >> Regs::VirtualShift;
+ Addr raddr = daddr & Regs::VirtualMask;
+
+ if (!regValid(raddr))
+ panic("invalid register: cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+ cpu, index, daddr, pkt->getAddr(), pkt->getSize());
+
+ const Regs::Info &info = regInfo(raddr);
+ if (!info.read)
+ panic("read %s (write only): "
+ "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+ info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize());
+
+ panic("read %s (invalid size): "
+ "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+ info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize());
+
+ prepareRead(cpu, index);
+
+ uint64_t value M5_VAR_USED = 0;
+ if (pkt->getSize() == 4) {
+ uint32_t reg = regData32(raddr);
+ pkt->set(reg);
+ value = reg;
+ }
+
+ if (pkt->getSize() == 8) {
+ uint64_t reg = regData64(raddr);
+ pkt->set(reg);
+ value = reg;
+ }
+
+ DPRINTF(EthernetPIO,
+ "read %s: cpu=%d vnic=%d da=%#x pa=%#x size=%d val=%#x\n",
+ info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize(), value);
+
+ // reading the interrupt status register has the side effect of
+ // clearing it
+ if (raddr == Regs::IntrStatus)
+ devIntrClear();
+
+ return pioDelay;
+}
+
+/**
+ * IPR read of device register
+
+ Fault
+Device::iprRead(Addr daddr, ContextID cpu, uint64_t &result)
+{
+ if (!regValid(daddr))
+ panic("invalid address: da=%#x", daddr);
+
+ const Regs::Info &info = regInfo(daddr);
+ if (!info.read)
+ panic("reading %s (write only): cpu=%d da=%#x", info.name, cpu, daddr);
+
+ DPRINTF(EthernetPIO, "IPR read %s: cpu=%d da=%#x\n",
+ info.name, cpu, daddr);
+
+ prepareRead(cpu, 0);
+
+ if (info.size == 4)
+ result = regData32(daddr);
+
+ if (info.size == 8)
+ result = regData64(daddr);
+
+ DPRINTF(EthernetPIO, "IPR read %s: cpu=%s da=%#x val=%#x\n",
+ info.name, cpu, result);
+
+ return NoFault;
+}
+*/
+/**
+ * I/O write of device register
+ */
+Tick
+Device::write(PacketPtr pkt)
+{
+ assert(config.command & PCI_CMD_MSE);
+ assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]);
+
+ ContextID cpu = pkt->req->contextId();
+ Addr daddr = pkt->getAddr() - BARAddrs[0];
+ Addr index = daddr >> Regs::VirtualShift;
+ Addr raddr = daddr & Regs::VirtualMask;
+
+ if (!regValid(raddr))
+ panic("invalid register: cpu=%d, da=%#x pa=%#x size=%d",
+ cpu, daddr, pkt->getAddr(), pkt->getSize());
+
+ const Regs::Info &info = regInfo(raddr);
+ if (!info.write)
+ panic("write %s (read only): "
+ "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+ info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize());
+
+ if (pkt->getSize() != info.size)
+ panic("write %s (invalid size): "
+ "cpu=%d vnic=%d da=%#x pa=%#x size=%d",
+ info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize());
+
+ VirtualReg &vnic = virtualRegs[index];
+
+ DPRINTF(EthernetPIO,
+ "write %s vnic %d: cpu=%d val=%#x da=%#x pa=%#x size=%d\n",
+ info.name, index, cpu, info.size == 4 ? pkt->get<uint32_t>() :
+ pkt->get<uint64_t>(), daddr, pkt->getAddr(), pkt->getSize());
+
+ prepareWrite(cpu, index);
+
+ switch (raddr) {
+ case Regs::Config:
+ changeConfig(pkt->get<uint32_t>());
+ break;
+
+ case Regs::Command:
+ command(pkt->get<uint32_t>());
+ break;
+
+ case Regs::IntrStatus:
+ devIntrClear(regs.IntrStatus & pkt->get<uint32_t>());
+ break;
+
+ case Regs::IntrMask:
+ devIntrChangeMask(pkt->get<uint32_t>());
+ break;
+
+ case Regs::RxData:
+ if (Regs::get_RxDone_Busy(vnic.RxDone))
+ panic("receive machine busy with another request! rxState=%s",
+ RxStateStrings[rxState]);
+
+ vnic.rxUnique = rxUnique++;
+ vnic.RxDone = Regs::RxDone_Busy;
+ vnic.RxData = pkt->get<uint64_t>();
+ rxBusyCount++;
+
+ if (Regs::get_RxData_Vaddr(pkt->get<uint64_t>())) {
+ panic("vtophys not implemented in newmem");
+#ifdef SINIC_VTOPHYS
+ Addr vaddr = Regs::get_RxData_Addr(reg64);
+ Addr paddr = vtophys(req->xc, vaddr);
+ DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d): "
+ "vaddr=%#x, paddr=%#x\n",
+ index, vnic.rxUnique, vaddr, paddr);
+
+ vnic.RxData = Regs::set_RxData_Addr(vnic.RxData, paddr);
+#endif
+ } else {
+ DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d)\n",
+ index, vnic.rxUnique);
+ }
+
+ if (vnic.rxIndex == rxFifo.end()) {
+ DPRINTF(EthernetPIO, "request new packet...appending to rxList\n");
+ rxList.push_back(index);
+ } else {
+ DPRINTF(EthernetPIO, "packet exists...appending to rxBusy\n");
+ rxBusy.push_back(index);
+ }
+
+ if (rxEnable && (rxState == rxIdle || rxState == rxFifoBlock)) {
+ rxState = rxFifoBlock;
+ rxKick();
+ }
+ break;
+
+ case Regs::TxData:
+ if (Regs::get_TxDone_Busy(vnic.TxDone))
+ panic("transmit machine busy with another request! txState=%s",
+ TxStateStrings[txState]);
+
+ vnic.txUnique = txUnique++;
+ vnic.TxDone = Regs::TxDone_Busy;
+
+ if (Regs::get_TxData_Vaddr(pkt->get<uint64_t>())) {
+ panic("vtophys won't work here in newmem.\n");
+#ifdef SINIC_VTOPHYS
+ Addr vaddr = Regs::get_TxData_Addr(reg64);
+ Addr paddr = vtophys(req->xc, vaddr);
+ DPRINTF(EthernetPIO, "write TxData vnic %d (txunique %d): "
+ "vaddr=%#x, paddr=%#x\n",
+ index, vnic.txUnique, vaddr, paddr);
+
+ vnic.TxData = Regs::set_TxData_Addr(vnic.TxData, paddr);
+#endif
+ } else {
+ DPRINTF(EthernetPIO, "write TxData vnic %d (txunique %d)\n",
+ index, vnic.txUnique);
+ }
+
+ if (txList.empty() || txList.front() != index)
+ txList.push_back(index);
+ if (txEnable && txState == txIdle && txList.front() == index) {
+ txState = txFifoBlock;
+ txKick();
+ }
+ break;
+ }
+
+ return pioDelay;
+}
+
+void
+Device::devIntrPost(uint32_t interrupts)
+{
+ if ((interrupts & Regs::Intr_Res))
+ panic("Cannot set a reserved interrupt");
+
+ regs.IntrStatus |= interrupts;
+
+ DPRINTF(EthernetIntr,
+ "interrupt written to intStatus: intr=%#x status=%#x mask=%#x\n",
+ interrupts, regs.IntrStatus, regs.IntrMask);
+
+ interrupts = regs.IntrStatus & regs.IntrMask;
+
+ // Intr_RxHigh is special, we only signal it if we've emptied the fifo
+ // and then filled it above the high watermark
+ if (rxEmpty)
+ rxEmpty = false;
+ else
+ interrupts &= ~Regs::Intr_RxHigh;
+
+ // Intr_TxLow is special, we only signal it if we've filled up the fifo
+ // and then dropped below the low watermark
+ if (txFull)
+ txFull = false;
+ else
+ interrupts &= ~Regs::Intr_TxLow;
+
+ if (interrupts) {
+ Tick when = curTick();
+ if ((interrupts & Regs::Intr_NoDelay) == 0)
+ when += intrDelay;
+ cpuIntrPost(when);
+ }
+}
+
+void
+Device::devIntrClear(uint32_t interrupts)
+{
+ if ((interrupts & Regs::Intr_Res))
+ panic("Cannot clear a reserved interrupt");
+
+ regs.IntrStatus &= ~interrupts;
+
+ DPRINTF(EthernetIntr,
+ "interrupt cleared from intStatus: intr=%x status=%x mask=%x\n",
+ interrupts, regs.IntrStatus, regs.IntrMask);
+
+ if (!(regs.IntrStatus & regs.IntrMask))
+ cpuIntrClear();
+}
+
+void
+Device::devIntrChangeMask(uint32_t newmask)
+{
+ if (regs.IntrMask == newmask)
+ return;
+
+ regs.IntrMask = newmask;
+
+ DPRINTF(EthernetIntr,
+ "interrupt mask changed: intStatus=%x intMask=%x masked=%x\n",
+ regs.IntrStatus, regs.IntrMask, regs.IntrStatus & regs.IntrMask);
+
+ if (regs.IntrStatus & regs.IntrMask)
+ cpuIntrPost(curTick());
+ else
+ cpuIntrClear();
+}
+
+void
+Base::cpuIntrPost(Tick when)
+{
+ // If the interrupt you want to post is later than an interrupt
+ // already scheduled, just let it post in the coming one and don't
+ // schedule another.
+ // HOWEVER, must be sure that the scheduled intrTick is in the
+ // future (this was formerly the source of a bug)
+ /**
+ * @todo this warning should be removed and the intrTick code should
+ * be fixed.
+ */
+ assert(when >= curTick());
+ assert(intrTick >= curTick() || intrTick == 0);
+ if (!cpuIntrEnable) {
+ DPRINTF(EthernetIntr, "interrupts not enabled.\n",
+ intrTick);
+ return;
+ }
+
+ if (when > intrTick && intrTick != 0) {
+ DPRINTF(EthernetIntr, "don't need to schedule event...intrTick=%d\n",
+ intrTick);
+ return;
+ }
+
+ intrTick = when;
+ if (intrTick < curTick()) {
+ Debug::breakpoint();
+ intrTick = curTick();
+ }
+
+ DPRINTF(EthernetIntr, "going to schedule an interrupt for intrTick=%d\n",
+ intrTick);
+
+ if (intrEvent)
+ intrEvent->squash();
+ intrEvent = new IntrEvent(this, true);
+ schedule(intrEvent, intrTick);
+}
+
+void
+Base::cpuInterrupt()
+{
+ assert(intrTick == curTick());
+
+ // Whether or not there's a pending interrupt, we don't care about
+ // it anymore
+ intrEvent = 0;
+ intrTick = 0;
+
+ // Don't send an interrupt if there's already one
+ if (cpuPendingIntr) {
+ DPRINTF(EthernetIntr,
+ "would send an interrupt now, but there's already pending\n");
+ } else {
+ // Send interrupt
+ cpuPendingIntr = true;
+
+ DPRINTF(EthernetIntr, "posting interrupt\n");
+ intrPost();
+ }
+}
+
+void
+Base::cpuIntrClear()
+{
+ if (!cpuPendingIntr)
+ return;
+
+ if (intrEvent) {
+ intrEvent->squash();
+ intrEvent = 0;
+ }
+
+ intrTick = 0;
+
+ cpuPendingIntr = false;
+
+ DPRINTF(EthernetIntr, "clearing cchip interrupt\n");
+ intrClear();
+}
+
+bool
+Base::cpuIntrPending() const
+{ return cpuPendingIntr; }
+
+void
+Device::changeConfig(uint32_t newconf)
+{
+ uint32_t changed = regs.Config ^ newconf;
+ if (!changed)
+ return;
+
+ regs.Config = newconf;
+
+ if ((changed & Regs::Config_IntEn)) {
+ cpuIntrEnable = regs.Config & Regs::Config_IntEn;
+ if (cpuIntrEnable) {
+ if (regs.IntrStatus & regs.IntrMask)
+ cpuIntrPost(curTick());
+ } else {
+ cpuIntrClear();
+ }
+ }
+
+ if ((changed & Regs::Config_TxEn)) {
+ txEnable = regs.Config & Regs::Config_TxEn;
+ if (txEnable)
+ txKick();
+ }
+
+ if ((changed & Regs::Config_RxEn)) {
+ rxEnable = regs.Config & Regs::Config_RxEn;
+ if (rxEnable)
+ rxKick();
+ }
+}
+
+void
+Device::command(uint32_t command)
+{
+ if (command & Regs::Command_Intr)
+ devIntrPost(Regs::Intr_Soft);
+
+ if (command & Regs::Command_Reset)
+ reset();
+}
+
+void
+Device::reset()
+{
+ using namespace Regs;
+
+ memset(&regs, 0, sizeof(regs));
+
+ regs.Config = 0;
+ if (params()->rx_thread)
+ regs.Config |= Config_RxThread;
+ if (params()->tx_thread)
+ regs.Config |= Config_TxThread;
+ if (params()->rss)
+ regs.Config |= Config_RSS;
+ if (params()->zero_copy)
+ regs.Config |= Config_ZeroCopy;
+ if (params()->delay_copy)
+ regs.Config |= Config_DelayCopy;
+ if (params()->virtual_addr)
+ regs.Config |= Config_Vaddr;
+
+ if (params()->delay_copy && params()->zero_copy)
+ panic("Can't delay copy and zero copy");
+
+ regs.IntrMask = Intr_Soft | Intr_RxHigh | Intr_RxPacket | Intr_TxLow;
+ regs.RxMaxCopy = params()->rx_max_copy;
+ regs.TxMaxCopy = params()->tx_max_copy;
+ regs.ZeroCopySize = params()->zero_copy_size;
+ regs.ZeroCopyMark = params()->zero_copy_threshold;
+ regs.VirtualCount = params()->virtual_count;
+ regs.RxMaxIntr = params()->rx_max_intr;
+ regs.RxFifoSize = params()->rx_fifo_size;
+ regs.TxFifoSize = params()->tx_fifo_size;
+ regs.RxFifoLow = params()->rx_fifo_low_mark;
+ regs.TxFifoLow = params()->tx_fifo_threshold;
+ regs.RxFifoHigh = params()->rx_fifo_threshold;
+ regs.TxFifoHigh = params()->tx_fifo_high_mark;
+ regs.HwAddr = params()->hardware_address;
+
+ if (regs.RxMaxCopy < regs.ZeroCopyMark)
+ panic("Must be able to copy at least as many bytes as the threshold");
+
+ if (regs.ZeroCopySize >= regs.ZeroCopyMark)
+ panic("The number of bytes to copy must be less than the threshold");
+
+ rxList.clear();
+ rxBusy.clear();
+ rxActive = -1;
+ txList.clear();
+ rxBusyCount = 0;
+ rxDirtyCount = 0;
+ rxMappedCount = 0;
+
+ rxState = rxIdle;
+ txState = txIdle;
+
+ rxFifo.clear();
+ rxFifoPtr = rxFifo.end();
+ txFifo.clear();
+ rxEmpty = false;
+ rxLow = true;
+ txFull = false;
+
+ int size = virtualRegs.size();
+ virtualRegs.clear();
+ virtualRegs.resize(size);
+ for (int i = 0; i < size; ++i)
+ virtualRegs[i].rxIndex = rxFifo.end();
+}
+
+void
+Device::rxDmaDone()
+{
+ assert(rxState == rxCopy);
+ rxState = rxCopyDone;
+ DPRINTF(EthernetDMA, "end rx dma write paddr=%#x len=%d\n",
+ rxDmaAddr, rxDmaLen);
+ DDUMP(EthernetData, rxDmaData, rxDmaLen);
+
+ // If the transmit state machine has a pending DMA, let it go first
+ if (txState == txBeginCopy)
+ txKick();
+
+ rxKick();
+}
+
+void
+Device::rxKick()
+{
+ VirtualReg *vnic = NULL;
+
+ DPRINTF(EthernetSM, "rxKick: rxState=%s (rxFifo.size=%d)\n",
+ RxStateStrings[rxState], rxFifo.size());
+
+ if (rxKickTick > curTick()) {
+ DPRINTF(EthernetSM, "rxKick: exiting, can't run till %d\n",
+ rxKickTick);
+ return;
+ }
+
+ next:
+ rxFifo.check();
+ if (rxState == rxIdle)
+ goto exit;
+
+ if (rxActive == -1) {
+ if (rxState != rxFifoBlock)
+ panic("no active vnic while in state %s", RxStateStrings[rxState]);
+
+ DPRINTF(EthernetSM, "processing rxState=%s\n",
+ RxStateStrings[rxState]);
+ } else {
+ vnic = &virtualRegs[rxActive];
+ DPRINTF(EthernetSM,
+ "processing rxState=%s for vnic %d (rxunique %d)\n",
+ RxStateStrings[rxState], rxActive, vnic->rxUnique);
+ }
+
+ switch (rxState) {
+ case rxFifoBlock:
+ if (DTRACE(EthernetSM)) {
+ PacketFifo::iterator end = rxFifo.end();
+ int size = virtualRegs.size();
+ for (int i = 0; i < size; ++i) {
+ VirtualReg *vn = &virtualRegs[i];
+ bool busy = Regs::get_RxDone_Busy(vn->RxDone);
+ if (vn->rxIndex != end) {
+#ifndef NDEBUG
+ bool dirty = vn->rxPacketOffset > 0;
+ const char *status;
+
+ if (busy && dirty)
+ status = "busy,dirty";
+ else if (busy)
+ status = "busy";
+ else if (dirty)
+ status = "dirty";
+ else
+ status = "mapped";
+
+ DPRINTF(EthernetSM,
+ "vnic %d %s (rxunique %d), packet %d, slack %d\n",
+ i, status, vn->rxUnique,
+ rxFifo.countPacketsBefore(vn->rxIndex),
+ vn->rxIndex->slack);
+#endif
+ } else if (busy) {
+ DPRINTF(EthernetSM, "vnic %d unmapped (rxunique %d)\n",
+ i, vn->rxUnique);
+ }
+ }
+ }
+
+ if (!rxBusy.empty()) {
+ rxActive = rxBusy.front();
+ rxBusy.pop_front();
+ vnic = &virtualRegs[rxActive];
+
+ if (vnic->rxIndex == rxFifo.end())
+ panic("continuing vnic without packet\n");
+
+ DPRINTF(EthernetSM,
+ "continue processing for vnic %d (rxunique %d)\n",
+ rxActive, vnic->rxUnique);
+
+ rxState = rxBeginCopy;
+
+ int vnic_distance = rxFifo.countPacketsBefore(vnic->rxIndex);
+ totalVnicDistance += vnic_distance;
+ numVnicDistance += 1;
+ if (vnic_distance > _maxVnicDistance) {
+ maxVnicDistance = vnic_distance;
+ _maxVnicDistance = vnic_distance;
+ }
+
+ break;
+ }
+
+ if (rxFifoPtr == rxFifo.end()) {
+ DPRINTF(EthernetSM, "receive waiting for data. Nothing to do.\n");
+ goto exit;
+ }
+
+ if (rxList.empty())
+ panic("Not idle, but nothing to do!");
+
+ assert(!rxFifo.empty());
+
+ rxActive = rxList.front();
+ rxList.pop_front();
+ vnic = &virtualRegs[rxActive];
+
+ DPRINTF(EthernetSM,
+ "processing new packet for vnic %d (rxunique %d)\n",
+ rxActive, vnic->rxUnique);
+
+ // Grab a new packet from the fifo.
+ vnic->rxIndex = rxFifoPtr++;
+ vnic->rxIndex->priv = rxActive;
+ vnic->rxPacketOffset = 0;
+ vnic->rxPacketBytes = vnic->rxIndex->packet->length;
+ assert(vnic->rxPacketBytes);
+ rxMappedCount++;
+
+ vnic->rxDoneData = 0;
+ /* scope for variables */ {
+ IpPtr ip(vnic->rxIndex->packet);
+ if (ip) {
+ DPRINTF(Ethernet, "ID is %d\n", ip->id());
+ vnic->rxDoneData |= Regs::RxDone_IpPacket;
+ rxIpChecksums++;
+ if (cksum(ip) != 0) {
+ DPRINTF(EthernetCksum, "Rx IP Checksum Error\n");
+ vnic->rxDoneData |= Regs::RxDone_IpError;
+ }
+ TcpPtr tcp(ip);
+ UdpPtr udp(ip);
+ if (tcp) {
+ DPRINTF(Ethernet,
+ "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n",
+ tcp->sport(), tcp->dport(), tcp->seq(),
+ tcp->ack());
+ vnic->rxDoneData |= Regs::RxDone_TcpPacket;
+ rxTcpChecksums++;
+ if (cksum(tcp) != 0) {
+ DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n");
+ vnic->rxDoneData |= Regs::RxDone_TcpError;
+ }
+ } else if (udp) {
+ vnic->rxDoneData |= Regs::RxDone_UdpPacket;
+ rxUdpChecksums++;
+ if (cksum(udp) != 0) {
+ DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n");
+ vnic->rxDoneData |= Regs::RxDone_UdpError;
+ }
+ }
+ }
+ }
+ rxState = rxBeginCopy;
+ break;
+
+ case rxBeginCopy:
+ if (dmaPending() || drainState() != DrainState::Running)
+ goto exit;
+
+ rxDmaAddr = pciToDma(Regs::get_RxData_Addr(vnic->RxData));
+ rxDmaLen = min<unsigned>(Regs::get_RxData_Len(vnic->RxData),
+ vnic->rxPacketBytes);
+
+ /*
+ * if we're doing zero/delay copy and we're below the fifo
+ * threshold, see if we should try to do the zero/defer copy
+ */
+ if ((Regs::get_Config_ZeroCopy(regs.Config) ||
+ Regs::get_Config_DelayCopy(regs.Config)) &&
+ !Regs::get_RxData_NoDelay(vnic->RxData) && rxLow) {
+ if (rxDmaLen > regs.ZeroCopyMark)
+ rxDmaLen = regs.ZeroCopySize;
+ }
+ rxDmaData = vnic->rxIndex->packet->data + vnic->rxPacketOffset;
+ rxState = rxCopy;
+ if (rxDmaAddr == 1LL) {
+ rxState = rxCopyDone;
+ break;
+ }
+
+ dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaEvent, rxDmaData);
+ break;
+
+ case rxCopy:
+ DPRINTF(EthernetSM, "receive machine still copying\n");
+ goto exit;
+
+ case rxCopyDone:
+ vnic->RxDone = vnic->rxDoneData;
+ vnic->RxDone |= Regs::RxDone_Complete;
+ rxBusyCount--;
+
+ if (vnic->rxPacketBytes == rxDmaLen) {
+ if (vnic->rxPacketOffset)
+ rxDirtyCount--;
+
+ // Packet is complete. Indicate how many bytes were copied
+ vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone, rxDmaLen);
+
+ DPRINTF(EthernetSM,
+ "rxKick: packet complete on vnic %d (rxunique %d)\n",
+ rxActive, vnic->rxUnique);
+ rxFifo.remove(vnic->rxIndex);
+ vnic->rxIndex = rxFifo.end();
+ rxMappedCount--;
+ } else {
+ if (!vnic->rxPacketOffset)
+ rxDirtyCount++;
+
+ vnic->rxPacketBytes -= rxDmaLen;
+ vnic->rxPacketOffset += rxDmaLen;
+ vnic->RxDone |= Regs::RxDone_More;
+ vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone,
+ vnic->rxPacketBytes);
+ DPRINTF(EthernetSM,
+ "rxKick: packet not complete on vnic %d (rxunique %d): "
+ "%d bytes left\n",
+ rxActive, vnic->rxUnique, vnic->rxPacketBytes);
+ }
+
+ rxActive = -1;
+ rxState = rxBusy.empty() && rxList.empty() ? rxIdle : rxFifoBlock;
+
+ if (rxFifo.empty()) {
+ devIntrPost(Regs::Intr_RxEmpty);
+ rxEmpty = true;
+ }
+
+ if (rxFifo.size() < regs.RxFifoLow)
+ rxLow = true;
+
+ if (rxFifo.size() > regs.RxFifoHigh)
+ rxLow = false;
+
+ devIntrPost(Regs::Intr_RxDMA);
+ break;
+
+ default:
+ panic("Invalid rxState!");
+ }
+
+ DPRINTF(EthernetSM, "entering next rxState=%s\n",
+ RxStateStrings[rxState]);
+
+ goto next;
+
+ exit:
+ /**
+ * @todo do we want to schedule a future kick?
+ */
+ DPRINTF(EthernetSM, "rx state machine exited rxState=%s\n",
+ RxStateStrings[rxState]);
+}
+
+void
+Device::txDmaDone()
+{
+ assert(txState == txCopy);
+ txState = txCopyDone;
+ DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n",
+ txDmaAddr, txDmaLen);
+ DDUMP(EthernetData, txDmaData, txDmaLen);
+
+ // If the receive state machine has a pending DMA, let it go first
+ if (rxState == rxBeginCopy)
+ rxKick();
+
+ txKick();
+}
+
+void
+Device::transmit()
+{
+ if (txFifo.empty()) {
+ DPRINTF(Ethernet, "nothing to transmit\n");
+ return;
+ }
+
+ uint32_t interrupts;
+ EthPacketPtr packet = txFifo.front();
+ if (!interface->sendPacket(packet)) {
+ DPRINTF(Ethernet, "Packet Transmit: failed txFifo available %d\n",
+ txFifo.avail());
+ return;
+ }
+
+ txFifo.pop();
+#if TRACING_ON
+ if (DTRACE(Ethernet)) {
+ IpPtr ip(packet);
+ if (ip) {
+ DPRINTF(Ethernet, "ID is %d\n", ip->id());
+ TcpPtr tcp(ip);
+ if (tcp) {
+ DPRINTF(Ethernet,
+ "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n",
+ tcp->sport(), tcp->dport(), tcp->seq(),
+ tcp->ack());
+ }
+ }
+ }
+#endif
+
+ DDUMP(EthernetData, packet->data, packet->length);
+ txBytes += packet->length;
+ txPackets++;
+
+ DPRINTF(Ethernet, "Packet Transmit: successful txFifo Available %d\n",
+ txFifo.avail());
+
+ interrupts = Regs::Intr_TxPacket;
+ if (txFifo.size() < regs.TxFifoLow)
+ interrupts |= Regs::Intr_TxLow;
+ devIntrPost(interrupts);
+}
+
+void
+Device::txKick()
+{
+ VirtualReg *vnic;
+ DPRINTF(EthernetSM, "txKick: txState=%s (txFifo.size=%d)\n",
+ TxStateStrings[txState], txFifo.size());
+
+ if (txKickTick > curTick()) {
+ DPRINTF(EthernetSM, "txKick: exiting, can't run till %d\n",
+ txKickTick);
+ return;
+ }
+
+ next:
+ if (txState == txIdle)
+ goto exit;
+
+ assert(!txList.empty());
+ vnic = &virtualRegs[txList.front()];
+
+ switch (txState) {
+ case txFifoBlock:
+ assert(Regs::get_TxDone_Busy(vnic->TxDone));
+ if (!txPacket) {
+ // Grab a new packet from the fifo.
+ txPacket = make_shared<EthPacketData>(16384);
+ txPacketOffset = 0;
+ }
+
+ if (txFifo.avail() - txPacket->length <
+ Regs::get_TxData_Len(vnic->TxData)) {
+ DPRINTF(EthernetSM, "transmit fifo full. Nothing to do.\n");
+ goto exit;
+ }
+
+ txState = txBeginCopy;
+ break;
+
+ case txBeginCopy:
+ if (dmaPending() || drainState() != DrainState::Running)
+ goto exit;
+
+ txDmaAddr = pciToDma(Regs::get_TxData_Addr(vnic->TxData));
+ txDmaLen = Regs::get_TxData_Len(vnic->TxData);
+ txDmaData = txPacket->data + txPacketOffset;
+ txState = txCopy;
+
+ dmaRead(txDmaAddr, txDmaLen, &txDmaEvent, txDmaData);
+ break;
+
+ case txCopy:
+ DPRINTF(EthernetSM, "transmit machine still copying\n");
+ goto exit;
+
+ case txCopyDone:
+ vnic->TxDone = txDmaLen | Regs::TxDone_Complete;
+ txPacket->length += txDmaLen;
+ if ((vnic->TxData & Regs::TxData_More)) {
+ txPacketOffset += txDmaLen;
+ txState = txIdle;
+ devIntrPost(Regs::Intr_TxDMA);
+ break;
+ }
+
+ assert(txPacket->length <= txFifo.avail());
+ if ((vnic->TxData & Regs::TxData_Checksum)) {
+ IpPtr ip(txPacket);
+ if (ip) {
+ TcpPtr tcp(ip);
+ if (tcp) {
+ tcp->sum(0);
+ tcp->sum(cksum(tcp));
+ txTcpChecksums++;
+ }
+
+ UdpPtr udp(ip);
+ if (udp) {
+ udp->sum(0);
+ udp->sum(cksum(udp));
+ txUdpChecksums++;
+ }
+
+ ip->sum(0);
+ ip->sum(cksum(ip));
+ txIpChecksums++;
+ }
+ }
+
+ txFifo.push(txPacket);
+ if (txFifo.avail() < regs.TxMaxCopy) {
+ devIntrPost(Regs::Intr_TxFull);
+ txFull = true;
+ }
+ txPacket = 0;
+ transmit();
+ txList.pop_front();
+ txState = txList.empty() ? txIdle : txFifoBlock;
+ devIntrPost(Regs::Intr_TxDMA);
+ break;
+
+ default:
+ panic("Invalid txState!");
+ }
+
+ DPRINTF(EthernetSM, "entering next txState=%s\n",
+ TxStateStrings[txState]);
+
+ goto next;
+
+ exit:
+ /**
+ * @todo do we want to schedule a future kick?
+ */
+ DPRINTF(EthernetSM, "tx state machine exited txState=%s\n",
+ TxStateStrings[txState]);
+}
+
+void
+Device::transferDone()
+{
+ if (txFifo.empty()) {
+ DPRINTF(Ethernet, "transfer complete: txFifo empty...nothing to do\n");
+ return;
+ }
+
+ DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n");
+
+ reschedule(txEvent, clockEdge(Cycles(1)), true);
+}
+
+bool
+Device::rxFilter(const EthPacketPtr &packet)
+{
+ if (!Regs::get_Config_Filter(regs.Config))
+ return false;
+
+ panic("receive filter not implemented\n");
+ bool drop = true;
+
+#if 0
+ string type;
+
+ EthHdr *eth = packet->eth();
+ if (eth->unicast()) {
+ // If we're accepting all unicast addresses
+ if (acceptUnicast)
+ drop = false;
+
+ // If we make a perfect match
+ if (acceptPerfect && params->eaddr == eth.dst())
+ drop = false;
+
+ if (acceptArp && eth->type() == ETH_TYPE_ARP)
+ drop = false;
+
+ } else if (eth->broadcast()) {
+ // if we're accepting broadcasts
+ if (acceptBroadcast)
+ drop = false;
+
+ } else if (eth->multicast()) {
+ // if we're accepting all multicasts
+ if (acceptMulticast)
+ drop = false;
+
+ }
+
+ if (drop) {
+ DPRINTF(Ethernet, "rxFilter drop\n");
+ DDUMP(EthernetData, packet->data, packet->length);
+ }
+#endif
+ return drop;
+}
+
+bool
+Device::recvPacket(EthPacketPtr packet)
+{
+ rxBytes += packet->length;
+ rxPackets++;
+
+ DPRINTF(Ethernet, "Receiving packet from wire, rxFifo Available is %d\n",
+ rxFifo.avail());
+
+ if (!rxEnable) {
+ DPRINTF(Ethernet, "receive disabled...packet dropped\n");
+ return true;
+ }
+
+ if (rxFilter(packet)) {
+ DPRINTF(Ethernet, "packet filtered...dropped\n");
+ return true;
+ }
+
+ if (rxFifo.size() >= regs.RxFifoHigh)
+ devIntrPost(Regs::Intr_RxHigh);
+
+ if (!rxFifo.push(packet)) {
+ DPRINTF(Ethernet,
+ "packet will not fit in receive buffer...packet dropped\n");
+ return false;
+ }
+
+ // If we were at the last element, back up one ot go to the new
+ // last element of the list.
+ if (rxFifoPtr == rxFifo.end())
+ --rxFifoPtr;
+
+ devIntrPost(Regs::Intr_RxPacket);
+ rxKick();
+ return true;
+}
+
+void
+Device::drainResume()
+{
+ Drainable::drainResume();
+
+ // During drain we could have left the state machines in a waiting state and
+ // they wouldn't get out until some other event occured to kick them.
+ // This way they'll get out immediately
+ txKick();
+ rxKick();
+}
+
+//=====================================================================
+//
+//
+void
+Base::serialize(CheckpointOut &cp) const
+{
+ // Serialize the PciDevice base class
+ PciDevice::serialize(cp);
+
+ SERIALIZE_SCALAR(rxEnable);
+ SERIALIZE_SCALAR(txEnable);
+ SERIALIZE_SCALAR(cpuIntrEnable);
+
+ /*
+ * Keep track of pending interrupt status.
+ */
+ SERIALIZE_SCALAR(intrTick);
+ SERIALIZE_SCALAR(cpuPendingIntr);
+ Tick intrEventTick = 0;
+ if (intrEvent)
+ intrEventTick = intrEvent->when();
+ SERIALIZE_SCALAR(intrEventTick);
+}
+
+void
+Base::unserialize(CheckpointIn &cp)
+{
+ // Unserialize the PciDevice base class
+ PciDevice::unserialize(cp);
+
+ UNSERIALIZE_SCALAR(rxEnable);
+ UNSERIALIZE_SCALAR(txEnable);
+ UNSERIALIZE_SCALAR(cpuIntrEnable);
+
+ /*
+ * Keep track of pending interrupt status.
+ */
+ UNSERIALIZE_SCALAR(intrTick);
+ UNSERIALIZE_SCALAR(cpuPendingIntr);
+ Tick intrEventTick;
+ UNSERIALIZE_SCALAR(intrEventTick);
+ if (intrEventTick) {
+ intrEvent = new IntrEvent(this, true);
+ schedule(intrEvent, intrEventTick);
+ }
+}
+
+void
+Device::serialize(CheckpointOut &cp) const
+{
+ int count;
+
+ // Serialize the PciDevice base class
+ Base::serialize(cp);
+
+ if (rxState == rxCopy)
+ panic("can't serialize with an in flight dma request rxState=%s",
+ RxStateStrings[rxState]);
+
+ if (txState == txCopy)
+ panic("can't serialize with an in flight dma request txState=%s",
+ TxStateStrings[txState]);
+
+ /*
+ * Serialize the device registers that could be modified by the OS.
+ */
+ SERIALIZE_SCALAR(regs.Config);
+ SERIALIZE_SCALAR(regs.IntrStatus);
+ SERIALIZE_SCALAR(regs.IntrMask);
+ SERIALIZE_SCALAR(regs.RxData);
+ SERIALIZE_SCALAR(regs.TxData);
+
+ /*
+ * Serialize the virtual nic state
+ */
+ int virtualRegsSize = virtualRegs.size();
+ SERIALIZE_SCALAR(virtualRegsSize);
+ for (int i = 0; i < virtualRegsSize; ++i) {
+ const VirtualReg *vnic = &virtualRegs[i];
+
+ std::string reg = csprintf("vnic%d", i);
+ paramOut(cp, reg + ".RxData", vnic->RxData);
+ paramOut(cp, reg + ".RxDone", vnic->RxDone);
+ paramOut(cp, reg + ".TxData", vnic->TxData);
+ paramOut(cp, reg + ".TxDone", vnic->TxDone);
+
+ bool rxPacketExists = vnic->rxIndex != rxFifo.end();
+ paramOut(cp, reg + ".rxPacketExists", rxPacketExists);
+ if (rxPacketExists) {
+ int rxPacket = 0;
+ auto i = rxFifo.begin();
+ while (i != vnic->rxIndex) {
+ assert(i != rxFifo.end());
+ ++i;
+ ++rxPacket;
+ }
+
+ paramOut(cp, reg + ".rxPacket", rxPacket);
+ paramOut(cp, reg + ".rxPacketOffset", vnic->rxPacketOffset);
+ paramOut(cp, reg + ".rxPacketBytes", vnic->rxPacketBytes);
+ }
+ paramOut(cp, reg + ".rxDoneData", vnic->rxDoneData);
+ }
+
+ int rxFifoPtr = -1;
+ if (this->rxFifoPtr != rxFifo.end())
+ rxFifoPtr = rxFifo.countPacketsBefore(this->rxFifoPtr);
+ SERIALIZE_SCALAR(rxFifoPtr);
+
+ SERIALIZE_SCALAR(rxActive);
+ SERIALIZE_SCALAR(rxBusyCount);
+ SERIALIZE_SCALAR(rxDirtyCount);
+ SERIALIZE_SCALAR(rxMappedCount);
+
+ VirtualList::const_iterator i, end;
+ for (count = 0, i = rxList.begin(), end = rxList.end(); i != end; ++i)
+ paramOut(cp, csprintf("rxList%d", count++), *i);
+ int rxListSize = count;
+ SERIALIZE_SCALAR(rxListSize);
+
+ for (count = 0, i = rxBusy.begin(), end = rxBusy.end(); i != end; ++i)
+ paramOut(cp, csprintf("rxBusy%d", count++), *i);
+ int rxBusySize = count;
+ SERIALIZE_SCALAR(rxBusySize);
+
+ for (count = 0, i = txList.begin(), end = txList.end(); i != end; ++i)
+ paramOut(cp, csprintf("txList%d", count++), *i);
+ int txListSize = count;
+ SERIALIZE_SCALAR(txListSize);
+
+ /*
+ * Serialize rx state machine
+ */
+ int rxState = this->rxState;
+ SERIALIZE_SCALAR(rxState);
+ SERIALIZE_SCALAR(rxEmpty);
+ SERIALIZE_SCALAR(rxLow);
+ rxFifo.serialize("rxFifo", cp);
+
+ /*
+ * Serialize tx state machine
+ */
+ int txState = this->txState;
+ SERIALIZE_SCALAR(txState);
+ SERIALIZE_SCALAR(txFull);
+ txFifo.serialize("txFifo", cp);
+ bool txPacketExists = txPacket != nullptr;
+ SERIALIZE_SCALAR(txPacketExists);
+ if (txPacketExists) {
+ txPacket->serialize("txPacket", cp);
+ SERIALIZE_SCALAR(txPacketOffset);
+ SERIALIZE_SCALAR(txPacketBytes);
+ }
+
+ /*
+ * If there's a pending transmit, store the time so we can
+ * reschedule it later
+ */
+ Tick transmitTick = txEvent.scheduled() ? txEvent.when() - curTick() : 0;
+ SERIALIZE_SCALAR(transmitTick);
+}
+
+void
+Device::unserialize(CheckpointIn &cp)
+{
+ // Unserialize the PciDevice base class
+ Base::unserialize(cp);
+
+ /*
+ * Unserialize the device registers that may have been written by the OS.
+ */
+ UNSERIALIZE_SCALAR(regs.Config);
+ UNSERIALIZE_SCALAR(regs.IntrStatus);
+ UNSERIALIZE_SCALAR(regs.IntrMask);
+ UNSERIALIZE_SCALAR(regs.RxData);
+ UNSERIALIZE_SCALAR(regs.TxData);
+
+ UNSERIALIZE_SCALAR(rxActive);
+ UNSERIALIZE_SCALAR(rxBusyCount);
+ UNSERIALIZE_SCALAR(rxDirtyCount);
+ UNSERIALIZE_SCALAR(rxMappedCount);
+
+ int rxListSize;
+ UNSERIALIZE_SCALAR(rxListSize);
+ rxList.clear();
+ for (int i = 0; i < rxListSize; ++i) {
+ int value;
+ paramIn(cp, csprintf("rxList%d", i), value);
+ rxList.push_back(value);
+ }
+
+ int rxBusySize;
+ UNSERIALIZE_SCALAR(rxBusySize);
+ rxBusy.clear();
+ for (int i = 0; i < rxBusySize; ++i) {
+ int value;
+ paramIn(cp, csprintf("rxBusy%d", i), value);
+ rxBusy.push_back(value);
+ }
+
+ int txListSize;
+ UNSERIALIZE_SCALAR(txListSize);
+ txList.clear();
+ for (int i = 0; i < txListSize; ++i) {
+ int value;
+ paramIn(cp, csprintf("txList%d", i), value);
+ txList.push_back(value);
+ }
+
+ /*
+ * Unserialize rx state machine
+ */
+ int rxState;
+ UNSERIALIZE_SCALAR(rxState);
+ UNSERIALIZE_SCALAR(rxEmpty);
+ UNSERIALIZE_SCALAR(rxLow);
+ this->rxState = (RxState) rxState;
+ rxFifo.unserialize("rxFifo", cp);
+
+ int rxFifoPtr;
+ UNSERIALIZE_SCALAR(rxFifoPtr);
+ if (rxFifoPtr >= 0) {
+ this->rxFifoPtr = rxFifo.begin();
+ for (int i = 0; i < rxFifoPtr; ++i)
+ ++this->rxFifoPtr;
+ } else {
+ this->rxFifoPtr = rxFifo.end();
+ }
+
+ /*
+ * Unserialize tx state machine
+ */
+ int txState;
+ UNSERIALIZE_SCALAR(txState);
+ UNSERIALIZE_SCALAR(txFull);
+ this->txState = (TxState) txState;
+ txFifo.unserialize("txFifo", cp);
+ bool txPacketExists;
+ UNSERIALIZE_SCALAR(txPacketExists);
+ txPacket = 0;
+ if (txPacketExists) {
+ txPacket = make_shared<EthPacketData>(16384);
+ txPacket->unserialize("txPacket", cp);
+ UNSERIALIZE_SCALAR(txPacketOffset);
+ UNSERIALIZE_SCALAR(txPacketBytes);
+ }
+
+ /*
+ * unserialize the virtual nic registers/state
+ *
+ * this must be done after the unserialization of the rxFifo
+ * because the packet iterators depend on the fifo being populated
+ */
+ int virtualRegsSize;
+ UNSERIALIZE_SCALAR(virtualRegsSize);
+ virtualRegs.clear();
+ virtualRegs.resize(virtualRegsSize);
+ for (int i = 0; i < virtualRegsSize; ++i) {
+ VirtualReg *vnic = &virtualRegs[i];
+ std::string reg = csprintf("vnic%d", i);
+
+ paramIn(cp, reg + ".RxData", vnic->RxData);
+ paramIn(cp, reg + ".RxDone", vnic->RxDone);
+ paramIn(cp, reg + ".TxData", vnic->TxData);
+ paramIn(cp, reg + ".TxDone", vnic->TxDone);
+
+ vnic->rxUnique = rxUnique++;
+ vnic->txUnique = txUnique++;
+
+ bool rxPacketExists;
+ paramIn(cp, reg + ".rxPacketExists", rxPacketExists);
+ if (rxPacketExists) {
+ int rxPacket;
+ paramIn(cp, reg + ".rxPacket", rxPacket);
+ vnic->rxIndex = rxFifo.begin();
+ while (rxPacket--)
+ ++vnic->rxIndex;
+
+ paramIn(cp, reg + ".rxPacketOffset",
+ vnic->rxPacketOffset);
+ paramIn(cp, reg + ".rxPacketBytes", vnic->rxPacketBytes);
+ } else {
+ vnic->rxIndex = rxFifo.end();
+ }
+ paramIn(cp, reg + ".rxDoneData", vnic->rxDoneData);
+ }
+
+ /*
+ * If there's a pending transmit, reschedule it now
+ */
+ Tick transmitTick;
+ UNSERIALIZE_SCALAR(transmitTick);
+ if (transmitTick)
+ schedule(txEvent, curTick() + transmitTick);
+
+ pioPort.sendRangeChange();
+
+}
+
+} // namespace Sinic
+
+Sinic::Device *
+SinicParams::create()
+{
+ return new Sinic::Device(this);
+}
diff --git a/src/dev/net/sinic.hh b/src/dev/net/sinic.hh
new file mode 100644
index 000000000..b041cb16d
--- /dev/null
+++ b/src/dev/net/sinic.hh
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+#ifndef __DEV_NET_SINIC_HH__
+#define __DEV_NET_SINIC_HH__
+
+#include "base/inet.hh"
+#include "base/statistics.hh"
+#include "dev/io_device.hh"
+#include "dev/net/etherdevice.hh"
+#include "dev/net/etherint.hh"
+#include "dev/net/etherpkt.hh"
+#include "dev/net/pktfifo.hh"
+#include "dev/net/sinicreg.hh"
+#include "dev/pci/device.hh"
+#include "params/Sinic.hh"
+#include "sim/eventq.hh"
+
+namespace Sinic {
+
+class Interface;
+class Base : public EtherDevBase
+{
+ protected:
+ bool rxEnable;
+ bool txEnable;
+
+ protected:
+ Tick intrDelay;
+ Tick intrTick;
+ bool cpuIntrEnable;
+ bool cpuPendingIntr;
+ void cpuIntrPost(Tick when);
+ void cpuInterrupt();
+ void cpuIntrClear();
+
+ typedef EventWrapper<Base, &Base::cpuInterrupt> IntrEvent;
+ friend void IntrEvent::process();
+ IntrEvent *intrEvent;
+ Interface *interface;
+
+ bool cpuIntrPending() const;
+ void cpuIntrAck() { cpuIntrClear(); }
+
+/**
+ * Serialization stuff
+ */
+ public:
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+
+/**
+ * Construction/Destruction/Parameters
+ */
+ public:
+ typedef SinicParams Params;
+ const Params *params() const { return (const Params *)_params; }
+ Base(const Params *p);
+};
+
+class Device : public Base
+{
+ protected:
+ /** Receive State Machine States */
+ enum RxState {
+ rxIdle,
+ rxFifoBlock,
+ rxBeginCopy,
+ rxCopy,
+ rxCopyDone
+ };
+
+ /** Transmit State Machine states */
+ enum TxState {
+ txIdle,
+ txFifoBlock,
+ txBeginCopy,
+ txCopy,
+ txCopyDone
+ };
+
+ /** device register file */
+ struct {
+ uint32_t Config; // 0x00
+ uint32_t Command; // 0x04
+ uint32_t IntrStatus; // 0x08
+ uint32_t IntrMask; // 0x0c
+ uint32_t RxMaxCopy; // 0x10
+ uint32_t TxMaxCopy; // 0x14
+ uint32_t ZeroCopySize; // 0x18
+ uint32_t ZeroCopyMark; // 0x1c
+ uint32_t VirtualCount; // 0x20
+ uint32_t RxMaxIntr; // 0x24
+ uint32_t RxFifoSize; // 0x28
+ uint32_t TxFifoSize; // 0x2c
+ uint32_t RxFifoLow; // 0x30
+ uint32_t TxFifoLow; // 0x34
+ uint32_t RxFifoHigh; // 0x38
+ uint32_t TxFifoHigh; // 0x3c
+ uint64_t RxData; // 0x40
+ uint64_t RxDone; // 0x48
+ uint64_t RxWait; // 0x50
+ uint64_t TxData; // 0x58
+ uint64_t TxDone; // 0x60
+ uint64_t TxWait; // 0x68
+ uint64_t HwAddr; // 0x70
+ uint64_t RxStatus; // 0x78
+ } regs;
+
+ struct VirtualReg {
+ uint64_t RxData;
+ uint64_t RxDone;
+ uint64_t TxData;
+ uint64_t TxDone;
+
+ PacketFifo::iterator rxIndex;
+ unsigned rxPacketOffset;
+ unsigned rxPacketBytes;
+ uint64_t rxDoneData;
+
+ Counter rxUnique;
+ Counter txUnique;
+
+ VirtualReg()
+ : RxData(0), RxDone(0), TxData(0), TxDone(0),
+ rxPacketOffset(0), rxPacketBytes(0), rxDoneData(0)
+ { }
+ };
+ typedef std::vector<VirtualReg> VirtualRegs;
+ typedef std::list<unsigned> VirtualList;
+ Counter rxUnique;
+ Counter txUnique;
+ VirtualRegs virtualRegs;
+ VirtualList rxList;
+ VirtualList rxBusy;
+ int rxActive;
+ VirtualList txList;
+
+ int rxBusyCount;
+ int rxMappedCount;
+ int rxDirtyCount;
+
+ uint8_t &regData8(Addr daddr) { return *((uint8_t *)&regs + daddr); }
+ uint32_t &regData32(Addr daddr) { return *(uint32_t *)&regData8(daddr); }
+ uint64_t &regData64(Addr daddr) { return *(uint64_t *)&regData8(daddr); }
+
+ protected:
+ RxState rxState;
+ PacketFifo rxFifo;
+ PacketFifo::iterator rxFifoPtr;
+ bool rxEmpty;
+ bool rxLow;
+ Addr rxDmaAddr;
+ uint8_t *rxDmaData;
+ unsigned rxDmaLen;
+
+ TxState txState;
+ PacketFifo txFifo;
+ bool txFull;
+ EthPacketPtr txPacket;
+ int txPacketOffset;
+ int txPacketBytes;
+ Addr txDmaAddr;
+ uint8_t *txDmaData;
+ int txDmaLen;
+
+ protected:
+ void reset();
+
+ void rxKick();
+ Tick rxKickTick;
+ typedef EventWrapper<Device, &Device::rxKick> RxKickEvent;
+ friend void RxKickEvent::process();
+
+ void txKick();
+ Tick txKickTick;
+ typedef EventWrapper<Device, &Device::txKick> TxKickEvent;
+ friend void TxKickEvent::process();
+
+ /**
+ * Retransmit event
+ */
+ void transmit();
+ void txEventTransmit()
+ {
+ transmit();
+ if (txState == txFifoBlock)
+ txKick();
+ }
+ typedef EventWrapper<Device, &Device::txEventTransmit> TxEvent;
+ friend void TxEvent::process();
+ TxEvent txEvent;
+
+ void txDump() const;
+ void rxDump() const;
+
+ /**
+ * receive address filter
+ */
+ bool rxFilter(const EthPacketPtr &packet);
+
+/**
+ * device configuration
+ */
+ void changeConfig(uint32_t newconfig);
+ void command(uint32_t command);
+
+/**
+ * device ethernet interface
+ */
+ public:
+ bool recvPacket(EthPacketPtr packet);
+ void transferDone();
+ EtherInt *getEthPort(const std::string &if_name, int idx) override;
+
+/**
+ * DMA parameters
+ */
+ protected:
+ void rxDmaDone();
+ friend class EventWrapper<Device, &Device::rxDmaDone>;
+ EventWrapper<Device, &Device::rxDmaDone> rxDmaEvent;
+
+ void txDmaDone();
+ friend class EventWrapper<Device, &Device::txDmaDone>;
+ EventWrapper<Device, &Device::txDmaDone> txDmaEvent;
+
+ Tick dmaReadDelay;
+ Tick dmaReadFactor;
+ Tick dmaWriteDelay;
+ Tick dmaWriteFactor;
+
+/**
+ * Interrupt management
+ */
+ protected:
+ void devIntrPost(uint32_t interrupts);
+ void devIntrClear(uint32_t interrupts = Regs::Intr_All);
+ void devIntrChangeMask(uint32_t newmask);
+
+/**
+ * Memory Interface
+ */
+ public:
+ Tick read(PacketPtr pkt) override;
+ Tick write(PacketPtr pkt) override;
+ virtual void drainResume() override;
+
+ void prepareIO(ContextID cpu, int index);
+ void prepareRead(ContextID cpu, int index);
+ void prepareWrite(ContextID cpu, int index);
+ // Fault iprRead(Addr daddr, ContextID cpu, uint64_t &result);
+
+/**
+ * Statistics
+ */
+ private:
+ Stats::Scalar totalVnicDistance;
+ Stats::Scalar numVnicDistance;
+ Stats::Scalar maxVnicDistance;
+ Stats::Formula avgVnicDistance;
+
+ int _maxVnicDistance;
+
+ public:
+ void regStats() override;
+ void resetStats() override;
+
+/**
+ * Serialization stuff
+ */
+ public:
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+
+ public:
+ Device(const Params *p);
+ ~Device();
+};
+
+/*
+ * Ethernet Interface for an Ethernet Device
+ */
+class Interface : public EtherInt
+{
+ private:
+ Device *dev;
+
+ public:
+ Interface(const std::string &name, Device *d)
+ : EtherInt(name), dev(d)
+ { }
+
+ virtual bool recvPacket(EthPacketPtr pkt) { return dev->recvPacket(pkt); }
+ virtual void sendDone() { dev->transferDone(); }
+};
+
+} // namespace Sinic
+
+#endif // __DEV_NET_SINIC_HH__
diff --git a/src/dev/net/sinicreg.hh b/src/dev/net/sinicreg.hh
new file mode 100644
index 000000000..fc74889c1
--- /dev/null
+++ b/src/dev/net/sinicreg.hh
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Nathan Binkert
+ */
+
+#ifndef __DEV_NET_SINICREG_HH__
+#define __DEV_NET_SINICREG_HH__
+
+#define __SINIC_REG32(NAME, VAL) static const uint32_t NAME = (VAL);
+#define __SINIC_REG64(NAME, VAL) static const uint64_t NAME = (VAL);
+
+#define __SINIC_VAL32(NAME, OFFSET, WIDTH) \
+ static const uint32_t NAME##_width = WIDTH; \
+ static const uint32_t NAME##_offset = OFFSET; \
+ static const uint32_t NAME##_mask = (1 << WIDTH) - 1; \
+ static const uint32_t NAME = ((1 << WIDTH) - 1) << OFFSET; \
+ static inline uint32_t get_##NAME(uint32_t reg) \
+ { return (reg & NAME) >> OFFSET; } \
+ static inline uint32_t set_##NAME(uint32_t reg, uint32_t val) \
+ { return (reg & ~NAME) | ((val << OFFSET) & NAME); }
+
+#define __SINIC_VAL64(NAME, OFFSET, WIDTH) \
+ static const uint64_t NAME##_width = WIDTH; \
+ static const uint64_t NAME##_offset = OFFSET; \
+ static const uint64_t NAME##_mask = (ULL(1) << WIDTH) - 1; \
+ static const uint64_t NAME = ((ULL(1) << WIDTH) - 1) << OFFSET; \
+ static inline uint64_t get_##NAME(uint64_t reg) \
+ { return (reg & NAME) >> OFFSET; } \
+ static inline uint64_t set_##NAME(uint64_t reg, uint64_t val) \
+ { return (reg & ~NAME) | ((val << OFFSET) & NAME); }
+
+namespace Sinic {
+namespace Regs {
+
+static const int VirtualShift = 8;
+static const int VirtualMask = 0xff;
+
+// Registers
+__SINIC_REG32(Config, 0x00) // 32: configuration register
+__SINIC_REG32(Command, 0x04) // 32: command register
+__SINIC_REG32(IntrStatus, 0x08) // 32: interrupt status
+__SINIC_REG32(IntrMask, 0x0c) // 32: interrupt mask
+__SINIC_REG32(RxMaxCopy, 0x10) // 32: max bytes per rx copy
+__SINIC_REG32(TxMaxCopy, 0x14) // 32: max bytes per tx copy
+__SINIC_REG32(ZeroCopySize, 0x18) // 32: bytes to copy if below threshold
+__SINIC_REG32(ZeroCopyMark, 0x1c) // 32: only zero-copy above this threshold
+__SINIC_REG32(VirtualCount, 0x20) // 32: number of virutal NICs
+__SINIC_REG32(RxMaxIntr, 0x24) // 32: max receives per interrupt
+__SINIC_REG32(RxFifoSize, 0x28) // 32: rx fifo capacity in bytes
+__SINIC_REG32(TxFifoSize, 0x2c) // 32: tx fifo capacity in bytes
+__SINIC_REG32(RxFifoLow, 0x30) // 32: rx fifo low watermark
+__SINIC_REG32(TxFifoLow, 0x34) // 32: tx fifo low watermark
+__SINIC_REG32(RxFifoHigh, 0x38) // 32: rx fifo high watermark
+__SINIC_REG32(TxFifoHigh, 0x3c) // 32: tx fifo high watermark
+__SINIC_REG32(RxData, 0x40) // 64: receive data
+__SINIC_REG32(RxDone, 0x48) // 64: receive done
+__SINIC_REG32(RxWait, 0x50) // 64: receive done (busy wait)
+__SINIC_REG32(TxData, 0x58) // 64: transmit data
+__SINIC_REG32(TxDone, 0x60) // 64: transmit done
+__SINIC_REG32(TxWait, 0x68) // 64: transmit done (busy wait)
+__SINIC_REG32(HwAddr, 0x70) // 64: mac address
+__SINIC_REG32(RxStatus, 0x78)
+__SINIC_REG32(Size, 0x80) // register addres space size
+
+// Config register bits
+__SINIC_VAL32(Config_ZeroCopy, 12, 1) // enable zero copy
+__SINIC_VAL32(Config_DelayCopy,11, 1) // enable delayed copy
+__SINIC_VAL32(Config_RSS, 10, 1) // enable receive side scaling
+__SINIC_VAL32(Config_RxThread, 9, 1) // enable receive threads
+__SINIC_VAL32(Config_TxThread, 8, 1) // enable transmit thread
+__SINIC_VAL32(Config_Filter, 7, 1) // enable receive filter
+__SINIC_VAL32(Config_Vlan, 6, 1) // enable vlan tagging
+__SINIC_VAL32(Config_Vaddr, 5, 1) // enable virtual addressing
+__SINIC_VAL32(Config_Desc, 4, 1) // enable tx/rx descriptors
+__SINIC_VAL32(Config_Poll, 3, 1) // enable polling
+__SINIC_VAL32(Config_IntEn, 2, 1) // enable interrupts
+__SINIC_VAL32(Config_TxEn, 1, 1) // enable transmit
+__SINIC_VAL32(Config_RxEn, 0, 1) // enable receive
+
+// Command register bits
+__SINIC_VAL32(Command_Intr, 1, 1) // software interrupt
+__SINIC_VAL32(Command_Reset, 0, 1) // reset chip
+
+// Interrupt register bits
+__SINIC_VAL32(Intr_Soft, 8, 1) // software interrupt
+__SINIC_VAL32(Intr_TxLow, 7, 1) // tx fifo dropped below watermark
+__SINIC_VAL32(Intr_TxFull, 6, 1) // tx fifo full
+__SINIC_VAL32(Intr_TxDMA, 5, 1) // tx dma completed w/ interrupt
+__SINIC_VAL32(Intr_TxPacket, 4, 1) // packet transmitted
+__SINIC_VAL32(Intr_RxHigh, 3, 1) // rx fifo above high watermark
+__SINIC_VAL32(Intr_RxEmpty, 2, 1) // rx fifo empty
+__SINIC_VAL32(Intr_RxDMA, 1, 1) // rx dma completed w/ interrupt
+__SINIC_VAL32(Intr_RxPacket, 0, 1) // packet received
+__SINIC_REG32(Intr_All, 0x01ff) // all valid interrupts
+__SINIC_REG32(Intr_NoDelay, 0x01cc) // interrupts that aren't coalesced
+__SINIC_REG32(Intr_Res, ~0x01ff) // reserved interrupt bits
+
+// RX Data Description
+__SINIC_VAL64(RxData_NoDelay, 61, 1) // Don't Delay this copy
+__SINIC_VAL64(RxData_Vaddr, 60, 1) // Addr is virtual
+__SINIC_VAL64(RxData_Len, 40, 20) // 0 - 256k
+__SINIC_VAL64(RxData_Addr, 0, 40) // Address 1TB
+
+// TX Data Description
+__SINIC_VAL64(TxData_More, 63, 1) // Packet not complete (will dma more)
+__SINIC_VAL64(TxData_Checksum, 62, 1) // do checksum
+__SINIC_VAL64(TxData_Vaddr, 60, 1) // Addr is virtual
+__SINIC_VAL64(TxData_Len, 40, 20) // 0 - 256k
+__SINIC_VAL64(TxData_Addr, 0, 40) // Address 1TB
+
+// RX Done/Busy Information
+__SINIC_VAL64(RxDone_Packets, 32, 16) // number of packets in rx fifo
+__SINIC_VAL64(RxDone_Busy, 31, 1) // receive dma busy copying
+__SINIC_VAL64(RxDone_Complete, 30, 1) // valid data (packet complete)
+__SINIC_VAL64(RxDone_More, 29, 1) // Packet has more data (dma again)
+__SINIC_VAL64(RxDone_Empty, 28, 1) // rx fifo is empty
+__SINIC_VAL64(RxDone_High, 27, 1) // rx fifo is above the watermark
+__SINIC_VAL64(RxDone_NotHigh, 26, 1) // rxfifo never hit the high watermark
+__SINIC_VAL64(RxDone_TcpError, 25, 1) // TCP packet error (bad checksum)
+__SINIC_VAL64(RxDone_UdpError, 24, 1) // UDP packet error (bad checksum)
+__SINIC_VAL64(RxDone_IpError, 23, 1) // IP packet error (bad checksum)
+__SINIC_VAL64(RxDone_TcpPacket, 22, 1) // this is a TCP packet
+__SINIC_VAL64(RxDone_UdpPacket, 21, 1) // this is a UDP packet
+__SINIC_VAL64(RxDone_IpPacket, 20, 1) // this is an IP packet
+__SINIC_VAL64(RxDone_CopyLen, 0, 20) // up to 256k
+
+// TX Done/Busy Information
+__SINIC_VAL64(TxDone_Packets, 32, 16) // number of packets in tx fifo
+__SINIC_VAL64(TxDone_Busy, 31, 1) // transmit dma busy copying
+__SINIC_VAL64(TxDone_Complete, 30, 1) // valid data (packet complete)
+__SINIC_VAL64(TxDone_Full, 29, 1) // tx fifo is full
+__SINIC_VAL64(TxDone_Low, 28, 1) // tx fifo is below the watermark
+__SINIC_VAL64(TxDone_Res0, 27, 1) // reserved
+__SINIC_VAL64(TxDone_Res1, 26, 1) // reserved
+__SINIC_VAL64(TxDone_Res2, 25, 1) // reserved
+__SINIC_VAL64(TxDone_Res3, 24, 1) // reserved
+__SINIC_VAL64(TxDone_Res4, 23, 1) // reserved
+__SINIC_VAL64(TxDone_Res5, 22, 1) // reserved
+__SINIC_VAL64(TxDone_Res6, 21, 1) // reserved
+__SINIC_VAL64(TxDone_Res7, 20, 1) // reserved
+__SINIC_VAL64(TxDone_CopyLen, 0, 20) // up to 256k
+
+__SINIC_VAL64(RxStatus_Dirty, 48, 16)
+__SINIC_VAL64(RxStatus_Mapped, 32, 16)
+__SINIC_VAL64(RxStatus_Busy, 16, 16)
+__SINIC_VAL64(RxStatus_Head, 0, 16)
+
+struct Info
+{
+ uint8_t size;
+ bool read;
+ bool write;
+ const char *name;
+};
+
+} // namespace Regs
+
+inline const Regs::Info&
+regInfo(Addr daddr)
+{
+ static Regs::Info invalid = { 0, false, false, "invalid" };
+ static Regs::Info info [] = {
+ { 4, true, true, "Config" },
+ { 4, false, true, "Command" },
+ { 4, true, true, "IntrStatus" },
+ { 4, true, true, "IntrMask" },
+ { 4, true, false, "RxMaxCopy" },
+ { 4, true, false, "TxMaxCopy" },
+ { 4, true, false, "ZeroCopySize" },
+ { 4, true, false, "ZeroCopyMark" },
+ { 4, true, false, "VirtualCount" },
+ { 4, true, false, "RxMaxIntr" },
+ { 4, true, false, "RxFifoSize" },
+ { 4, true, false, "TxFifoSize" },
+ { 4, true, false, "RxFifoLow" },
+ { 4, true, false, "TxFifoLow" },
+ { 4, true, false, "RxFifoHigh" },
+ { 4, true, false, "TxFifoHigh" },
+ { 8, true, true, "RxData" },
+ invalid,
+ { 8, true, false, "RxDone" },
+ invalid,
+ { 8, true, false, "RxWait" },
+ invalid,
+ { 8, true, true, "TxData" },
+ invalid,
+ { 8, true, false, "TxDone" },
+ invalid,
+ { 8, true, false, "TxWait" },
+ invalid,
+ { 8, true, false, "HwAddr" },
+ invalid,
+ { 8, true, false, "RxStatus" },
+ invalid,
+ };
+
+ return info[daddr / 4];
+}
+
+inline bool
+regValid(Addr daddr)
+{
+ if (daddr > Regs::Size)
+ return false;
+
+ if (regInfo(daddr).size == 0)
+ return false;
+
+ return true;
+}
+
+} // namespace Sinic
+
+#endif // __DEV_NET_SINICREG_HH__
diff --git a/src/dev/net/tcp_iface.cc b/src/dev/net/tcp_iface.cc
new file mode 100644
index 000000000..035ec8fd0
--- /dev/null
+++ b/src/dev/net/tcp_iface.cc
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabor Dozsa
+ */
+
+/* @file
+ * TCP stream socket based interface class implementation for multi gem5 runs.
+ */
+
+#include "dev/net/tcp_iface.hh"
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstring>
+
+#include "base/types.hh"
+#include "debug/MultiEthernet.hh"
+
+// MSG_NOSIGNAL does not exists on OS X
+#if defined(__APPLE__) || defined(__MACH__)
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL SO_NOSIGPIPE
+#endif
+#endif
+
+using namespace std;
+
+vector<int> TCPIface::sockRegistry;
+
+TCPIface::TCPIface(string server_name, unsigned server_port,
+ unsigned multi_rank, Tick sync_start, Tick sync_repeat,
+ EventManager *em) :
+ MultiIface(multi_rank, sync_start, sync_repeat, em)
+{
+ struct addrinfo addr_hint, *addr_results;
+ int ret;
+
+ string port_str = to_string(server_port);
+
+ sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ panic_if(sock < 0, "socket() failed: %s", strerror(errno));
+
+ bzero(&addr_hint, sizeof(addr_hint));
+ addr_hint.ai_family = AF_INET;
+ addr_hint.ai_socktype = SOCK_STREAM;
+ addr_hint.ai_protocol = IPPROTO_TCP;
+
+ ret = getaddrinfo(server_name.c_str(), port_str.c_str(),
+ &addr_hint, &addr_results);
+ panic_if(ret < 0, "getaddrinf() failed: %s", strerror(errno));
+
+ DPRINTF(MultiEthernet, "Connecting to %s:%u\n",
+ server_name.c_str(), port_str.c_str());
+
+ ret = ::connect(sock, (struct sockaddr *)(addr_results->ai_addr),
+ addr_results->ai_addrlen);
+ panic_if(ret < 0, "connect() failed: %s", strerror(errno));
+
+ freeaddrinfo(addr_results);
+ // add our socket to the static registry
+ sockRegistry.push_back(sock);
+ // let the server know who we are
+ sendTCP(sock, &multi_rank, sizeof(multi_rank));
+}
+
+TCPIface::~TCPIface()
+{
+ int M5_VAR_USED ret;
+
+ ret = close(sock);
+ assert(ret == 0);
+}
+
+void
+TCPIface::sendTCP(int sock, void *buf, unsigned length)
+{
+ ssize_t ret;
+
+ ret = ::send(sock, buf, length, MSG_NOSIGNAL);
+ panic_if(ret < 0, "send() failed: %s", strerror(errno));
+ panic_if(ret != length, "send() failed");
+}
+
+bool
+TCPIface::recvTCP(int sock, void *buf, unsigned length)
+{
+ ssize_t ret;
+
+ ret = ::recv(sock, buf, length, MSG_WAITALL );
+ if (ret < 0) {
+ if (errno == ECONNRESET || errno == EPIPE)
+ inform("recv(): %s", strerror(errno));
+ else if (ret < 0)
+ panic("recv() failed: %s", strerror(errno));
+ } else if (ret == 0) {
+ inform("recv(): Connection closed");
+ } else if (ret != length)
+ panic("recv() failed");
+
+ return (ret == length);
+}
+
+void
+TCPIface::syncRaw(MultiHeaderPkt::MsgType sync_req, Tick sync_tick)
+{
+ /*
+ * Barrier is simply implemented by point-to-point messages to the server
+ * for now. This method is called by only one TCPIface object.
+ * The server will send back an 'ack' message when it gets the
+ * sync request from all clients.
+ */
+ MultiHeaderPkt::Header header_pkt;
+ header_pkt.msgType = sync_req;
+ header_pkt.sendTick = sync_tick;
+
+ for (auto s : sockRegistry)
+ sendTCP(s, (void *)&header_pkt, sizeof(header_pkt));
+}
+
diff --git a/src/dev/net/tcp_iface.hh b/src/dev/net/tcp_iface.hh
new file mode 100644
index 000000000..2eb2c1c07
--- /dev/null
+++ b/src/dev/net/tcp_iface.hh
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabor Dozsa
+ */
+
+/* @file
+ * TCP stream socket based interface class for multi gem5 runs.
+ *
+ * For a high level description about multi gem5 see comments in
+ * header file multi_iface.hh.
+ *
+ * The TCP subclass of MultiIface uses a separate server process
+ * (see tcp_server.[hh,cc] under directory gem5/util/multi). Each gem5
+ * process connects to the server via a stream socket. The server process
+ * transfers messages and co-ordinates the synchronisation among the gem5
+ * peers.
+ */
+#ifndef __DEV_NET_TCP_IFACE_HH__
+#define __DEV_NET_TCP_IFACE_HH__
+
+
+#include <string>
+
+#include "dev/net/multi_iface.hh"
+
+class EventManager;
+
+class TCPIface : public MultiIface
+{
+ private:
+ /**
+ * The stream socket to connect to the server.
+ */
+ int sock;
+
+ /**
+ * Registry for all sockets to the server opened by this gem5 process.
+ */
+ static std::vector<int> sockRegistry;
+
+ private:
+
+ /**
+ * Send out a message through a TCP stream socket.
+ *
+ * @param sock TCP stream socket.
+ * @param buf Start address of the message.
+ * @param length Size of the message in bytes.
+ */
+ void
+ sendTCP(int sock, void *buf, unsigned length);
+
+ /**
+ * Receive the next incoming message through a TCP stream socket.
+ *
+ * @param sock TCP stream socket.
+ * @param buf Start address of buffer to store the message.
+ * @param length Exact size of the expected message in bytes.
+ */
+ bool recvTCP(int sock, void *buf, unsigned length);
+
+
+ protected:
+
+ virtual void
+ sendRaw(void *buf, unsigned length,
+ const MultiHeaderPkt::AddressType dest_addr=nullptr) override
+ {
+ sendTCP(sock, buf, length);
+ }
+
+ virtual bool recvRaw(void *buf, unsigned length) override
+ {
+ return recvTCP(sock, buf, length);
+ }
+
+ virtual void syncRaw(MultiHeaderPkt::MsgType sync_req,
+ Tick sync_tick) override;
+
+ public:
+ /**
+ * The ctor creates and connects the stream socket to the server.
+ * @param server_name The name (or IP address) of the host running the
+ * server process.
+ * @param server_port The port number the server listening for new
+ * connections.
+ * @param sync_start The tick for the first multi synchronisation.
+ * @param sync_repeat The frequency of multi synchronisation.
+ * @param em The EventManager object associated with the simulated
+ * Ethernet link.
+ */
+ TCPIface(std::string server_name, unsigned server_port,
+ unsigned multi_rank, Tick sync_start, Tick sync_repeat,
+ EventManager *em);
+
+ ~TCPIface() override;
+};
+
+#endif // __DEV_NET_TCP_IFACE_HH__