blob: de566a5cc764c7ad34f8c3f6c4d77f409a4074fe [file] [log] [blame]
/*
* Copyright (c) 2020 Advanced Micro Devices, Inc.
* 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.
*/
#include "mem/ruby/network/garnet/GarnetNetwork.hh"
#include <cassert>
#include "base/cast.hh"
#include "base/compiler.hh"
#include "debug/RubyNetwork.hh"
#include "mem/ruby/common/NetDest.hh"
#include "mem/ruby/network/MessageBuffer.hh"
#include "mem/ruby/network/garnet/CommonTypes.hh"
#include "mem/ruby/network/garnet/CreditLink.hh"
#include "mem/ruby/network/garnet/GarnetLink.hh"
#include "mem/ruby/network/garnet/NetworkInterface.hh"
#include "mem/ruby/network/garnet/NetworkLink.hh"
#include "mem/ruby/network/garnet/Router.hh"
#include "mem/ruby/system/RubySystem.hh"
namespace gem5
{
namespace ruby
{
namespace garnet
{
/*
* GarnetNetwork sets up the routers and links and collects stats.
* Default parameters (GarnetNetwork.py) can be overwritten from command line
* (see configs/network/Network.py)
*/
GarnetNetwork::GarnetNetwork(const Params &p)
: Network(p)
{
m_num_rows = p.num_rows;
m_ni_flit_size = p.ni_flit_size;
m_max_vcs_per_vnet = 0;
m_buffers_per_data_vc = p.buffers_per_data_vc;
m_buffers_per_ctrl_vc = p.buffers_per_ctrl_vc;
m_routing_algorithm = p.routing_algorithm;
m_enable_fault_model = p.enable_fault_model;
if (m_enable_fault_model)
fault_model = p.fault_model;
m_vnet_type.resize(m_virtual_networks);
for (int i = 0 ; i < m_virtual_networks ; i++) {
if (m_vnet_type_names[i] == "response")
m_vnet_type[i] = DATA_VNET_; // carries data (and ctrl) packets
else
m_vnet_type[i] = CTRL_VNET_; // carries only ctrl packets
}
// record the routers
for (std::vector<BasicRouter*>::const_iterator i = p.routers.begin();
i != p.routers.end(); ++i) {
Router* router = safe_cast<Router*>(*i);
m_routers.push_back(router);
// initialize the router's network pointers
router->init_net_ptr(this);
}
// record the network interfaces
for (std::vector<ClockedObject*>::const_iterator i = p.netifs.begin();
i != p.netifs.end(); ++i) {
NetworkInterface *ni = safe_cast<NetworkInterface *>(*i);
m_nis.push_back(ni);
ni->init_net_ptr(this);
}
// Print Garnet version
inform("Garnet version %s\n", garnetVersion);
}
void
GarnetNetwork::init()
{
Network::init();
for (int i=0; i < m_nodes; i++) {
m_nis[i]->addNode(m_toNetQueues[i], m_fromNetQueues[i]);
}
// The topology pointer should have already been initialized in the
// parent network constructor
assert(m_topology_ptr != NULL);
m_topology_ptr->createLinks(this);
// Initialize topology specific parameters
if (getNumRows() > 0) {
// Only for Mesh topology
// m_num_rows and m_num_cols are only used for
// implementing XY or custom routing in RoutingUnit.cc
m_num_rows = getNumRows();
m_num_cols = m_routers.size() / m_num_rows;
assert(m_num_rows * m_num_cols == m_routers.size());
} else {
m_num_rows = -1;
m_num_cols = -1;
}
// FaultModel: declare each router to the fault model
if (isFaultModelEnabled()) {
for (std::vector<Router*>::const_iterator i= m_routers.begin();
i != m_routers.end(); ++i) {
Router* router = safe_cast<Router*>(*i);
[[maybe_unused]] int router_id =
fault_model->declare_router(router->get_num_inports(),
router->get_num_outports(),
router->get_vc_per_vnet(),
getBuffersPerDataVC(),
getBuffersPerCtrlVC());
assert(router_id == router->get_id());
router->printAggregateFaultProbability(std::cout);
router->printFaultVector(std::cout);
}
}
}
/*
* This function creates a link from the Network Interface (NI)
* into the Network.
* It creates a Network Link from the NI to a Router and a Credit Link from
* the Router to the NI
*/
void
GarnetNetwork::makeExtInLink(NodeID global_src, SwitchID dest, BasicLink* link,
std::vector<NetDest>& routing_table_entry)
{
NodeID local_src = getLocalNodeID(global_src);
assert(local_src < m_nodes);
GarnetExtLink* garnet_link = safe_cast<GarnetExtLink*>(link);
// GarnetExtLink is bi-directional
NetworkLink* net_link = garnet_link->m_network_links[LinkDirection_In];
net_link->setType(EXT_IN_);
CreditLink* credit_link = garnet_link->m_credit_links[LinkDirection_In];
m_networklinks.push_back(net_link);
m_creditlinks.push_back(credit_link);
PortDirection dst_inport_dirn = "Local";
m_max_vcs_per_vnet = std::max(m_max_vcs_per_vnet,
m_routers[dest]->get_vc_per_vnet());
/*
* We check if a bridge was enabled at any end of the link.
* The bridge is enabled if either of clock domain
* crossing (CDC) or Serializer-Deserializer(SerDes) unit is
* enabled for the link at each end. The bridge encapsulates
* the functionality for both CDC and SerDes and is a Consumer
* object similiar to a NetworkLink.
*
* If a bridge was enabled we connect the NI and Routers to
* bridge before connecting the link. Example, if an external
* bridge is enabled, we would connect:
* NI--->NetworkBridge--->GarnetExtLink---->Router
*/
if (garnet_link->extBridgeEn) {
DPRINTF(RubyNetwork, "Enable external bridge for %s\n",
garnet_link->name());
m_nis[local_src]->
addOutPort(garnet_link->extNetBridge[LinkDirection_In],
garnet_link->extCredBridge[LinkDirection_In],
dest, m_routers[dest]->get_vc_per_vnet());
} else {
m_nis[local_src]->addOutPort(net_link, credit_link, dest,
m_routers[dest]->get_vc_per_vnet());
}
if (garnet_link->intBridgeEn) {
DPRINTF(RubyNetwork, "Enable internal bridge for %s\n",
garnet_link->name());
m_routers[dest]->
addInPort(dst_inport_dirn,
garnet_link->intNetBridge[LinkDirection_In],
garnet_link->intCredBridge[LinkDirection_In]);
} else {
m_routers[dest]->addInPort(dst_inport_dirn, net_link, credit_link);
}
}
/*
* This function creates a link from the Network to a NI.
* It creates a Network Link from a Router to the NI and
* a Credit Link from NI to the Router
*/
void
GarnetNetwork::makeExtOutLink(SwitchID src, NodeID global_dest,
BasicLink* link,
std::vector<NetDest>& routing_table_entry)
{
NodeID local_dest = getLocalNodeID(global_dest);
assert(local_dest < m_nodes);
assert(src < m_routers.size());
assert(m_routers[src] != NULL);
GarnetExtLink* garnet_link = safe_cast<GarnetExtLink*>(link);
// GarnetExtLink is bi-directional
NetworkLink* net_link = garnet_link->m_network_links[LinkDirection_Out];
net_link->setType(EXT_OUT_);
CreditLink* credit_link = garnet_link->m_credit_links[LinkDirection_Out];
m_networklinks.push_back(net_link);
m_creditlinks.push_back(credit_link);
PortDirection src_outport_dirn = "Local";
m_max_vcs_per_vnet = std::max(m_max_vcs_per_vnet,
m_routers[src]->get_vc_per_vnet());
/*
* We check if a bridge was enabled at any end of the link.
* The bridge is enabled if either of clock domain
* crossing (CDC) or Serializer-Deserializer(SerDes) unit is
* enabled for the link at each end. The bridge encapsulates
* the functionality for both CDC and SerDes and is a Consumer
* object similiar to a NetworkLink.
*
* If a bridge was enabled we connect the NI and Routers to
* bridge before connecting the link. Example, if an external
* bridge is enabled, we would connect:
* NI<---NetworkBridge<---GarnetExtLink<----Router
*/
if (garnet_link->extBridgeEn) {
DPRINTF(RubyNetwork, "Enable external bridge for %s\n",
garnet_link->name());
m_nis[local_dest]->
addInPort(garnet_link->extNetBridge[LinkDirection_Out],
garnet_link->extCredBridge[LinkDirection_Out]);
} else {
m_nis[local_dest]->addInPort(net_link, credit_link);
}
if (garnet_link->intBridgeEn) {
DPRINTF(RubyNetwork, "Enable internal bridge for %s\n",
garnet_link->name());
m_routers[src]->
addOutPort(src_outport_dirn,
garnet_link->intNetBridge[LinkDirection_Out],
routing_table_entry, link->m_weight,
garnet_link->intCredBridge[LinkDirection_Out],
m_routers[src]->get_vc_per_vnet());
} else {
m_routers[src]->
addOutPort(src_outport_dirn, net_link,
routing_table_entry,
link->m_weight, credit_link,
m_routers[src]->get_vc_per_vnet());
}
}
/*
* This function creates an internal network link between two routers.
* It adds both the network link and an opposite credit link.
*/
void
GarnetNetwork::makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link,
std::vector<NetDest>& routing_table_entry,
PortDirection src_outport_dirn,
PortDirection dst_inport_dirn)
{
GarnetIntLink* garnet_link = safe_cast<GarnetIntLink*>(link);
// GarnetIntLink is unidirectional
NetworkLink* net_link = garnet_link->m_network_link;
net_link->setType(INT_);
CreditLink* credit_link = garnet_link->m_credit_link;
m_networklinks.push_back(net_link);
m_creditlinks.push_back(credit_link);
m_max_vcs_per_vnet = std::max(m_max_vcs_per_vnet,
std::max(m_routers[dest]->get_vc_per_vnet(),
m_routers[src]->get_vc_per_vnet()));
/*
* We check if a bridge was enabled at any end of the link.
* The bridge is enabled if either of clock domain
* crossing (CDC) or Serializer-Deserializer(SerDes) unit is
* enabled for the link at each end. The bridge encapsulates
* the functionality for both CDC and SerDes and is a Consumer
* object similiar to a NetworkLink.
*
* If a bridge was enabled we connect the NI and Routers to
* bridge before connecting the link. Example, if a source
* bridge is enabled, we would connect:
* Router--->NetworkBridge--->GarnetIntLink---->Router
*/
if (garnet_link->dstBridgeEn) {
DPRINTF(RubyNetwork, "Enable destination bridge for %s\n",
garnet_link->name());
m_routers[dest]->addInPort(dst_inport_dirn,
garnet_link->dstNetBridge, garnet_link->dstCredBridge);
} else {
m_routers[dest]->addInPort(dst_inport_dirn, net_link, credit_link);
}
if (garnet_link->srcBridgeEn) {
DPRINTF(RubyNetwork, "Enable source bridge for %s\n",
garnet_link->name());
m_routers[src]->
addOutPort(src_outport_dirn, garnet_link->srcNetBridge,
routing_table_entry,
link->m_weight, garnet_link->srcCredBridge,
m_routers[dest]->get_vc_per_vnet());
} else {
m_routers[src]->addOutPort(src_outport_dirn, net_link,
routing_table_entry,
link->m_weight, credit_link,
m_routers[dest]->get_vc_per_vnet());
}
}
// Total routers in the network
int
GarnetNetwork::getNumRouters()
{
return m_routers.size();
}
// Get ID of router connected to a NI.
int
GarnetNetwork::get_router_id(int global_ni, int vnet)
{
NodeID local_ni = getLocalNodeID(global_ni);
return m_nis[local_ni]->get_router_id(vnet);
}
void
GarnetNetwork::regStats()
{
Network::regStats();
// Packets
m_packets_received
.init(m_virtual_networks)
.name(name() + ".packets_received")
.flags(statistics::pdf | statistics::total | statistics::nozero |
statistics::oneline)
;
m_packets_injected
.init(m_virtual_networks)
.name(name() + ".packets_injected")
.flags(statistics::pdf | statistics::total | statistics::nozero |
statistics::oneline)
;
m_packet_network_latency
.init(m_virtual_networks)
.name(name() + ".packet_network_latency")
.flags(statistics::oneline)
;
m_packet_queueing_latency
.init(m_virtual_networks)
.name(name() + ".packet_queueing_latency")
.flags(statistics::oneline)
;
for (int i = 0; i < m_virtual_networks; i++) {
m_packets_received.subname(i, csprintf("vnet-%i", i));
m_packets_injected.subname(i, csprintf("vnet-%i", i));
m_packet_network_latency.subname(i, csprintf("vnet-%i", i));
m_packet_queueing_latency.subname(i, csprintf("vnet-%i", i));
}
m_avg_packet_vnet_latency
.name(name() + ".average_packet_vnet_latency")
.flags(statistics::oneline);
m_avg_packet_vnet_latency =
m_packet_network_latency / m_packets_received;
m_avg_packet_vqueue_latency
.name(name() + ".average_packet_vqueue_latency")
.flags(statistics::oneline);
m_avg_packet_vqueue_latency =
m_packet_queueing_latency / m_packets_received;
m_avg_packet_network_latency
.name(name() + ".average_packet_network_latency");
m_avg_packet_network_latency =
sum(m_packet_network_latency) / sum(m_packets_received);
m_avg_packet_queueing_latency
.name(name() + ".average_packet_queueing_latency");
m_avg_packet_queueing_latency
= sum(m_packet_queueing_latency) / sum(m_packets_received);
m_avg_packet_latency
.name(name() + ".average_packet_latency");
m_avg_packet_latency
= m_avg_packet_network_latency + m_avg_packet_queueing_latency;
// Flits
m_flits_received
.init(m_virtual_networks)
.name(name() + ".flits_received")
.flags(statistics::pdf | statistics::total | statistics::nozero |
statistics::oneline)
;
m_flits_injected
.init(m_virtual_networks)
.name(name() + ".flits_injected")
.flags(statistics::pdf | statistics::total | statistics::nozero |
statistics::oneline)
;
m_flit_network_latency
.init(m_virtual_networks)
.name(name() + ".flit_network_latency")
.flags(statistics::oneline)
;
m_flit_queueing_latency
.init(m_virtual_networks)
.name(name() + ".flit_queueing_latency")
.flags(statistics::oneline)
;
for (int i = 0; i < m_virtual_networks; i++) {
m_flits_received.subname(i, csprintf("vnet-%i", i));
m_flits_injected.subname(i, csprintf("vnet-%i", i));
m_flit_network_latency.subname(i, csprintf("vnet-%i", i));
m_flit_queueing_latency.subname(i, csprintf("vnet-%i", i));
}
m_avg_flit_vnet_latency
.name(name() + ".average_flit_vnet_latency")
.flags(statistics::oneline);
m_avg_flit_vnet_latency = m_flit_network_latency / m_flits_received;
m_avg_flit_vqueue_latency
.name(name() + ".average_flit_vqueue_latency")
.flags(statistics::oneline);
m_avg_flit_vqueue_latency =
m_flit_queueing_latency / m_flits_received;
m_avg_flit_network_latency
.name(name() + ".average_flit_network_latency");
m_avg_flit_network_latency =
sum(m_flit_network_latency) / sum(m_flits_received);
m_avg_flit_queueing_latency
.name(name() + ".average_flit_queueing_latency");
m_avg_flit_queueing_latency =
sum(m_flit_queueing_latency) / sum(m_flits_received);
m_avg_flit_latency
.name(name() + ".average_flit_latency");
m_avg_flit_latency =
m_avg_flit_network_latency + m_avg_flit_queueing_latency;
// Hops
m_avg_hops.name(name() + ".average_hops");
m_avg_hops = m_total_hops / sum(m_flits_received);
// Links
m_total_ext_in_link_utilization
.name(name() + ".ext_in_link_utilization");
m_total_ext_out_link_utilization
.name(name() + ".ext_out_link_utilization");
m_total_int_link_utilization
.name(name() + ".int_link_utilization");
m_average_link_utilization
.name(name() + ".avg_link_utilization");
m_average_vc_load
.init(m_virtual_networks * m_max_vcs_per_vnet)
.name(name() + ".avg_vc_load")
.flags(statistics::pdf | statistics::total | statistics::nozero |
statistics::oneline)
;
// Traffic distribution
for (int source = 0; source < m_routers.size(); ++source) {
m_data_traffic_distribution.push_back(
std::vector<statistics::Scalar *>());
m_ctrl_traffic_distribution.push_back(
std::vector<statistics::Scalar *>());
for (int dest = 0; dest < m_routers.size(); ++dest) {
statistics::Scalar *data_packets = new statistics::Scalar();
statistics::Scalar *ctrl_packets = new statistics::Scalar();
data_packets->name(name() + ".data_traffic_distribution." + "n" +
std::to_string(source) + "." + "n" + std::to_string(dest));
m_data_traffic_distribution[source].push_back(data_packets);
ctrl_packets->name(name() + ".ctrl_traffic_distribution." + "n" +
std::to_string(source) + "." + "n" + std::to_string(dest));
m_ctrl_traffic_distribution[source].push_back(ctrl_packets);
}
}
}
void
GarnetNetwork::collateStats()
{
RubySystem *rs = params().ruby_system;
double time_delta = double(curCycle() - rs->getStartCycle());
for (int i = 0; i < m_networklinks.size(); i++) {
link_type type = m_networklinks[i]->getType();
int activity = m_networklinks[i]->getLinkUtilization();
if (type == EXT_IN_)
m_total_ext_in_link_utilization += activity;
else if (type == EXT_OUT_)
m_total_ext_out_link_utilization += activity;
else if (type == INT_)
m_total_int_link_utilization += activity;
m_average_link_utilization +=
(double(activity) / time_delta);
std::vector<unsigned int> vc_load = m_networklinks[i]->getVcLoad();
for (int j = 0; j < vc_load.size(); j++) {
m_average_vc_load[j] += ((double)vc_load[j] / time_delta);
}
}
// Ask the routers to collate their statistics
for (int i = 0; i < m_routers.size(); i++) {
m_routers[i]->collateStats();
}
}
void
GarnetNetwork::resetStats()
{
for (int i = 0; i < m_routers.size(); i++) {
m_routers[i]->resetStats();
}
for (int i = 0; i < m_networklinks.size(); i++) {
m_networklinks[i]->resetStats();
}
for (int i = 0; i < m_creditlinks.size(); i++) {
m_creditlinks[i]->resetStats();
}
}
void
GarnetNetwork::print(std::ostream& out) const
{
out << "[GarnetNetwork]";
}
void
GarnetNetwork::update_traffic_distribution(RouteInfo route)
{
int src_node = route.src_router;
int dest_node = route.dest_router;
int vnet = route.vnet;
if (m_vnet_type[vnet] == DATA_VNET_)
(*m_data_traffic_distribution[src_node][dest_node])++;
else
(*m_ctrl_traffic_distribution[src_node][dest_node])++;
}
uint32_t
GarnetNetwork::functionalWrite(Packet *pkt)
{
uint32_t num_functional_writes = 0;
for (unsigned int i = 0; i < m_routers.size(); i++) {
num_functional_writes += m_routers[i]->functionalWrite(pkt);
}
for (unsigned int i = 0; i < m_nis.size(); ++i) {
num_functional_writes += m_nis[i]->functionalWrite(pkt);
}
for (unsigned int i = 0; i < m_networklinks.size(); ++i) {
num_functional_writes += m_networklinks[i]->functionalWrite(pkt);
}
return num_functional_writes;
}
} // namespace garnet
} // namespace ruby
} // namespace gem5