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()
type="int", default=50000,
help="network-level deadlock threshold.")
-
def create_network(options, ruby):
# Set the network classes based on the command line options
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()
m_scheduled_wakeups.insert(time);
}
- void scheduleEventAbsolute(Tick timeAbs);
+ ClockedObject *
+ getObject()
+ {
+ return em;
+ }
- protected:
+
+ void scheduleEventAbsolute(Tick timeAbs);
void scheduleEvent(Cycles timeDelta);
private:
m_latency = p->latency;
m_bandwidth_factor = p->bandwidth_factor;
m_weight = p->weight;
+ mVnets = p->supported_vnets;
}
void
Cycles m_latency;
int m_bandwidth_factor;
int m_weight;
+ std::vector<int> mVnets;
};
inline std::ostream&
# 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'
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
int network_num, std::string vnet_type);
virtual void makeExtOutLink(SwitchID src, NodeID dest, BasicLink* link,
- const NetDest& routing_table_entry) = 0;
+ std::vector<NetDest>& routing_table_entry) = 0;
virtual void makeExtInLink(NodeID src, SwitchID dest, BasicLink* link,
- const NetDest& routing_table_entry) = 0;
+ std::vector<NetDest>& routing_table_entry) = 0;
virtual void makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link,
- const NetDest& routing_table_entry,
+ std::vector<NetDest>& routing_table_entry,
PortDirection src_outport,
PortDirection dst_inport) = 0;
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
* All rights reserved.
*
// 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<BasicExtLink *> &ext_links,
const vector<BasicIntLink *> &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
// Initialize weight, latency, and inter switched vectors
int num_switches = max_switch_id+1;
- Matrix topology_weights(num_switches,
- vector<int>(num_switches, INFINITE_LATENCY));
+ Matrix topology_weights(m_vnets,
+ vector<vector<int>>(num_switches,
+ vector<int>(num_switches, INFINITE_LATENCY)));
Matrix component_latencies(num_switches,
- vector<int>(num_switches, -1));
+ vector<vector<int>>(num_switches,
+ vector<int>(m_vnets, -1)));
Matrix component_inter_switches(num_switches,
- vector<int>(num_switches, 0));
+ vector<vector<int>>(num_switches,
+ vector<int>(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<int, int> src_dest = (*i).first;
- BasicLink* link = (*i).second.link;
+ for (auto link_group : m_link_map) {
+ std::pair<int, int> src_dest = link_group.first;
+ vector<bool> 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<LinkEntry> 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<NetDest> 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);
}
}
}
assert(dest <= m_number_of_switches+m_nodes+m_nodes);
std::pair<int, int> 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<LinkEntry> 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<NetDest>& routing_table_entry)
{
// Make sure we're not trying to connect two end-point nodes
// directly together
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<LinkEntry> links = m_link_map[src_dest];
+ for (int l = 0; l < links.size(); l++) {
+ link_entry = links[l];
+ std::vector<NetDest> 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<LinkEntry> links = m_link_map[src_dest];
+ for (int l = 0; l < links.size(); l++) {
+ link_entry = links[l];
+ std::vector<NetDest> 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<LinkEntry> links = m_link_map[src_dest];
+ for (int l = 0; l < links.size(); l++) {
+ link_entry = links[l];
+ std::vector<NetDest> 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);
+ }
+ }
}
}
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];
}
}
}
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;
// 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);
}
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;
}
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
* All rights reserved.
*
class NetDest;
class Network;
-typedef std::vector<std::vector<int> > 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<std::vector<std::vector<int>>> Matrix;
typedef std::string PortDirection;
struct LinkEntry
PortDirection dst_inport_dirn;
};
-typedef std::map<std::pair<SwitchID, SwitchID>, LinkEntry> LinkMap;
+typedef std::map<std::pair<SwitchID, SwitchID>,
+ std::vector<LinkEntry>> LinkMap;
class Topology
{
public:
- Topology(uint32_t num_routers, const std::vector<BasicExtLink *> &ext_links,
+ Topology(uint32_t num_nodes, uint32_t num_routers, uint32_t num_vnets,
+ const std::vector<BasicExtLink *> &ext_links,
const std::vector<BasicIntLink *> &int_links);
uint32_t numSwitches() const { return m_number_of_switches; }
PortDirection src_outport_dirn = "",
PortDirection dest_inport_dirn = "");
void makeLink(Network *net, SwitchID src, SwitchID dest,
- const NetDest& routing_table_entry);
+ std::vector<NetDest>& 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<std::vector<int>> 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<BasicExtLink*> m_ext_link_vector;
std::vector<BasicIntLink*> m_int_link_vector;
// 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_};
* 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 << "]";
+}
+
+
+
{
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; }
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
#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)
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
// 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
#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"
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&
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&
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"
"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'
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'
# 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")
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 2008 Princeton University
* Copyright (c) 2016 Georgia Institute of Technology
* All rights reserved.
#include <cassert>
#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"
void
GarnetNetwork::makeExtInLink(NodeID global_src, SwitchID dest, BasicLink* link,
- const NetDest& routing_table_entry)
+ std::vector<NetDest>& routing_table_entry)
{
NodeID local_src = getLocalNodeID(global_src);
assert(local_src < m_nodes);
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);
+ }
+
}
/*
void
GarnetNetwork::makeExtOutLink(SwitchID src, NodeID global_dest,
BasicLink* link,
- const NetDest& routing_table_entry)
+ std::vector<NetDest>& routing_table_entry)
{
NodeID local_dest = getLocalNodeID(global_dest);
assert(local_dest < m_nodes);
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);
+ }
}
/*
void
GarnetNetwork::makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link,
- const NetDest& routing_table_entry,
+ std::vector<NetDest>& routing_table_entry,
PortDirection src_outport_dirn,
PortDirection dst_inport_dirn)
{
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
// 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
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 2008 Princeton University
* Copyright (c) 2016 Georgia Institute of Technology
* All rights reserved.
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<NetDest>& routing_table_entry);
void makeExtInLink(NodeID src, SwitchID dest, BasicLink* link,
- const NetDest& routing_table_entry);
+ std::vector<NetDest>& routing_table_entry);
void makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link,
- const NetDest& routing_table_entry,
+ std::vector<NetDest>& routing_table_entry,
PortDirection src_outport_dirn,
PortDirection dest_inport_dirn);
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;
}
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;
}
"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")
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
(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(),
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)));
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);
}
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)
}
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);
}
--- /dev/null
+/*
+ * 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 <cmath>
+
+#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);
+}
--- /dev/null
+/*
+ * 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 <iostream>
+#include <queue>
+#include <vector>
+
+#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<int> lenBuffer;
+ std::vector<int> sizeSent;
+ std::vector<int> flitsSent;
+ std::vector<std::queue<int>> extraCredit;
+
+};
+
+#endif // __MEM_RUBY_NETWORK_GARNET2_0_NETWORK_BRIDGE_HH__
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 2020 Inria
* Copyright (c) 2016 Georgia Institute of Technology
* Copyright (c) 2008 Princeton University
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)
{
// 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);
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
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
// 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);
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();
}
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;
}
// 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
// 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++) {
// 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
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 ;
}
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);
}
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
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
// 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()
{
}
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;
}
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;
}
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 2020 Inria
* Copyright (c) 2016 Georgia Institute of Technology
* Copyright (c) 2008 Princeton University
#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"
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<int> _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<flit *> m_stall_queue;
+ bool messageEnqueuedThisCycle;
+ private:
+ std::vector<int> _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<int> m_vc_allocator;
- int m_vc_round_robin; // For round robin scheduling
- /** Used to model link contention. */
- flitBuffer outFlitQueue;
- flitBuffer outCreditQueue;
+ std::vector<OutputPort *> outPorts;
+ std::vector<InputPort *> inPorts;
int m_deadlock_threshold;
std::vector<OutVcState> outVcState;
- NetworkLink *inNetLink;
- NetworkLink *outNetLink;
- CreditLink *inCreditLink;
- CreditLink *outCreditLink;
-
- // Queue for stalled flits
- std::deque<flit *> m_stall_queue;
std::vector<int> m_stall_count;
// Input Flit Buffers
// The flit buffers which will serve the Consumer
std::vector<flitBuffer> niOutVcs;
- std::vector<Cycles> m_ni_out_vcs_enqueue_time;
+ std::vector<Tick> m_ni_out_vcs_enqueue_time;
// The Message buffers that takes messages from the protocol
std::vector<MessageBuffer *> inNode_ptr;
// When a vc stays busy for a long time, it indicates a deadlock
std::vector<int> 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__
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 2020 Inria
* Copyright (c) 2016 Georgia Institute of Technology
* Copyright (c) 2008 Princeton University
#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
}
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
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 2020 Inria
* Copyright (c) 2016 Georgia Institute of Technology
* Copyright (c) 2008 Princeton University
~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<unsigned int> & 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(); }
uint32_t functionalWrite(Packet *);
void resetStats();
+ std::vector<int> 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<unsigned int> m_vc_load;
+
+ protected:
+ flitBuffer linkBuffer;
+ Consumer *link_consumer;
+ flitBuffer *link_srcQueue;
+
};
#endif // __MEM_RUBY_NETWORK_GARNET2_0_NETWORKLINK_HH__
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;
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;
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();
}
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();
}
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();
}
{
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;
}
{
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;
}
}
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));
+ }
}
}
}
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));
}
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 2020 Inria
* Copyright (c) 2016 Georgia Institute of Technology
* Copyright (c) 2008 Princeton University
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();
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++) {
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<InputUnit>(input_unit));
void
Router::addOutPort(PortDirection outport_dirn,
NetworkLink *out_link,
- const NetDest& routing_table_entry, int link_weight,
+ std::vector<NetDest>& 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<OutputUnit>(output_unit));
void addInPort(PortDirection inport_dirn, NetworkLink *link,
CreditLink *credit_link);
void addOutPort(PortDirection outport_dirn, NetworkLink *link,
- const NetDest& routing_table_entry,
+ std::vector<NetDest>& routing_table_entry,
int link_weight, CreditLink *credit_link);
Cycles get_pipe_stages(){ return m_latency; }
return m_output_unit[port].get();
}
+ int getBitWidth() { return m_bit_width; }
+
PortDirection getOutportDirection(int outport);
PortDirection getInportDirection(int inport);
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;
#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"
}
void
-RoutingUnit::addRoute(const NetDest& routing_table_entry)
+RoutingUnit::addRoute(std::vector<NetDest>& 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
m_weight_table.push_back(link_weight);
}
+bool
+RoutingUnit::supportsVnet(int vnet, std::vector<int> 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)
{
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];
}
// 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);
}
PortDirection inport_dirn);
// Topology-agnostic Routing Table based routing (default)
- void addRoute(const NetDest& routing_table_entry);
+ void addRoute(std::vector<NetDest>& routing_table_entry);
void addWeight(int link_weight);
// get output port from routing table
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<int> sVnets);
+
+
private:
Router *m_router;
// Routing Table
- std::vector<NetDest> m_routing_table;
+ std::vector<std::vector<NetDest>> m_routing_table;
std::vector<int> m_weight_table;
// Inport and Outport direction to idx maps
Source('flitBuffer.cc')
Source('flit.cc')
Source('Credit.cc')
+Source('NetworkBridge.cc')
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);
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()),
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++;
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
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;
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++) {
#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;
}
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);
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);
}
}
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;
private:
flitBuffer inputBuffer;
- std::pair<VC_state_type, Cycles> m_vc_state;
+ std::pair<VC_state_type, Tick> m_vc_state;
int m_output_port;
- Cycles m_enqueue_time;
+ Tick m_enqueue_time;
int m_output_vc;
};
#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;
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_;
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
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 << "]";
}
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<flit_stage, Cycles> get_stage() { return m_stage; }
- Cycles get_src_delay() { return src_delay; }
+ std::pair<flit_stage, Tick> 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;
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<flit_stage, Cycles> m_stage;
+ Tick src_delay;
+ std::pair<flit_stage, Tick> m_stage;
};
inline std::ostream&
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 2008 Princeton University
* Copyright (c) 2016 Georgia Institute of Technology
* All rights reserved.
}
bool
-flitBuffer::isReady(Cycles curTime)
+flitBuffer::isReady(Tick curTime)
{
if (m_buffer.size() != 0 ) {
flit *t_flit = peekTopFlit();
flitBuffer();
flitBuffer(int maximum_size);
- bool isReady(Cycles curTime);
+ bool isReady(Tick curTime);
bool isEmpty();
void print(std::ostream& out) const;
bool isFull();
/*
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
* Copyright (c) 2019 ARM Limited
* All rights reserved.
*
void
SimpleNetwork::makeExtOutLink(SwitchID src, NodeID global_dest,
BasicLink* link,
- const NetDest& routing_table_entry)
+ std::vector<NetDest>& routing_table_entry)
{
NodeID local_dest = getLocalNodeID(global_dest);
assert(local_dest < m_nodes);
SimpleExtLink *simple_link = safe_cast<SimpleExtLink*>(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<NetDest>& routing_table_entry)
{
NodeID local_src = getLocalNodeID(global_src);
assert(local_src < m_nodes);
// From a switch to a switch
void
SimpleNetwork::makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link,
- const NetDest& routing_table_entry,
+ std::vector<NetDest>& routing_table_entry,
PortDirection src_outport,
PortDirection dst_inport)
{
SimpleIntLink *simple_link = safe_cast<SimpleIntLink*>(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);
}
// Methods used by Topology to setup the network
void makeExtOutLink(SwitchID src, NodeID dest, BasicLink* link,
- const NetDest& routing_table_entry);
+ std::vector<NetDest>& routing_table_entry);
void makeExtInLink(NodeID src, SwitchID dest, BasicLink* link,
- const NetDest& routing_table_entry);
+ std::vector<NetDest>& routing_table_entry);
void makeInternalLink(SwitchID src, SwitchID dest, BasicLink* link,
- const NetDest& routing_table_entry,
+ std::vector<NetDest>& routing_table_entry,
PortDirection src_outport,
PortDirection dst_inport);