From: Srikant Bharadwaj Date: Thu, 19 Jul 2018 17:34:24 +0000 (-0400) Subject: mem-garnet: Integration of HeteroGarnet X-Git-Tag: v20.1.0.0~132 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=b9f1c71fe781e823b399584a5a694420287b2a8c;p=gem5.git mem-garnet: Integration of HeteroGarnet This upgrades the garnet model to support HeteroGarnet 1) Static and dynamic multi-freq domains in network 2) Support for CDC 3) Separate links for each message class 4) Separate linkwidth for each message class 5) Support for SerDes Change-Id: I6d00e3b5cb3745e849d221066cb46b2138c47871 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/32597 Maintainer: Jason Lowe-Power Tested-by: kokoro Reviewed-by: Jason Lowe-Power --- diff --git a/configs/example/garnet_synth_traffic.py b/configs/example/garnet_synth_traffic.py index 9878c23f1..c56e1a800 100644 --- a/configs/example/garnet_synth_traffic.py +++ b/configs/example/garnet_synth_traffic.py @@ -145,7 +145,7 @@ root = Root(full_system = False, system = system) root.system.mem_mode = 'timing' # Not much point in this being higher than the L1 latency -m5.ticks.setGlobalFrequency('1ns') +m5.ticks.setGlobalFrequency('1ps') # instantiate configuration m5.instantiate() diff --git a/configs/network/Network.py b/configs/network/Network.py index 96cc91358..20d68c0ae 100644 --- a/configs/network/Network.py +++ b/configs/network/Network.py @@ -75,7 +75,6 @@ def define_options(parser): type="int", default=50000, help="network-level deadlock threshold.") - def create_network(options, ruby): # Set the network classes based on the command line options @@ -109,6 +108,70 @@ def init_network(options, network, InterfaceClass): network.routing_algorithm = options.routing_algorithm network.garnet_deadlock_threshold = options.garnet_deadlock_threshold + # Create Bridges and connect them to the corresponding links + for intLink in network.int_links: + intLink.src_net_bridge = NetworkBridge( + link = intLink.network_link, + vtype = 'OBJECT_LINK', + width = intLink.src_node.width) + intLink.src_cred_bridge = NetworkBridge( + link = intLink.credit_link, + vtype = 'LINK_OBJECT', + width = intLink.src_node.width) + intLink.dst_net_bridge = NetworkBridge( + link = intLink.network_link, + vtype = 'LINK_OBJECT', + width = intLink.dst_node.width) + intLink.dst_cred_bridge = NetworkBridge( + link = intLink.credit_link, + vtype = 'OBJECT_LINK', + width = intLink.dst_node.width) + + for extLink in network.ext_links: + ext_net_bridges = [] + ext_net_bridges.append(NetworkBridge(link = + extLink.network_links[0], + vtype = 'OBJECT_LINK', + width = extLink.width)) + ext_net_bridges.append(NetworkBridge(link = + extLink.network_links[1], + vtype = 'LINK_OBJECT', + width = extLink.width)) + extLink.ext_net_bridge = ext_net_bridges + + ext_credit_bridges = [] + ext_credit_bridges.append(NetworkBridge(link = + extLink.credit_links[0], + vtype = 'LINK_OBJECT', + width = extLink.width)) + ext_credit_bridges.append(NetworkBridge(link = + extLink.credit_links[1], + vtype = 'OBJECT_LINK', + width = extLink.width)) + extLink.ext_cred_bridge = ext_credit_bridges + + int_net_bridges = [] + int_net_bridges.append(NetworkBridge(link = + extLink.network_links[0], + vtype = 'LINK_OBJECT', + width = extLink.int_node.width)) + int_net_bridges.append(NetworkBridge(link = + extLink.network_links[1], + vtype = 'OBJECT_LINK', + width = extLink.int_node.width)) + extLink.int_net_bridge = int_net_bridges + + int_cred_bridges = [] + int_cred_bridges.append(NetworkBridge(link = + extLink.credit_links[0], + vtype = 'OBJECT_LINK', + width = extLink.int_node.width)) + int_cred_bridges.append(NetworkBridge(link = + extLink.credit_links[1], + vtype = 'LINK_OBJECT', + width = extLink.int_node.width)) + extLink.int_cred_bridge = int_cred_bridges + if options.network == "simple": network.setup_buffers() diff --git a/src/mem/ruby/common/Consumer.hh b/src/mem/ruby/common/Consumer.hh index b0d35bf70..2e1868403 100644 --- a/src/mem/ruby/common/Consumer.hh +++ b/src/mem/ruby/common/Consumer.hh @@ -68,9 +68,14 @@ class Consumer m_scheduled_wakeups.insert(time); } - void scheduleEventAbsolute(Tick timeAbs); + ClockedObject * + getObject() + { + return em; + } - protected: + + void scheduleEventAbsolute(Tick timeAbs); void scheduleEvent(Cycles timeDelta); private: diff --git a/src/mem/ruby/network/BasicLink.cc b/src/mem/ruby/network/BasicLink.cc index 2b55e7ec8..b1691cd55 100644 --- a/src/mem/ruby/network/BasicLink.cc +++ b/src/mem/ruby/network/BasicLink.cc @@ -34,6 +34,7 @@ BasicLink::BasicLink(const Params *p) m_latency = p->latency; m_bandwidth_factor = p->bandwidth_factor; m_weight = p->weight; + mVnets = p->supported_vnets; } void diff --git a/src/mem/ruby/network/BasicLink.hh b/src/mem/ruby/network/BasicLink.hh index 755e5c7a7..dfa94b850 100644 --- a/src/mem/ruby/network/BasicLink.hh +++ b/src/mem/ruby/network/BasicLink.hh @@ -56,6 +56,7 @@ class BasicLink : public SimObject Cycles m_latency; int m_bandwidth_factor; int m_weight; + std::vector mVnets; }; inline std::ostream& diff --git a/src/mem/ruby/network/BasicLink.py b/src/mem/ruby/network/BasicLink.py index ff3e03a09..9d6a7bb15 100644 --- a/src/mem/ruby/network/BasicLink.py +++ b/src/mem/ruby/network/BasicLink.py @@ -37,6 +37,7 @@ class BasicLink(SimObject): # Garnet models this by flit size bandwidth_factor = Param.Int("generic bandwidth factor, usually in bytes") weight = Param.Int(1, "used to restrict routing in shortest path analysis") + supported_vnets = VectorParam.Int([], "Vnets supported Default:All([])") class BasicExtLink(BasicLink): type = 'BasicExtLink' diff --git a/src/mem/ruby/network/Network.cc b/src/mem/ruby/network/Network.cc index bf3b637c3..cda99b196 100644 --- a/src/mem/ruby/network/Network.cc +++ b/src/mem/ruby/network/Network.cc @@ -90,8 +90,9 @@ Network::Network(const Params *p) assert(m_nodes != 0); assert(m_virtual_networks != 0); - m_topology_ptr = new Topology(p->routers.size(), p->ext_links, - p->int_links); + m_topology_ptr = new Topology(m_nodes, p->routers.size(), + m_virtual_networks, + p->ext_links, p->int_links); // Allocate to and from queues // Queues that are getting messages from protocol diff --git a/src/mem/ruby/network/Network.hh b/src/mem/ruby/network/Network.hh index 6348f6c47..f151aed94 100644 --- a/src/mem/ruby/network/Network.hh +++ b/src/mem/ruby/network/Network.hh @@ -99,11 +99,11 @@ class Network : public ClockedObject int network_num, std::string vnet_type); virtual void makeExtOutLink(SwitchID src, NodeID dest, BasicLink* link, - const NetDest& routing_table_entry) = 0; + std::vector& routing_table_entry) = 0; virtual void makeExtInLink(NodeID src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry) = 0; + std::vector& routing_table_entry) = 0; virtual void makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry, + std::vector& routing_table_entry, PortDirection src_outport, PortDirection dst_inport) = 0; diff --git a/src/mem/ruby/network/Topology.cc b/src/mem/ruby/network/Topology.cc index 6da251e0b..13219a547 100644 --- a/src/mem/ruby/network/Topology.cc +++ b/src/mem/ruby/network/Topology.cc @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood * All rights reserved. * @@ -48,11 +49,12 @@ const int INFINITE_LATENCY = 10000; // Yes, this is a big hack // the second m_nodes set of SwitchIDs represent the the output queues // of the network. -Topology::Topology(uint32_t num_routers, +Topology::Topology(uint32_t num_nodes, uint32_t num_routers, + uint32_t num_vnets, const vector &ext_links, const vector &int_links) : m_nodes(MachineType_base_number(MachineType_NUM)), - m_number_of_switches(num_routers), + m_number_of_switches(num_routers), m_vnets(num_vnets), m_ext_link_vector(ext_links), m_int_link_vector(int_links) { // Total nodes/controllers in network @@ -119,40 +121,88 @@ Topology::createLinks(Network *net) // Initialize weight, latency, and inter switched vectors int num_switches = max_switch_id+1; - Matrix topology_weights(num_switches, - vector(num_switches, INFINITE_LATENCY)); + Matrix topology_weights(m_vnets, + vector>(num_switches, + vector(num_switches, INFINITE_LATENCY))); Matrix component_latencies(num_switches, - vector(num_switches, -1)); + vector>(num_switches, + vector(m_vnets, -1))); Matrix component_inter_switches(num_switches, - vector(num_switches, 0)); + vector>(num_switches, + vector(m_vnets, 0))); // Set identity weights to zero - for (int i = 0; i < topology_weights.size(); i++) { - topology_weights[i][i] = 0; + for (int i = 0; i < topology_weights[0].size(); i++) { + for (int v = 0; v < m_vnets; v++) { + topology_weights[v][i][i] = 0; + } } // Fill in the topology weights and bandwidth multipliers - for (LinkMap::const_iterator i = m_link_map.begin(); - i != m_link_map.end(); ++i) { - std::pair src_dest = (*i).first; - BasicLink* link = (*i).second.link; + for (auto link_group : m_link_map) { + std::pair src_dest = link_group.first; + vector vnet_done(m_vnets, 0); int src = src_dest.first; int dst = src_dest.second; - component_latencies[src][dst] = link->m_latency; - topology_weights[src][dst] = link->m_weight; + + // Iterate over all links for this source and destination + std::vector link_entries = link_group.second; + for (int l = 0; l < link_entries.size(); l++) { + BasicLink* link = link_entries[l].link; + if (link->mVnets.size() == 0) { + for (int v = 0; v < m_vnets; v++) { + // Two links connecting same src and destination + // cannot carry same vnets. + fatal_if(vnet_done[v], "Two links connecting same src" + " and destination cannot support same vnets"); + + component_latencies[src][dst][v] = link->m_latency; + topology_weights[v][src][dst] = link->m_weight; + vnet_done[v] = true; + } + } else { + for (int v = 0; v < link->mVnets.size(); v++) { + int vnet = link->mVnets[v]; + // Two links connecting same src and destination + // cannot carry same vnets. + fatal_if(vnet_done[vnet], "Two links connecting same src" + " and destination cannot support same vnets"); + + component_latencies[src][dst][vnet] = link->m_latency; + topology_weights[vnet][src][dst] = link->m_weight; + vnet_done[vnet] = true; + } + } + } } // Walk topology and hookup the links Matrix dist = shortest_path(topology_weights, component_latencies, component_inter_switches); - for (int i = 0; i < topology_weights.size(); i++) { - for (int j = 0; j < topology_weights[i].size(); j++) { - int weight = topology_weights[i][j]; - if (weight > 0 && weight != INFINITE_LATENCY) { - NetDest destination_set = - shortest_path_to_node(i, j, topology_weights, dist); - makeLink(net, i, j, destination_set); + for (int i = 0; i < topology_weights[0].size(); i++) { + for (int j = 0; j < topology_weights[0][i].size(); j++) { + std::vector routingMap; + routingMap.resize(m_vnets); + + // Not all sources and destinations are connected + // by direct links. We only construct the links + // which have been configured in topology. + bool realLink = false; + + for (int v = 0; v < m_vnets; v++) { + int weight = topology_weights[v][i][j]; + if (weight > 0 && weight != INFINITE_LATENCY) { + realLink = true; + routingMap[v] = + shortest_path_to_node(i, j, topology_weights, dist, v); + } + } + // Make one link for each set of vnets between + // a given source and destination. We do not + // want to create one link for each vnet. + if (realLink) { + makeLink(net, i, j, routingMap); } } } @@ -167,19 +217,32 @@ Topology::addLink(SwitchID src, SwitchID dest, BasicLink* link, assert(dest <= m_number_of_switches+m_nodes+m_nodes); std::pair src_dest_pair; - LinkEntry link_entry; - src_dest_pair.first = src; src_dest_pair.second = dest; + LinkEntry link_entry; + link_entry.link = link; link_entry.src_outport_dirn = src_outport_dirn; link_entry.dst_inport_dirn = dst_inport_dirn; - m_link_map[src_dest_pair] = link_entry; + + auto lit = m_link_map.find(src_dest_pair); + if (lit != m_link_map.end()) { + // HeteroGarnet allows multiple links between + // same source-destination pair supporting + // different vnets. If there is a link already + // between a given pair of source and destination + // add this new link to it. + lit->second.push_back(link_entry); + } else { + std::vector links; + links.push_back(link_entry); + m_link_map[src_dest_pair] = links; + } } void Topology::makeLink(Network *net, SwitchID src, SwitchID dest, - const NetDest& routing_table_entry) + std::vector& routing_table_entry) { // Make sure we're not trying to connect two end-point nodes // directly together @@ -191,27 +254,73 @@ Topology::makeLink(Network *net, SwitchID src, SwitchID dest, if (src < m_nodes) { src_dest.first = src; src_dest.second = dest; - link_entry = m_link_map[src_dest]; - net->makeExtInLink(src, dest - (2 * m_nodes), link_entry.link, - routing_table_entry); + std::vector links = m_link_map[src_dest]; + for (int l = 0; l < links.size(); l++) { + link_entry = links[l]; + std::vector linkRoute; + linkRoute.resize(m_vnets); + BasicLink *link = link_entry.link; + if (link->mVnets.size() == 0) { + net->makeExtInLink(src, dest - (2 * m_nodes), link, + routing_table_entry); + } else { + for (int v = 0; v< link->mVnets.size(); v++) { + int vnet = link->mVnets[v]; + linkRoute[vnet] = routing_table_entry[vnet]; + } + net->makeExtInLink(src, dest - (2 * m_nodes), link, + linkRoute); + } + } } else if (dest < 2*m_nodes) { assert(dest >= m_nodes); NodeID node = dest - m_nodes; src_dest.first = src; src_dest.second = dest; - link_entry = m_link_map[src_dest]; - net->makeExtOutLink(src - (2 * m_nodes), node, link_entry.link, - routing_table_entry); + std::vector links = m_link_map[src_dest]; + for (int l = 0; l < links.size(); l++) { + link_entry = links[l]; + std::vector linkRoute; + linkRoute.resize(m_vnets); + BasicLink *link = link_entry.link; + if (link->mVnets.size() == 0) { + net->makeExtOutLink(src - (2 * m_nodes), node, link, + routing_table_entry); + } else { + for (int v = 0; v< link->mVnets.size(); v++) { + int vnet = link->mVnets[v]; + linkRoute[vnet] = routing_table_entry[vnet]; + } + net->makeExtOutLink(src - (2 * m_nodes), node, link, + linkRoute); + } + } } else { assert((src >= 2 * m_nodes) && (dest >= 2 * m_nodes)); src_dest.first = src; src_dest.second = dest; - link_entry = m_link_map[src_dest]; - net->makeInternalLink(src - (2 * m_nodes), dest - (2 * m_nodes), - link_entry.link, - routing_table_entry, + std::vector links = m_link_map[src_dest]; + for (int l = 0; l < links.size(); l++) { + link_entry = links[l]; + std::vector linkRoute; + linkRoute.resize(m_vnets); + BasicLink *link = link_entry.link; + if (link->mVnets.size() == 0) { + net->makeInternalLink(src - (2 * m_nodes), + dest - (2 * m_nodes), link, routing_table_entry, link_entry.src_outport_dirn, link_entry.dst_inport_dirn); + } else { + for (int v = 0; v< link->mVnets.size(); v++) { + int vnet = link->mVnets[v]; + linkRoute[vnet] = routing_table_entry[vnet]; + } + net->makeInternalLink(src - (2 * m_nodes), + dest - (2 * m_nodes), link, linkRoute, + link_entry.src_outport_dirn, + link_entry.dst_inport_dirn); + } + } } } @@ -221,34 +330,54 @@ void Topology::extend_shortest_path(Matrix ¤t_dist, Matrix &latencies, Matrix &inter_switches) { - bool change = true; - int nodes = current_dist.size(); - - while (change) { - change = false; - for (int i = 0; i < nodes; i++) { - for (int j = 0; j < nodes; j++) { - int minimum = current_dist[i][j]; - int previous_minimum = minimum; - int intermediate_switch = -1; - for (int k = 0; k < nodes; k++) { - minimum = min(minimum, - current_dist[i][k] + current_dist[k][j]); - if (previous_minimum != minimum) { - intermediate_switch = k; - inter_switches[i][j] = - inter_switches[i][k] + - inter_switches[k][j] + 1; + int nodes = current_dist[0].size(); + + // We find the shortest path for each vnet for a given pair of + // source and destinations. This is done simply by traversing via + // all other nodes and finding the minimum distance. + for (int v = 0; v < m_vnets; v++) { + // There is a different topology for each vnet. Here we try to + // build a topology by finding the minimum number of intermediate + // switches needed to reach the destination + bool change = true; + while (change) { + change = false; + for (int i = 0; i < nodes; i++) { + for (int j = 0; j < nodes; j++) { + // We follow an iterative process to build the shortest + // path tree: + // 1. Start from the direct connection (if there is one, + // otherwise assume a hypothetical infinite weight link). + // 2. Then we iterate through all other nodes considering + // new potential intermediate switches. If we find any + // lesser weight combination, we set(update) that as the + // new weight between the source and destination. + // 3. Repeat for all pairs of nodes. + // 4. Go to step 1 if there was any new update done in + // Step 2. + int minimum = current_dist[v][i][j]; + int previous_minimum = minimum; + int intermediate_switch = -1; + for (int k = 0; k < nodes; k++) { + minimum = min(minimum, + current_dist[v][i][k] + current_dist[v][k][j]); + if (previous_minimum != minimum) { + intermediate_switch = k; + inter_switches[i][j][v] = + inter_switches[i][k][v] + + inter_switches[k][j][v] + 1; + } + previous_minimum = minimum; + } + if (current_dist[v][i][j] != minimum) { + change = true; + current_dist[v][i][j] = minimum; + assert(intermediate_switch >= 0); + assert(intermediate_switch < latencies[i].size()); + latencies[i][j][v] = + latencies[i][intermediate_switch][v] + + latencies[intermediate_switch][j][v]; } - previous_minimum = minimum; - } - if (current_dist[i][j] != minimum) { - change = true; - current_dist[i][j] = minimum; - assert(intermediate_switch >= 0); - assert(intermediate_switch < latencies[i].size()); - latencies[i][j] = latencies[i][intermediate_switch] + - latencies[intermediate_switch][j]; } } } @@ -267,14 +396,16 @@ Topology::shortest_path(const Matrix &weights, Matrix &latencies, bool Topology::link_is_shortest_path_to_node(SwitchID src, SwitchID next, SwitchID final, const Matrix &weights, - const Matrix &dist) + const Matrix &dist, int vnet) { - return weights[src][next] + dist[next][final] == dist[src][final]; + return weights[vnet][src][next] + dist[vnet][next][final] == + dist[vnet][src][final]; } NetDest Topology::shortest_path_to_node(SwitchID src, SwitchID next, - const Matrix &weights, const Matrix &dist) + const Matrix &weights, const Matrix &dist, + int vnet) { NetDest result; int d = 0; @@ -292,7 +423,7 @@ Topology::shortest_path_to_node(SwitchID src, SwitchID next, // 2*MachineType_base_number(MachineType_NUM)-1] for the // component network if (link_is_shortest_path_to_node(src, next, d + max_machines, - weights, dist)) { + weights, dist, vnet)) { MachineID mach = {(MachineType)m, i}; result.add(mach); } @@ -302,9 +433,9 @@ Topology::shortest_path_to_node(SwitchID src, SwitchID next, DPRINTF(RubyNetwork, "Returning shortest path\n" "(src-(2*max_machines)): %d, (next-(2*max_machines)): %d, " - "src: %d, next: %d, result: %s\n", + "src: %d, next: %d, vnet:%d result: %s\n", (src-(2*max_machines)), (next-(2*max_machines)), - src, next, result); + src, next, vnet, result); return result; } diff --git a/src/mem/ruby/network/Topology.hh b/src/mem/ruby/network/Topology.hh index ef8104ad5..40b8a86df 100644 --- a/src/mem/ruby/network/Topology.hh +++ b/src/mem/ruby/network/Topology.hh @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood * All rights reserved. * @@ -51,7 +52,13 @@ class NetDest; class Network; -typedef std::vector > Matrix; +/* + * We use a three-dimensional vector matrix for calculating + * the shortest paths for each pair of source and destination + * and for each type of virtual network. The three dimensions + * represent the source ID, destination ID, and vnet number. + */ +typedef std::vector>> Matrix; typedef std::string PortDirection; struct LinkEntry @@ -61,12 +68,14 @@ struct LinkEntry PortDirection dst_inport_dirn; }; -typedef std::map, LinkEntry> LinkMap; +typedef std::map, + std::vector> LinkMap; class Topology { public: - Topology(uint32_t num_routers, const std::vector &ext_links, + Topology(uint32_t num_nodes, uint32_t num_routers, uint32_t num_vnets, + const std::vector &ext_links, const std::vector &int_links); uint32_t numSwitches() const { return m_number_of_switches; } @@ -78,23 +87,26 @@ class Topology PortDirection src_outport_dirn = "", PortDirection dest_inport_dirn = ""); void makeLink(Network *net, SwitchID src, SwitchID dest, - const NetDest& routing_table_entry); + std::vector& routing_table_entry); // Helper functions based on chapter 29 of Cormen et al. void extend_shortest_path(Matrix ¤t_dist, Matrix &latencies, Matrix &inter_switches); - std::vector> shortest_path(const Matrix &weights, + Matrix shortest_path(const Matrix &weights, Matrix &latencies, Matrix &inter_switches); bool link_is_shortest_path_to_node(SwitchID src, SwitchID next, - SwitchID final, const Matrix &weights, const Matrix &dist); + SwitchID final, const Matrix &weights, const Matrix &dist, + int vnet); NetDest shortest_path_to_node(SwitchID src, SwitchID next, - const Matrix &weights, const Matrix &dist); + const Matrix &weights, const Matrix &dist, + int vnet); const uint32_t m_nodes; const uint32_t m_number_of_switches; + int m_vnets; std::vector m_ext_link_vector; std::vector m_int_link_vector; diff --git a/src/mem/ruby/network/garnet2.0/CommonTypes.hh b/src/mem/ruby/network/garnet2.0/CommonTypes.hh index 9862e7288..94aa60096 100644 --- a/src/mem/ruby/network/garnet2.0/CommonTypes.hh +++ b/src/mem/ruby/network/garnet2.0/CommonTypes.hh @@ -35,7 +35,8 @@ // All common enums and typedefs go here -enum flit_type {HEAD_, BODY_, TAIL_, HEAD_TAIL_, NUM_FLIT_TYPE_}; +enum flit_type {HEAD_, BODY_, TAIL_, HEAD_TAIL_, + CREDIT_, NUM_FLIT_TYPE_}; enum VC_state_type {IDLE_, VC_AB_, ACTIVE_, NUM_VC_STATE_TYPE_}; enum VNET_type {CTRL_VNET_, DATA_VNET_, NULL_VNET_, NUM_VNET_TYPE_}; enum flit_stage {I_, VA_, SA_, ST_, LT_, NUM_FLIT_STAGE_}; diff --git a/src/mem/ruby/network/garnet2.0/Credit.cc b/src/mem/ruby/network/garnet2.0/Credit.cc index eb286ec49..3e56b4ed5 100644 --- a/src/mem/ruby/network/garnet2.0/Credit.cc +++ b/src/mem/ruby/network/garnet2.0/Credit.cc @@ -27,17 +27,60 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "mem/ruby/network/garnet2.0/Credit.hh" +#include "base/trace.hh" +#include "debug/RubyNetwork.hh" + // Credit Signal for buffers inside VC // Carries m_vc (inherits from flit.hh) // and m_is_free_signal (whether VC is free or not) -Credit::Credit(int vc, bool is_free_signal, Cycles curTime) +Credit::Credit(int vc, bool is_free_signal, Tick curTime) { m_id = 0; m_vc = vc; m_is_free_signal = is_free_signal; m_time = curTime; + m_type = CREDIT_; +} + +flit * +Credit::serialize(int ser_id, int parts, uint32_t bWidth) +{ + DPRINTF(RubyNetwork, "Serializing a credit\n"); + bool new_free = false; + if ((ser_id+1 == parts) && m_is_free_signal) { + new_free = true; + } + Credit *new_credit_flit = new Credit(m_vc, new_free, m_time); + return new_credit_flit; } + +flit * +Credit::deserialize(int des_id, int num_flits, uint32_t bWidth) +{ + DPRINTF(RubyNetwork, "DeSerializing a credit vc:%d free:%d\n", + m_vc, m_is_free_signal); + if (m_is_free_signal) { + // We are not going to get anymore credits for this vc + // So send a credit in any case + return new Credit(m_vc, true, m_time); + } + + return new Credit(m_vc, false, m_time); +} + +void +Credit::print(std::ostream& out) const +{ + out << "[Credit:: "; + out << "Type=" << m_type << " "; + out << "VC=" << m_vc << " "; + out << "FreeVC=" << m_is_free_signal << " "; + out << "Set Time=" << m_time << " "; + out << "]"; +} + + + diff --git a/src/mem/ruby/network/garnet2.0/Credit.hh b/src/mem/ruby/network/garnet2.0/Credit.hh index bd0c3c8ba..bcfd76f24 100644 --- a/src/mem/ruby/network/garnet2.0/Credit.hh +++ b/src/mem/ruby/network/garnet2.0/Credit.hh @@ -46,7 +46,14 @@ class Credit : public flit { public: Credit() {}; - Credit(int vc, bool is_free_signal, Cycles curTime); + Credit(int vc, bool is_free_signal, Tick curTime); + + // Functions used by SerDes + flit* serialize(int ser_id, int parts, uint32_t bWidth); + flit* deserialize(int des_id, int num_flits, uint32_t bWidth); + void print(std::ostream& out) const; + + ~Credit() {}; bool is_free_signal() { return m_is_free_signal; } diff --git a/src/mem/ruby/network/garnet2.0/CrossbarSwitch.cc b/src/mem/ruby/network/garnet2.0/CrossbarSwitch.cc index eda9e9edb..3148446aa 100644 --- a/src/mem/ruby/network/garnet2.0/CrossbarSwitch.cc +++ b/src/mem/ruby/network/garnet2.0/CrossbarSwitch.cc @@ -61,17 +61,17 @@ CrossbarSwitch::wakeup() m_router->get_id(), m_router->curCycle()); for (auto& switch_buffer : switchBuffers) { - if (!switch_buffer.isReady(m_router->curCycle())) { + if (!switch_buffer.isReady(curTick())) { continue; } flit *t_flit = switch_buffer.peekTopFlit(); - if (t_flit->is_stage(ST_, m_router->curCycle())) { + if (t_flit->is_stage(ST_, curTick())) { int outport = t_flit->get_outport(); // flit performs LT_ in the next cycle - t_flit->advance_stage(LT_, m_router->curCycle() + Cycles(1)); - t_flit->set_time(m_router->curCycle() + Cycles(1)); + t_flit->advance_stage(LT_, m_router->clockEdge(Cycles(1))); + t_flit->set_time(m_router->clockEdge(Cycles(1))); // This will take care of waking up the Network Link // in the next cycle diff --git a/src/mem/ruby/network/garnet2.0/GarnetLink.cc b/src/mem/ruby/network/garnet2.0/GarnetLink.cc index b14553003..99d891f95 100644 --- a/src/mem/ruby/network/garnet2.0/GarnetLink.cc +++ b/src/mem/ruby/network/garnet2.0/GarnetLink.cc @@ -30,7 +30,9 @@ #include "mem/ruby/network/garnet2.0/GarnetLink.hh" +#include "debug/RubyNetwork.hh" #include "mem/ruby/network/garnet2.0/CreditLink.hh" +#include "mem/ruby/network/garnet2.0/NetworkBridge.hh" #include "mem/ruby/network/garnet2.0/NetworkLink.hh" GarnetIntLink::GarnetIntLink(const Params *p) @@ -40,11 +42,43 @@ GarnetIntLink::GarnetIntLink(const Params *p) m_network_link = p->network_link; m_credit_link = p->credit_link; + + srcCdcEn = p->src_cdc; + dstCdcEn = p->dst_cdc; + + srcSerdesEn = p->src_serdes; + dstSerdesEn = p->dst_serdes; + + srcBridgeEn = false; + dstBridgeEn = false; + + if (srcCdcEn || srcSerdesEn) { + srcBridgeEn = true; + srcNetBridge = p->src_net_bridge; + srcCredBridge = p->src_cred_bridge; + } + if (dstCdcEn || dstSerdesEn) { + dstBridgeEn = true; + dstNetBridge = p->dst_net_bridge; + dstCredBridge = p->dst_cred_bridge; + } + } void GarnetIntLink::init() { + if (srcBridgeEn) { + assert(srcNetBridge && srcCredBridge); + srcNetBridge->initBridge(srcCredBridge, srcCdcEn, srcSerdesEn); + srcCredBridge->initBridge(srcNetBridge, srcCdcEn, srcSerdesEn); + } + + if (dstBridgeEn) { + assert(dstNetBridge && dstCredBridge); + dstNetBridge->initBridge(dstCredBridge, dstCdcEn, dstSerdesEn); + dstCredBridge->initBridge(dstNetBridge, dstCdcEn, dstSerdesEn); + } } void @@ -71,11 +105,55 @@ GarnetExtLink::GarnetExtLink(const Params *p) // Out m_network_links[1] = p->network_links[1]; m_credit_links[1] = p->credit_links[1]; + + + extCdcEn = p->ext_cdc; + intCdcEn = p->int_cdc; + + extSerdesEn = p->ext_serdes; + intSerdesEn = p->int_serdes; + + extBridgeEn = false; + intBridgeEn = false; + + if (extCdcEn || extSerdesEn) { + extBridgeEn = true; + extNetBridge[0] = p->ext_net_bridge[0]; + extCredBridge[0] = p->ext_cred_bridge[0]; + extNetBridge[1] = p->ext_net_bridge[1]; + extCredBridge[1] = p->ext_cred_bridge[1]; + } + + if (intCdcEn || intSerdesEn) { + intBridgeEn = true; + intNetBridge[0] = p->int_net_bridge[0]; + intNetBridge[1] = p->int_net_bridge[1]; + intCredBridge[0] = p->int_cred_bridge[0]; + intCredBridge[1] = p->int_cred_bridge[1]; + } + } void GarnetExtLink::init() { + if (extBridgeEn) { + assert(extNetBridge[0] && extCredBridge[0] && + extNetBridge[1] && extCredBridge[1]); + extNetBridge[0]->initBridge(extCredBridge[0], extCdcEn, extSerdesEn); + extCredBridge[0]->initBridge(extNetBridge[0], extCdcEn, extSerdesEn); + extNetBridge[1]->initBridge(extCredBridge[1], extCdcEn, extSerdesEn); + extCredBridge[1]->initBridge(extNetBridge[1], extCdcEn, extSerdesEn); + } + + if (intBridgeEn) { + assert(intNetBridge[0] && intCredBridge[0] && + intNetBridge[1] && intCredBridge[1]); + intNetBridge[0]->initBridge(intCredBridge[0], intCdcEn, intSerdesEn); + intCredBridge[0]->initBridge(intNetBridge[0], intCdcEn, intSerdesEn); + intNetBridge[1]->initBridge(intCredBridge[1], intCdcEn, intSerdesEn); + intCredBridge[1]->initBridge(intNetBridge[1], intCdcEn, intSerdesEn); + } } void diff --git a/src/mem/ruby/network/garnet2.0/GarnetLink.hh b/src/mem/ruby/network/garnet2.0/GarnetLink.hh index 1c7e74327..089bcce45 100644 --- a/src/mem/ruby/network/garnet2.0/GarnetLink.hh +++ b/src/mem/ruby/network/garnet2.0/GarnetLink.hh @@ -37,6 +37,7 @@ #include "mem/ruby/network/BasicLink.hh" #include "mem/ruby/network/garnet2.0/CreditLink.hh" +#include "mem/ruby/network/garnet2.0/NetworkBridge.hh" #include "mem/ruby/network/garnet2.0/NetworkLink.hh" #include "params/GarnetExtLink.hh" #include "params/GarnetIntLink.hh" @@ -56,6 +57,21 @@ class GarnetIntLink : public BasicIntLink protected: NetworkLink* m_network_link; CreditLink* m_credit_link; + + bool srcBridgeEn; + bool dstBridgeEn; + + bool srcSerdesEn; + bool dstSerdesEn; + + bool srcCdcEn; + bool dstCdcEn; + + NetworkBridge* srcNetBridge; + NetworkBridge* dstNetBridge; + + NetworkBridge* srcCredBridge; + NetworkBridge* dstCredBridge; }; inline std::ostream& @@ -79,8 +95,24 @@ class GarnetExtLink : public BasicExtLink friend class GarnetNetwork; protected: + bool extBridgeEn; + bool intBridgeEn; + + bool extSerdesEn; + bool intSerdesEn; + + bool extCdcEn; + bool intCdcEn; + NetworkLink* m_network_links[2]; CreditLink* m_credit_links[2]; + + NetworkBridge* extNetBridge[2]; + NetworkBridge* intNetBridge[2]; + + NetworkBridge* extCredBridge[2]; + NetworkBridge* intCredBridge[2]; + }; inline std::ostream& diff --git a/src/mem/ruby/network/garnet2.0/GarnetLink.py b/src/mem/ruby/network/garnet2.0/GarnetLink.py index 3f1bba696..33a3d3149 100644 --- a/src/mem/ruby/network/garnet2.0/GarnetLink.py +++ b/src/mem/ruby/network/garnet2.0/GarnetLink.py @@ -30,6 +30,11 @@ from m5.proxy import * from m5.objects.ClockedObject import ClockedObject from m5.objects.BasicLink import BasicIntLink, BasicExtLink +class CDCType(Enum): vals = [ + 'LINK_OBJECT', + 'OBJECT_LINK', + ] + class NetworkLink(ClockedObject): type = 'NetworkLink' cxx_header = "mem/ruby/network/garnet2.0/NetworkLink.hh" @@ -39,11 +44,23 @@ class NetworkLink(ClockedObject): "virtual channels per virtual network") virt_nets = Param.Int(Parent.number_of_virtual_networks, "number of virtual networks") + supported_vnets = VectorParam.Int(Parent.supported_vnets, + "Vnets supported") + width = Param.UInt32(Parent.width, "bit-width of the link") class CreditLink(NetworkLink): type = 'CreditLink' cxx_header = "mem/ruby/network/garnet2.0/CreditLink.hh" +class NetworkBridge(CreditLink): + type = 'NetworkBridge' + cxx_header = "mem/ruby/network/garnet2.0/NetworkBridge.hh" + link = Param.NetworkLink("Associated Network Link") + vtype = Param.CDCType('LINK_OBJECT', + "Direction of CDC LINK->OBJECT or OBJECT->LINK") + serdes_latency = Param.Cycles(1, "Latency of SerDes Unit") + cdc_latency = Param.Cycles(1, "Latency of CDC Unit") + # Interior fixed pipeline links between routers class GarnetIntLink(BasicIntLink): type = 'GarnetIntLink' @@ -53,6 +70,34 @@ class GarnetIntLink(BasicIntLink): network_link = Param.NetworkLink(NetworkLink(), "forward link") credit_link = Param.CreditLink(CreditLink(), "backward flow-control link") + # The src_cdc and dst_cdc flags are used to enable the + # clock domain crossing(CDC) at the source and destination + # end of the link respectively. This is required when the + # link and the objected connected to the link are operating + # at different clock domains. These flags should be set + # in the network topology files. + src_cdc = Param.Bool(False, "Enable Clock Domain Crossing") + dst_cdc = Param.Bool(False, "Enable Clock Domain Crossing") + + # The src_serdes and dst_serdes flags are used to enable + # the Serializer-Deserializer units at the source and + # destination end of the link respectively. Enabling + # these flags is necessary when the connecting object + # supports a different flit width. + src_serdes = Param.Bool(False, "Enable Serializer-Deserializer") + dst_serdes = Param.Bool(False, "Enable Serializer-Deserializer") + + # The network bridge encapsulates both the CDC and Ser-Des + # units in HeteroGarnet. This is automatically enabled when + # either CDC or Ser-Des is enabled. + src_net_bridge = Param.NetworkBridge(NULL, "Network Bridge at source") + dst_net_bridge = Param.NetworkBridge(NULL, "Network Bridge at dest") + src_cred_bridge = Param.NetworkBridge(NULL, "Credit Bridge at source") + dst_cred_bridge = Param.NetworkBridge(NULL, "Credit Bridge at dest") + + width = Param.UInt32(Parent.ni_flit_size, + "bit width supported by the router") + # Exterior fixed pipeline links between a router and a controller class GarnetExtLink(BasicExtLink): type = 'GarnetExtLink' @@ -74,3 +119,36 @@ class GarnetExtLink(BasicExtLink): # Out uni-directional link _cls.append(CreditLink()); credit_links = VectorParam.CreditLink(_cls, "backward flow-control links") + + # The ext_cdc and intt_cdc flags are used to enable the + # clock domain crossing(CDC) at the external and internal + # end of the link respectively. This is required when the + # link and the objected connected to the link are operating + # at different clock domains. These flags should be set + # in the network topology files. + ext_cdc = Param.Bool(False, "Enable Clock Domain Crossing") + int_cdc = Param.Bool(False, "Enable Clock Domain Crossing") + + # The ext_serdes and int_serdes flags are used to enable + # the Serializer-Deserializer units at the external and + # internal end of the link respectively. Enabling + # these flags is necessary when the connecting object + # supports a different flit width. + ext_serdes = Param.Bool(False, "Enable Serializer-Deserializer") + int_serdes = Param.Bool(False, "Enable Serializer-Deserializer") + + # The network bridge encapsulates both the CDC and Ser-Des + # units in HeteroGarnet. This is automatically enabled when + # either CDC or Ser-Des is enabled. + ext_net_bridge = VectorParam.NetworkBridge(NULL, + "Network Bridge at external end") + ext_cred_bridge = VectorParam.NetworkBridge(NULL, + "Credit Bridge at external end") + int_net_bridge = VectorParam.NetworkBridge(NULL, + "Network Bridge at internal end") + int_cred_bridge = VectorParam.NetworkBridge(NULL, + "Credit Bridge at intternal end") + + + width = Param.UInt32(Parent.ni_flit_size, + "bit width supported by the router") diff --git a/src/mem/ruby/network/garnet2.0/GarnetNetwork.cc b/src/mem/ruby/network/garnet2.0/GarnetNetwork.cc index a88302b6c..63562cd91 100644 --- a/src/mem/ruby/network/garnet2.0/GarnetNetwork.cc +++ b/src/mem/ruby/network/garnet2.0/GarnetNetwork.cc @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 2008 Princeton University * Copyright (c) 2016 Georgia Institute of Technology * All rights reserved. @@ -33,6 +34,7 @@ #include #include "base/cast.hh" +#include "debug/RubyNetwork.hh" #include "mem/ruby/common/NetDest.hh" #include "mem/ruby/network/MessageBuffer.hh" #include "mem/ruby/network/garnet2.0/CommonTypes.hh" @@ -147,7 +149,7 @@ GarnetNetwork::init() void GarnetNetwork::makeExtInLink(NodeID global_src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry) + std::vector& routing_table_entry) { NodeID local_src = getLocalNodeID(global_src); assert(local_src < m_nodes); @@ -163,8 +165,42 @@ GarnetNetwork::makeExtInLink(NodeID global_src, SwitchID dest, BasicLink* link, m_creditlinks.push_back(credit_link); PortDirection dst_inport_dirn = "Local"; - m_routers[dest]->addInPort(dst_inport_dirn, net_link, credit_link); - m_nis[local_src]->addOutPort(net_link, credit_link, dest); + + /* + * 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); + } else { + m_nis[local_src]->addOutPort(net_link, credit_link, dest); + } + + 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); + } + } /* @@ -176,7 +212,7 @@ GarnetNetwork::makeExtInLink(NodeID global_src, SwitchID dest, BasicLink* link, void GarnetNetwork::makeExtOutLink(SwitchID src, NodeID global_dest, BasicLink* link, - const NetDest& routing_table_entry) + std::vector& routing_table_entry) { NodeID local_dest = getLocalNodeID(global_dest); assert(local_dest < m_nodes); @@ -194,10 +230,44 @@ GarnetNetwork::makeExtOutLink(SwitchID src, NodeID global_dest, m_creditlinks.push_back(credit_link); PortDirection src_outport_dirn = "Local"; - m_routers[src]->addOutPort(src_outport_dirn, net_link, - routing_table_entry, - link->m_weight, credit_link); - m_nis[local_dest]->addInPort(net_link, credit_link); + + /* + * 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]); + } else { + m_routers[src]-> + addOutPort(src_outport_dirn, net_link, + routing_table_entry, + link->m_weight, credit_link); + } } /* @@ -207,7 +277,7 @@ GarnetNetwork::makeExtOutLink(SwitchID src, NodeID global_dest, void GarnetNetwork::makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry, + std::vector& routing_table_entry, PortDirection src_outport_dirn, PortDirection dst_inport_dirn) { @@ -221,10 +291,40 @@ GarnetNetwork::makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link, m_networklinks.push_back(net_link); m_creditlinks.push_back(credit_link); - m_routers[dest]->addInPort(dst_inport_dirn, net_link, credit_link); - m_routers[src]->addOutPort(src_outport_dirn, net_link, - routing_table_entry, - link->m_weight, credit_link); + /* + * 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); + } else { + m_routers[src]->addOutPort(src_outport_dirn, net_link, + routing_table_entry, + link->m_weight, credit_link); + } } // Total routers in the network @@ -236,11 +336,11 @@ GarnetNetwork::getNumRouters() // Get ID of router connected to a NI. int -GarnetNetwork::get_router_id(int global_ni) +GarnetNetwork::get_router_id(int global_ni, int vnet) { NodeID local_ni = getLocalNodeID(global_ni); - return m_nis[local_ni]->get_router_id(); + return m_nis[local_ni]->get_router_id(vnet); } void diff --git a/src/mem/ruby/network/garnet2.0/GarnetNetwork.hh b/src/mem/ruby/network/garnet2.0/GarnetNetwork.hh index 3821dd8b8..5173fa760 100644 --- a/src/mem/ruby/network/garnet2.0/GarnetNetwork.hh +++ b/src/mem/ruby/network/garnet2.0/GarnetNetwork.hh @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 2008 Princeton University * Copyright (c) 2016 Georgia Institute of Technology * All rights reserved. @@ -81,16 +82,16 @@ class GarnetNetwork : public Network return m_vnet_type[vnet]; } int getNumRouters(); - int get_router_id(int ni); + int get_router_id(int ni, int vnet); // Methods used by Topology to setup the network void makeExtOutLink(SwitchID src, NodeID dest, BasicLink* link, - const NetDest& routing_table_entry); + std::vector& routing_table_entry); void makeExtInLink(NodeID src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry); + std::vector& routing_table_entry); void makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry, + std::vector& routing_table_entry, PortDirection src_outport_dirn, PortDirection dest_inport_dirn); @@ -109,13 +110,13 @@ class GarnetNetwork : public Network void increment_received_packets(int vnet) { m_packets_received[vnet]++; } void - increment_packet_network_latency(Cycles latency, int vnet) + increment_packet_network_latency(Tick latency, int vnet) { m_packet_network_latency[vnet] += latency; } void - increment_packet_queueing_latency(Cycles latency, int vnet) + increment_packet_queueing_latency(Tick latency, int vnet) { m_packet_queueing_latency[vnet] += latency; } @@ -124,13 +125,13 @@ class GarnetNetwork : public Network void increment_received_flits(int vnet) { m_flits_received[vnet]++; } void - increment_flit_network_latency(Cycles latency, int vnet) + increment_flit_network_latency(Tick latency, int vnet) { m_flit_network_latency[vnet] += latency; } void - increment_flit_queueing_latency(Cycles latency, int vnet) + increment_flit_queueing_latency(Tick latency, int vnet) { m_flit_queueing_latency[vnet] += latency; } diff --git a/src/mem/ruby/network/garnet2.0/GarnetNetwork.py b/src/mem/ruby/network/garnet2.0/GarnetNetwork.py index 04c0ef46b..012ab60d8 100644 --- a/src/mem/ruby/network/garnet2.0/GarnetNetwork.py +++ b/src/mem/ruby/network/garnet2.0/GarnetNetwork.py @@ -70,3 +70,5 @@ class GarnetRouter(BasicRouter): "virtual channels per virtual network") virt_nets = Param.UInt32(Parent.number_of_virtual_networks, "number of virtual networks") + width = Param.UInt32(Parent.ni_flit_size, + "bit width supported by the router") diff --git a/src/mem/ruby/network/garnet2.0/InputUnit.cc b/src/mem/ruby/network/garnet2.0/InputUnit.cc index 640e3b4ac..db24aefad 100644 --- a/src/mem/ruby/network/garnet2.0/InputUnit.cc +++ b/src/mem/ruby/network/garnet2.0/InputUnit.cc @@ -70,9 +70,13 @@ void InputUnit::wakeup() { flit *t_flit; - if (m_in_link->isReady(m_router->curCycle())) { + if (m_in_link->isReady(curTick())) { t_flit = m_in_link->consumeLink(); + DPRINTF(RubyNetwork, "Router[%d] Consuming:%s Width: %d Flit:%s\n", + m_router->get_id(), m_in_link->name(), + m_router->getBitWidth(), *t_flit); + assert(t_flit->m_width == m_router->getBitWidth()); int vc = t_flit->get_vc(); t_flit->increment_hops(); // for stats @@ -80,7 +84,7 @@ InputUnit::wakeup() (t_flit->get_type() == HEAD_TAIL_)) { assert(virtualChannels[vc].get_state() == IDLE_); - set_vc_active(vc, m_router->curCycle()); + set_vc_active(vc, curTick()); // Route computation for this vc int outport = m_router->route_compute(t_flit->get_route(), @@ -109,26 +113,32 @@ InputUnit::wakeup() if (pipe_stages == 1) { // 1-cycle router // Flit goes for SA directly - t_flit->advance_stage(SA_, m_router->curCycle()); + t_flit->advance_stage(SA_, curTick()); } else { assert(pipe_stages > 1); // Router delay is modeled by making flit wait in buffer for // (pipe_stages cycles - 1) cycles before going for SA Cycles wait_time = pipe_stages - Cycles(1); - t_flit->advance_stage(SA_, m_router->curCycle() + wait_time); + t_flit->advance_stage(SA_, m_router->clockEdge(wait_time)); // Wakeup the router in that cycle to perform SA m_router->schedule_wakeup(Cycles(wait_time)); } + + if (m_in_link->isReady(curTick())) { + m_router->schedule_wakeup(Cycles(1)); + } } } // Send a credit back to upstream router for this VC. // Called by SwitchAllocator when the flit in this VC wins the Switch. void -InputUnit::increment_credit(int in_vc, bool free_signal, Cycles curTime) +InputUnit::increment_credit(int in_vc, bool free_signal, Tick curTime) { + DPRINTF(RubyNetwork, "Router[%d]: Sending a credit vc:%d free:%d to %s\n", + m_router->get_id(), in_vc, free_signal, m_credit_link->name()); Credit *t_credit = new Credit(in_vc, free_signal, curTime); creditQueue.insert(t_credit); m_credit_link->scheduleEventAbsolute(m_router->clockEdge(Cycles(1))); diff --git a/src/mem/ruby/network/garnet2.0/InputUnit.hh b/src/mem/ruby/network/garnet2.0/InputUnit.hh index d0e2ef66b..40ef251fb 100644 --- a/src/mem/ruby/network/garnet2.0/InputUnit.hh +++ b/src/mem/ruby/network/garnet2.0/InputUnit.hh @@ -55,13 +55,13 @@ class InputUnit : public Consumer inline PortDirection get_direction() { return m_direction; } inline void - set_vc_idle(int vc, Cycles curTime) + set_vc_idle(int vc, Tick curTime) { virtualChannels[vc].set_idle(curTime); } inline void - set_vc_active(int vc, Cycles curTime) + set_vc_active(int vc, Tick curTime) { virtualChannels[vc].set_active(curTime); } @@ -90,13 +90,13 @@ class InputUnit : public Consumer return virtualChannels[invc].get_outvc(); } - inline Cycles + inline Tick get_enqueue_time(int invc) { return virtualChannels[invc].get_enqueue_time(); } - void increment_credit(int in_vc, bool free_signal, Cycles curTime); + void increment_credit(int in_vc, bool free_signal, Tick curTime); inline flit* peekTopFlit(int vc) @@ -111,13 +111,13 @@ class InputUnit : public Consumer } inline bool - need_stage(int vc, flit_stage stage, Cycles time) + need_stage(int vc, flit_stage stage, Tick time) { return virtualChannels[vc].need_stage(stage, time); } inline bool - isReady(int invc, Cycles curTime) + isReady(int invc, Tick curTime) { return virtualChannels[invc].isReady(curTime); } diff --git a/src/mem/ruby/network/garnet2.0/NetworkBridge.cc b/src/mem/ruby/network/garnet2.0/NetworkBridge.cc new file mode 100644 index 000000000..f088b256e --- /dev/null +++ b/src/mem/ruby/network/garnet2.0/NetworkBridge.cc @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2020 Advanced Micro Devices, Inc. + * All rights reserved. + * + * For use for simulation and test purposes only + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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: Srikant Bharadwaj + */ + + +#include "mem/ruby/network/garnet2.0/NetworkBridge.hh" + +#include + +#include "debug/RubyNetwork.hh" +#include "params/GarnetIntLink.hh" + +NetworkBridge::NetworkBridge(const Params *p) + :CreditLink(p) +{ + enCdc = true; + enSerDes = true; + mType = p->vtype; + + cdcLatency = p->cdc_latency; + serDesLatency = p->serdes_latency; + lastScheduledAt = 0; + + nLink = p->link; + if (mType == Enums::LINK_OBJECT) { + nLink->setLinkConsumer(this); + setSourceQueue(nLink->getBuffer(), nLink); + } else if (mType == Enums::OBJECT_LINK) { + nLink->setSourceQueue(&linkBuffer, this); + setLinkConsumer(nLink); + } else { + // CDC type must be set + panic("CDC type must be set"); + } + + lenBuffer.resize(p->vcs_per_vnet * p->virt_nets); + sizeSent.resize(p->vcs_per_vnet * p->virt_nets); + flitsSent.resize(p->vcs_per_vnet * p->virt_nets); + extraCredit.resize(p->vcs_per_vnet * p->virt_nets); +} + +void +NetworkBridge::initBridge(NetworkBridge *coBrid, bool cdc_en, bool serdes_en) +{ + coBridge = coBrid; + enCdc = cdc_en; + enSerDes = serdes_en; +} + +NetworkBridge::~NetworkBridge() +{ +} + +void +NetworkBridge::scheduleFlit(flit *t_flit, Cycles latency) +{ + Cycles totLatency = latency; + + if (enCdc) { + // Add the CDC latency + totLatency = latency + cdcLatency; + } + + Tick sendTime = link_consumer->getObject()->clockEdge(totLatency); + Tick nextAvailTick = lastScheduledAt + link_consumer->getObject()->\ + cyclesToTicks(Cycles(1)); + sendTime = std::max(nextAvailTick, sendTime); + t_flit->set_time(sendTime); + lastScheduledAt = sendTime; + linkBuffer.insert(t_flit); + link_consumer->scheduleEventAbsolute(sendTime); +} + +void +NetworkBridge::neutralize(int vc, int eCredit) +{ + extraCredit[vc].push(eCredit); +} + +void +NetworkBridge::flitisizeAndSend(flit *t_flit) +{ + // Serialize-Deserialize only if it is enabled + if (enSerDes) { + // Calculate the target-width + int target_width = bitWidth; + int cur_width = nLink->bitWidth; + if (mType == Enums::OBJECT_LINK) { + target_width = nLink->bitWidth; + cur_width = bitWidth; + } + + DPRINTF(RubyNetwork, "Target width: %d Current: %d\n", + target_width, cur_width); + assert(target_width != cur_width); + + int vc = t_flit->get_vc(); + + if (target_width > cur_width) { + // Deserialize + // This deserializer combines flits from the + // same message together + int num_flits = 0; + int flitPossible = 0; + if (t_flit->get_type() == CREDIT_) { + lenBuffer[vc]++; + assert(extraCredit[vc].front()); + if (lenBuffer[vc] == extraCredit[vc].front()) { + flitPossible = 1; + extraCredit[vc].pop(); + lenBuffer[vc] = 0; + } + } else if (t_flit->get_type() == TAIL_ || + t_flit->get_type() == HEAD_TAIL_) { + // If its the end of packet, then send whatever + // is available. + int sizeAvail = (t_flit->msgSize - sizeSent[vc]); + flitPossible = ceil((float)sizeAvail/(float)target_width); + assert (flitPossible < 2); + num_flits = (t_flit->get_id() + 1) - flitsSent[vc]; + // Stop tracking the packet. + flitsSent[vc] = 0; + sizeSent[vc] = 0; + } else { + // If we are yet to receive the complete packet + // track the size recieved and flits deserialized. + int sizeAvail = + ((t_flit->get_id() + 1)*cur_width) - sizeSent[vc]; + flitPossible = floor((float)sizeAvail/(float)target_width); + assert (flitPossible < 2); + num_flits = (t_flit->get_id() + 1) - flitsSent[vc]; + if (flitPossible) { + sizeSent[vc] += target_width; + flitsSent[vc] = t_flit->get_id() + 1; + } + } + + DPRINTF(RubyNetwork, "Deserialize :%dB -----> %dB " + " vc:%d\n", cur_width, target_width, vc); + + flit *fl = NULL; + if (flitPossible) { + fl = t_flit->deserialize(lenBuffer[vc], num_flits, + target_width); + } + + // Inform the credit serializer about the number + // of flits that were generated. + if (t_flit->get_type() != CREDIT_ && fl) { + coBridge->neutralize(vc, num_flits); + } + + // Schedule only if we are done deserializing + if (fl) { + DPRINTF(RubyNetwork, "Scheduling a flit\n"); + lenBuffer[vc] = 0; + scheduleFlit(fl, serDesLatency); + } + // Delete this flit, new flit is sent in any case + delete t_flit; + } else { + // Serialize + DPRINTF(RubyNetwork, "Serializing flit :%d -----> %d " + "(vc:%d, Original Message Size: %d)\n", + cur_width, target_width, vc, t_flit->msgSize); + + int flitPossible = 0; + if (t_flit->get_type() == CREDIT_) { + // We store the deserialization ratio and then + // access it when serializing credits in the + // oppposite direction. + assert(extraCredit[vc].front()); + flitPossible = extraCredit[vc].front(); + extraCredit[vc].pop(); + } else if (t_flit->get_type() == HEAD_ || + t_flit->get_type() == BODY_) { + int sizeAvail = + ((t_flit->get_id() + 1)*cur_width) - sizeSent[vc]; + flitPossible = floor((float)sizeAvail/(float)target_width); + if (flitPossible) { + sizeSent[vc] += flitPossible*target_width; + flitsSent[vc] += flitPossible; + } + } else { + int sizeAvail = t_flit->msgSize - sizeSent[vc]; + flitPossible = ceil((float)sizeAvail/(float)target_width); + sizeSent[vc] = 0; + flitsSent[vc] = 0; + } + assert(flitPossible > 0); + + // Schedule all the flits + // num_flits could be zero for credits + for (int i = 0; i < flitPossible; i++) { + // Ignore neutralized credits + flit *fl = t_flit->serialize(i, flitPossible, target_width); + scheduleFlit(fl, serDesLatency); + DPRINTF(RubyNetwork, "Serialized to flit[%d of %d parts]:" + " %s\n", i+1, flitPossible, *fl); + } + + if (t_flit->get_type() != CREDIT_) { + coBridge->neutralize(vc, flitPossible); + } + // Delete this flit, new flit is sent in any case + delete t_flit; + } + return; + } + + // If only CDC is enabled schedule it + scheduleFlit(t_flit, Cycles(0)); +} +void +NetworkBridge::wakeup() +{ + flit *t_flit; + + if (link_srcQueue->isReady(curTick())) { + t_flit = link_srcQueue->getTopFlit(); + DPRINTF(RubyNetwork, "Recieved flit %s\n", *t_flit); + flitisizeAndSend(t_flit); + return; + } + assert(!link_srcQueue->getSize()); +} + +NetworkBridge * +NetworkBridgeParams::create() +{ + return new NetworkBridge(this); +} diff --git a/src/mem/ruby/network/garnet2.0/NetworkBridge.hh b/src/mem/ruby/network/garnet2.0/NetworkBridge.hh new file mode 100644 index 000000000..f12e0cd01 --- /dev/null +++ b/src/mem/ruby/network/garnet2.0/NetworkBridge.hh @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020 Advanced Micro Devices, Inc. + * All rights reserved. + * + * For use for simulation and test purposes only + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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: Srikant Bharadwaj + */ + +#ifndef __MEM_RUBY_NETWORK_GARNET2_0_NETWORK_BRIDGE_HH__ +#define __MEM_RUBY_NETWORK_GARNET2_0_NETWORK_BRIDGE_HH__ + +#include +#include +#include + +#include "mem/ruby/common/Consumer.hh" +#include "mem/ruby/network/garnet2.0/CommonTypes.hh" +#include "mem/ruby/network/garnet2.0/CreditLink.hh" +#include "mem/ruby/network/garnet2.0/GarnetLink.hh" +#include "mem/ruby/network/garnet2.0/NetworkLink.hh" +#include "mem/ruby/network/garnet2.0/flitBuffer.hh" +#include "params/NetworkBridge.hh" + +class GarnetNetwork; + +class NetworkBridge: public CreditLink +{ + public: + typedef NetworkBridgeParams Params; + NetworkBridge(const Params *p); + ~NetworkBridge(); + + void initBridge(NetworkBridge *coBrid, bool cdc_en, bool serdes_en); + + void wakeup(); + void neutralize(int vc, int eCredit); + + void scheduleFlit(flit *t_flit, Cycles latency); + void flitisizeAndSend(flit *t_flit); + + protected: + // Pointer to co-existing bridge + // CreditBridge for Network Bridge and vice versa + NetworkBridge *coBridge; + + // Link connected toBridge + // could be a source or destination + // depending on mType + NetworkLink *nLink; + + // CDC enable/disable + bool enCdc; + // SerDes enable/disable + bool enSerDes; + + // Type of Bridge + int mType; + + Cycles cdcLatency; + Cycles serDesLatency; + + Tick lastScheduledAt; + + // Used by Credit Deserializer + std::vector lenBuffer; + std::vector sizeSent; + std::vector flitsSent; + std::vector> extraCredit; + +}; + +#endif // __MEM_RUBY_NETWORK_GARNET2_0_NETWORK_BRIDGE_HH__ diff --git a/src/mem/ruby/network/garnet2.0/NetworkInterface.cc b/src/mem/ruby/network/garnet2.0/NetworkInterface.cc index f4d446d4c..46db22db8 100644 --- a/src/mem/ruby/network/garnet2.0/NetworkInterface.cc +++ b/src/mem/ruby/network/garnet2.0/NetworkInterface.cc @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 2020 Inria * Copyright (c) 2016 Georgia Institute of Technology * Copyright (c) 2008 Princeton University @@ -45,9 +46,8 @@ using namespace std; 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_router_id(-1), m_vc_allocator(m_virtual_networks, 0), - m_vc_round_robin(0), outFlitQueue(), outCreditQueue(), + m_virtual_networks(p->virt_nets), m_vc_per_vnet(0), + m_vc_allocator(m_virtual_networks, 0), m_deadlock_threshold(p->garnet_deadlock_threshold), vc_busy_counter(m_virtual_networks, 0) { @@ -57,7 +57,7 @@ NetworkInterface::NetworkInterface(const Params *p) // instantiating the NI flit buffers for (auto& time : m_ni_out_vcs_enqueue_time) { - time = Cycles(INFINITE_); + time = Tick(INFINITE_); } m_stall_count.resize(m_virtual_networks); @@ -77,10 +77,14 @@ void NetworkInterface::addInPort(NetworkLink *in_link, CreditLink *credit_link) { - inNetLink = in_link; + InputPort *newInPort = new InputPort(in_link, credit_link); + inPorts.push_back(newInPort); + DPRINTF(RubyNetwork, "Adding input port:%s with vnets %s\n", + in_link->name(), newInPort->printVnets()); + in_link->setLinkConsumer(this); - outCreditLink = credit_link; - credit_link->setSourceQueue(&outCreditQueue); + credit_link->setSourceQueue(newInPort->outCreditQueue(), this); + } void @@ -88,13 +92,14 @@ NetworkInterface::addOutPort(NetworkLink *out_link, CreditLink *credit_link, SwitchID router_id) { - inCreditLink = credit_link; - credit_link->setLinkConsumer(this); + OutputPort *newOutPort = new OutputPort(out_link, credit_link, router_id); + outPorts.push_back(newOutPort); - outNetLink = out_link; - out_link->setSourceQueue(&outFlitQueue); + DPRINTF(RubyNetwork, "OutputPort:%s Vnet: %s\n", + out_link->name(), newOutPort->printVnets()); - m_router_id = router_id; + out_link->setSourceQueue(newOutPort->outFlitQueue(), this); + credit_link->setLinkConsumer(this); } void @@ -128,11 +133,12 @@ NetworkInterface::incrementStats(flit *t_flit) // Latency m_net_ptr->increment_received_flits(vnet); - Cycles network_delay = - t_flit->get_dequeue_time() - t_flit->get_enqueue_time() - Cycles(1); - Cycles src_queueing_delay = t_flit->get_src_delay(); - Cycles dest_queueing_delay = (curCycle() - t_flit->get_dequeue_time()); - Cycles queueing_delay = src_queueing_delay + dest_queueing_delay; + Tick network_delay = + t_flit->get_dequeue_time() - + t_flit->get_enqueue_time() - cyclesToTicks(Cycles(1)); + Tick src_queueing_delay = t_flit->get_src_delay(); + Tick dest_queueing_delay = (curTick() - t_flit->get_dequeue_time()); + Tick queueing_delay = src_queueing_delay + dest_queueing_delay; m_net_ptr->increment_flit_network_latency(network_delay, vnet); m_net_ptr->increment_flit_queueing_latency(queueing_delay, vnet); @@ -160,9 +166,14 @@ NetworkInterface::incrementStats(flit *t_flit) void NetworkInterface::wakeup() { - DPRINTF(RubyNetwork, "Network Interface %d connected to router %d " - "woke up at time: %lld\n", m_id, m_router_id, curCycle()); + std::ostringstream oss; + for (auto &oPort: outPorts) { + oss << oPort->routerID() << "[" << oPort->printVnets() << "] "; + } + DPRINTF(RubyNetwork, "Network Interface %d connected to router:%s " + "woke up. Period: %ld\n", m_id, oss.str(), clockPeriod()); + assert(curTick() == clockEdge()); MsgPtr msg_ptr; Tick curTime = clockEdge(); @@ -183,63 +194,81 @@ NetworkInterface::wakeup() } scheduleOutputLink(); - checkReschedule(); // Check if there are flits stalling a virtual channel. Track if a // message is enqueued to restrict ejection to one message per cycle. - bool messageEnqueuedThisCycle = checkStallQueue(); + checkStallQueue(); /*********** Check the incoming flit link **********/ - if (inNetLink->isReady(curCycle())) { - flit *t_flit = inNetLink->consumeLink(); - int vnet = t_flit->get_vnet(); - t_flit->set_dequeue_time(curCycle()); - - // If a tail flit is received, enqueue into the protocol buffers if - // space is available. Otherwise, exchange non-tail flits for credits. - if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) { - if (!messageEnqueuedThisCycle && - outNode_ptr[vnet]->areNSlotsAvailable(1, curTime)) { - // Space is available. Enqueue to protocol buffer. - outNode_ptr[vnet]->enqueue(t_flit->get_msg_ptr(), curTime, - cyclesToTicks(Cycles(1))); - + DPRINTF(RubyNetwork, "Number of input ports: %d\n", inPorts.size()); + for (auto &iPort: inPorts) { + NetworkLink *inNetLink = iPort->inNetLink(); + if (inNetLink->isReady(curTick())) { + flit *t_flit = inNetLink->consumeLink(); + DPRINTF(RubyNetwork, "Recieved flit:%s\n", *t_flit); + assert(t_flit->m_width == iPort->bitWidth()); + + int vnet = t_flit->get_vnet(); + t_flit->set_dequeue_time(curTick()); + + // If a tail flit is received, enqueue into the protocol buffers + // if space is available. Otherwise, exchange non-tail flits for + // credits. + if (t_flit->get_type() == TAIL_ || + t_flit->get_type() == HEAD_TAIL_) { + if (!iPort->messageEnqueuedThisCycle && + outNode_ptr[vnet]->areNSlotsAvailable(1, curTime)) { + // Space is available. Enqueue to protocol buffer. + outNode_ptr[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 *cFlit = new Credit(t_flit->get_vc(), + true, curTick()); + iPort->sendCredit(cFlit); + // Update stats and delete flit pointer + incrementStats(t_flit); + delete t_flit; + } else { + // No space available- Place tail flit in stall queue and + // set up a callback for when protocol buffer is dequeued. + // Stat update and flit pointer deletion will occur upon + // unstall. + iPort->m_stall_queue.push_back(t_flit); + m_stall_count[vnet]++; + + outNode_ptr[vnet]->registerDequeueCallback([this]() { + dequeueCallback(); }); + } + } else { + // Non-tail flit. Send back a credit but not VC free signal. + Credit *cFlit = new Credit(t_flit->get_vc(), false, + curTick()); // Simply send a credit back since we are not buffering // this flit in the NI - sendCredit(t_flit, true); + iPort->sendCredit(cFlit); - // Update stats and delete flit pointer + // Update stats and delete flit pointer. incrementStats(t_flit); delete t_flit; - } else { - // No space available- Place tail flit in stall queue and set - // up a callback for when protocol buffer is dequeued. Stat - // update and flit pointer deletion will occur upon unstall. - m_stall_queue.push_back(t_flit); - m_stall_count[vnet]++; - - auto cb = std::bind(&NetworkInterface::dequeueCallback, this); - outNode_ptr[vnet]->registerDequeueCallback(cb); } - } else { - // Non-tail flit. Send back a credit but not VC free signal. - sendCredit(t_flit, false); - - // Update stats and delete flit pointer. - incrementStats(t_flit); - delete t_flit; } } /****************** Check the incoming credit link *******/ - if (inCreditLink->isReady(curCycle())) { - Credit *t_credit = (Credit*) inCreditLink->consumeLink(); - outVcState[t_credit->get_vc()].increment_credit(); - if (t_credit->is_free_signal()) { - outVcState[t_credit->get_vc()].setState(IDLE_, curCycle()); + for (auto &oPort: outPorts) { + CreditLink *inCreditLink = oPort->inCreditLink(); + if (inCreditLink->isReady(curTick())) { + Credit *t_credit = (Credit*) inCreditLink->consumeLink(); + outVcState[t_credit->get_vc()].increment_credit(); + if (t_credit->is_free_signal()) { + outVcState[t_credit->get_vc()].setState(IDLE_, + curTick()); + } + delete t_credit; } - delete t_credit; } @@ -247,61 +276,68 @@ NetworkInterface::wakeup() // was unstalled in the same cycle as a new message arrives. In this // case, we should schedule another wakeup to ensure the credit is sent // back. - if (outCreditQueue.getSize() > 0) { - outCreditLink->scheduleEventAbsolute(clockEdge(Cycles(1))); + for (auto &iPort: inPorts) { + if (iPort->outCreditQueue()->getSize() > 0) { + DPRINTF(RubyNetwork, "Sending a credit %s via %s at %ld\n", + *(iPort->outCreditQueue()->peekTopFlit()), + iPort->outCreditLink()->name(), clockEdge(Cycles(1))); + iPort->outCreditLink()-> + scheduleEventAbsolute(clockEdge(Cycles(1))); + } } + checkReschedule(); } void -NetworkInterface::sendCredit(flit *t_flit, bool is_free) -{ - Credit *credit_flit = new Credit(t_flit->get_vc(), is_free, curCycle()); - outCreditQueue.insert(credit_flit); -} - -bool NetworkInterface::checkStallQueue() { - bool messageEnqueuedThisCycle = false; - Tick curTime = clockEdge(); - - if (!m_stall_queue.empty()) { - for (auto stallIter = m_stall_queue.begin(); - stallIter != m_stall_queue.end(); ) { - flit *stallFlit = *stallIter; - int vnet = stallFlit->get_vnet(); - - // If we can now eject to the protocol buffer, send back credits - if (outNode_ptr[vnet]->areNSlotsAvailable(1, curTime)) { - outNode_ptr[vnet]->enqueue(stallFlit->get_msg_ptr(), curTime, - cyclesToTicks(Cycles(1))); - - // Send back a credit with free signal now that the VC is no - // longer stalled. - sendCredit(stallFlit, true); - - // Update Stats - incrementStats(stallFlit); - - // Flit can now safely be deleted and removed from stall queue - delete stallFlit; - m_stall_queue.erase(stallIter); - m_stall_count[vnet]--; - - // If there are no more stalled messages for this vnet, the - // callback on it's MessageBuffer is not needed. - if (m_stall_count[vnet] == 0) - outNode_ptr[vnet]->unregisterDequeueCallback(); - - messageEnqueuedThisCycle = true; - break; - } else { - ++stallIter; + // Check all stall queues. + // There is one stall queue for each input link + for (auto &iPort: inPorts) { + iPort->messageEnqueuedThisCycle = false; + Tick curTime = clockEdge(); + + if (!iPort->m_stall_queue.empty()) { + for (auto stallIter = iPort->m_stall_queue.begin(); + stallIter != iPort->m_stall_queue.end(); ) { + flit *stallFlit = *stallIter; + int vnet = stallFlit->get_vnet(); + + // If we can now eject to the protocol buffer, + // send back credits + if (outNode_ptr[vnet]->areNSlotsAvailable(1, + curTime)) { + outNode_ptr[vnet]->enqueue(stallFlit->get_msg_ptr(), + curTime, cyclesToTicks(Cycles(1))); + + // Send back a credit with free signal now that the + // VC is no longer stalled. + Credit *cFlit = new Credit(stallFlit->get_vc(), true, + curTick()); + iPort->sendCredit(cFlit); + + // Update Stats + incrementStats(stallFlit); + + // Flit can now safely be deleted and removed from stall + // queue + delete stallFlit; + iPort->m_stall_queue.erase(stallIter); + m_stall_count[vnet]--; + + // If there are no more stalled messages for this vnet, the + // callback on it's MessageBuffer is not needed. + if (m_stall_count[vnet] == 0) + outNode_ptr[vnet]->unregisterDequeueCallback(); + + iPort->messageEnqueuedThisCycle = true; + break; + } else { + ++stallIter; + } } } } - - return messageEnqueuedThisCycle; } // Embed the protocol message into flits @@ -316,8 +352,14 @@ NetworkInterface::flitisizeMessage(MsgPtr msg_ptr, int vnet) // 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()); + OutputPort *oPort = getOutportForVnet(vnet); + assert(oPort); + int num_flits = (int)divCeil((float) m_net_ptr->MessageSizeType_to_int( + net_msg_ptr->getMessageSize()), (float)oPort->bitWidth()); + + DPRINTF(RubyNetwork, "Message Size:%d vnet:%d bitWidth:%d\n", + m_net_ptr->MessageSizeType_to_int(net_msg_ptr->getMessageSize()), + vnet, oPort->bitWidth()); // loop to convert all multicast messages into unicast messages for (int ctr = 0; ctr < dest_nodes.size(); ctr++) { @@ -355,13 +397,14 @@ NetworkInterface::flitisizeMessage(MsgPtr msg_ptr, int vnet) // 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.src_router = oPort->routerID(); route.dest_ni = destID; - route.dest_router = m_net_ptr->get_router_id(destID); + route.dest_router = m_net_ptr->get_router_id(destID, vnet); // initialize hops_traversed to -1 // so that the first router increments it to 0 @@ -371,14 +414,16 @@ NetworkInterface::flitisizeMessage(MsgPtr msg_ptr, int 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()); + m_net_ptr->MessageSizeType_to_int( + net_msg_ptr->getMessageSize()), + oPort->bitWidth(), curTick()); - fl->set_src_delay(curCycle() - ticksToCycles(msg_ptr->getTime())); + fl->set_src_delay(curTick() - ticksToCycles(msg_ptr->getTime())); niOutVcs[vc].insert(fl); } - m_ni_out_vcs_enqueue_time[vc] = curCycle(); - outVcState[vc].setState(ACTIVE_, curCycle()); + m_ni_out_vcs_enqueue_time[vc] = curTick(); + outVcState[vc].setState(ACTIVE_, curTick()); } return true ; } @@ -394,7 +439,7 @@ NetworkInterface::calculateVC(int vnet) m_vc_allocator[vnet] = 0; if (outVcState[(vnet*m_vc_per_vnet) + delta].isInState( - IDLE_, curCycle())) { + IDLE_, curTick())) { vc_busy_counter[vnet] = 0; return ((vnet*m_vc_per_vnet) + delta); } @@ -408,6 +453,67 @@ NetworkInterface::calculateVC(int vnet) return -1; } +void +NetworkInterface::scheduleOutputPort(OutputPort *oPort) +{ + int vc = oPort->vcRoundRobin(); + + for (int i = 0; i < niOutVcs.size(); i++) { + vc++; + if (vc == niOutVcs.size()) + vc = 0; + + int t_vnet = get_vnet(vc); + if (oPort->isVnetSupported(t_vnet)) { + // model buffer backpressure + if (niOutVcs[vc].isReady(curTick()) && + outVcState[vc].has_credit()) { + + bool is_candidate_vc = true; + 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 (niOutVcs[t_vc].isReady(curTick())) { + 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; + + // Update the round robin arbiter + oPort->vcRoundRobin(vc); + + outVcState[vc].decrement_credit(); + + // Just removing the top flit + flit *t_flit = niOutVcs[vc].getTopFlit(); + t_flit->set_time(clockEdge(Cycles(1))); + + // Scheduling the flit + scheduleFlit(t_flit); + + if (t_flit->get_type() == TAIL_ || + t_flit->get_type() == HEAD_TAIL_) { + m_ni_out_vcs_enqueue_time[vc] = Tick(INFINITE_); + } + + // Done with this port, continue to schedule + // other ports + return; + } + } + } +} + + /** This function looks at the NI buffers * if some buffer has flits which are ready to traverse the link in the next @@ -418,54 +524,57 @@ NetworkInterface::calculateVC(int vnet) void NetworkInterface::scheduleOutputLink() { - int vc = m_vc_round_robin; - - for (int i = 0; i < niOutVcs.size(); i++) { - vc++; - if (vc == niOutVcs.size()) - vc = 0; - - // model buffer backpressure - if (niOutVcs[vc].isReady(curCycle()) && - outVcState[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 (niOutVcs[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; + // Schedule each output link + for (auto &oPort: outPorts) { + scheduleOutputPort(oPort); + } +} - m_vc_round_robin = vc; +NetworkInterface::InputPort * +NetworkInterface::getInportForVnet(int vnet) +{ + for (auto &iPort : inPorts) { + if (iPort->isVnetSupported(vnet)) { + return iPort; + } + } - outVcState[vc].decrement_credit(); - // Just removing the flit - flit *t_flit = niOutVcs[vc].getTopFlit(); - t_flit->set_time(curCycle() + Cycles(1)); - outFlitQueue.insert(t_flit); - // schedule the out link - outNetLink->scheduleEventAbsolute(clockEdge(Cycles(1))); + return nullptr; +} - if (t_flit->get_type() == TAIL_ || - t_flit->get_type() == HEAD_TAIL_) { - m_ni_out_vcs_enqueue_time[vc] = Cycles(INFINITE_); - } - return; +/* + * This function returns the outport which supports the given vnet. + * Currently, HeteroGarnet does not support multiple outports to + * support same vnet. Thus, this function returns the first-and + * only outport which supports the vnet. + */ +NetworkInterface::OutputPort * +NetworkInterface::getOutportForVnet(int vnet) +{ + for (auto &oPort : outPorts) { + if (oPort->isVnetSupported(vnet)) { + return oPort; } } + + return nullptr; +} +void +NetworkInterface::scheduleFlit(flit *t_flit) +{ + OutputPort *oPort = getOutportForVnet(t_flit->get_vnet()); + + if (oPort) { + DPRINTF(RubyNetwork, "Scheduling at %s time:%ld flit:%s Message:%s\n", + oPort->outNetLink()->name(), clockEdge(Cycles(1)), + *t_flit, *(t_flit->get_msg_ptr())); + oPort->outFlitQueue()->insert(t_flit); + oPort->outNetLink()->scheduleEventAbsolute(clockEdge(Cycles(1))); + return; + } + + panic("No output port found for vnet:%d\n", t_flit->get_vnet()); + return; } int @@ -482,7 +591,9 @@ NetworkInterface::get_vnet(int 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 +// output VC buffer. +// Also check if we have to reschedule because of a clock period +// difference. void NetworkInterface::checkReschedule() { @@ -498,7 +609,26 @@ NetworkInterface::checkReschedule() } for (auto& ni_out_vc : niOutVcs) { - if (ni_out_vc.isReady(curCycle() + Cycles(1))) { + if (ni_out_vc.isReady(clockEdge(Cycles(1)))) { + scheduleEvent(Cycles(1)); + return; + } + } + + // Check if any input links have flits to be popped. + // This can happen if the links are operating at + // a higher frequency. + for (auto &iPort : inPorts) { + NetworkLink *inNetLink = iPort->inNetLink(); + if (inNetLink->isReady(curTick())) { + scheduleEvent(Cycles(1)); + return; + } + } + + for (auto &oPort : outPorts) { + CreditLink *inCreditLink = oPort->inCreditLink(); + if (inCreditLink->isReady(curTick())) { scheduleEvent(Cycles(1)); return; } @@ -519,7 +649,9 @@ NetworkInterface::functionalWrite(Packet *pkt) num_functional_writes += ni_out_vc.functionalWrite(pkt); } - num_functional_writes += outFlitQueue.functionalWrite(pkt); + for (auto &oPort: outPorts) { + num_functional_writes += oPort->outFlitQueue()->functionalWrite(pkt); + } return num_functional_writes; } diff --git a/src/mem/ruby/network/garnet2.0/NetworkInterface.hh b/src/mem/ruby/network/garnet2.0/NetworkInterface.hh index 7e3083844..945b446d7 100644 --- a/src/mem/ruby/network/garnet2.0/NetworkInterface.hh +++ b/src/mem/ruby/network/garnet2.0/NetworkInterface.hh @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 2020 Inria * Copyright (c) 2016 Georgia Institute of Technology * Copyright (c) 2008 Princeton University @@ -37,6 +38,7 @@ #include "mem/ruby/common/Consumer.hh" #include "mem/ruby/network/garnet2.0/CommonTypes.hh" +#include "mem/ruby/network/garnet2.0/Credit.hh" #include "mem/ruby/network/garnet2.0/CreditLink.hh" #include "mem/ruby/network/garnet2.0/GarnetNetwork.hh" #include "mem/ruby/network/garnet2.0/NetworkLink.hh" @@ -67,37 +69,213 @@ class NetworkInterface : public ClockedObject, public Consumer void print(std::ostream& out) const; int get_vnet(int vc); - int get_router_id() { return m_router_id; } void init_net_ptr(GarnetNetwork *net_ptr) { m_net_ptr = net_ptr; } uint32_t functionalWrite(Packet *); + void scheduleFlit(flit *t_flit); + + int get_router_id(int vnet) + { + OutputPort *oPort = getOutportForVnet(vnet); + assert(oPort); + return oPort->routerID(); + } + + class OutputPort + { + public: + OutputPort(NetworkLink *outLink, CreditLink *creditLink, + int routerID) + { + _vnets = outLink->mVnets; + _outFlitQueue = new flitBuffer(); + + _outNetLink = outLink; + _inCreditLink = creditLink; + + _routerID = routerID; + _bitWidth = outLink->bitWidth; + _vcRoundRobin = 0; + + } + + flitBuffer * + outFlitQueue() + { + return _outFlitQueue; + } + + NetworkLink * + outNetLink() + { + return _outNetLink; + } + + CreditLink * + inCreditLink() + { + return _inCreditLink; + } + + int + routerID() + { + return _routerID; + } + + uint32_t bitWidth() + { + return _bitWidth; + } + + bool isVnetSupported(int pVnet) + { + if (!_vnets.size()) { + return true; + } + + for (auto &it : _vnets) { + if (it == pVnet) { + return true; + } + } + return false; + + } + + std::string + printVnets() + { + std::stringstream ss; + for (auto &it : _vnets) { + ss << it; + ss << " "; + } + return ss.str(); + } + + int vcRoundRobin() + { + return _vcRoundRobin; + } + + void vcRoundRobin(int vc) + { + _vcRoundRobin = vc; + } + + + private: + std::vector _vnets; + flitBuffer *_outFlitQueue; + + NetworkLink *_outNetLink; + CreditLink *_inCreditLink; + + int _vcRoundRobin; // For round robin scheduling + + int _routerID; + uint32_t _bitWidth; + }; + + class InputPort + { + public: + InputPort(NetworkLink *inLink, CreditLink *creditLink) + { + _vnets = inLink->mVnets; + _outCreditQueue = new flitBuffer(); + + _inNetLink = inLink; + _outCreditLink = creditLink; + _bitWidth = inLink->bitWidth; + } + + flitBuffer * + outCreditQueue() + { + return _outCreditQueue; + } + + NetworkLink * + inNetLink() + { + return _inNetLink; + } + + CreditLink * + outCreditLink() + { + return _outCreditLink; + } + + bool isVnetSupported(int pVnet) + { + if (!_vnets.size()) { + return true; + } + + for (auto &it : _vnets) { + if (it == pVnet) { + return true; + } + } + return false; + + } + + void sendCredit(Credit *cFlit) + { + _outCreditQueue->insert(cFlit); + } + + uint32_t bitWidth() + { + return _bitWidth; + } + + std::string + printVnets() + { + std::stringstream ss; + for (auto &it : _vnets) { + ss << it; + ss << " "; + } + return ss.str(); + } + + // Queue for stalled flits + std::deque m_stall_queue; + bool messageEnqueuedThisCycle; + private: + std::vector _vnets; + flitBuffer *_outCreditQueue; + + NetworkLink *_inNetLink; + CreditLink *_outCreditLink; + uint32_t _bitWidth; + }; + + private: GarnetNetwork *m_net_ptr; const NodeID m_id; const int m_virtual_networks, m_vc_per_vnet; int m_router_id; // id of my router std::vector m_vc_allocator; - int m_vc_round_robin; // For round robin scheduling - /** Used to model link contention. */ - flitBuffer outFlitQueue; - flitBuffer outCreditQueue; + std::vector outPorts; + std::vector inPorts; int m_deadlock_threshold; std::vector outVcState; - NetworkLink *inNetLink; - NetworkLink *outNetLink; - CreditLink *inCreditLink; - CreditLink *outCreditLink; - - // Queue for stalled flits - std::deque m_stall_queue; std::vector m_stall_count; // Input Flit Buffers // The flit buffers which will serve the Consumer std::vector niOutVcs; - std::vector m_ni_out_vcs_enqueue_time; + std::vector m_ni_out_vcs_enqueue_time; // The Message buffers that takes messages from the protocol std::vector inNode_ptr; @@ -106,15 +284,19 @@ class NetworkInterface : public ClockedObject, public Consumer // When a vc stays busy for a long time, it indicates a deadlock std::vector vc_busy_counter; - bool checkStallQueue(); + void checkStallQueue(); bool flitisizeMessage(MsgPtr msg_ptr, int vnet); int calculateVC(int vnet); + + void scheduleOutputPort(OutputPort *oPort); void scheduleOutputLink(); void checkReschedule(); - void sendCredit(flit *t_flit, bool is_free); void incrementStats(flit *t_flit); + + InputPort *getInportForVnet(int vnet); + OutputPort *getOutportForVnet(int vnet); }; #endif // __MEM_RUBY_NETWORK_GARNET2_0_NETWORKINTERFACE_HH__ diff --git a/src/mem/ruby/network/garnet2.0/NetworkLink.cc b/src/mem/ruby/network/garnet2.0/NetworkLink.cc index d4bccd828..8d00470d4 100644 --- a/src/mem/ruby/network/garnet2.0/NetworkLink.cc +++ b/src/mem/ruby/network/garnet2.0/NetworkLink.cc @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 2020 Inria * Copyright (c) 2016 Georgia Institute of Technology * Copyright (c) 2008 Princeton University @@ -31,16 +32,26 @@ #include "mem/ruby/network/garnet2.0/NetworkLink.hh" +#include "base/trace.hh" +#include "debug/RubyNetwork.hh" #include "mem/ruby/network/garnet2.0/CreditLink.hh" NetworkLink::NetworkLink(const Params *p) : ClockedObject(p), Consumer(this), m_id(p->link_id), m_type(NUM_LINK_TYPES_), - m_latency(p->link_latency), + m_latency(p->link_latency), m_link_utilized(0), + m_vc_load(p->vcs_per_vnet * p->virt_nets), linkBuffer(), link_consumer(nullptr), - link_srcQueue(nullptr), m_link_utilized(0), - m_vc_load(p->vcs_per_vnet * p->virt_nets) + link_srcQueue(nullptr) { + int num_vnets = (p->supported_vnets).size(); + assert(num_vnets > 0); + mVnets.resize(num_vnets); + bitWidth = p->width; + for (int i = 0; i < num_vnets; i++) { + mVnets[i] = p->supported_vnets[i]; + } + DPRINTF(RubyNetwork,"Created with bitwidth:%d\n", bitWidth); } void @@ -50,23 +61,40 @@ NetworkLink::setLinkConsumer(Consumer *consumer) } void -NetworkLink::setSourceQueue(flitBuffer* src_queue) +NetworkLink::setSourceQueue(flitBuffer *src_queue, ClockedObject *srcClockObj) { link_srcQueue = src_queue; + src_object = srcClockObj; } void NetworkLink::wakeup() { + DPRINTF(RubyNetwork, "Woke up to transfer flits from %s\n", + src_object->name()); assert(link_srcQueue != nullptr); - if (link_srcQueue->isReady(curCycle())) { + assert(curTick() == clockEdge()); + if (link_srcQueue->isReady(curTick())) { flit *t_flit = link_srcQueue->getTopFlit(); - t_flit->set_time(curCycle() + m_latency); + DPRINTF(RubyNetwork, "Transmission will finish at %ld :%s\n", + clockEdge(m_latency), *t_flit); + if (m_type != NUM_LINK_TYPES_) { + // Only for assertions and debug messages + assert(t_flit->m_width == bitWidth); + assert((std::find(mVnets.begin(), mVnets.end(), + t_flit->get_vnet()) != mVnets.end()) || + (mVnets.size() == 0)); + } + t_flit->set_time(clockEdge(m_latency)); linkBuffer.insert(t_flit); link_consumer->scheduleEventAbsolute(clockEdge(m_latency)); m_link_utilized++; m_vc_load[t_flit->get_vc()]++; } + + if (!link_srcQueue->isEmpty()) { + scheduleEvent(Cycles(1)); + } } void diff --git a/src/mem/ruby/network/garnet2.0/NetworkLink.hh b/src/mem/ruby/network/garnet2.0/NetworkLink.hh index bc3d73a79..5810d0d30 100644 --- a/src/mem/ruby/network/garnet2.0/NetworkLink.hh +++ b/src/mem/ruby/network/garnet2.0/NetworkLink.hh @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 2020 Inria * Copyright (c) 2016 Georgia Institute of Technology * Copyright (c) 2008 Princeton University @@ -51,17 +52,21 @@ class NetworkLink : public ClockedObject, public Consumer ~NetworkLink() = default; void setLinkConsumer(Consumer *consumer); - void setSourceQueue(flitBuffer *src_queue); + void setSourceQueue(flitBuffer *src_queue, ClockedObject *srcClockObject); void setType(link_type type) { m_type = type; } link_type getType() { return m_type; } void print(std::ostream& out) const {} int get_id() const { return m_id; } - void wakeup(); + flitBuffer *getBuffer() { return &linkBuffer;} + virtual void wakeup(); unsigned int getLinkUtilization() const { return m_link_utilized; } const std::vector & getVcLoad() const { return m_vc_load; } - inline bool isReady(Cycles curTime) { return linkBuffer.isReady(curTime); } + inline bool isReady(Tick curTime) + { + return linkBuffer.isReady(curTime); + } inline flit* peekLink() { return linkBuffer.peekTopFlit(); } inline flit* consumeLink() { return linkBuffer.getTopFlit(); } @@ -69,18 +74,25 @@ class NetworkLink : public ClockedObject, public Consumer uint32_t functionalWrite(Packet *); void resetStats(); + std::vector mVnets; + uint32_t bitWidth; + private: const int m_id; link_type m_type; const Cycles m_latency; - flitBuffer linkBuffer; - Consumer *link_consumer; - flitBuffer *link_srcQueue; + ClockedObject *src_object; // Statistical variables unsigned int m_link_utilized; std::vector m_vc_load; + + protected: + flitBuffer linkBuffer; + Consumer *link_consumer; + flitBuffer *link_srcQueue; + }; #endif // __MEM_RUBY_NETWORK_GARNET2_0_NETWORKLINK_HH__ diff --git a/src/mem/ruby/network/garnet2.0/OutVcState.hh b/src/mem/ruby/network/garnet2.0/OutVcState.hh index d22ad3d0e..05252bcb5 100644 --- a/src/mem/ruby/network/garnet2.0/OutVcState.hh +++ b/src/mem/ruby/network/garnet2.0/OutVcState.hh @@ -45,12 +45,12 @@ class OutVcState void decrement_credit(); inline bool - isInState(VC_state_type state, Cycles request_time) + isInState(VC_state_type state, Tick request_time) { return ((m_vc_state == state) && (request_time >= m_time) ); } inline void - setState(VC_state_type state, Cycles time) + setState(VC_state_type state, Tick time) { m_vc_state = state; m_time = time; @@ -58,7 +58,7 @@ class OutVcState private: int m_id ; - Cycles m_time; + Tick m_time; VC_state_type m_vc_state; int m_credit_count; int m_max_credit_count; diff --git a/src/mem/ruby/network/garnet2.0/OutputUnit.cc b/src/mem/ruby/network/garnet2.0/OutputUnit.cc index 2270feaf3..9a048c978 100644 --- a/src/mem/ruby/network/garnet2.0/OutputUnit.cc +++ b/src/mem/ruby/network/garnet2.0/OutputUnit.cc @@ -51,9 +51,11 @@ OutputUnit::OutputUnit(int id, PortDirection direction, Router *router) void OutputUnit::decrement_credit(int out_vc) { - DPRINTF(RubyNetwork, "Router %d OutputUnit %d decrementing credit for " - "outvc %d at time: %lld\n", - m_router->get_id(), m_id, out_vc, m_router->curCycle()); + DPRINTF(RubyNetwork, "Router %d OutputUnit %s decrementing credit:%d for " + "outvc %d at time: %lld for %s\n", m_router->get_id(), + m_router->getPortDirectionName(get_direction()), + outVcState[out_vc].get_credit_count(), + out_vc, m_router->curCycle(), m_credit_link->name()); outVcState[out_vc].decrement_credit(); } @@ -61,9 +63,11 @@ OutputUnit::decrement_credit(int out_vc) void OutputUnit::increment_credit(int out_vc) { - DPRINTF(RubyNetwork, "Router %d OutputUnit %d incrementing credit for " - "outvc %d at time: %lld\n", - m_router->get_id(), m_id, out_vc, m_router->curCycle()); + DPRINTF(RubyNetwork, "Router %d OutputUnit %s incrementing credit:%d for " + "outvc %d at time: %lld from:%s\n", m_router->get_id(), + m_router->getPortDirectionName(get_direction()), + outVcState[out_vc].get_credit_count(), + out_vc, m_router->curCycle(), m_credit_link->name()); outVcState[out_vc].increment_credit(); } @@ -74,7 +78,7 @@ OutputUnit::increment_credit(int out_vc) bool OutputUnit::has_credit(int out_vc) { - assert(outVcState[out_vc].isInState(ACTIVE_, m_router->curCycle())); + assert(outVcState[out_vc].isInState(ACTIVE_, curTick())); return outVcState[out_vc].has_credit(); } @@ -85,7 +89,7 @@ OutputUnit::has_free_vc(int vnet) { int vc_base = vnet*m_vc_per_vnet; for (int vc = vc_base; vc < vc_base + m_vc_per_vnet; vc++) { - if (is_vc_idle(vc, m_router->curCycle())) + if (is_vc_idle(vc, curTick())) return true; } @@ -98,8 +102,8 @@ OutputUnit::select_free_vc(int vnet) { int vc_base = vnet*m_vc_per_vnet; for (int vc = vc_base; vc < vc_base + m_vc_per_vnet; vc++) { - if (is_vc_idle(vc, m_router->curCycle())) { - outVcState[vc].setState(ACTIVE_, m_router->curCycle()); + if (is_vc_idle(vc, curTick())) { + outVcState[vc].setState(ACTIVE_, curTick()); return vc; } } @@ -118,14 +122,18 @@ OutputUnit::select_free_vc(int vnet) void OutputUnit::wakeup() { - if (m_credit_link->isReady(m_router->curCycle())) { + if (m_credit_link->isReady(curTick())) { Credit *t_credit = (Credit*) m_credit_link->consumeLink(); increment_credit(t_credit->get_vc()); if (t_credit->is_free_signal()) - set_vc_state(IDLE_, t_credit->get_vc(), m_router->curCycle()); + set_vc_state(IDLE_, t_credit->get_vc(), curTick()); delete t_credit; + + if (m_credit_link->isReady(curTick())) { + scheduleEvent(Cycles(1)); + } } } diff --git a/src/mem/ruby/network/garnet2.0/OutputUnit.hh b/src/mem/ruby/network/garnet2.0/OutputUnit.hh index dbb35eeed..699470730 100644 --- a/src/mem/ruby/network/garnet2.0/OutputUnit.hh +++ b/src/mem/ruby/network/garnet2.0/OutputUnit.hh @@ -75,13 +75,13 @@ class OutputUnit : public Consumer } inline void - set_vc_state(VC_state_type state, int vc, Cycles curTime) + set_vc_state(VC_state_type state, int vc, Tick curTime) { outVcState[vc].setState(state, curTime); } inline bool - is_vc_idle(int vc, Cycles curTime) + is_vc_idle(int vc, Tick curTime) { return (outVcState[vc].isInState(IDLE_, curTime)); } diff --git a/src/mem/ruby/network/garnet2.0/Router.cc b/src/mem/ruby/network/garnet2.0/Router.cc index 73b7dce2e..82d49e9bd 100644 --- a/src/mem/ruby/network/garnet2.0/Router.cc +++ b/src/mem/ruby/network/garnet2.0/Router.cc @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 2020 Inria * Copyright (c) 2016 Georgia Institute of Technology * Copyright (c) 2008 Princeton University @@ -43,8 +44,9 @@ using namespace std; Router::Router(const Params *p) : BasicRouter(p), Consumer(this), m_latency(p->latency), m_virtual_networks(p->virt_nets), m_vc_per_vnet(p->vcs_per_vnet), - m_num_vcs(m_virtual_networks * m_vc_per_vnet), m_network_ptr(nullptr), - routingUnit(this), switchAllocator(this), crossbarSwitch(this) + m_num_vcs(m_virtual_networks * m_vc_per_vnet), m_bit_width(p->width), + m_network_ptr(nullptr), routingUnit(this), switchAllocator(this), + crossbarSwitch(this) { m_input_unit.clear(); m_output_unit.clear(); @@ -63,6 +65,7 @@ void Router::wakeup() { DPRINTF(RubyNetwork, "Router %d woke up\n", m_id); + assert(clockEdge() == curTick()); // check for incoming flits for (int inport = 0; inport < m_input_unit.size(); inport++) { @@ -90,13 +93,17 @@ void Router::addInPort(PortDirection inport_dirn, NetworkLink *in_link, CreditLink *credit_link) { + fatal_if(in_link->bitWidth != m_bit_width, "Widths of link %s(%d)does" + " not match that of Router%d(%d). Consider inserting SerDes " + "Units.", in_link->name(), in_link->bitWidth, m_id, m_bit_width); + int port_num = m_input_unit.size(); InputUnit *input_unit = new InputUnit(port_num, inport_dirn, this); input_unit->set_in_link(in_link); input_unit->set_credit_link(credit_link); in_link->setLinkConsumer(this); - credit_link->setSourceQueue(input_unit->getCreditQueue()); + credit_link->setSourceQueue(input_unit->getCreditQueue(), this); m_input_unit.push_back(std::shared_ptr(input_unit)); @@ -106,16 +113,19 @@ Router::addInPort(PortDirection inport_dirn, void Router::addOutPort(PortDirection outport_dirn, NetworkLink *out_link, - const NetDest& routing_table_entry, int link_weight, + std::vector& routing_table_entry, int link_weight, CreditLink *credit_link) { + fatal_if(out_link->bitWidth != m_bit_width, "Widths of units do not match." + " Consider inserting SerDes Units"); + int port_num = m_output_unit.size(); OutputUnit *output_unit = new OutputUnit(port_num, outport_dirn, this); output_unit->set_out_link(out_link); output_unit->set_credit_link(credit_link); credit_link->setLinkConsumer(this); - out_link->setSourceQueue(output_unit->getOutQueue()); + out_link->setSourceQueue(output_unit->getOutQueue(), this); m_output_unit.push_back(std::shared_ptr(output_unit)); diff --git a/src/mem/ruby/network/garnet2.0/Router.hh b/src/mem/ruby/network/garnet2.0/Router.hh index 19fc7f007..ba3a581ab 100644 --- a/src/mem/ruby/network/garnet2.0/Router.hh +++ b/src/mem/ruby/network/garnet2.0/Router.hh @@ -68,7 +68,7 @@ class Router : public BasicRouter, public Consumer void addInPort(PortDirection inport_dirn, NetworkLink *link, CreditLink *credit_link); void addOutPort(PortDirection outport_dirn, NetworkLink *link, - const NetDest& routing_table_entry, + std::vector& routing_table_entry, int link_weight, CreditLink *credit_link); Cycles get_pipe_stages(){ return m_latency; } @@ -100,6 +100,8 @@ class Router : public BasicRouter, public Consumer return m_output_unit[port].get(); } + int getBitWidth() { return m_bit_width; } + PortDirection getOutportDirection(int outport); PortDirection getInportDirection(int inport); @@ -131,6 +133,7 @@ class Router : public BasicRouter, public Consumer private: Cycles m_latency; int m_virtual_networks, m_vc_per_vnet, m_num_vcs; + uint32_t m_bit_width; GarnetNetwork *m_network_ptr; RoutingUnit routingUnit; diff --git a/src/mem/ruby/network/garnet2.0/RoutingUnit.cc b/src/mem/ruby/network/garnet2.0/RoutingUnit.cc index 9c0d21b47..5ba0dec6b 100644 --- a/src/mem/ruby/network/garnet2.0/RoutingUnit.cc +++ b/src/mem/ruby/network/garnet2.0/RoutingUnit.cc @@ -31,7 +31,7 @@ #include "mem/ruby/network/garnet2.0/RoutingUnit.hh" #include "base/cast.hh" -#include "base/logging.hh" +#include "debug/RubyNetwork.hh" #include "mem/ruby/network/garnet2.0/InputUnit.hh" #include "mem/ruby/network/garnet2.0/Router.hh" #include "mem/ruby/slicc_interface/Message.hh" @@ -44,9 +44,14 @@ RoutingUnit::RoutingUnit(Router *router) } void -RoutingUnit::addRoute(const NetDest& routing_table_entry) +RoutingUnit::addRoute(std::vector& routing_table_entry) { - m_routing_table.push_back(routing_table_entry); + if (routing_table_entry.size() > m_routing_table.size()) { + m_routing_table.resize(routing_table_entry.size()); + } + for (int v = 0; v < routing_table_entry.size(); v++) { + m_routing_table[v].push_back(routing_table_entry[v]); + } } void @@ -55,13 +60,29 @@ RoutingUnit::addWeight(int link_weight) m_weight_table.push_back(link_weight); } +bool +RoutingUnit::supportsVnet(int vnet, std::vector sVnets) +{ + // If all vnets are supported, return true + if (sVnets.size() == 0) { + return true; + } + + // Find the vnet in the vector, return true + if (std::find(sVnets.begin(), sVnets.end(), vnet) != sVnets.end()) { + return true; + } + + // Not supported vnet + return false; +} + /* * This is the default routing algorithm in garnet. * The routing table is populated during topology creation. * Routes can be biased via weight assignments in the topology file. * Correct weight assignments are critical to provide deadlock avoidance. */ - int RoutingUnit::lookupRoutingTable(int vnet, NetDest msg_destination) { @@ -78,8 +99,9 @@ RoutingUnit::lookupRoutingTable(int vnet, NetDest msg_destination) int num_candidates = 0; // Identify the minimum weight among the candidate output links - for (int link = 0; link < m_routing_table.size(); link++) { - if (msg_destination.intersectionIsNotEmpty(m_routing_table[link])) { + for (int link = 0; link < m_routing_table[vnet].size(); link++) { + if (msg_destination.intersectionIsNotEmpty( + m_routing_table[vnet][link])) { if (m_weight_table[link] <= min_weight) min_weight = m_weight_table[link]; @@ -87,11 +109,11 @@ RoutingUnit::lookupRoutingTable(int vnet, NetDest msg_destination) } // Collect all candidate output links with this minimum weight - for (int link = 0; link < m_routing_table.size(); link++) { - if (msg_destination.intersectionIsNotEmpty(m_routing_table[link])) { + for (int link = 0; link < m_routing_table[vnet].size(); link++) { + if (msg_destination.intersectionIsNotEmpty( + m_routing_table[vnet][link])) { if (m_weight_table[link] == min_weight) { - num_candidates++; output_link_candidates.push_back(link); } diff --git a/src/mem/ruby/network/garnet2.0/RoutingUnit.hh b/src/mem/ruby/network/garnet2.0/RoutingUnit.hh index aff8f7e26..a0ed90127 100644 --- a/src/mem/ruby/network/garnet2.0/RoutingUnit.hh +++ b/src/mem/ruby/network/garnet2.0/RoutingUnit.hh @@ -49,7 +49,7 @@ class RoutingUnit PortDirection inport_dirn); // Topology-agnostic Routing Table based routing (default) - void addRoute(const NetDest& routing_table_entry); + void addRoute(std::vector& routing_table_entry); void addWeight(int link_weight); // get output port from routing table @@ -69,11 +69,16 @@ class RoutingUnit int inport, PortDirection inport_dirn); + // Returns true if vnet is present in the vector + // of vnets or if the vector supports all vnets. + bool supportsVnet(int vnet, std::vector sVnets); + + private: Router *m_router; // Routing Table - std::vector m_routing_table; + std::vector> m_routing_table; std::vector m_weight_table; // Inport and Outport direction to idx maps diff --git a/src/mem/ruby/network/garnet2.0/SConscript b/src/mem/ruby/network/garnet2.0/SConscript index a621036c0..e3bc1108a 100644 --- a/src/mem/ruby/network/garnet2.0/SConscript +++ b/src/mem/ruby/network/garnet2.0/SConscript @@ -49,3 +49,4 @@ Source('VirtualChannel.cc') Source('flitBuffer.cc') Source('flit.cc') Source('Credit.cc') +Source('NetworkBridge.cc') diff --git a/src/mem/ruby/network/garnet2.0/SwitchAllocator.cc b/src/mem/ruby/network/garnet2.0/SwitchAllocator.cc index 33cddc3f8..144f2085b 100644 --- a/src/mem/ruby/network/garnet2.0/SwitchAllocator.cc +++ b/src/mem/ruby/network/garnet2.0/SwitchAllocator.cc @@ -114,7 +114,7 @@ SwitchAllocator::arbitrate_inports() for (int invc_iter = 0; invc_iter < m_num_vcs; invc_iter++) { auto input_unit = m_router->getInputUnit(inport); - if (input_unit->need_stage(invc, SA_, m_router->curCycle())) { + if (input_unit->need_stage(invc, SA_, curTick())) { // This flit is in SA stage int outport = input_unit->get_outport(invc); @@ -192,7 +192,7 @@ SwitchAllocator::arbitrate_outports() DPRINTF(RubyNetwork, "SwitchAllocator at Router %d " "granted outvc %d at outport %d " "to invc %d at inport %d to flit %s at " - "time: %lld\n", + "cycle: %lld\n", m_router->get_id(), outvc, m_router->getPortDirectionName( output_unit->get_direction()), @@ -218,7 +218,7 @@ SwitchAllocator::arbitrate_outports() output_unit->decrement_credit(outvc); // flit ready for Switch Traversal - t_flit->advance_stage(ST_, m_router->curCycle()); + t_flit->advance_stage(ST_, curTick()); m_router->grant_switch(inport, t_flit); m_output_arbiter_activity++; @@ -226,20 +226,18 @@ SwitchAllocator::arbitrate_outports() t_flit->get_type() == HEAD_TAIL_) { // This Input VC should now be empty - assert(!(input_unit->isReady(invc, m_router->curCycle()))); + assert(!(input_unit->isReady(invc, curTick()))); // Free this VC - input_unit->set_vc_idle(invc, m_router->curCycle()); + input_unit->set_vc_idle(invc, curTick()); // Send a credit back // along with the information that this VC is now idle - input_unit->increment_credit(invc, true, - m_router->curCycle()); + input_unit->increment_credit(invc, true, curTick()); } else { // Send a credit back // but do not indicate that the VC is idle - input_unit->increment_credit(invc, false, - m_router->curCycle()); + input_unit->increment_credit(invc, false, curTick()); } // remove this request @@ -313,14 +311,14 @@ SwitchAllocator::send_allowed(int inport, int invc, int outport, int outvc) auto input_unit = m_router->getInputUnit(inport); // enqueue time of this flit - Cycles t_enqueue_time = input_unit->get_enqueue_time(invc); + Tick t_enqueue_time = input_unit->get_enqueue_time(invc); // check if any other flit is ready for SA and for same output port // and was enqueued before this flit int vc_base = vnet*m_vc_per_vnet; for (int vc_offset = 0; vc_offset < m_vc_per_vnet; vc_offset++) { int temp_vc = vc_base + vc_offset; - if (input_unit->need_stage(temp_vc, SA_, m_router->curCycle()) && + if (input_unit->need_stage(temp_vc, SA_, curTick()) && (input_unit->get_outport(temp_vc) == outport) && (input_unit->get_enqueue_time(temp_vc) < t_enqueue_time)) { return false; @@ -350,7 +348,7 @@ SwitchAllocator::vc_allocate(int outport, int inport, int invc) void SwitchAllocator::check_for_wakeup() { - Cycles nextCycle = m_router->curCycle() + Cycles(1); + Tick nextCycle = m_router->clockEdge(Cycles(1)); for (int i = 0; i < m_num_inports; i++) { for (int j = 0; j < m_num_vcs; j++) { diff --git a/src/mem/ruby/network/garnet2.0/VirtualChannel.cc b/src/mem/ruby/network/garnet2.0/VirtualChannel.cc index a469a84fd..0b53983f9 100644 --- a/src/mem/ruby/network/garnet2.0/VirtualChannel.cc +++ b/src/mem/ruby/network/garnet2.0/VirtualChannel.cc @@ -32,23 +32,23 @@ #include "mem/ruby/network/garnet2.0/VirtualChannel.hh" VirtualChannel::VirtualChannel() - : inputBuffer(), m_vc_state(IDLE_, Cycles(0)), m_output_port(-1), + : inputBuffer(), m_vc_state(IDLE_, Tick(0)), m_output_port(-1), m_enqueue_time(INFINITE_), m_output_vc(-1) { } void -VirtualChannel::set_idle(Cycles curTime) +VirtualChannel::set_idle(Tick curTime) { m_vc_state.first = IDLE_; m_vc_state.second = curTime; - m_enqueue_time = Cycles(INFINITE_); + m_enqueue_time = Tick(INFINITE_); m_output_port = -1; m_output_vc = -1; } void -VirtualChannel::set_active(Cycles curTime) +VirtualChannel::set_active(Tick curTime) { m_vc_state.first = ACTIVE_; m_vc_state.second = curTime; @@ -56,7 +56,7 @@ VirtualChannel::set_active(Cycles curTime) } bool -VirtualChannel::need_stage(flit_stage stage, Cycles time) +VirtualChannel::need_stage(flit_stage stage, Tick time) { if (inputBuffer.isReady(time)) { assert(m_vc_state.first == ACTIVE_ && m_vc_state.second <= time); diff --git a/src/mem/ruby/network/garnet2.0/VirtualChannel.hh b/src/mem/ruby/network/garnet2.0/VirtualChannel.hh index 752dfb440..f20629964 100644 --- a/src/mem/ruby/network/garnet2.0/VirtualChannel.hh +++ b/src/mem/ruby/network/garnet2.0/VirtualChannel.hh @@ -43,20 +43,20 @@ class VirtualChannel VirtualChannel(); ~VirtualChannel() = default; - bool need_stage(flit_stage stage, Cycles time); - void set_idle(Cycles curTime); - void set_active(Cycles curTime); + bool need_stage(flit_stage stage, Tick time); + void set_idle(Tick curTime); + void set_active(Tick curTime); void set_outvc(int outvc) { m_output_vc = outvc; } inline int get_outvc() { return m_output_vc; } void set_outport(int outport) { m_output_port = outport; }; inline int get_outport() { return m_output_port; } - inline Cycles get_enqueue_time() { return m_enqueue_time; } - inline void set_enqueue_time(Cycles time) { m_enqueue_time = time; } + inline Tick get_enqueue_time() { return m_enqueue_time; } + inline void set_enqueue_time(Tick time) { m_enqueue_time = time; } inline VC_state_type get_state() { return m_vc_state.first; } inline bool - isReady(Cycles curTime) + isReady(Tick curTime) { return inputBuffer.isReady(curTime); } @@ -68,7 +68,7 @@ class VirtualChannel } inline void - set_state(VC_state_type m_state, Cycles curTime) + set_state(VC_state_type m_state, Tick curTime) { m_vc_state.first = m_state; m_vc_state.second = curTime; @@ -90,9 +90,9 @@ class VirtualChannel private: flitBuffer inputBuffer; - std::pair m_vc_state; + std::pair m_vc_state; int m_output_port; - Cycles m_enqueue_time; + Tick m_enqueue_time; int m_output_vc; }; diff --git a/src/mem/ruby/network/garnet2.0/flit.cc b/src/mem/ruby/network/garnet2.0/flit.cc index 4219e04f8..28a79d4a4 100644 --- a/src/mem/ruby/network/garnet2.0/flit.cc +++ b/src/mem/ruby/network/garnet2.0/flit.cc @@ -30,9 +30,12 @@ #include "mem/ruby/network/garnet2.0/flit.hh" +#include "base/intmath.hh" +#include "debug/RubyNetwork.hh" + // Constructor for the flit flit::flit(int id, int vc, int vnet, RouteInfo route, int size, - MsgPtr msg_ptr, Cycles curTime) + MsgPtr msg_ptr, int MsgSize, uint32_t bWidth, Tick curTime) { m_size = size; m_msg_ptr = msg_ptr; @@ -44,7 +47,9 @@ flit::flit(int id, int vc, int vnet, RouteInfo route, int size, m_vc = vc; m_route = route; m_stage.first = I_; - m_stage.second = m_time; + m_stage.second = curTime; + m_width = bWidth; + msgSize = MsgSize; if (size == 1) { m_type = HEAD_TAIL_; @@ -58,6 +63,38 @@ flit::flit(int id, int vc, int vnet, RouteInfo route, int size, m_type = BODY_; } +flit * +flit::serialize(int ser_id, int parts, uint32_t bWidth) +{ + assert(m_width > bWidth); + + int ratio = (int)divCeil(m_width, bWidth); + int new_id = (m_id*ratio) + ser_id; + int new_size = (int)divCeil((float)msgSize, (float)bWidth); + assert(new_id < new_size); + + flit *fl = new flit(new_id, m_vc, m_vnet, m_route, + new_size, m_msg_ptr, msgSize, bWidth, m_time); + fl->set_enqueue_time(m_enqueue_time); + fl->set_src_delay(src_delay); + return fl; +} + +flit * +flit::deserialize(int des_id, int num_flits, uint32_t bWidth) +{ + int ratio = (int)divCeil((float)bWidth, (float)m_width); + int new_id = ((int)divCeil((float)(m_id+1), (float)ratio)) - 1; + int new_size = (int)divCeil((float)msgSize, (float)bWidth); + assert(new_id < new_size); + + flit *fl = new flit(new_id, m_vc, m_vnet, m_route, + new_size, m_msg_ptr, msgSize, bWidth, m_time); + fl->set_enqueue_time(m_enqueue_time); + fl->set_src_delay(src_delay); + return fl; +} + // Flit can be printed out for debugging purposes void flit::print(std::ostream& out) const @@ -65,13 +102,15 @@ flit::print(std::ostream& out) const out << "[flit:: "; out << "Id=" << m_id << " "; out << "Type=" << m_type << " "; + out << "Size=" << m_size << " "; out << "Vnet=" << m_vnet << " "; out << "VC=" << m_vc << " "; out << "Src NI=" << m_route.src_ni << " "; out << "Src Router=" << m_route.src_router << " "; out << "Dest NI=" << m_route.dest_ni << " "; out << "Dest Router=" << m_route.dest_router << " "; - out << "Enqueue Time=" << m_enqueue_time << " "; + out << "Set Time=" << m_time << " "; + out << "Width=" << m_width<< " "; out << "]"; } diff --git a/src/mem/ruby/network/garnet2.0/flit.hh b/src/mem/ruby/network/garnet2.0/flit.hh index d5c1f66f6..1bbd152a7 100644 --- a/src/mem/ruby/network/garnet2.0/flit.hh +++ b/src/mem/ruby/network/garnet2.0/flit.hh @@ -43,41 +43,44 @@ class flit public: flit() {} flit(int id, int vc, int vnet, RouteInfo route, int size, - MsgPtr msg_ptr, Cycles curTime); + MsgPtr msg_ptr, int MsgSize, uint32_t bWidth, Tick curTime); + + virtual ~flit(){}; int get_outport() {return m_outport; } int get_size() { return m_size; } - Cycles get_enqueue_time() { return m_enqueue_time; } - Cycles get_dequeue_time() { return m_dequeue_time; } + Tick get_enqueue_time() { return m_enqueue_time; } + Tick get_dequeue_time() { return m_dequeue_time; } int get_id() { return m_id; } - Cycles get_time() { return m_time; } + Tick get_time() { return m_time; } int get_vnet() { return m_vnet; } int get_vc() { return m_vc; } RouteInfo get_route() { return m_route; } MsgPtr& get_msg_ptr() { return m_msg_ptr; } flit_type get_type() { return m_type; } - std::pair get_stage() { return m_stage; } - Cycles get_src_delay() { return src_delay; } + std::pair get_stage() { return m_stage; } + Tick get_src_delay() { return src_delay; } void set_outport(int port) { m_outport = port; } - void set_time(Cycles time) { m_time = time; } + void set_time(Tick time) { m_time = time; } void set_vc(int vc) { m_vc = vc; } void set_route(RouteInfo route) { m_route = route; } - void set_src_delay(Cycles delay) { src_delay = delay; } - void set_dequeue_time(Cycles time) { m_dequeue_time = time; } + void set_src_delay(Tick delay) { src_delay = delay; } + void set_dequeue_time(Tick time) { m_dequeue_time = time; } + void set_enqueue_time(Tick time) { m_enqueue_time = time; } void increment_hops() { m_route.hops_traversed++; } - void print(std::ostream& out) const; + virtual void print(std::ostream& out) const; bool - is_stage(flit_stage stage, Cycles time) + is_stage(flit_stage stage, Tick time) { return (stage == m_stage.first && time >= m_stage.second); } void - advance_stage(flit_stage t_stage, Cycles newTime) + advance_stage(flit_stage t_stage, Tick newTime) { m_stage.first = t_stage; m_stage.second = newTime; @@ -96,18 +99,24 @@ class flit bool functionalWrite(Packet *pkt); + virtual flit* serialize(int ser_id, int parts, uint32_t bWidth); + virtual flit* deserialize(int des_id, int num_flits, uint32_t bWidth); + + uint32_t m_width; + int msgSize; protected: int m_id; int m_vnet; int m_vc; RouteInfo m_route; int m_size; - Cycles m_enqueue_time, m_dequeue_time, m_time; + Tick m_enqueue_time, m_dequeue_time; + Tick m_time; flit_type m_type; MsgPtr m_msg_ptr; int m_outport; - Cycles src_delay; - std::pair m_stage; + Tick src_delay; + std::pair m_stage; }; inline std::ostream& diff --git a/src/mem/ruby/network/garnet2.0/flitBuffer.cc b/src/mem/ruby/network/garnet2.0/flitBuffer.cc index 53b3e11db..c72163985 100644 --- a/src/mem/ruby/network/garnet2.0/flitBuffer.cc +++ b/src/mem/ruby/network/garnet2.0/flitBuffer.cc @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 2008 Princeton University * Copyright (c) 2016 Georgia Institute of Technology * All rights reserved. @@ -47,7 +48,7 @@ flitBuffer::isEmpty() } bool -flitBuffer::isReady(Cycles curTime) +flitBuffer::isReady(Tick curTime) { if (m_buffer.size() != 0 ) { flit *t_flit = peekTopFlit(); diff --git a/src/mem/ruby/network/garnet2.0/flitBuffer.hh b/src/mem/ruby/network/garnet2.0/flitBuffer.hh index f4bb28bf9..f98ecf6df 100644 --- a/src/mem/ruby/network/garnet2.0/flitBuffer.hh +++ b/src/mem/ruby/network/garnet2.0/flitBuffer.hh @@ -44,7 +44,7 @@ class flitBuffer flitBuffer(); flitBuffer(int maximum_size); - bool isReady(Cycles curTime); + bool isReady(Tick curTime); bool isEmpty(); void print(std::ostream& out) const; bool isFull(); diff --git a/src/mem/ruby/network/simple/SimpleNetwork.cc b/src/mem/ruby/network/simple/SimpleNetwork.cc index d3b551512..edffc3d92 100644 --- a/src/mem/ruby/network/simple/SimpleNetwork.cc +++ b/src/mem/ruby/network/simple/SimpleNetwork.cc @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Advanced Micro Devices, Inc. * Copyright (c) 2019 ARM Limited * All rights reserved. * @@ -85,7 +86,7 @@ SimpleNetwork::init() void SimpleNetwork::makeExtOutLink(SwitchID src, NodeID global_dest, BasicLink* link, - const NetDest& routing_table_entry) + std::vector& routing_table_entry) { NodeID local_dest = getLocalNodeID(global_dest); assert(local_dest < m_nodes); @@ -95,14 +96,14 @@ SimpleNetwork::makeExtOutLink(SwitchID src, NodeID global_dest, SimpleExtLink *simple_link = safe_cast(link); m_switches[src]->addOutPort(m_fromNetQueues[local_dest], - routing_table_entry, simple_link->m_latency, + routing_table_entry[0], simple_link->m_latency, simple_link->m_bw_multiplier); } // From an endpoint node to a switch void SimpleNetwork::makeExtInLink(NodeID global_src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry) + std::vector& routing_table_entry) { NodeID local_src = getLocalNodeID(global_src); assert(local_src < m_nodes); @@ -112,7 +113,7 @@ SimpleNetwork::makeExtInLink(NodeID global_src, SwitchID dest, BasicLink* link, // From a switch to a switch void SimpleNetwork::makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry, + std::vector& routing_table_entry, PortDirection src_outport, PortDirection dst_inport) { @@ -131,7 +132,7 @@ SimpleNetwork::makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link, SimpleIntLink *simple_link = safe_cast(link); m_switches[dest]->addInPort(queues); - m_switches[src]->addOutPort(queues, routing_table_entry, + m_switches[src]->addOutPort(queues, routing_table_entry[0], simple_link->m_latency, simple_link->m_bw_multiplier); } diff --git a/src/mem/ruby/network/simple/SimpleNetwork.hh b/src/mem/ruby/network/simple/SimpleNetwork.hh index b2e5080de..90e26128f 100644 --- a/src/mem/ruby/network/simple/SimpleNetwork.hh +++ b/src/mem/ruby/network/simple/SimpleNetwork.hh @@ -60,11 +60,11 @@ class SimpleNetwork : public Network // Methods used by Topology to setup the network void makeExtOutLink(SwitchID src, NodeID dest, BasicLink* link, - const NetDest& routing_table_entry); + std::vector& routing_table_entry); void makeExtInLink(NodeID src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry); + std::vector& routing_table_entry); void makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link, - const NetDest& routing_table_entry, + std::vector& routing_table_entry, PortDirection src_outport, PortDirection dst_inport);