diff --git a/libs/libvtrutil/src/vtr_geometry.h b/libs/libvtrutil/src/vtr_geometry.h index 65c53ecea65..cc8be19c139 100644 --- a/libs/libvtrutil/src/vtr_geometry.h +++ b/libs/libvtrutil/src/vtr_geometry.h @@ -48,6 +48,7 @@ template class Point { public: //Constructors Point(T x_val, T y_val) noexcept; + Point(); public: //Accessors //Coordinates diff --git a/libs/libvtrutil/src/vtr_geometry.tpp b/libs/libvtrutil/src/vtr_geometry.tpp index a1a852850c1..1517bcfc699 100644 --- a/libs/libvtrutil/src/vtr_geometry.tpp +++ b/libs/libvtrutil/src/vtr_geometry.tpp @@ -10,6 +10,11 @@ Point::Point(T x_val, T y_val) noexcept //pass } +template +Point::Point() { + //pass +} + template T Point::x() const { return x_; @@ -74,6 +79,11 @@ Rect::Rect(Point bottom_left_val, Point top_right_val) //pass } +template +Rect::Rect() { + //pass +} + template T Rect::xmin() const { return bottom_left_.x(); diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 40a81bb41b5..00811f29496 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -21,6 +21,7 @@ #include "router_lookahead.h" #include "place_macro.h" #include "compressed_grid.h" +#include "rr_graph_obj.h" //A Context is collection of state relating to a particular part of VPR // @@ -136,6 +137,9 @@ struct DeviceContext : public Context { /* chan_width is for x|y-directed channels; i.e. between rows */ t_chan_width chan_width; + /* Object to define routing resources */ + RRGraph rr_graph; + /* Structures to define the routing architecture of the FPGA. */ std::vector rr_nodes; /* autogenerated in build_rr_graph */ diff --git a/vpr/src/device/check_rr_graph_obj.cpp b/vpr/src/device/check_rr_graph_obj.cpp index 253092fb730..69c772fcbf7 100644 --- a/vpr/src/device/check_rr_graph_obj.cpp +++ b/vpr/src/device/check_rr_graph_obj.cpp @@ -107,7 +107,7 @@ static bool check_rr_graph_source_nodes(const RRGraph& rr_graph) { } } - return invalid_sources; + return !invalid_sources; } /*********************************************************************** @@ -136,7 +136,7 @@ static bool check_rr_graph_sink_nodes(const RRGraph& rr_graph) { } } - return invalid_sinks; + return !invalid_sinks; } /*********************************************************************** @@ -153,48 +153,43 @@ static bool check_rr_graph_sink_nodes(const RRGraph& rr_graph) { * will work properly. **********************************************************************/ bool check_rr_graph(const RRGraph& rr_graph) { - bool check_flag = true; size_t num_err = 0; if (false == check_rr_graph_duplicated_edges(rr_graph)) { VTR_LOG_WARN("Fail in checking duplicated edges !\n"); - check_flag = false; num_err++; } if (false == check_rr_graph_dangling_nodes(rr_graph)) { VTR_LOG_WARN("Fail in checking dangling nodes !\n"); - check_flag = false; num_err++; } if (false == check_rr_graph_source_nodes(rr_graph)) { VTR_LOG_WARN("Fail in checking source nodes!\n"); - check_flag = false; num_err++; } if (false == check_rr_graph_sink_nodes(rr_graph)) { VTR_LOG_WARN("Fail in checking sink nodes!\n"); - check_flag = false; num_err++; } if (false == check_rr_graph_source_nodes(rr_graph)) { VTR_LOG_WARN("Fail in checking source nodes!\n"); - check_flag = false; num_err++; } if (false == check_rr_graph_sink_nodes(rr_graph)) { VTR_LOG_WARN("Fail in checking sink nodes!\n"); - check_flag = false; num_err++; } /* Error out if there is any fatal errors found */ - VTR_LOG_WARN("Checked Routing Resource graph with %d errors !\n", - num_err); + if (0 < num_err) { + VTR_LOG_WARN("Checked Routing Resource graph with %d errors !\n", + num_err); + } - return check_flag; + return (0 == num_err); } diff --git a/vpr/src/device/create_rr_graph.cpp b/vpr/src/device/create_rr_graph.cpp new file mode 100644 index 00000000000..e57c536ce2c --- /dev/null +++ b/vpr/src/device/create_rr_graph.cpp @@ -0,0 +1,149 @@ +/* Standard header files required go first */ +#include + +/* EXTERNAL library header files go second*/ +#include "vtr_assert.h" +#include "vtr_time.h" + +/* VPR header files go then */ +#include "vpr_types.h" +#include "rr_graph_obj.h" +#include "check_rr_graph_obj.h" +#include "create_rr_graph.h" + +/* Finally we include global variables */ +#include "globals.h" + +/******************************************************************** + * TODO: remove when this conversion (from traditional to new data structure) + * is no longer needed + * This function will convert an existing rr_graph in device_ctx to the RRGraph + *object + * This function is used to test our RRGraph if it is acceptable in downstream + *routers + ********************************************************************/ +void convert_rr_graph(std::vector& vpr_segments) { + vtr::ScopedStartFinishTimer timer("Build routing resource graph object"); + + /* IMPORTANT: to build clock tree, + * vpr added segments to the original arch segments + * This is why to use vpr_segments as an inputs!!! + */ + auto& device_ctx = g_vpr_ctx.mutable_device(); + + /* make sure we have a clean empty rr_graph */ + device_ctx.rr_graph.clear(); + + /* The number of switches are in general small, + * reserve switches may not bring significant memory efficiency + * So, we just use create_switch to push_back each time + */ + device_ctx.rr_graph.reserve_switches(device_ctx.rr_switch_inf.size()); + // Create the switches + for (size_t iswitch = 0; iswitch < device_ctx.rr_switch_inf.size(); ++iswitch) { + device_ctx.rr_graph.create_switch(device_ctx.rr_switch_inf[iswitch]); + } + + /* The number of segments are in general small, reserve segments may not bring + * significant memory efficiency */ + device_ctx.rr_graph.reserve_segments(vpr_segments.size()); + // Create the segments + for (size_t iseg = 0; iseg < vpr_segments.size(); ++iseg) { + device_ctx.rr_graph.create_segment(vpr_segments[iseg]); + } + + /* Reserve list of nodes to be memory efficient */ + device_ctx.rr_graph.reserve_nodes(device_ctx.rr_nodes.size()); + + // Create the nodes + std::map old_to_new_rr_node; + for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); ++inode) { + auto& node = device_ctx.rr_nodes[inode]; + RRNodeId rr_node = device_ctx.rr_graph.create_node(node.type()); + + device_ctx.rr_graph.set_node_xlow(rr_node, node.xlow()); + device_ctx.rr_graph.set_node_ylow(rr_node, node.ylow()); + device_ctx.rr_graph.set_node_xhigh(rr_node, node.xhigh()); + device_ctx.rr_graph.set_node_yhigh(rr_node, node.yhigh()); + + device_ctx.rr_graph.set_node_capacity(rr_node, node.capacity()); + + device_ctx.rr_graph.set_node_ptc_num(rr_node, node.ptc_num()); + + device_ctx.rr_graph.set_node_cost_index(rr_node, node.cost_index()); + + if (CHANX == node.type() || CHANY == node.type()) { + device_ctx.rr_graph.set_node_direction(rr_node, node.direction()); + } + if (IPIN == node.type() || OPIN == node.type()) { + device_ctx.rr_graph.set_node_side(rr_node, node.side()); + } + device_ctx.rr_graph.set_node_R(rr_node, node.R()); + device_ctx.rr_graph.set_node_C(rr_node, node.C()); + + /* Set up segment id */ + short irc_data = node.cost_index(); + short iseg = device_ctx.rr_indexed_data[irc_data].seg_index; + device_ctx.rr_graph.set_node_segment(rr_node, RRSegmentId(iseg)); + + VTR_ASSERT(!old_to_new_rr_node.count(inode)); + old_to_new_rr_node[inode] = rr_node; + } + + /* Reserve list of edges to be memory efficient */ + { + int num_edges_to_reserve = 0; + for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); ++inode) { + const auto& node = device_ctx.rr_nodes[inode]; + num_edges_to_reserve += node.num_edges(); + } + device_ctx.rr_graph.reserve_edges(num_edges_to_reserve); + } + + // Create the edges + std::map, RREdgeId> old_to_new_rr_edge; // Key: + // {inode,iedge} + for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); ++inode) { + const auto& node = device_ctx.rr_nodes[inode]; + for (int iedge = 0; iedge < node.num_edges(); ++iedge) { + int isink_node = node.edge_sink_node(iedge); + int iswitch = node.edge_switch(iedge); + + VTR_ASSERT(old_to_new_rr_node.count(inode)); + VTR_ASSERT(old_to_new_rr_node.count(isink_node)); + + RREdgeId rr_edge = device_ctx.rr_graph.create_edge(old_to_new_rr_node[inode], + old_to_new_rr_node[isink_node], + RRSwitchId(iswitch)); + + auto key = std::make_pair(inode, iedge); + VTR_ASSERT(!old_to_new_rr_edge.count(key)); + old_to_new_rr_edge[key] = rr_edge; + } + } + + /* Partition edges to be two class: configurable (1st part) and + * non-configurable (2nd part) + * See how the router will use the edges and determine the strategy + * if we want to partition the edges first or depends on the routing needs + */ + device_ctx.rr_graph.partition_edges(); + + /* Essential check for rr_graph, build look-up and */ + if (false == device_ctx.rr_graph.validate()) { + /* Error out if built-in validator of rr_graph fails */ + vpr_throw(VPR_ERROR_ROUTE, + __FILE__, + __LINE__, + "Fundamental errors occurred when validating rr_graph object!\n"); + } + + /* Error out if advanced checker of rr_graph fails */ + if (false == check_rr_graph(device_ctx.rr_graph)) { + vpr_throw(VPR_ERROR_ROUTE, + __FILE__, + __LINE__, + "Advanced checking rr_graph object fails! Routing may still work " + "but not smooth\n"); + } +} diff --git a/vpr/src/device/create_rr_graph.h b/vpr/src/device/create_rr_graph.h new file mode 100644 index 00000000000..5cc57ec924a --- /dev/null +++ b/vpr/src/device/create_rr_graph.h @@ -0,0 +1,17 @@ +#ifndef CREATE_RR_GRAPH_H +#define CREATE_RR_GRAPH_H + +/* + * Notes in include header files in a head file + * Only include the neccessary header files + * that is required by the data types in the function/class declarations! + */ +#include "rr_graph_obj.h" + +/* IMPORTANT: to build clock tree, + * vpr added segments to the original arch segments + * This is why to use vpr_segments as an inputs!!! + */ +void convert_rr_graph(std::vector& vpr_segments); + +#endif diff --git a/vpr/src/device/rr_graph_obj.cpp b/vpr/src/device/rr_graph_obj.cpp index 0cd9c49e83f..53066e89aff 100644 --- a/vpr/src/device/rr_graph_obj.cpp +++ b/vpr/src/device/rr_graph_obj.cpp @@ -714,7 +714,6 @@ bool RRGraph::validate_edge_sink_nodes() const { * Warnings are thrown if optional checking fails */ bool RRGraph::validate() const { - bool check_flag = true; size_t num_err = 0; initialize_fast_node_lookup(); @@ -724,34 +723,32 @@ bool RRGraph::validate() const { */ if (false == validate_sizes()) { VTR_LOG_WARN("Fail in validating node- and edge-related vector sizes!\n"); - check_flag = false; num_err++; } /* Fundamental check */ if (false == validate_nodes_edges()) { VTR_LOG_WARN("Fail in validating edges connected to each node!\n"); - check_flag = false; num_err++; } if (false == validate_node_segments()) { VTR_LOG_WARN("Fail in validating segment IDs of nodes !\n"); - check_flag = false; num_err++; } if (false == validate_edge_switches()) { VTR_LOG_WARN("Fail in validating switch IDs of edges !\n"); - check_flag = false; num_err++; } /* Error out if there is any fatal errors found */ - VTR_LOG_ERROR("Routing Resource graph is not valid due to %d fatal errors !\n", - num_err); + if (0 < num_err) { + VTR_LOG_ERROR("Routing Resource graph is not valid due to %d fatal errors !\n", + num_err); + } - return check_flag; + return (0 == num_err); } bool RRGraph::is_dirty() const { @@ -827,6 +824,7 @@ RRNodeId RRGraph::create_node(const t_rr_type& type) { node_sides_.push_back(NUM_SIDES); node_Rs_.push_back(0.); node_Cs_.push_back(0.); + node_segments_.push_back(RRSegmentId::INVALID()); node_in_edges_.emplace_back(); //Initially empty node_out_edges_.emplace_back(); //Initially empty diff --git a/vpr/src/route/rr_graph.cpp b/vpr/src/route/rr_graph.cpp index 3aa6afd94a9..733ac6e41a4 100644 --- a/vpr/src/route/rr_graph.cpp +++ b/vpr/src/route/rr_graph.cpp @@ -33,6 +33,7 @@ #include "rr_graph_reader.h" #include "router_lookahead_map.h" #include "rr_graph_clock.h" +#include "create_rr_graph.h" #include "rr_types.h" @@ -375,6 +376,9 @@ void create_rr_graph(const t_graph_type graph_type, print_rr_graph_stats(); + /* Create rr_graph object: load rr_nodes to the object */ + convert_rr_graph(segment_inf); + //Write out rr graph file if needed if (!det_routing_arch->write_rr_graph_filename.empty()) { write_rr_graph(det_routing_arch->write_rr_graph_filename.c_str(), segment_inf); diff --git a/vpr/src/util/rr_graph_obj_util.cpp b/vpr/src/util/rr_graph_obj_util.cpp new file mode 100644 index 00000000000..d7f824421d0 --- /dev/null +++ b/vpr/src/util/rr_graph_obj_util.cpp @@ -0,0 +1,30 @@ +/**************************************************************************** + * This file include most-utilized functions that manipulate on the + * RRGraph object + ***************************************************************************/ +#include +#include "rr_graph_obj_util.h" + +/**************************************************************************** + * Find the switches interconnecting two nodes + * Return a vector of switch ids + ***************************************************************************/ +std::vector find_rr_graph_switches(const RRGraph& rr_graph, + const RRNodeId& from_node, + const RRNodeId& to_node) { + std::vector switches; + std::vector edges = rr_graph.find_edges(from_node, to_node); + if (true == edges.empty()) { + /* edge is open, we return an empty vector of switches */ + return switches; + } + + /* Reach here, edge list is not empty, find switch id one by one + * and update the switch list + */ + for (auto edge : edges) { + switches.push_back(rr_graph.edge_switch(edge)); + } + + return switches; +} diff --git a/vpr/src/util/rr_graph_obj_util.h b/vpr/src/util/rr_graph_obj_util.h new file mode 100644 index 00000000000..cade12c22ad --- /dev/null +++ b/vpr/src/util/rr_graph_obj_util.h @@ -0,0 +1,14 @@ +#ifndef RR_GRAPH_OBJ_UTIL_H +#define RR_GRAPH_OBJ_UTIL_H + +/* Include header files which include data structures used by + * the function declaration + */ +#include "rr_graph_obj.h" + +/* Get node-to-node switches in a RRGraph */ +std::vector find_rr_graph_switches(const RRGraph& rr_graph, + const RRNodeId& from_node, + const RRNodeId& to_node); + +#endif diff --git a/vpr/src/util/rr_graph_obj_utils.h b/vpr/src/util/rr_graph_obj_utils.h new file mode 100644 index 00000000000..910b0ce3acd --- /dev/null +++ b/vpr/src/util/rr_graph_obj_utils.h @@ -0,0 +1,165 @@ +#ifndef RR_GRAPH_OBJ_UTILS_H +#define RR_GRAPH_OBJ_UTILS_H + +/* Include header files which include data structures used by + * the function declaration + */ +#include +#include "vtr_vector_map.h" + +/* + * + * Templated utility functions for cleaning and reordering IdMaps + * + */ + +//Returns true if all elements are contiguously ascending values (i.e. equal to their index) +template +bool are_contiguous(const Container& values) { + using T = typename Container::value_type; + size_t i = 0; + for (T val : values) { + if (val != T(i)) { + return false; + } + ++i; + } + return true; +} + +//Returns true if all elements in the vector 'values' evaluate true +template +bool all_valid(const Container& values) { + for (auto val : values) { + if (!val) { + return false; + } + } + return true; +} + +//Builds a mapping from old to new ids by skipping values marked invalid +template +Container compress_ids(const Container& ids) { + using Id = typename Container::value_type; + Container id_map(ids.size()); + size_t i = 0; + for (auto id : ids) { + if (id) { + //Valid + id_map[id] = Id(i); + ++i; + } + } + + return id_map; +} + +//Returns a vector based on 'values', which has had entries dropped & re-ordered according according to 'id_map'. +//Each entry in id_map corresponds to the assoicated element in 'values'. +//The value of the id_map entry is the new ID of the entry in values. +// +//If it is an invalid ID, the element in values is dropped. +//Otherwise the element is moved to the new ID location. +template +ValueContainer clean_and_reorder_values(const ValueContainer& values, const IdContainer& id_map) { + using Id = typename IdContainer::value_type; + VTR_ASSERT(values.size() == id_map.size()); + + //Allocate space for the values that will not be dropped + ValueContainer result(values.size()); + + //Move over the valid entries to their new locations + size_t new_count = 0; + for (size_t cur_idx = 0; cur_idx < values.size(); ++cur_idx) { + Id old_id = Id(cur_idx); + + Id new_id = id_map[old_id]; + if (new_id) { + //There is a valid mapping + result[new_id] = std::move(values[old_id]); + ++new_count; + } + } + + result.resize(new_count); + + return result; +} + +//Returns the set of new valid Ids defined by 'id_map' +//TODO: merge with clean_and_reorder_values +template +Container clean_and_reorder_ids(const Container& id_map) { + //For IDs, the values are the new id's stored in the map + using Id = typename Container::value_type; + + //Allocate a new vector to store the values that have been not dropped + Container result(id_map.size()); + + //Move over the valid entries to their new locations + size_t new_count = 0; + for (size_t cur_idx = 0; cur_idx < id_map.size(); ++cur_idx) { + Id old_id = Id(cur_idx); + + Id new_id = id_map[old_id]; + if (new_id) { + result[new_id] = new_id; + ++new_count; + } + } + + result.resize(new_count); + + return result; +} + +//Count how many of the Id's referenced in 'range' have a valid +//new mapping in 'id_map' +template +size_t count_valid_refs(R range, const vtr::vector_map& id_map) { + size_t valid_count = 0; + + for (Id old_id : range) { + if (id_map[old_id]) { + ++valid_count; + } + } + + return valid_count; +} + +//Updates the Ids in 'values' based on id_map, even if the original or new mapping is not valid +template +Container update_all_refs(const Container& values, const vtr::vector_map& id_map) { + Container updated; + + for (ValId orig_val : values) { + //The original item was valid + ValId new_val = id_map[orig_val]; + //The original item exists in the new mapping + updated.emplace_back(new_val); + } + + return updated; +} + +template +ValueContainer update_valid_refs(const ValueContainer& values, const IdContainer& id_map) { + ValueContainer updated; + + for (auto orig_val : values) { + if (orig_val) { + //Original item valid + + auto new_val = id_map[orig_val]; + if (new_val) { + //The original item exists in the new mapping + updated.emplace_back(new_val); + } + } + } + return updated; +} + +#endif