std::string interfaceName = csprintf("%s.interface%d", name(), i);
Interface *interface = new Interface(interfaceName, this,
p->output_buffer_size, p->delay,
- p->delay_var, p->fabric_speed);
+ p->delay_var, p->fabric_speed, i);
interfaces.push_back(interface);
}
}
return interface;
}
+bool
+EtherSwitch::Interface::PortFifo::push(EthPacketPtr ptr, unsigned senderId)
+{
+ assert(ptr->length);
+
+ _size += ptr->length;
+ fifo.emplace_hint(fifo.end(), ptr, curTick(), senderId);
+
+ // Drop the extra pushed packets from end of the fifo
+ while (avail() < 0) {
+ DPRINTF(Ethernet, "Fifo is full. Drop packet: len=%d\n",
+ std::prev(fifo.end())->packet->length);
+
+ _size -= std::prev(fifo.end())->packet->length;
+ fifo.erase(std::prev(fifo.end()));
+ }
+
+ if (empty()) {
+ warn("EtherSwitch: Packet length (%d) exceeds the maximum storage "
+ "capacity of port fifo (%d)", ptr->length, _maxsize);
+ }
+
+ // Return true if the newly pushed packet gets inserted
+ // at the head of the queue, otherwise return false
+ // We need this information to deschedule the event that has been
+ // scheduled for the old head of queue packet and schedule a new one
+ if (!empty() && fifo.begin()->packet == ptr) {
+ return true;
+ }
+ return false;
+}
+
+void
+EtherSwitch::Interface::PortFifo::pop()
+{
+ if (empty())
+ return;
+
+ assert(_size >= fifo.begin()->packet->length);
+ // Erase the packet at the head of the queue
+ _size -= fifo.begin()->packet->length;
+ fifo.erase(fifo.begin());
+}
+
+void
+EtherSwitch::Interface::PortFifo::clear()
+{
+ fifo.clear();
+ _size = 0;
+}
+
EtherSwitch::Interface::Interface(const std::string &name,
EtherSwitch *etherSwitch,
uint64_t outputBufferSize, Tick delay,
- Tick delay_var, double rate)
+ Tick delay_var, double rate, unsigned id)
: EtherInt(name), ticksPerByte(rate), switchDelay(delay),
- delayVar(delay_var), parent(etherSwitch),
- outputFifo(outputBufferSize), txEvent(this)
+ delayVar(delay_var), interfaceId(id), parent(etherSwitch),
+ outputFifo(name + ".outputFifo", outputBufferSize), txEvent(this)
{
}
if (!receiver || destMacAddr.multicast() || destMacAddr.broadcast()) {
for (auto it : parent->interfaces)
if (it != this)
- it->enqueue(packet);
+ it->enqueue(packet, interfaceId);
} else {
DPRINTF(Ethernet, "sending packet from MAC %x on port "
"%s to MAC %x on port %s\n", uint64_t(srcMacAddr),
this->name(), uint64_t(destMacAddr), receiver->name());
- receiver->enqueue(packet);
+ receiver->enqueue(packet, interfaceId);
}
// At the output port, we either have buffer space (no drop) or
// don't (drop packet); in both cases packet is received on
}
void
-EtherSwitch::Interface::enqueue(EthPacketPtr packet)
+EtherSwitch::Interface::enqueue(EthPacketPtr packet, unsigned senderId)
{
- if (!outputFifo.push(packet)) {
- // output buffer full, drop packet
- DPRINTF(Ethernet, "output buffer full, drop packet\n");
- return;
- }
-
// assuming per-interface transmission events,
- // if there was nothing in the Fifo before push the
- // current packet, then we need to schedule an event at
- // curTick + switchingDelay to send this packet out the external link
+ // if the newly push packet gets inserted at the head of the queue
+ // (either there was nothing in the queue or the priority of the new
+ // packet was higher than the packets already in the fifo)
+ // then we need to schedule an event at
+ // "curTick" + "switchingDelay of the packet at the head of the fifo"
+ // to send this packet out the external link
// otherwise, there is already a txEvent scheduled
- if (!txEvent.scheduled()) {
- parent->schedule(txEvent, curTick() + switchingDelay());
+ if (outputFifo.push(packet, senderId)) {
+ parent->reschedule(txEvent, curTick() + switchingDelay());
}
}
EtherSwitch::serialize(CheckpointOut &cp) const
{
for (auto it : interfaces)
- it->serialize(it->name(), cp);
+ it->serializeSection(cp, it->name());
+
}
void
EtherSwitch::unserialize(CheckpointIn &cp)
{
for (auto it : interfaces)
- it->unserialize(it->name(), cp);
+ it->unserializeSection(cp, it->name());
+
}
void
-EtherSwitch::Interface::serialize(const std::string &base, CheckpointOut &cp)
-const
+EtherSwitch::Interface::serialize(CheckpointOut &cp) const
{
bool event_scheduled = txEvent.scheduled();
- paramOut(cp, base + ".event_scheduled", event_scheduled);
+ SERIALIZE_SCALAR(event_scheduled);
+
if (event_scheduled) {
Tick event_time = txEvent.when();
- paramOut(cp, base + ".event_time", event_time);
+ SERIALIZE_SCALAR(event_time);
}
-
- outputFifo.serialize(base + "outputFifo", cp);
+ outputFifo.serializeSection(cp, "outputFifo");
}
void
-EtherSwitch::Interface::unserialize(const std::string &base, CheckpointIn &cp)
+EtherSwitch::Interface::unserialize(CheckpointIn &cp)
{
bool event_scheduled;
- paramIn(cp, base + ".event_scheduled", event_scheduled);
+ UNSERIALIZE_SCALAR(event_scheduled);
+
if (event_scheduled) {
Tick event_time;
- paramIn(cp, base + ".event_time", event_time);
+ UNSERIALIZE_SCALAR(event_time);
parent->schedule(txEvent, event_time);
}
+ outputFifo.unserializeSection(cp, "outputFifo");
+}
+
+void
+EtherSwitch::Interface::PortFifoEntry::serialize(CheckpointOut &cp) const
+{
+ packet->serialize("packet", cp);
+ SERIALIZE_SCALAR(recvTick);
+ SERIALIZE_SCALAR(srcId);
+}
+
+void
+EtherSwitch::Interface::PortFifoEntry::unserialize(CheckpointIn &cp)
+{
+ packet = make_shared<EthPacketData>(16384);
+ packet->unserialize("packet", cp);
+ UNSERIALIZE_SCALAR(recvTick);
+ UNSERIALIZE_SCALAR(srcId);
+}
+
+void
+EtherSwitch::Interface::PortFifo::serialize(CheckpointOut &cp) const
+{
+ SERIALIZE_SCALAR(_size);
+ int fifosize = fifo.size();
- outputFifo.unserialize(base + "outputFifo", cp);
+ SERIALIZE_SCALAR(fifosize);
+
+ int i = 0;
+ for (const auto &entry : fifo)
+ entry.serializeSection(cp, csprintf("entry%d", i++));
+}
+
+void
+EtherSwitch::Interface::PortFifo::unserialize(CheckpointIn &cp)
+{
+ UNSERIALIZE_SCALAR(_size);
+ int fifosize;
+
+ UNSERIALIZE_SCALAR(fifosize);
+ fifo.clear();
+
+ for (int i = 0; i < fifosize; ++i) {
+ PortFifoEntry entry(nullptr, 0, 0);
+
+ entry.unserializeSection(cp, csprintf("entry%d", i));
+
+ fifo.insert(entry);
+
+ }
}
EtherSwitch *
#ifndef __DEV_ETHERSWITCH_HH__
#define __DEV_ETHERSWITCH_HH__
-#include <unordered_map>
+#include <map>
+#include <set>
#include "base/inet.hh"
#include "dev/net/etherint.hh"
/**
* Model for an Ethernet switch port
*/
- class Interface : public EtherInt
+ class Interface : public EtherInt, public Serializable
{
public:
Interface(const std::string &name, EtherSwitch *_etherSwitch,
uint64_t outputBufferSize, Tick delay, Tick delay_var,
- double rate);
+ double rate, unsigned id);
/**
* When a packet is received from a device, route it
* through an (several) output queue(s)
/**
* enqueue packet to the outputFifo
*/
- void enqueue(EthPacketPtr packet);
+ void enqueue(EthPacketPtr packet, unsigned senderId);
void sendDone() {}
Tick switchingDelay();
Interface* lookupDestPort(Net::EthAddr destAddr);
void learnSenderAddr(Net::EthAddr srcMacAddr, Interface *sender);
- void serialize(const std::string &base, CheckpointOut &cp) const;
- void unserialize(const std::string &base, CheckpointIn &cp);
+ void serialize(CheckpointOut &cp) const;
+ void unserialize(CheckpointIn &cp);
private:
const double ticksPerByte;
const Tick switchDelay;
const Tick delayVar;
+ const unsigned interfaceId;
+
EtherSwitch *parent;
+ protected:
+ struct PortFifoEntry : public Serializable
+ {
+ PortFifoEntry(EthPacketPtr pkt, Tick recv_tick, unsigned id)
+ : packet(pkt), recvTick(recv_tick), srcId(id) {}
+
+ EthPacketPtr packet;
+ Tick recvTick;
+ // id of the port that the packet has been received from
+ unsigned srcId;
+ ~PortFifoEntry()
+ {
+ packet = nullptr;
+ recvTick = 0;
+ srcId = 0;
+ }
+ void serialize(CheckpointOut &cp) const;
+ void unserialize(CheckpointIn &cp);
+ };
+
+ class PortFifo : public Serializable
+ {
+ protected:
+ struct EntryOrder {
+ bool operator() (const PortFifoEntry& lhs,
+ const PortFifoEntry& rhs) const
+ {
+ if (lhs.recvTick == rhs.recvTick)
+ return lhs.srcId < rhs.srcId;
+ else
+ return lhs.recvTick < rhs.recvTick;
+ }
+ };
+ std::set<PortFifoEntry, EntryOrder> fifo;
+
+ const std::string objName;
+ const unsigned _maxsize;
+ unsigned _size;
+
+ public:
+ PortFifo(const std::string &name, int max)
+ :objName(name), _maxsize(max), _size(0) {}
+ ~PortFifo() {}
+
+ const std::string name() { return objName; }
+ // Returns the available capacity of the fifo.
+ // It can return a negative value because in "push" function
+ // we first push the received packet into the fifo and then
+ // check if we exceed the available capacity (if avail() < 0)
+ // and remove packets from the end of fifo
+ int avail() const { return _maxsize - _size; }
+
+ EthPacketPtr front() { return fifo.begin()->packet; }
+ bool empty() const { return _size == 0; }
+ unsigned size() const { return _size; }
+
+ /**
+ * Push a packet into the fifo
+ * and sort the packets with same recv tick by port id
+ */
+ bool push(EthPacketPtr ptr, unsigned senderId);
+ void pop();
+ void clear();
+ /**
+ * Serialization stuff
+ */
+ void serialize(CheckpointOut &cp) const;
+ void unserialize(CheckpointIn &cp);
+ };
/**
* output fifo at each interface
*/
- PacketFifo outputFifo;
+ PortFifo outputFifo;
void transmit();
EventWrapper<Interface, &Interface::transmit> txEvent;
};