diff options
Diffstat (limited to 'src/mem/ruby/network/garnet2.0/NetworkInterface.cc')
-rw-r--r-- | src/mem/ruby/network/garnet2.0/NetworkInterface.cc | 443 |
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); +} |