summaryrefslogtreecommitdiff
path: root/src/mem/ruby/network/garnet2.0/NetworkInterface.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem/ruby/network/garnet2.0/NetworkInterface.cc')
-rw-r--r--src/mem/ruby/network/garnet2.0/NetworkInterface.cc443
1 files changed, 443 insertions, 0 deletions
diff --git a/src/mem/ruby/network/garnet2.0/NetworkInterface.cc b/src/mem/ruby/network/garnet2.0/NetworkInterface.cc
new file mode 100644
index 000000000..6bdaf39af
--- /dev/null
+++ b/src/mem/ruby/network/garnet2.0/NetworkInterface.cc
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2008 Princeton University
+ * Copyright (c) 2016 Georgia Institute of Technology
+ * 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: Niket Agarwal
+ * Tushar Krishna
+ */
+
+
+#include "mem/ruby/network/garnet2.0/NetworkInterface.hh"
+
+#include <cassert>
+#include <cmath>
+
+#include "base/cast.hh"
+#include "base/stl_helpers.hh"
+#include "debug/RubyNetwork.hh"
+#include "mem/ruby/network/MessageBuffer.hh"
+#include "mem/ruby/network/garnet2.0/Credit.hh"
+#include "mem/ruby/network/garnet2.0/flitBuffer.hh"
+#include "mem/ruby/slicc_interface/Message.hh"
+
+using namespace std;
+using m5::stl_helpers::deletePointers;
+
+NetworkInterface::NetworkInterface(const Params *p)
+ : ClockedObject(p), Consumer(this), m_id(p->id),
+ m_virtual_networks(p->virt_nets), m_vc_per_vnet(p->vcs_per_vnet),
+ m_num_vcs(m_vc_per_vnet * m_virtual_networks)
+{
+ m_router_id = -1;
+ m_vc_round_robin = 0;
+ m_ni_out_vcs.resize(m_num_vcs);
+ m_ni_out_vcs_enqueue_time.resize(m_num_vcs);
+ outCreditQueue = new flitBuffer();
+
+ // instantiating the NI flit buffers
+ for (int i = 0; i < m_num_vcs; i++) {
+ m_ni_out_vcs[i] = new flitBuffer();
+ m_ni_out_vcs_enqueue_time[i] = Cycles(INFINITE_);
+ }
+
+ m_vc_allocator.resize(m_virtual_networks); // 1 allocator per vnet
+ for (int i = 0; i < m_virtual_networks; i++) {
+ m_vc_allocator[i] = 0;
+ }
+}
+
+void
+NetworkInterface::init()
+{
+ for (int i = 0; i < m_num_vcs; i++) {
+ m_out_vc_state.push_back(new OutVcState(i, m_net_ptr));
+ }
+}
+
+NetworkInterface::~NetworkInterface()
+{
+ deletePointers(m_out_vc_state);
+ deletePointers(m_ni_out_vcs);
+ delete outCreditQueue;
+ delete outFlitQueue;
+}
+
+void
+NetworkInterface::addInPort(NetworkLink *in_link,
+ CreditLink *credit_link)
+{
+ inNetLink = in_link;
+ in_link->setLinkConsumer(this);
+ outCreditLink = credit_link;
+ credit_link->setSourceQueue(outCreditQueue);
+}
+
+void
+NetworkInterface::addOutPort(NetworkLink *out_link,
+ CreditLink *credit_link,
+ SwitchID router_id)
+{
+ inCreditLink = credit_link;
+ credit_link->setLinkConsumer(this);
+
+ outNetLink = out_link;
+ outFlitQueue = new flitBuffer();
+ out_link->setSourceQueue(outFlitQueue);
+
+ m_router_id = router_id;
+}
+
+void
+NetworkInterface::addNode(vector<MessageBuffer *>& in,
+ vector<MessageBuffer *>& out)
+{
+ inNode_ptr = in;
+ outNode_ptr = out;
+
+ for (auto& it : in) {
+ if (it != nullptr) {
+ it->setConsumer(this);
+ }
+ }
+}
+
+
+/*
+ * The NI wakeup checks whether there are any ready messages in the protocol
+ * buffer. If yes, it picks that up, flitisizes it into a number of flits and
+ * puts it into an output buffer and schedules the output link. On a wakeup
+ * it also checks whether there are flits in the input link. If yes, it picks
+ * them up and if the flit is a tail, the NI inserts the corresponding message
+ * into the protocol buffer. It also checks for credits being sent by the
+ * downstream router.
+ */
+
+void
+NetworkInterface::wakeup()
+{
+ DPRINTF(RubyNetwork, "Network Interface %d connected to router %d "
+ "woke up at time: %lld\n", m_id, m_router_id, curCycle());
+
+ MsgPtr msg_ptr;
+ Tick curTime = clockEdge();
+
+ // Checking for messages coming from the protocol
+ // can pick up a message/cycle for each virtual net
+ for (int vnet = 0; vnet < inNode_ptr.size(); ++vnet) {
+ MessageBuffer *b = inNode_ptr[vnet];
+ if (b == nullptr) {
+ continue;
+ }
+
+ if (b->isReady(curTime)) { // Is there a message waiting
+ msg_ptr = b->peekMsgPtr();
+ if (flitisizeMessage(msg_ptr, vnet)) {
+ b->dequeue(curTime);
+ } else {
+ break;
+ }
+ }
+ }
+
+ scheduleOutputLink();
+ checkReschedule();
+
+ /*********** Check the incoming flit link **********/
+
+ if (inNetLink->isReady(curCycle())) {
+ flit *t_flit = inNetLink->consumeLink();
+ bool free_signal = false;
+ if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
+ free_signal = true;
+
+ // enqueue into the protocol buffers
+ outNode_ptr[t_flit->get_vnet()]->enqueue(
+ t_flit->get_msg_ptr(), curTime, cyclesToTicks(Cycles(1)));
+ }
+ // Simply send a credit back since we are not buffering
+ // this flit in the NI
+ Credit *t_credit = new Credit(t_flit->get_vc(), free_signal,
+ curCycle());
+ outCreditQueue->insert(t_credit);
+ outCreditLink->
+ scheduleEventAbsolute(clockEdge(Cycles(1)));
+
+ int vnet = t_flit->get_vnet();
+
+ // Update Stats
+
+ // Latency
+ m_net_ptr->increment_received_flits(vnet);
+ Cycles network_delay = curCycle() - t_flit->get_enqueue_time();
+ Cycles queueing_delay = t_flit->get_src_delay();
+
+ m_net_ptr->increment_flit_network_latency(network_delay, vnet);
+ m_net_ptr->increment_flit_queueing_latency(queueing_delay, vnet);
+
+ if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
+ m_net_ptr->increment_received_packets(vnet);
+ m_net_ptr->increment_packet_network_latency(network_delay, vnet);
+ m_net_ptr->increment_packet_queueing_latency(queueing_delay, vnet);
+ }
+
+ // Hops
+ m_net_ptr->increment_total_hops(t_flit->get_route().hops_traversed);
+
+ delete t_flit;
+ }
+
+ /****************** Check the incoming credit link *******/
+
+ if (inCreditLink->isReady(curCycle())) {
+ Credit *t_credit = (Credit*) inCreditLink->consumeLink();
+ m_out_vc_state[t_credit->get_vc()]->increment_credit();
+ if (t_credit->is_free_signal()) {
+ m_out_vc_state[t_credit->get_vc()]->setState(IDLE_, curCycle());
+ }
+ delete t_credit;
+ }
+}
+
+
+// Embed the protocol message into flits
+bool
+NetworkInterface::flitisizeMessage(MsgPtr msg_ptr, int vnet)
+{
+ Message *net_msg_ptr = msg_ptr.get();
+ NetDest net_msg_dest = net_msg_ptr->getDestination();
+
+ // gets all the destinations associated with this message.
+ vector<NodeID> dest_nodes = net_msg_dest.getAllDest();
+
+ // Number of flits is dependent on the link bandwidth available.
+ // This is expressed in terms of bytes/cycle or the flit size
+ int num_flits = (int) ceil((double) m_net_ptr->MessageSizeType_to_int(
+ net_msg_ptr->getMessageSize())/m_net_ptr->getNiFlitSize());
+
+ // loop to convert all multicast messages into unicast messages
+ for (int ctr = 0; ctr < dest_nodes.size(); ctr++) {
+
+ // this will return a free output virtual channel
+ int vc = calculateVC(vnet);
+
+ if (vc == -1) {
+ return false ;
+ }
+ MsgPtr new_msg_ptr = msg_ptr->clone();
+ NodeID destID = dest_nodes[ctr];
+
+ Message *new_net_msg_ptr = new_msg_ptr.get();
+ if (dest_nodes.size() > 1) {
+ NetDest personal_dest;
+ for (int m = 0; m < (int) MachineType_NUM; m++) {
+ if ((destID >= MachineType_base_number((MachineType) m)) &&
+ destID < MachineType_base_number((MachineType) (m+1))) {
+ // calculating the NetDest associated with this destID
+ personal_dest.clear();
+ personal_dest.add((MachineID) {(MachineType) m, (destID -
+ MachineType_base_number((MachineType) m))});
+ new_net_msg_ptr->getDestination() = personal_dest;
+ break;
+ }
+ }
+ net_msg_dest.removeNetDest(personal_dest);
+ // removing the destination from the original message to reflect
+ // that a message with this particular destination has been
+ // flitisized and an output vc is acquired
+ net_msg_ptr->getDestination().removeNetDest(personal_dest);
+ }
+
+ // Embed Route into the flits
+ // NetDest format is used by the routing table
+ // Custom routing algorithms just need destID
+ RouteInfo route;
+ route.vnet = vnet;
+ route.net_dest = new_net_msg_ptr->getDestination();
+ route.src_ni = m_id;
+ route.src_router = m_router_id;
+ route.dest_ni = destID;
+ route.dest_router = m_net_ptr->get_router_id(destID);
+
+ // initialize hops_traversed to -1
+ // so that the first router increments it to 0
+ route.hops_traversed = -1;
+
+ m_net_ptr->increment_injected_packets(vnet);
+ for (int i = 0; i < num_flits; i++) {
+ m_net_ptr->increment_injected_flits(vnet);
+ flit *fl = new flit(i, vc, vnet, route, num_flits, new_msg_ptr,
+ curCycle());
+
+ fl->set_src_delay(curCycle() - ticksToCycles(msg_ptr->getTime()));
+ m_ni_out_vcs[vc]->insert(fl);
+ }
+
+ m_ni_out_vcs_enqueue_time[vc] = curCycle();
+ m_out_vc_state[vc]->setState(ACTIVE_, curCycle());
+ }
+ return true ;
+}
+
+// Looking for a free output vc
+int
+NetworkInterface::calculateVC(int vnet)
+{
+ for (int i = 0; i < m_vc_per_vnet; i++) {
+ int delta = m_vc_allocator[vnet];
+ m_vc_allocator[vnet]++;
+ if (m_vc_allocator[vnet] == m_vc_per_vnet)
+ m_vc_allocator[vnet] = 0;
+
+ if (m_out_vc_state[(vnet*m_vc_per_vnet) + delta]->isInState(
+ IDLE_, curCycle())) {
+ return ((vnet*m_vc_per_vnet) + delta);
+ }
+ }
+ return -1;
+}
+
+
+/** This function looks at the NI buffers
+ * if some buffer has flits which are ready to traverse the link in the next
+ * cycle, and the downstream output vc associated with this flit has buffers
+ * left, the link is scheduled for the next cycle
+ */
+
+void
+NetworkInterface::scheduleOutputLink()
+{
+ int vc = m_vc_round_robin;
+ m_vc_round_robin++;
+ if (m_vc_round_robin == m_num_vcs)
+ m_vc_round_robin = 0;
+
+ for (int i = 0; i < m_num_vcs; i++) {
+ vc++;
+ if (vc == m_num_vcs)
+ vc = 0;
+
+ // model buffer backpressure
+ if (m_ni_out_vcs[vc]->isReady(curCycle()) &&
+ m_out_vc_state[vc]->has_credit()) {
+
+ bool is_candidate_vc = true;
+ int t_vnet = get_vnet(vc);
+ int vc_base = t_vnet * m_vc_per_vnet;
+
+ if (m_net_ptr->isVNetOrdered(t_vnet)) {
+ for (int vc_offset = 0; vc_offset < m_vc_per_vnet;
+ vc_offset++) {
+ int t_vc = vc_base + vc_offset;
+ if (m_ni_out_vcs[t_vc]->isReady(curCycle())) {
+ if (m_ni_out_vcs_enqueue_time[t_vc] <
+ m_ni_out_vcs_enqueue_time[vc]) {
+ is_candidate_vc = false;
+ break;
+ }
+ }
+ }
+ }
+ if (!is_candidate_vc)
+ continue;
+
+ m_out_vc_state[vc]->decrement_credit();
+ // Just removing the flit
+ flit *t_flit = m_ni_out_vcs[vc]->getTopFlit();
+ t_flit->set_time(curCycle() + Cycles(1));
+ outFlitQueue->insert(t_flit);
+ // schedule the out link
+ outNetLink->scheduleEventAbsolute(clockEdge(Cycles(1)));
+
+ if (t_flit->get_type() == TAIL_ ||
+ t_flit->get_type() == HEAD_TAIL_) {
+ m_ni_out_vcs_enqueue_time[vc] = Cycles(INFINITE_);
+ }
+ return;
+ }
+ }
+}
+
+int
+NetworkInterface::get_vnet(int vc)
+{
+ for (int i = 0; i < m_virtual_networks; i++) {
+ if (vc >= (i*m_vc_per_vnet) && vc < ((i+1)*m_vc_per_vnet)) {
+ return i;
+ }
+ }
+ fatal("Could not determine vc");
+}
+
+
+// Wakeup the NI in the next cycle if there are waiting
+// messages in the protocol buffer, or waiting flits in the
+// output VC buffer
+void
+NetworkInterface::checkReschedule()
+{
+ for (const auto& it : inNode_ptr) {
+ if (it == nullptr) {
+ continue;
+ }
+
+ while (it->isReady(clockEdge())) { // Is there a message waiting
+ scheduleEvent(Cycles(1));
+ return;
+ }
+ }
+
+ for (int vc = 0; vc < m_num_vcs; vc++) {
+ if (m_ni_out_vcs[vc]->isReady(curCycle() + Cycles(1))) {
+ scheduleEvent(Cycles(1));
+ return;
+ }
+ }
+}
+
+void
+NetworkInterface::print(std::ostream& out) const
+{
+ out << "[Network Interface]";
+}
+
+uint32_t
+NetworkInterface::functionalWrite(Packet *pkt)
+{
+ uint32_t num_functional_writes = 0;
+ for (unsigned int i = 0; i < m_num_vcs; ++i) {
+ num_functional_writes += m_ni_out_vcs[i]->functionalWrite(pkt);
+ }
+
+ num_functional_writes += outFlitQueue->functionalWrite(pkt);
+ return num_functional_writes;
+}
+
+NetworkInterface *
+GarnetNetworkInterfaceParams::create()
+{
+ return new NetworkInterface(this);
+}