Krishna Srinivasan has uploaded this change for review. ( https://gem5-review.googlesource.com/c/public/gem5/+/13024

Change subject: mem, misc: Added PCI Express links to work with my PCI Express switch and root complex
......................................................................

mem, misc: Added PCI Express links to work with my PCI Express switch and root complex

Change-Id: I5aea0aac7cd21da93a147399813a9038325cc0cb
---
M src/dev/net/Ethernet.py
M src/mem/SConscript
A src/mem/pcie_link.cc
A src/mem/pcie_link.hh
4 files changed, 1,561 insertions(+), 2 deletions(-)



diff --git a/src/dev/net/Ethernet.py b/src/dev/net/Ethernet.py
index 71665c5..8264573 100644
--- a/src/dev/net/Ethernet.py
+++ b/src/dev/net/Ethernet.py
@@ -43,6 +43,21 @@
 from m5.params import *
 from m5.proxy import *
 from PciDevice import PciDevice
+from MemObject import MemObject
+
+class PCIELink(MemObject):
+    type = 'PCIELink'
+    cxx_header = 'mem/pcie_link.hh'
+    upstreamSlave = SlavePort("upstream slaveport")
+    downstreamMaster = MasterPort("downstream masterport for pio requests")
+    upstreamMaster   = MasterPort("upstream master port for dma requests")
+    downstreamSlave  = SlavePort("downstream slave port")
+    delay = Param.Latency('0us', "packet transmit delay")
+    delay_var = Param.Latency('0ns', "packet transmit delay variability")
+    speed = Param.NetworkBandwidth('2.5Gbps', "link speed") #Gen1, 2 or 3
+    mps = Param.Int('64' , "Max Payload Size in Bytes")
+    max_queue_size = Param.Int('4' , "Size of the replay buffer")
+    lanes = Param.Int('1' , "Number of lanes on link") # 1,2,4 ,8 or 16

 class EtherObject(SimObject):
     type = 'EtherObject'
@@ -88,7 +103,7 @@
     type = 'EtherSwitch'
     cxx_header = "dev/net/etherswitch.hh"
     dump = Param.EtherDump(NULL, "dump object")
- fabric_speed = Param.NetworkBandwidth('10Gbps', "switch fabric speed in bits " + fabric_speed = Param.NetworkBandwidth('10Gbps',"switch fabric speed in bit"
                                           "per second")
     interface = VectorMasterPort("Ethernet Interface")
output_buffer_size = Param.MemorySize('1MB', "size of output port buffers")
diff --git a/src/mem/SConscript b/src/mem/SConscript
index b9d5672..1eb9de3 100644
--- a/src/mem/SConscript
+++ b/src/mem/SConscript
@@ -82,6 +82,7 @@
 Source('hmc_controller.cc')
 Source('serial_link.cc')
 Source('mem_delay.cc')
+Source('pcie_link.cc')

 if env['TARGET_ISA'] != 'null':
     Source('fs_translating_port_proxy.cc')
@@ -119,7 +120,7 @@
 DebugFlag("DRAMSim2")
 DebugFlag('HMCController')
 DebugFlag('SerialLink')
-
+DebugFlag('PCI_EXPRESS')
 DebugFlag("MemChecker")
 DebugFlag("MemCheckerMonitor")
 DebugFlag("QOS")
diff --git a/src/mem/pcie_link.cc b/src/mem/pcie_link.cc
new file mode 100644
index 0000000..0c54d56
--- /dev/null
+++ b/src/mem/pcie_link.cc
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) Krishna Srinivasan
+ * 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: Krishna Parasuram Srinivasan
+ */
+
+/**
+ * @file
+ * Definition of a PCI Express Link
+ */
+
+
+
+#include "mem/pcie_link.hh"
+
+#include <cmath>
+
+#include "base/inifile.hh"
+#include "base/intmath.hh"
+#include "base/random.hh"
+#include "base/str.hh"
+#include "base/trace.hh"
+#include "debug/PCI_EXPRESS.hh"
+#include "sim/core.hh"
+#include "sim/serialize.hh"
+#include "sim/system.hh"
+
+PCIELinkPacket :: PCIELinkPacket()
+{
+    // Initialize a PCIELinkPacket with null and zero values
+    pkt = NULL ;
+    dllp = NULL ;
+    isDLLP = false ;
+    isTLP = false ;
+    seqNum = 0 ;
+}
+
+int
+PCIELinkPacket :: getSize()
+{
+ // if pkt != NULL, this PCIELinkPacket encapsulates a TLP (Gem5 packet).
+    // Calc size accordingly taking overheads and payload size into account
+
+    if (pkt != NULL) {
+        if (pkt->isRequest() && pkt->hasData()) {
+ return (PCIE_REQUEST_HEADER_SIZE + pkt->getSize() + DLL_OVERHEAD
+                    + PHYSICAL_OVERHEAD) * ENCODING_FACTOR ;
+
+        } else if (pkt->isRequest()) {
+            return (PCIE_REQUEST_HEADER_SIZE + DLL_OVERHEAD +
+                    PHYSICAL_OVERHEAD) * ENCODING_FACTOR ;
+
+        } else if (pkt->isResponse() && pkt->hasData()) {
+            return (PCIE_RESPONSE_HEADER_SIZE + pkt->getSize() +
+                    DLL_OVERHEAD + PHYSICAL_OVERHEAD) * ENCODING_FACTOR ;
+        } else {
+            return (PCIE_RESPONSE_HEADER_SIZE  + DLL_OVERHEAD +
+                    PHYSICAL_OVERHEAD) * ENCODING_FACTOR ;
+        }
+    }
+
+    // if dllp != NULL, this PCIELinkPacket encapsulates an ACK DLLP
+    if (dllp != NULL)
+        return (DLLP_SIZE + PHYSICAL_OVERHEAD) * ENCODING_FACTOR  ;
+    return 0 ;
+}
+
+
+ReplayBuffer :: ReplayBuffer (int max_size)
+{
+    // Initialize the buffer with null ptrs, indicating an empty buffer
+    maximumSize = max_size ;
+    for (int i = 0 ; i < max_size ; i++)
+        queue.push_back(NULL) ;
+}
+
+int
+ReplayBuffer::size()
+{
+    // Count the number of entries in the replay buffer by counting the
+    // number of non-null ptrs stored.
+    int count = 0 ;
+    for (std::deque<PCIELinkPacket*>::iterator it = queue.begin() ;
+         it != queue.end() ; it++) {
+
+        if ((*it) != NULL) count++ ;
+    }
+    return count ;
+}
+
+
+void
+ReplayBuffer::pushBack(PCIELinkPacket * ptr)
+{
+    // Store a PCIELinkPacket ptr at the back of the replay buffer.
+    if (size() >= maximumSize)
+        return;
+    for (std::deque<PCIELinkPacket*>::iterator it = queue.begin() ;
+             it != queue.end() ; it++) {
+
+        if (*it == NULL) {
+            *it = ptr ;
+            break ;
+        }
+    }
+}
+
+PCIELinkPacket *
+ReplayBuffer :: popFront()
+{
+ // Remove and return the PCIELinkPacket ptr stored at the front of the buff
+    if (queue.front() == NULL)
+        return NULL ;
+
+    PCIELinkPacket * temp = queue.front() ;
+    queue.pop_front() ;
+    queue.push_back(NULL) ;
+    return temp ;
+}
+
+
+PCIELinkPacket *
+ReplayBuffer :: get (int idx)
+{
+    // Get the PCIELinkPacket ptr stored at the idx position in the buffer.
+    if (idx > maximumSize)
+        return NULL ;
+    return queue[idx] ;
+}
+
+bool
+ReplayBuffer::empty()
+{
+    // Is the buffer empty ? Check for non-null values stored in it.
+    for (std::deque<PCIELinkPacket*>::iterator it = queue.begin() ;
+         it != queue.end() ; it++) {
+
+        if (*it != NULL)
+            return false ;
+    }
+    return true ;
+
+}
+
+PCIELink :: Link :: Link(const std::string & _name, PCIELink *p,
+                         double rate, Tick delay, Tick delay_var,
+                         LinkMasterPort * master_port_src,
+                         LinkSlavePort *  slave_port_src,
+                         LinkMasterPort * master_port_dest,
+                         LinkSlavePort *  slave_port_dest,
+                         int num_lanes, int max_queue_size,
+                         int mps, Link ** opposite_link)
+
+    : __name(_name), parent(p), ticksPerByte(rate), linkDelay(delay),
+      delayVar(delay_var), masterPortSrc(master_port_src),
+      slavePortSrc(slave_port_src),
+      masterPortDest(master_port_dest),
+      slavePortDest(slave_port_dest),
+ lanes(num_lanes), packet(NULL), doneEvent([this]{txDone() ; } , _name),
+      txQueueEvent([this]{processTxQueue();} , _name),
+      timeoutEvent([this]{timeoutFunc() ; }, _name),
+      ackEvent([this]{sendAck() ; }, _name),
+      buffer(max_queue_size), sendSeqNum(0), recvSeqNum(0), lastAcked(0),
+      maxQueueSize(max_queue_size), retryResp(false), retryReq(false),
+      replayPacket(NULL), replayDLLP(NULL), retransmit(false),
+      retransmitIdx(0), otherLink(opposite_link)
+{
+    // Assign ack_factor based on number of lanes.
+    float ack_factor = 1.4 ;
+    if (lanes == 8) {
+        ack_factor = 2.5 ;
+    }
+
+    if (lanes >=12) {
+        ack_factor = 3.0 ;
+    }
+
+    // Calculate the retryTime based on the formula in pcie_link.hh
+
+ // Time to transmit a byte on an x1 link ; ticksperByte * ENCODING_FACTOR
+    float symbol_time = ticksPerByte * ENCODING_FACTOR  ;
+
+    // assume internal delay of 0 and ack factor of 1.4/2.5/3.0
+    retryTime = (((mps + PCIE_REQUEST_HEADER_SIZE + DLL_OVERHEAD +
+                    PHYSICAL_OVERHEAD)*3*ack_factor)/lanes) * symbol_time ;
+
+}
+
+PCIELink::LinkMasterPort::LinkMasterPort (const std::string & _name,
+                                          PCIELink * parent_ptr,
+                                          Link ** transmit_link_dptr)
+
+    : MasterPort(_name, parent_ptr),
+      parent(parent_ptr),
+      transmitLink(transmit_link_dptr)
+
+{}
+
+
+PCIELink::LinkSlavePort::LinkSlavePort (const std::string & _name,
+                                        PCIELink * parent_ptr,
+                                        Link ** transmit_link_dptr)
+    : SlavePort(_name, parent_ptr),
+      parent(parent_ptr),
+      transmitLink(transmit_link_dptr)
+
+{}
+
+
+
+PCIELink :: PCIELink(Params *p)
+
+    : MemObject(p),
+      upstreamSlave(p->name +".upstream_slave", this, &(this->links[0])),
+ downstreamSlave(p->name +".downstream_slave", this, &(this->links[1])), + upstreamMaster(p->name + ".upstream_master", this , &(this->links[0])), + downstreamMaster(p->name +".downstream_master", this, &(this->links[1]))
+{
+    // Dynamically create the 2 Links that make up the PCIELink
+
+    links[0] = new Link (p->name + ".down_link", this, p->speed, p->delay,
+                         p->delay_var, (LinkMasterPort*)&upstreamMaster,
+                         (LinkSlavePort*) & upstreamSlave,
+                         (LinkMasterPort*)&downstreamMaster,
+                         (LinkSlavePort*)&downstreamSlave,
+                         p->lanes, p->max_queue_size, p->mps,
+                         (Link**)&(this->links[1])) ;
+
+   links[1] = new Link  (p->name + ".up_link", this, p->speed, p->delay,
+                         p->delay_var, (LinkMasterPort*)&downstreamMaster,
+                         (LinkSlavePort*)&downstreamSlave,
+                         (LinkMasterPort*)&upstreamMaster,
+                         (LinkSlavePort*)&upstreamSlave,
+                         p->lanes, p->max_queue_size, p->mps,
+                         (Link**)&(this->links[0])) ;
+
+}
+
+bool
+PCIELink::Link::transmit(PCIELinkPacket * pkt)
+{
+    if (busy()) {
+        DPRINTF(PCI_EXPRESS, "packet not sent, link busy\n");
+        return false;
+    }
+
+    DPRINTF(PCI_EXPRESS, "packet sent: len=%d\n", pkt->getSize());
+
+    packet = pkt;
+
+ // calculate the time to transmit a PCIELinkPacket on the unidirectional
+    // link based on the configured bandwidth and number of lanes
+
+ Tick delay = (Tick)ceil(((double)pkt->getSize()*(ticksPerByte/lanes))+1.0);
+    if (delayVar != 0)
+        delay += random_mt.random<Tick>(0, delayVar);
+
+    DPRINTF(PCI_EXPRESS, "scheduling packet: delay=%d, (rate=%f)\n", delay,
+           ticksPerByte);
+
+    // schedule the done event after a time corresponding to the time taken
+    // to transmit a packet on the unidirectional link
+    parent->schedule(doneEvent, curTick() + delay);
+
+    return true;
+}
+
+void
+PCIELink::Link::txDone()
+{
+
+ // If there is a delay assigned to the unidirectional link,queue the packet
+    // again till the delay time gets over. Used to model propagation delay
+    if (linkDelay > 0) {
+
+        DPRINTF(PCI_EXPRESS, "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);
+    }
+
+    PCIELinkPacket * _packet = packet ;
+
+    // This packet has finished transmission. Hence set it as NULL.
+    packet = NULL ;
+
+    assert(!busy());
+
+ // If an Ack DLLP id pending, transmit the Ack. Acks have highest priority.
+    if (replayDLLP != NULL) {
+        bool flag = transmit(replayDLLP) ;
+        if (flag) {
+            replayDLLP = NULL ;
+        } else {
+ return ; // This return should never take place. Add sanity checks.
+        }
+    }
+
+ // If a TLP has been transmitted for the first time, start the replay timer
+    // Also store the corresponding PCIELinkPAcket in the replay buffer.
+ // If any TLPs were unable to be accepted by the link interface due to an
+    // occupied link, make the attached device send them again.
+
+    if ( _packet == replayPacket) {
+
+        buffer.pushBack(_packet) ;
+        replayPacket = NULL ;
+        if (!timeoutEvent.scheduled()) {
+            parent->schedule(timeoutEvent , curTick() + retryTime) ;
+        }
+        if (retryReq) {
+            retryReq = false ;
+            slavePortSrc->sendRetryReq();
+        }
+        if (retryResp) {
+            retryResp = false ;
+            masterPortSrc->sendRetryResp() ;
+        }
+    }
+
+
+    // Retransmit a packet from the replay buffer if the replay timer has
+ // expired. Increment retransmitIdx so the next packet in replay buffer is
+    // retransmitted.
+
+    if (retransmit)
+    {
+
+        if (!timeoutEvent.scheduled()) {
+            parent->schedule(timeoutEvent , curTick() + retryTime) ;
+        }
+
+        if (retransmitIdx >= buffer.size()) {
+            retransmitIdx = 0 ;
+            retransmit = false ;
+            if (retryReq) {
+                retryReq = false ;
+                slavePortSrc->sendRetryReq();
+             }
+            if (retryResp) {
+                retryResp = false ;
+                masterPortSrc->sendRetryResp() ;
+            }
+        } else {
+            transmit(buffer.get(retransmitIdx) ) ;
+            retransmitIdx ++ ;
+            return ;
+        }
+    }
+
+    // Transmit pending TLP if any.
+    if (replayPacket != NULL) {
+        transmit(replayPacket) ;
+    }
+
+}
+
+void
+PCIELink::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);
+}
+
+void
+PCIELink::Link::txComplete(PCIELinkPacket * packet)
+{
+    // Call the receive function of the opposite link interface and pass
+    // the packet to it.
+    DPRINTF(PCI_EXPRESS, "packet received: len=%d\n", packet->getSize());
+
+    (*otherLink)->linkReceiveInterface(packet) ;
+}
+
+
+
+
+void
+PCIELink :: Link :: linkReceiveInterface ( PCIELinkPacket * packet)
+{
+
+    if (packet->isTLP) {
+
+        // If a TLP is received, check its sequence number. Attempt to send
+        // the Gem5 packet to the attached device, if the sequence  number
+        // equals recvSeqNum. This check preserves order of packets across
+        // the link.
+        if (packet->seqNum == recvSeqNum) {
+
+            bool success = (packet->pkt->isResponse()) ?
+                           slavePortSrc->sendTimingResp(packet->pkt) :
+                           masterPortSrc->sendTimingReq(packet->pkt) ;
+            if (!success)
+                return ;
+
+            recvSeqNum ++ ;
+        }
+
+        if (!ackEvent.scheduled()) {
+
+            // If Gem5 packet is successfully sent to the attached device,
+            // return an Ack to the interface which sent this packet.
+            PCIELinkPacket * _packet = new PCIELinkPacket ;
+            _packet->isDLLP = true ;
+            _packet->dllp = new DataLinkPacket(recvSeqNum -1) ;
+
+            bool _transmit = transmit(_packet) ;
+            if (!_transmit) {
+                 replayDLLP = _packet ;
+                 lastAcked = recvSeqNum - 1 ;
+                 parent->schedule(ackEvent, curTick() + retryTime/3) ;
+            }
+        }
+
+        return ;
+
+   } else if (packet->isDLLP) {
+
+        // If an Ack is received, first reset and hold the replay timer.
+        // Next remove all PCIELinkPackets from the replay buffer that
+        // encapsulate a TLP with a sequence number <= Ack sequence num.
+
+        uint64_t seq_num = packet->dllp->seqNum ;
+        if (timeoutEvent.scheduled())
+            parent->deschedule(timeoutEvent) ;
+
+        bool flag = false ;
+        while (!flag) {
+            PCIELinkPacket * temp = buffer.front() ;
+            if (temp == NULL) {
+                flag = true ;
+            } else if (temp->seqNum <= seq_num) {
+                buffer.popFront() ;
+                delete temp ;
+            } else {
+
+                flag = true ;
+                parent->schedule(timeoutEvent , curTick() + retryTime) ;
+            }
+        }
+
+        delete packet->dllp ;
+        delete packet ;
+        if (retryReq) {
+            retryReq = false ;
+            slavePortSrc->sendRetryReq() ;
+        }
+        if (retryResp) {
+            retryResp = false ;
+            masterPortSrc->sendRetryResp() ;
+        }
+    }
+}
+
+
+
+
+bool PCIELink::Link::linkTransmitInterface(PacketPtr pkt , bool master)
+{
+    // If replay buffer is full or if a retransmission is in
+    // place, reject the packet.
+    // replayPacket is non-null if either a TLP is waiting to be
+    // transmitted on the link , or if a TLP is currently being transmitted
+    // on the link.
+ if (replayPacket != NULL || buffer.size() >= maxQueueSize || retransmit) {
+        if (!master) {
+            retryReq = true ;
+        } else {
+            retryResp = true ;
+        }
+    return false ;
+  }
+
+    // Encapsulate the Gem5 packet within a PCIELinkPAcket and assign it a
+ // sequence number. Transmit the PCIELinkPacket on the unidirectional link.
+    PCIELinkPacket * _packet = new PCIELinkPacket ;
+    _packet->isTLP = true ;
+    _packet->pkt = pkt ;
+    _packet->seqNum = sendSeqNum ;
+    replayPacket = _packet ;
+    transmit(_packet) ;
+    sendSeqNum ++ ;
+    return true ;
+}
+
+
+
+bool
+PCIELink :: LinkSlavePort :: recvTimingReq(PacketPtr pkt)
+{
+ // calls the transmit function of the link interface attached to the port.
+    return (*transmitLink)->linkTransmitInterface(pkt , false) ;
+}
+
+Tick
+PCIELink :: LinkSlavePort :: recvAtomic(PacketPtr pkt)
+{
+    (*transmitLink)->masterPortDest->sendAtomic(pkt) ;
+
+    // Timing of atomic requests not modelled.
+    return 0 ;
+}
+
+void
+PCIELink :: LinkSlavePort :: recvFunctional(PacketPtr pkt)
+{
+    // Treat functional requests the same as timing ones ?
+    recvTimingReq(pkt);
+}
+
+bool
+PCIELink :: LinkMasterPort :: recvTimingResp(PacketPtr pkt)
+{
+    // calls the transmit function of the link interface attached to port.
+    return (*transmitLink)->linkTransmitInterface(pkt , true) ;
+}
+
+
+void
+PCIELink :: Link ::timeoutFunc()
+{
+    if (buffer.size() == 0 || buffer.front() == NULL || retransmit) {
+        return ;
+    }
+
+ // Attempt to initiate a retransmission by retransmitting the first packet
+    // from the replay buffer.
+    bool success = transmit(buffer.front()) ;
+    retransmit = true ;
+    retransmitIdx = (success) ? 1 : 0 ;
+}
+
+
+
+void
+PCIELink :: Link::sendAck()
+{
+    // Check if any unacked , but successfully received packets exist.
+    if (lastAcked == recvSeqNum - 1 )
+        return ;
+
+ // Create an Ack DLLP with the sequence number equivalent to the sequence
+    // number of most recently received unAcked packet.
+    PCIELinkPacket * _packet = new PCIELinkPacket ;
+    _packet->isDLLP = true ;
+    _packet->dllp = new DataLinkPacket(recvSeqNum - 1) ;
+    lastAcked = recvSeqNum - 1 ;
+    parent->schedule(ackEvent , curTick() + retryTime/3) ;
+    bool success = transmit(_packet) ;
+    if (!success)
+        replayDLLP = _packet ;
+}
+
+
+void
+PCIELink::init()
+{
+    if (!upstreamMaster.isConnected() || !upstreamSlave.isConnected() ||
+       !downstreamMaster.isConnected() || !downstreamSlave.isConnected()) {
+
+        fatal ("PCIE Link Ports must be connected !!\n") ;
+    }
+}
+
+
+BaseMasterPort&
+PCIELink::getMasterPort(const std::string &if_name, PortID idx)
+{
+    if (if_name == "downstreamMaster") {
+        return downstreamMaster;
+    } else if (if_name == "upstreamMaster") {
+        return upstreamMaster ;
+    } else {
+        // pass it along to our super class
+        return MemObject::getMasterPort(if_name, idx);
+    }
+}
+
+BaseSlavePort&
+PCIELink::getSlavePort(const std::string &if_name, PortID idx)
+{
+    if (if_name == "upstreamSlave") {
+        return upstreamSlave;
+    } else if (if_name == "downstreamSlave") {
+        return downstreamSlave ;
+    } else {
+        // pass it along to our super class
+        return MemObject::getSlavePort(if_name, idx);
+    }
+}
+
+
+PCIELink *
+PCIELinkParams::create()
+{
+    return new PCIELink(this);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mem/pcie_link.hh b/src/mem/pcie_link.hh
new file mode 100644
index 0000000..1aa5d21
--- /dev/null
+++ b/src/mem/pcie_link.hh
@@ -0,0 +1,877 @@
+/*
+ * Copyright (c) Krishna Srinivasan
+ * 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: Krishna Parasuram Srinivasan
+ */
+
+/**
+ * @file
+ * Declarations of classes in a PCI Express Link
+ */
+
+
+#ifndef __MEM_PCIE_LINK_HH__
+#define __MEM_PCIE_LINK_HH__
+
+#include <deque>
+#include <queue>
+#include <string>
+
+#include "base/cp_annotate.hh"
+#include "base/types.hh"
+#include "mem/mem_object.hh"
+#include "mem/port.hh"
+#include "params/PCIELink.hh"
+#include "sim/eventq.hh"
+
+/**
+ * TLP request and resp. header size is 12B with 32 bit PCI Express addresses
+ */
+#define PCIE_REQUEST_HEADER_SIZE 12
+
+#define PCIE_RESPONSE_HEADER_SIZE 12
+
+/**
+ * Data Link Layer overhead added to a TLP : Sequence number + LCRC - 6B
+ */
+#define DLL_OVERHEAD 6
+
+/**
+ * Physical layer overhead is 2B, due to framing characterss
+ */
+#define PHYSICAL_OVERHEAD 2
+
+/**
+ * 8b/10b encoding used for Gen 2 PCI Express
+ */
+#define ENCODING_FACTOR 1.25
+
+/**
+ * Size of DLLP is 6B
+ */
+#define DLLP_SIZE 6
+
+
+/**
+ * This header file contains class definitions used in a PCI Express Link.
+ * There are 4 main classes declared in this file
+ *
+ * 1. DataLinkPacket - Used to represent a data link layer packet in Gem5.
+ * Data Link Layer packets are generated and consumed at
+ *                     the ends of the PCI Express Link.
+ *
+ * 2. PCIELinkPacket - This class is used to represent a packet transmitted
+ * across the link. The transmitted packet can be either a
+ *                     Gem5 packet or a DataLinkPacket(DLLP).
+ *
+ * 3. ReplayBuffer   - This class represents the replay buffer used for
+ *                     reliable transmission across a PCI Express Link.
+ *                     Each link interface has a replay buffer.
+ *
+ * 4. PCIELink       - This class is the actual PCI Express Link in Gem5.
+ *                     It consists of 2 link interfaces, with each link
+ * interface used to transmit packets from devices attached
+ *                     on that end of the link.
+ *
+ * It is important to note that in this PCI Express Link implementation, Gem5
+ * packets are considered to represent the Transaction Layer Packets (TLPs)
+ * present in the PCI Express protocol. The size of the Transaction Layer pack. + * is based on the size of the Gem5 packet, as well as overheads present in the
+ * PCI Express protocol, as defined above.
+ * Both TLPs and DLLPs are encapsulated in a PCIELinkPacket before being sent + * out onto the unidirectional link attached to a particular link interface.
+ */
+
+
+/**
+ * Class used to represent an Ack DLLP. The Ack DLLP indicates that a packet
+ * has been successfully received by the device on one end of the link.
+ * The Ack DLLP contains a sequence number that indicates the sequence
+ * number of the successfully received packet
+ */
+
+class DataLinkPacket
+{
+  public:
+    /**
+     * This variable stores the sequence number associated with Ack DLLP
+     */
+    uint64_t seqNum ;
+
+    /**
+     * crc contained in Ack DLLP. Not used.
+     */
+    uint16_t crc ;
+
+    /**
+     * Function to return the size of the DLLP, which is a constant value
+     * since a DLLP does not contain a payload.
+     *
+     * @return The size in Bytes of the DLLP
+     */
+    int
+    getSize()
+    {
+       return DLLP_SIZE ;
+    }
+
+    /**
+     * Constructor to create a new data link packet (Ack)
+     *
+     * @param _seq_num The sequence number of the packet acknowledged
+                        by this Ack
+     */
+
+    DataLinkPacket(int seq_num) : seqNum(seq_num)
+    {}
+} ;
+
+
+/**
+ * Class used to represent a PCI Express packet transmitted across a link.
+ * This packet can either be a Gem5 packet (TLP) or a DLLP.If the packet is a
+ * Gem5 packet, the pkt pointer is valid. If the packet is a DLLP, the dllp
+ * pointer is valid
+ */
+
+class PCIELinkPacket
+{
+  public:
+
+    /**
+ * Pointer to a Gem5 packet if the PCIELinkPacket encapsulates a Gem5 pkt
+     */
+    PacketPtr pkt ;
+
+    /**
+ * Pointer to a DataLinkPacket, if this PCIELinkPacket encapsulates an Ack
+     */
+    DataLinkPacket * dllp ;
+
+    /**
+     * Does the PCIELinkPAcket encapsulate a TLP ?
+     */
+    bool isTLP ;
+
+    /**
+     * Does the PCIELinkPacket encapsulate a DLLP ?
+     */
+    bool isDLLP ;
+
+    /**
+     * Sequence number field of TLP if TLP is encapsulated
+     */
+    uint64_t seqNum ;
+
+    /**
+     * Constructor for the PCIE Link Packet.
+     * Assigns the pkt and dllp pointers as NULL
+ * Code that creates a new PCIELinkPAcket has to assign variables based on
+     * whether a TLP or DLLP is being created
+     */
+    PCIELinkPacket() ;
+
+    /**
+ * Returns the size of the PCIELinkPacket, depending on whether a TLP or + * DLLP is encapsulated within. Overheads such as header size and sequence
+     * numbers and framing characters are taken into account here.
+     *
+ * @ret The effective size of the PCIELinkPacket(not the size of the class)
+     */
+    int getSize() ;
+
+} ;
+
+
+/**
+ * Class representing a replay buffer, which is implemented as a queue.
+ * Each link interface contains a replay buffer. Replay Buffers store
+ * transmitted TLPs (Gem5 packets) till an Ack is received corresponding to
+ * the sequence number of the TLP. Once the Ack is received, the stored TLPs
+ * are deleted and the buffer can accept more packets
+ */
+class ReplayBuffer
+{
+  public:
+
+    /**
+     * Size of replay buffer in packets
+     */
+    int maximumSize ;
+
+    /**
+     * Double ended queue used to implement the replay buffer
+     */
+    std::deque<PCIELinkPacket*> queue ;
+
+    /**
+ * Constructor for the ReplayBuffer.Push as many NULL ptrs into the dequeue
+     * as there are entries in the replay buffer. A dequeue entry will
+ * contain a non NULL value if that pointer points to a packet stored in
+     * the buffer.
+     *
+     * @param Size of replay buffer
+     */
+    ReplayBuffer(int max_size) ;
+
+    /**
+ * Calculates the present number of entries in the replay buffer. There is + * a pointer with a non-null value for each entry in the replay buffer. The
+     * replay buffer is formatted such that the NULL entries are toward the
+     * back of the buffer.
+     * @return size of the replay buffer.
+     */
+    int size() ;
+
+
+    /**
+ * Push a ptr to a newly transmited PCIELinkPacket to the back of the buff. + * Replace the first NULL ptr with a ptr to the transmitted PCIELinkPacket
+     *
+     * @param ptr to the PCIELinkPacket to store in buffer.
+     */
+    void pushBack (PCIELinkPacket* ptr) ;
+
+    /**
+     * Remove the first entry in the replay buffer and return a ptr to the
+ * PCIELinkPacket. Also push back a NULL pointer to the dequeue,so that the
+     * size of the dequeue remains constant.
+     * @return Return the PCIELinkPacket ptr removed from the buffer
+     */
+    PCIELinkPacket * popFront() ;
+
+    /**
+     * @return return the first element of the buffer
+     */
+    PCIELinkPacket *
+    front()
+    {
+        return queue.front() ;
+    }
+
+    /**
+     * Get the nth element stored in the replay buffer
+     * @param idx of the element to return
+     *
+     * @return The nth element stored in the buffer
+     */
+    PCIELinkPacket * get(int idx) ;
+
+
+    /**
+ * Is the replay buffer empty ? Check if there is a non null value stored
+     *
+     * @return True if buffer is empty, false otherwise
+     */
+    bool empty() ;
+
+};
+
+
+/**
+ * This class represents a PCIE Link in Gem5. The PCIE link can be visualized + * as containing 2 interfaces.Each interface is attached through a master/slave + * port pair to the device on that end of the link. Each interface is attached + * to a unidirectional link(based on Ether Link),that can be used to transmit + * packets to the other interface.An interface's transmit function is called to + * send a packet to the other interface,while the interface's receive function + * is called to receive a packet from the unidirectional link attached to the + * other interface. Each link interface also contains a replay buffer, to hold
+ * unAcked transmitted Gem5 packets
+ */
+class PCIELink : public MemObject
+{
+  public :
+    // Forward Declarations
+    class LinkMasterPort ;
+    class LinkSlavePort ;
+
+    /**
+ * The Link Class used to represent a link interface + unidirectional link
+     * that the interface transmits packets on.
+     *
+     * The unidirectional link is based on the ethernet link in Gem5. It
+ * represents a unidirectional PCIE link, and can be configured with the
+     * number  of lanes and the bandwidth and delay(assumed to be 0). The
+     * unidirectional link accepts packets from the link interface via the
+     * transmit() function. The unidirectional link schedules an event for
+ * a future time based on the size of the PCIELinkPacket and the bandwidth + * and number of lanes of the link. Once the event occurs, the packet is
+     * transmitted to the other interface belonging to the PCIELink.
+     *
+     * Each link interface accepts Gem5 packets from its master and slave
+     * ports, denoted as masterPortSrc and slavePortSrc respectively. These
+ * attached master and slave ports are to be connected to a PCIE device's + * slave and master ports respectively.A PCIE device in this context refers
+     * to either a switch/root port or an endpoint(e.g. NVMe, NIC, etc.)
+     *
+ * In addition to receiving Gem5 packets from a device, each link interface + * also receives PCIELinkPackets from the other interface of the PCIELink,
+     * via the other interface's assigned unidirectional link. Each
+ * PCIELinkPacket can either encapsulate a Gem5 packet or a DataLinkPacket. + * If the received PCIELinkPacket encapsulates a Gem5 packet(TLP), then the + * Gem5 packet is sent to either the attached master or slave port based on
+     * whether the packet is a request or a response.
+     *
+ * DataLinkPackets originate from and are consumed by link interfaces. Each + * DataLinkPacket is an acknowledgement message for a particular TLP(Gem5 + * packet) that is successfully received by a link interface. Successfully
+     * received in this context indicates that devices attached to the
+     * interface accept the Gem5 packet.
+     *
+ * Each interface implements a simple version of the Ack/NAK protocol in + * the data link layer of real PCIE devices. Since unidirectional links are
+     * assumed to be error free, only Ack DLLPs are used. When an interface
+ * transmits a TLP (Gem5 packet),a pointer to the transmited PCIELinkPacket + * is stored in its replay buffer. If the other interface receives the TLP + * successfully, it sends back an Ack to acknowledge this TLP. The sending
+     * interface, on receipt of this Ack removes the replay buffer entry
+ * corresponding to the PCIELinkPAcket representing the acknowledged TLP.
+     *
+     * Each interface also maintains a replay timer. If after the timeout
+     * interval, an Ack is not received for a transmitted TLP, all stored
+ * PCIELinkPackets are retransmitted from the replay buffer. Conversely,
+     * the timer is reset on receipt of an ACk DLLP.
+     *
+     * An Ack DLLP need not be sent for every received TLP. An ack timer is
+     * maintained to send an Ack back for all successfully received TLPs on
+     * timer expiration.
+     *
+     * For more info on Ack/NAK protocol, refer to PCI Express System
+     * Architecture book.
+     */
+
+    class Link
+    {
+      public :
+        const std::string __name ;
+
+       /**
+        * Ptr to parent simobject, used to schedule events in Gem5
+        */
+        PCIELink * parent ;
+
+        /**
+         * Number of ticks a byte takes to be transmitted on the link
+         */
+        const double ticksPerByte;
+
+        /**
+         * Propagation delay across link. Set to 0.
+         */
+        const Tick linkDelay;
+
+        /**
+         * Not used. Set to 0.
+         */
+        const Tick delayVar;
+
+        /**
+         * Pointer to the master port attached to the link interface. Is
+ * connected to the slave/PIO port of a Gem5 device or root/switch port
+         */
+        LinkMasterPort * masterPortSrc ;
+
+        /**
+         * Pointer to the slave port attached to the link interface. Is
+         * connected to the master/DMA port of a Gem5 device or root port.
+         */
+        LinkSlavePort * slavePortSrc ;
+
+        /**
+ * Pointer to the master port attached to the other interface of the
+         * PCIELink.
+         */
+        LinkMasterPort * masterPortDest ;
+
+        /**
+         * Pointer to the slave port attached to the other interface of the
+         * PCIELink.
+         */
+        LinkSlavePort * slavePortDest ;
+
+        /**
+ * Number of lanes making up each unidirectional link. The bandwidth + * scales proportionately with number of lanes present. The number of
+         * lanes making up a link can be 1,2,4,8,12,16 or 32.
+         */
+        int lanes ;
+
+        /**
+         * Packet currently being transmitted on this unidirectional link
+         */
+        PCIELinkPacket * packet ;
+
+        /**
+         * Indicates whether a packet is currently being transmitted on the
+         * link.
+ * @return true if a packet is transmitted on the unidirectional link.
+         *         false if link is free
+         */
+        bool
+        busy()
+        {
+            return packet != NULL ;
+        }
+
+        /**
+         * Queue of inflight packets on the unidirectional link
+         */
+        std::deque<std::pair<Tick , PCIELinkPacket*>> txQueue ;
+
+        /**
+         * Denotes the function called when a PCIELinkPacket is finished
+         * being transmitted across the unidirectional link.
+         */
+        EventFunctionWrapper doneEvent ;
+
+        /**
+         * Denotes the function called when a PCIELinkPacket is ready to be
+         * sent to the receiving interface
+         */
+        EventFunctionWrapper txQueueEvent ;
+
+        /**
+ * Denotes function called when a timeout occurs and retransmission of
+         * packets on the unidirectional link needs to take place.
+         */
+        EventFunctionWrapper timeoutEvent ;
+
+        /**
+         * Denotes function called when an Ack needs to be sent back to the
+ * other interface to indicate the successful reception of TLPs (Gem5
+         * packets)
+         */
+        EventFunctionWrapper ackEvent ;
+
+        /**
+ * Replay buffer present in the link interface to hold transmitted but + * unAcked PCIELinkPackets that encapsulate a TLP. Remember that only
+         * TLPs(Gem5 packets) need an Ack, not DLLPs.
+         */
+        ReplayBuffer buffer ;
+
+        /**
+         * Link destructor. Needs to be implemented
+         */
+        ~Link(){}
+
+        /**
+ * Function used to transmit PCIELinkPackets on the unidirectional link
+         * The DoneEvent is scheduled after a duration corresponding to the
+ * time taken to transmit a PCIELinkPacket on the link, which depends + * on ticksPerByte, number of lanes present in the unidirectional link,
+         * etc. If a packet is currently being transmitted, return false.
+         *
+ * @param pkt Pointer to PCIELinkPAcket to transmit on unidirect. link.
+         * @return successful transmission or not
+         */
+        bool transmit(PCIELinkPacket * pkt) ;
+
+        /**
+ * Function called by an interface's attached master and slave ports to + * transmit a Gem5 packet on the unidirectional link. Each Gem5 packet + * is encapsulated within a PCIELinkPacket before being transmitted out + * onto the link. The PCIELinkPackets encapsulating a Gem5 packet are + * assigned a sequence number by the sending interface and are stored
+         * in the sending interface's replay buffer after transmission.
+         * @param pkt Gem5 packet to be transmitted.
+ * @param master Is the master port attached to interface sending this
+         * packet ?
+         * @return Packet successfully transmitted or needs to be resent ?.
+         */
+        bool linkTransmitInterface(PacketPtr pkt, bool master) ;
+
+        /**
+ * Function called by a link to send a packet to a receiving interface. + * Upon reception of a PCIELinkPAcket encapsulating a TLP, a sequence + * number check is performed to avoid out of order packets. The Gem5 + * packet is extracted from the PCIELinkPacket, is sent to the attached
+         * slave or master port. If the device attached to this interface
+ * accepts the Gem5 packet (TLP), an Ack is scheduled to be sent back + * to the sending interface with the sequence number of the received
+         * PCIELinkPacket.
+         *
+ * Upon reception of a PCIELinkPacket encapsulating a Ack DLLP, entries + * in the replay buffer are freed based on the sequence number of the + * Ack. All PCIELinkPAckets with a seq. number < Ack seq. number are + * freed from the replay buffer as reliable in order transmission of
+         * these packets is assumed to have taken place
+         *
+ * @param packet PCIELinkPAcket received from the unidirectional link.
+         */
+        void linkReceiveInterface(PCIELinkPacket * packet )  ;
+
+        /**
+ * Sequence number to be assigned to TLP before transmission across the
+         * link.
+         */
+        uint64_t sendSeqNum ;
+
+        /**
+         * Sequence number of the next TLP to be received from the other
+         * interface making up the PCIELink.
+         */
+        uint64_t recvSeqNum ;
+
+        /**
+         * Sequence number of the last packet an Ack was sent for. Used to
+         * avoid sending duplicate Acks.
+         */
+        uint64_t lastAcked ;
+
+        /**
+         * Size of the replay buffer present in the link interface
+         */
+        int  maxQueueSize ;
+
+        /**
+         * Time after which the retry timer expires and the contents of the
+         * replay buffer are replayed.
+         *
+         * Consider the following calculations.
+ * A=(Maximum payload size * Time to transmit a byte on the link)/Lanes + * B = AckFactor - 1.4 for x1, 1.4 for x2. 1.4 for x4, 2.5 for x8 and
+         *     with 128B Maximum Payload Size
+         * C = Internal Delay
+         * D = Rx_L0_Adjustment
+         *
+
+         * The retryTime is calculated as (A*B + C)*3 + D.
+         * In the implemented PCIELink, both the internal delay and
+         * Rx_L0_Adjustment times are taken to be 0.
+         *
+         * Maximum PAyload Size = Gem5 Cache Line Size
+         * Time to transmit a byte on the link = ticksPerByte
+         * Lanes = Number of lanes configured in each unidirectional link.
+         */
+        uint32_t retryTime ;
+
+        /**
+ * Was there a Gem5 response packet that could not be sent earlier due + * to the link being occupied ? Need to resend it once the link frees
+         * up ?
+         */
+        bool retryResp ;
+
+        /**
+ * Was there a Gem5 request packet that could not be sent earlier and
+         * needs to be resent ?
+         */
+        bool retryReq ;
+
+        /**
+ * TLP which is currently being transmitted on the unidirectional link.
+         * Could also represent the next TLP to be transmitted on the
+ * unidirectional link if a DLLP or retransmission was already being + * transmitted on the link when the TLP Gem5 packet was received from
+         * the device attached to the interface.
+         */
+        PCIELinkPacket * replayPacket ;
+
+        /**
+ * Next DLLP to be sent out onto the link once the current PCIELinkPAck
+         * is finished being transmitted on the unidirectional link
+         */
+        PCIELinkPacket * replayDLLP ;
+
+        /**
+ * Indicates whether the contents of the replay buffer in the interface
+         * need to be replayed.
+         */
+        bool retransmit ;
+
+        /**
+         * Indicates the replay buffer entry to be retransmitted next
+         */
+        int retransmitIdx ;
+
+        /**
+         * Double Pointer to the other Link making up the PCIELink
+         */
+        Link ** otherLink ;
+
+        /**
+         * Function called as soon as a PCIELinkPAcket is finished being
+         * transmitted on the unidirectional link. This function calls the
+         * linkReceiveInterface() function of the receiving interface.
+         */
+        void processTxQueue() ;
+
+        /**
+ * A PCIELinkPacket is done being transmitted on the unidirectional lnk
+         * This function does the following steps.
+ * 1. Set the packet variable to false to indicate that the unidirect.
+         *    link is free.
+         *
+         * 2. If the packet which just tranmsitted encapsulates a TLP, then
+ * check if retryReq or retryResp is true. If one of them is set, + * then make the master or slave ports of the interface ask for a + * packet retransmission from the device attached to this interface.
+         *
+ * 3. Now choose the next packet to be transmitted on the link,based on + * the following priority.Pending ACK DLLP (if any) > Next TLP to be + * retransmitted (if any) > Next TLP to be transmitted for the first
+         *    time(if any).
+         */
+        void txDone() ;
+
+        /**
+ * Once a PCIELinkPAcket is finished being transmitted on the unidirect
+         * link, this function is invoked. It calls the receive function of
+         * the other Link making up the PCIELink.
+         *
+         * @param pkt The packet that has finished transmission across the
+         * unidirectional link and needs to be sent to the opposing link
+         * interface.
+         */
+        void txComplete(PCIELinkPacket * pkt) ;
+
+        /**
+ * Function called on expiry of replay timer.Initiates a retransmission
+         * of the first PCIELinkPAcket in the replay buffer.
+         *
+         */
+        void timeoutFunc() ;
+
+        /**
+ * Function called on expiry of Ack Timer.This function checks if there
+         * are any unAcked TLPs that have been received. If so, it sends an
+ * Ack to the opposite interface, and updates the value of last_acked.
+         */
+        void sendAck() ;
+
+
+        /**
+ * Returns the name of this Link. Used for statistics and printing out
+         * config and debugging info.
+         */
+        const std::string name() const { return __name ; }
+
+        /**
+         * Link constructor called during creation of a Link instance.
+         *
+         * @param _name: Name of this Link
+         *
+         * @param p: Pointer to PCIELink which this Link is a part of
+         *
+         * @param rate The number of ticks to transmit a byte. The value of
+         * ticksperByte.
+         *
+ * @param delay The propagation delay of a packet transmitted across
+         * a unidrectional link
+         *
+         * @param delay_var The variance in delay.
+         *
+ * @param master_port_src The LinkMasterPort attached to this Link's
+           interface.
+         *
+         * @param slave_port_src The LinkSlavePort attached to this Link's
+           interface.
+         *
+ * @param master_port_dest The LinkMasterPort attached to the opposite
+         * interface.
+         *
+ * @param slave_port_dest The LinkSlavePort attached to the opposite
+         * interface.
+         *
+         * @param num_lanes The number of lanes in the unidirectional link.
+         *
+ * @param mps : The maximum payload size of a TLP tranmsiited across
+           the unidirectional link. Used to calculate retryTime.
+         *
+         * @param opposite_link A pointer to the other link making up the
+         *        PCIELink
+         */
+
+ Link (const std::string & _name, PCIELink * p, double rate, Tick delay,
+              Tick delay_var, LinkMasterPort * master_port_src ,
+ LinkSlavePort * slave_port_src,LinkMasterPort * master_port_dest, + LinkSlavePort * slave_port_dest,int num_lanes,int max_queue_size,
+              int mps, Link ** opposite_link );
+    } ;
+
+    /**
+ * Class that represents the Master Port attached to a Link. This Master + * Port is connected to the Slave Port of a device attached to one end of
+     * the PCIELink.
+     */
+    class LinkMasterPort : public MasterPort
+    {
+      public:
+        /**
+         * Called by the peer Slave Port belonging to a connected device to
+         * send a Gem5 packet to the Link Master Port.
+         *
+         * @param pkt The Gem5 packet to be received.
+         */
+        bool recvTimingResp(PacketPtr pkt) ;
+
+        void recvFromLink(PCIELinkPacket * pkt){} ;
+
+        void recvReqRetry(){}
+
+        /**
+         * Pointer to parent PCIELink
+         */
+        PCIELink * parent ;
+
+        /**
+         * Double Pointer to the Link this LinkMasterPort is attached to.
+         */
+        Link ** transmitLink ;
+
+        /**
+         * Constructor
+         * @param _name The name of this LinkMasterPort
+         * @param parent_ptr The parent PCIELink.
+ * @param transmit_link_dptr The Link this LinkMasterPort is attached
+         *        to
+         */
+        LinkMasterPort(const std::string & _name, PCIELink * parent_ptr,
+                       Link ** transmit_link_dptr) ;
+    } ;
+
+    /**
+     * Class that represents the Slave Port attached to a Link. This Slave
+ * Port is connected to the Master Port of a device attached to one end of
+     * the PCIELink.
+     */
+    class LinkSlavePort : public SlavePort
+    {
+      public:
+
+        void recvRespRetry(){}
+
+        /**
+ * Since PCI Express consists of a serial link, and not a bus, there is
+         * no need for address mapping of devices connected to the link.
+         *
+         * @retval Returns an empty AddrRangeList
+         */
+ AddrRangeList getAddrRanges() const{ AddrRangeList temp ; return temp;}
+
+        /**
+ * Function called by the peer Master Port belonging to a connected PCI + * device or switch/root port. This functions accepts a timing request
+         * packet
+         *
+         * @param pkt Timing Req packet which is received.
+         */
+        bool recvTimingReq(PacketPtr pkt) ;
+
+
+        /**
+ * Function to receive a functional request packet from peer MAsterPort
+         * Just passes packet along without modelling timing or delay
+         */
+        void recvFunctional(PacketPtr pkt) ;
+
+ /** Function to receive an atomic request packet from peer Slave Port.
+         *  Just passes packet along without modelling timing or delay
+         */
+        Tick recvAtomic(PacketPtr pkt) ;
+
+        /**
+         * Pointer to parent PCIELink
+         */
+        PCIELink * parent ;
+
+        /**
+         * Double Ptr to Link this LinkSlavePort is attached to.
+         */
+        Link ** transmitLink ;
+
+        /**
+         * Constructor.
+         * @param _name Name of the LinkSlavePort instance
+         * @param _parent pointer to parent PCIELink.
+ * @param transmit_link_dptr Double Ptr to the Link this LinkSlavePort
+         * is attached to.
+         */
+        LinkSlavePort(const std::string & _name, PCIELink * parent_ptr,
+                      Link ** transmit_link_dptr) ;
+    } ;
+
+    /**
+ * Upstream and Downstream Slave ports used to connect the PCIELink to the + * Master Ports of connected devices.Upstream refers to direction of CPU
+     * and downstream away from the CPU. One slave port is assigned to each
+     * of the 2 Links that make up the PCIElink.
+     */
+    LinkSlavePort upstreamSlave, downstreamSlave ;
+
+    /**
+ * Upstream and Downstream Master ports used to connect the PCIELink to the + * Slave Ports of connected devices. Upstream refers to direction of CPU + * and downstream away from the CPU. One master port is assigned to each of
+     * the 2 Links that make up the PCIELink.
+     */
+    LinkMasterPort upstreamMaster , downstreamMaster ;
+
+    /**
+     * Representing the 2 Links that make up a PCIELink. These are created
+     * on the heap.
+     */
+    Link * links[2] ;
+
+    /**
+     * Check connectivity of all 4 ports belonging to the PCIELink
+     */
+    void init() ;
+
+    /**
+     * Returns a reference to the LinkMasterPort based on name
+     */
+    BaseMasterPort& getMasterPort(const std::string &if_name, PortID idx) ;
+
+    /**
+     * Returns a reference to a LinkSlavePort based on name
+     */
+    BaseSlavePort&  getSlavePort(const std::string &if_name, PortID idx) ;
+
+    typedef PCIELinkParams Params ;
+
+    /**
+     * Constructor for PCIELink class.
+     * Dynamically creates the 2 Links that make up a PCIELink.
+ * @param p Used to initialize the PCIELink based on python configuration
+     */
+     PCIELink (Params * p) ;
+} ;
+
+#endif //__MEM_PCIE_LINK_HH__
+

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/13024
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: master
Gerrit-Change-Id: I5aea0aac7cd21da93a147399813a9038325cc0cb
Gerrit-Change-Number: 13024
Gerrit-PatchSet: 1
Gerrit-Owner: Krishna Srinivasan <[email protected]>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list
[email protected]
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to