| /* | |
| * 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 <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/etherdump.hh" | |
| #include "dev/etherint.hh" | |
| #include "dev/etherlink.hh" | |
| #include "dev/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) | |
| { } | |
| void | |
| EtherLink::serialize(ostream &os) | |
| { | |
| link[0]->serialize("link0", os); | |
| link[1]->serialize("link1", os); | |
| } | |
| void | |
| EtherLink::unserialize(Checkpoint *cp, const string §ion) | |
| { | |
| link[0]->unserialize("link0", cp, section); | |
| link[1]->unserialize("link1", cp, section); | |
| } | |
| void | |
| EtherLink::Link::txComplete(EthPacketPtr packet) | |
| { | |
| DPRINTF(Ethernet, "packet received: len=%d\n", packet->length); | |
| DDUMP(EthernetData, packet->data, packet->length); | |
| rxint->sendPacket(packet); | |
| } | |
| class LinkDelayEvent : public Event | |
| { | |
| protected: | |
| EtherLink::Link *link; | |
| EthPacketPtr packet; | |
| public: | |
| // non-scheduling version for createForUnserialize() | |
| LinkDelayEvent(); | |
| LinkDelayEvent(EtherLink::Link *link, EthPacketPtr pkt); | |
| void process(); | |
| virtual void serialize(ostream &os); | |
| void unserialize(Checkpoint *cp, const string §ion) {} | |
| void unserialize(Checkpoint *cp, const string §ion, | |
| EventQueue *eventq); | |
| static Serializable *createForUnserialize(Checkpoint *cp, | |
| const string §ion); | |
| }; | |
| void | |
| EtherLink::Link::txDone() | |
| { | |
| if (dump) | |
| dump->dump(packet); | |
| if (linkDelay > 0) { | |
| DPRINTF(Ethernet, "packet delayed: delay=%d\n", linkDelay); | |
| Event *event = new LinkDelayEvent(this, packet); | |
| parent->schedule(event, curTick() + linkDelay); | |
| } else { | |
| txComplete(packet); | |
| } | |
| packet = 0; | |
| assert(!busy()); | |
| txint->sendDone(); | |
| } | |
| 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, ostream &os) | |
| { | |
| bool packet_exists = packet != nullptr; | |
| paramOut(os, base + ".packet_exists", packet_exists); | |
| if (packet_exists) | |
| packet->serialize(base + ".packet", os); | |
| bool event_scheduled = doneEvent.scheduled(); | |
| paramOut(os, base + ".event_scheduled", event_scheduled); | |
| if (event_scheduled) { | |
| Tick event_time = doneEvent.when(); | |
| paramOut(os, base + ".event_time", event_time); | |
| } | |
| } | |
| void | |
| EtherLink::Link::unserialize(const string &base, Checkpoint *cp, | |
| const string §ion) | |
| { | |
| bool packet_exists; | |
| paramIn(cp, section, base + ".packet_exists", packet_exists); | |
| if (packet_exists) { | |
| packet = make_shared<EthPacketData>(16384); | |
| packet->unserialize(base + ".packet", cp, section); | |
| } | |
| bool event_scheduled; | |
| paramIn(cp, section, base + ".event_scheduled", event_scheduled); | |
| if (event_scheduled) { | |
| Tick event_time; | |
| paramIn(cp, section, base + ".event_time", event_time); | |
| parent->schedule(doneEvent, event_time); | |
| } | |
| } | |
| LinkDelayEvent::LinkDelayEvent() | |
| : Event(Default_Pri, AutoSerialize | AutoDelete), link(NULL) | |
| { | |
| } | |
| LinkDelayEvent::LinkDelayEvent(EtherLink::Link *l, EthPacketPtr p) | |
| : Event(Default_Pri, AutoSerialize | AutoDelete), link(l), packet(p) | |
| { | |
| } | |
| void | |
| LinkDelayEvent::process() | |
| { | |
| link->txComplete(packet); | |
| } | |
| void | |
| LinkDelayEvent::serialize(ostream &os) | |
| { | |
| paramOut(os, "type", string("LinkDelayEvent")); | |
| Event::serialize(os); | |
| EtherLink *parent = link->parent; | |
| bool number = link->number; | |
| SERIALIZE_OBJPTR(parent); | |
| SERIALIZE_SCALAR(number); | |
| packet->serialize("packet", os); | |
| } | |
| void | |
| LinkDelayEvent::unserialize(Checkpoint *cp, const string §ion, | |
| EventQueue *eventq) | |
| { | |
| Event::unserialize(cp, section, eventq); | |
| EtherLink *parent; | |
| bool number; | |
| UNSERIALIZE_OBJPTR(parent); | |
| UNSERIALIZE_SCALAR(number); | |
| link = parent->link[number]; | |
| packet = make_shared<EthPacketData>(16384); | |
| packet->unserialize("packet", cp, section); | |
| } | |
| Serializable * | |
| LinkDelayEvent::createForUnserialize(Checkpoint *cp, const string §ion) | |
| { | |
| return new LinkDelayEvent(); | |
| } | |
| REGISTER_SERIALIZEABLE("LinkDelayEvent", LinkDelayEvent) | |
| EtherLink * | |
| EtherLinkParams::create() | |
| { | |
| return new EtherLink(this); | |
| } |