1558 lines
53 KiB
C++
1558 lines
53 KiB
C++
/************************************************************************
|
|
* Member Functions of RRGraph
|
|
* include mutators, accessors and utility functions
|
|
***********************************************************************/
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <map>
|
|
#include <limits>
|
|
|
|
#include "vtr_geometry.h"
|
|
#include "vtr_vector_map.h"
|
|
#include "vtr_log.h"
|
|
#include "vtr_util.h"
|
|
#include "vtr_assert.h"
|
|
#include "rr_graph_obj.h"
|
|
#include "rr_graph_obj_utils.h"
|
|
|
|
/********************************************************************
|
|
* Constructors
|
|
*******************************************************************/
|
|
RRGraph::RRGraph()
|
|
: num_nodes_(0)
|
|
, num_edges_(0) {
|
|
//Pass
|
|
}
|
|
|
|
/********************************************************************
|
|
* Accessors
|
|
*******************************************************************/
|
|
RRGraph::lazy_node_range RRGraph::nodes() const {
|
|
return vtr::make_range(lazy_node_iterator(RRNodeId(0), invalid_node_ids_),
|
|
lazy_node_iterator(RRNodeId(num_nodes_), invalid_node_ids_));
|
|
}
|
|
|
|
RRGraph::lazy_edge_range RRGraph::edges() const {
|
|
return vtr::make_range(lazy_edge_iterator(RREdgeId(0), invalid_edge_ids_),
|
|
lazy_edge_iterator(RREdgeId(num_edges_), invalid_edge_ids_));
|
|
}
|
|
|
|
RRGraph::switch_range RRGraph::switches() const {
|
|
return vtr::make_range(switch_ids_.begin(), switch_ids_.end());
|
|
}
|
|
|
|
RRGraph::segment_range RRGraph::segments() const {
|
|
return vtr::make_range(segment_ids_.begin(), segment_ids_.end());
|
|
}
|
|
|
|
//Node attributes
|
|
t_rr_type RRGraph::node_type(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return node_types_[node];
|
|
}
|
|
|
|
size_t RRGraph::node_index(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return size_t(node);
|
|
}
|
|
|
|
short RRGraph::node_xlow(const RRNodeId& node) const {
|
|
return node_bounding_box(node).xmin();
|
|
}
|
|
|
|
short RRGraph::node_ylow(const RRNodeId& node) const {
|
|
return node_bounding_box(node).ymin();
|
|
}
|
|
|
|
short RRGraph::node_xhigh(const RRNodeId& node) const {
|
|
return node_bounding_box(node).xmax();
|
|
}
|
|
|
|
short RRGraph::node_yhigh(const RRNodeId& node) const {
|
|
return node_bounding_box(node).ymax();
|
|
}
|
|
|
|
short RRGraph::node_length(const RRNodeId& node) const {
|
|
return std::max(node_xhigh(node) - node_xlow(node), node_yhigh(node) - node_ylow(node));
|
|
}
|
|
|
|
vtr::Rect<short> RRGraph::node_bounding_box(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return node_bounding_boxes_[node];
|
|
}
|
|
|
|
/* Node starting and ending points */
|
|
/************************************************************************
|
|
* Get the coordinator of a starting point of a routing track
|
|
* For routing tracks in INC_DIRECTION
|
|
* (xlow, ylow) should be the starting point
|
|
*
|
|
* For routing tracks in DEC_DIRECTION
|
|
* (xhigh, yhigh) should be the starting point
|
|
*
|
|
* For routing tracks in BI_DIRECTION
|
|
* we always use (xhigh, yhigh)
|
|
***********************************************************************/
|
|
vtr::Point<short> RRGraph::node_start_coordinate(const RRNodeId& node) const {
|
|
/* Make sure we have CHANX or CHANY */
|
|
VTR_ASSERT((CHANX == node_type(node)) || (CHANY == node_type(node)));
|
|
|
|
vtr::Point<short> start_coordinate(node_xlow(node), node_ylow(node));
|
|
|
|
if (DEC_DIRECTION == node_direction(node)) {
|
|
start_coordinate.set(node_xhigh(node), node_yhigh(node));
|
|
}
|
|
|
|
return start_coordinate;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Get the coordinator of a end point of a routing track
|
|
* For routing tracks in INC_DIRECTION
|
|
* (xhigh, yhigh) should be the ending point
|
|
*
|
|
* For routing tracks in DEC_DIRECTION
|
|
* (xlow, ylow) should be the ending point
|
|
*
|
|
* For routing tracks in BI_DIRECTION
|
|
* we always use (xhigh, yhigh)
|
|
***********************************************************************/
|
|
vtr::Point<short> RRGraph::node_end_coordinate(const RRNodeId& node) const {
|
|
/* Make sure we have CHANX or CHANY */
|
|
VTR_ASSERT((CHANX == node_type(node)) || (CHANY == node_type(node)));
|
|
|
|
vtr::Point<short> end_coordinate(node_xhigh(node), node_yhigh(node));
|
|
|
|
if (DEC_DIRECTION == node_direction(node)) {
|
|
end_coordinate.set(node_xlow(node), node_ylow(node));
|
|
}
|
|
|
|
return end_coordinate;
|
|
}
|
|
|
|
short RRGraph::node_fan_in(const RRNodeId& node) const {
|
|
return node_num_in_edges_[node];
|
|
}
|
|
|
|
short RRGraph::node_fan_out(const RRNodeId& node) const {
|
|
return node_num_out_edges_[node];
|
|
}
|
|
|
|
short RRGraph::node_capacity(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return node_capacities_[node];
|
|
}
|
|
|
|
short RRGraph::node_ptc_num(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return node_ptc_nums_[node][0];
|
|
}
|
|
|
|
short RRGraph::node_pin_num(const RRNodeId& node) const {
|
|
VTR_ASSERT_MSG(node_type(node) == IPIN || node_type(node) == OPIN,
|
|
"Pin number valid only for IPIN/OPIN RR nodes");
|
|
return node_ptc_num(node);
|
|
}
|
|
|
|
short RRGraph::node_track_num(const RRNodeId& node) const {
|
|
VTR_ASSERT_MSG(node_type(node) == CHANX || node_type(node) == CHANY,
|
|
"Track number valid only for CHANX/CHANY RR nodes");
|
|
return node_ptc_num(node);
|
|
}
|
|
|
|
short RRGraph::node_class_num(const RRNodeId& node) const {
|
|
VTR_ASSERT_MSG(node_type(node) == SOURCE || node_type(node) == SINK, "Class number valid only for SOURCE/SINK RR nodes");
|
|
return node_ptc_num(node);
|
|
}
|
|
|
|
std::vector<short> RRGraph::node_track_ids(const RRNodeId& node) const {
|
|
VTR_ASSERT_MSG(node_type(node) == CHANX || node_type(node) == CHANY,
|
|
"Track number valid only for CHANX/CHANY RR nodes");
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return node_ptc_nums_[node];
|
|
}
|
|
|
|
short RRGraph::node_cost_index(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return node_cost_indices_[node];
|
|
}
|
|
|
|
e_direction RRGraph::node_direction(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
VTR_ASSERT_MSG(node_type(node) == CHANX || node_type(node) == CHANY, "Direction valid only for CHANX/CHANY RR nodes");
|
|
return node_directions_[node];
|
|
}
|
|
|
|
e_side RRGraph::node_side(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
VTR_ASSERT_MSG(node_type(node) == IPIN || node_type(node) == OPIN, "Side valid only for IPIN/OPIN RR nodes");
|
|
return node_sides_[node];
|
|
}
|
|
|
|
/* Get the resistance of a node */
|
|
float RRGraph::node_R(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return node_Rs_[node];
|
|
}
|
|
|
|
/* Get the capacitance of a node */
|
|
float RRGraph::node_C(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return node_Cs_[node];
|
|
}
|
|
|
|
short RRGraph::node_rc_data_index(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
return node_rc_data_indices_[node];
|
|
}
|
|
|
|
/*
|
|
* Get a segment id of a node in rr_graph
|
|
*/
|
|
RRSegmentId RRGraph::node_segment(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
|
|
return node_segments_[node];
|
|
}
|
|
|
|
RRGraph::edge_range RRGraph::node_edges(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
|
|
return vtr::make_range(node_edges_[node].get(),
|
|
node_edges_[node].get() + node_num_in_edges_[node] + node_num_out_edges_[node]);
|
|
}
|
|
|
|
RRGraph::edge_range RRGraph::node_in_edges(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
|
|
return vtr::make_range(node_edges_[node].get(),
|
|
node_edges_[node].get() + node_num_in_edges_[node]);
|
|
}
|
|
|
|
RRGraph::edge_range RRGraph::node_out_edges(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
|
|
return vtr::make_range((node_edges_[node].get() + node_num_in_edges_[node]),
|
|
(node_edges_[node].get() + node_num_in_edges_[node]) + node_num_out_edges_[node]);
|
|
}
|
|
|
|
/* Get the list of configurable edges from the input edges of a given node
|
|
* And return the range(iterators) of the list
|
|
*/
|
|
RRGraph::edge_range RRGraph::node_configurable_in_edges(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
|
|
return vtr::make_range(node_edges_[node].get(),
|
|
node_edges_[node].get() + node_num_in_edges_[node] - node_num_non_configurable_in_edges_[node]);
|
|
}
|
|
|
|
/* Get the list of non configurable edges from the input edges of a given node
|
|
* And return the range(iterators) of the list
|
|
*/
|
|
RRGraph::edge_range RRGraph::node_non_configurable_in_edges(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
|
|
return vtr::make_range(node_edges_[node].get() + node_num_in_edges_[node] - node_num_non_configurable_in_edges_[node],
|
|
node_edges_[node].get() + node_num_in_edges_[node]);
|
|
}
|
|
|
|
/* Get the list of configurable edges from the output edges of a given node
|
|
* And return the range(iterators) of the list
|
|
*/
|
|
RRGraph::edge_range RRGraph::node_configurable_out_edges(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
|
|
return vtr::make_range((node_edges_[node].get() + node_num_in_edges_[node]),
|
|
(node_edges_[node].get() + node_num_in_edges_[node]) + node_num_out_edges_[node] - node_num_non_configurable_out_edges_[node]);
|
|
}
|
|
|
|
/* Get the list of non configurable edges from the output edges of a given node
|
|
* And return the range(iterators) of the list
|
|
*/
|
|
RRGraph::edge_range RRGraph::node_non_configurable_out_edges(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
|
|
return vtr::make_range((node_edges_[node].get() + node_num_in_edges_[node]) + node_num_out_edges_[node] - node_num_non_configurable_out_edges_[node],
|
|
(node_edges_[node].get() + node_num_in_edges_[node]) + node_num_out_edges_[node]);
|
|
}
|
|
|
|
//Edge attributes
|
|
size_t RRGraph::edge_index(const RREdgeId& edge) const {
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
return size_t(edge);
|
|
}
|
|
|
|
RRNodeId RRGraph::edge_src_node(const RREdgeId& edge) const {
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
return edge_src_nodes_[edge];
|
|
}
|
|
RRNodeId RRGraph::edge_sink_node(const RREdgeId& edge) const {
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
return edge_sink_nodes_[edge];
|
|
}
|
|
|
|
RRSwitchId RRGraph::edge_switch(const RREdgeId& edge) const {
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
return edge_switches_[edge];
|
|
}
|
|
|
|
/* Check if the edge is a configurable edge (programmble) */
|
|
bool RRGraph::edge_is_configurable(const RREdgeId& edge) const {
|
|
/* Make sure we have a valid edge id */
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
|
|
auto iswitch = edge_switch(edge);
|
|
|
|
return switches_[iswitch].configurable();
|
|
}
|
|
|
|
/* Check if the edge is a non-configurable edge (hardwired) */
|
|
bool RRGraph::edge_is_non_configurable(const RREdgeId& edge) const {
|
|
/* Make sure we have a valid edge id */
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
return !edge_is_configurable(edge);
|
|
}
|
|
|
|
size_t RRGraph::switch_index(const RRSwitchId& switch_id) const {
|
|
VTR_ASSERT_SAFE(valid_switch_id(switch_id));
|
|
return size_t(switch_id);
|
|
}
|
|
|
|
/*
|
|
* Get a switch from the rr_switch list with a given id
|
|
*/
|
|
const t_rr_switch_inf& RRGraph::get_switch(const RRSwitchId& switch_id) const {
|
|
VTR_ASSERT_SAFE(valid_switch_id(switch_id));
|
|
return switches_[switch_id];
|
|
}
|
|
|
|
size_t RRGraph::segment_index(const RRSegmentId& segment_id) const {
|
|
VTR_ASSERT_SAFE(valid_segment_id(segment_id));
|
|
return size_t(segment_id);
|
|
}
|
|
|
|
/*
|
|
* Get a segment from the segment list with a given id
|
|
*/
|
|
const t_segment_inf& RRGraph::get_segment(const RRSegmentId& segment_id) const {
|
|
VTR_ASSERT_SAFE(valid_segment_id(segment_id));
|
|
return segments_[segment_id];
|
|
}
|
|
|
|
/************************************************************************
|
|
* Find all the edges interconnecting two nodes
|
|
* Return a vector of the edge ids
|
|
***********************************************************************/
|
|
std::vector<RREdgeId> RRGraph::find_edges(const RRNodeId& src_node, const RRNodeId& sink_node) const {
|
|
std::vector<RREdgeId> matching_edges;
|
|
|
|
/* Iterate over the outgoing edges of the source node */
|
|
for (auto edge : node_out_edges(src_node)) {
|
|
if (edge_sink_node(edge) == sink_node) {
|
|
/* Update edge list to return */
|
|
matching_edges.push_back(edge);
|
|
}
|
|
}
|
|
|
|
return matching_edges;
|
|
}
|
|
|
|
RRNodeId RRGraph::find_node(const short& x, const short& y, const t_rr_type& type, const int& ptc, const e_side& side) const {
|
|
initialize_fast_node_lookup();
|
|
|
|
size_t itype = type;
|
|
size_t iside = side;
|
|
|
|
/* Check if x, y, type and ptc, side is valid */
|
|
if ((x < 0) /* See if x is smaller than the index of first element */
|
|
|| (size_t(x) > node_lookup_.dim_size(0) - 1) /* See if x is large than the index of last element */
|
|
|| (0 == node_lookup_.dim_size(0))) { /* See if x is large than the index of last element */
|
|
/* Return a zero range! */
|
|
return RRNodeId::INVALID();
|
|
}
|
|
|
|
/* Check if x, y, type and ptc, side is valid */
|
|
if ((y < 0) /* See if y is smaller than the index of first element */
|
|
|| (size_t(y) > node_lookup_.dim_size(1) - 1) /* See if y is large than the index of last element */
|
|
|| (0 == node_lookup_.dim_size(1))) { /* See if y is large than the index of last element */
|
|
/* Return a zero range! */
|
|
return RRNodeId::INVALID();
|
|
}
|
|
|
|
/* Check if x, y, type and ptc, side is valid */
|
|
/* itype is always larger than -1, we can skip checking */
|
|
if ( (itype > node_lookup_.dim_size(2) - 1) /* See if type is large than the index of last element */
|
|
|| (0 == node_lookup_.dim_size(2))) { /* See if type is large than the index of last element */
|
|
/* Return a zero range! */
|
|
return RRNodeId::INVALID();
|
|
}
|
|
|
|
/* Check if x, y, type and ptc, side is valid */
|
|
if ((ptc < 0) /* See if ptc is smaller than the index of first element */
|
|
|| (size_t(ptc) > node_lookup_[x][y][type].size() - 1) /* See if ptc is large than the index of last element */
|
|
|| (0 == node_lookup_[x][y][type].size())) { /* See if ptc is large than the index of last element */
|
|
/* Return a zero range! */
|
|
return RRNodeId::INVALID();
|
|
}
|
|
|
|
/* Check if x, y, type and ptc, side is valid */
|
|
/* iside is always larger than -1, we can skip checking */
|
|
if ((iside > node_lookup_[x][y][type][ptc].size() - 1) /* See if side is large than the index of last element */
|
|
|| (0 == node_lookup_[x][y][type][ptc].size()) ) { /* See if side is large than the index of last element */
|
|
/* Return a zero range! */
|
|
return RRNodeId::INVALID();
|
|
}
|
|
|
|
return node_lookup_[x][y][itype][ptc][iside];
|
|
}
|
|
|
|
/* Find the channel width (number of tracks) of a channel [x][y] */
|
|
short RRGraph::chan_num_tracks(const short& x, const short& y, const t_rr_type& type) const {
|
|
/* Must be CHANX or CHANY */
|
|
VTR_ASSERT_MSG(CHANX == type || CHANY == type,
|
|
"Required node_type to be CHANX or CHANY!");
|
|
initialize_fast_node_lookup();
|
|
|
|
/* Check if x, y, type and ptc is valid */
|
|
if ((x < 0) /* See if x is smaller than the index of first element */
|
|
|| (size_t(x) > node_lookup_.dim_size(0) - 1)) { /* See if x is large than the index of last element */
|
|
/* Return a zero range! */
|
|
return 0;
|
|
}
|
|
|
|
/* Check if x, y, type and ptc is valid */
|
|
if ((y < 0) /* See if y is smaller than the index of first element */
|
|
|| (size_t(y) > node_lookup_.dim_size(1) - 1)) { /* See if y is large than the index of last element */
|
|
/* Return a zero range! */
|
|
return 0;
|
|
}
|
|
|
|
/* Check if x, y, type and ptc is valid */
|
|
if ((size_t(type) > node_lookup_.dim_size(2) - 1)) { /* See if type is large than the index of last element */
|
|
/* Return a zero range! */
|
|
return 0;
|
|
}
|
|
|
|
const auto& matching_nodes = node_lookup_[x][y][type];
|
|
|
|
return vtr::make_range(matching_nodes.begin(), matching_nodes.end()).size();
|
|
}
|
|
|
|
/* This function aims to print basic information about a node */
|
|
void RRGraph::print_node(const RRNodeId& node) const {
|
|
VTR_LOG("Node id: %d\n", node_index(node));
|
|
VTR_LOG("Node type: %s\n", rr_node_typename[node_type(node)]);
|
|
VTR_LOG("Node xlow: %d\n", node_xlow(node));
|
|
VTR_LOG("Node ylow: %d\n", node_ylow(node));
|
|
VTR_LOG("Node xhigh: %d\n", node_xhigh(node));
|
|
VTR_LOG("Node yhigh: %d\n", node_yhigh(node));
|
|
VTR_LOG("Node ptc: %d\n", node_ptc_num(node));
|
|
VTR_LOG("Node num in_edges: %d\n", node_in_edges(node).size());
|
|
VTR_LOG("Node num out_edges: %d\n", node_out_edges(node).size());
|
|
|
|
if (node_type(node) == CHANX || node_type(node) == CHANY) {
|
|
VTR_LOG("Node segment id: %d\n", node_segment(node));
|
|
}
|
|
}
|
|
|
|
/* Check if the segment id of a node is in range */
|
|
bool RRGraph::validate_node_segment(const RRNodeId& node) const {
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
/* Only CHANX and CHANY requires a valid segment id */
|
|
if ((CHANX == node_type(node))
|
|
|| (CHANY == node_type(node))) {
|
|
return valid_segment_id(node_segments_[node]);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/* Check if the segment id of every node is in range */
|
|
bool RRGraph::validate_node_segments() const {
|
|
bool all_valid = true;
|
|
for (size_t id = 0; id < num_nodes_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_node_id(RRNodeId(id))) {
|
|
/* Skip this id */
|
|
continue;
|
|
}
|
|
if (true == validate_node_segment(RRNodeId(id))) {
|
|
continue;
|
|
}
|
|
/* Reach here it means we find an invalid segment id */
|
|
all_valid = false;
|
|
/* Print a warning! */
|
|
VTR_LOG_WARN("Node %d has an invalid segment id (%d)!\n",
|
|
id, size_t(node_segment(RRNodeId(id))));
|
|
}
|
|
return all_valid;
|
|
}
|
|
|
|
/* Check if the switch id of a edge is in range */
|
|
bool RRGraph::validate_edge_switch(const RREdgeId& edge) const {
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
return valid_switch_id(edge_switches_[edge]);
|
|
}
|
|
|
|
/* Check if the switch id of every edge is in range */
|
|
bool RRGraph::validate_edge_switches() const {
|
|
bool all_valid = true;
|
|
for (size_t id = 0; id < num_edges_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_edge_id(RREdgeId(id))) {
|
|
/* Skip this id */
|
|
continue;
|
|
}
|
|
if (true == validate_edge_switch(RREdgeId(id))) {
|
|
continue;
|
|
}
|
|
/* Reach here it means we find an invalid segment id */
|
|
all_valid = false;
|
|
/* Print a warning! */
|
|
VTR_LOG_WARN("Edge %d has an invalid switch id (%d)!\n",
|
|
id, size_t(edge_switch(RREdgeId(id))));
|
|
}
|
|
return all_valid;
|
|
}
|
|
|
|
/* Check if a node is in the list of source_nodes of a edge */
|
|
bool RRGraph::validate_node_is_edge_src(const RRNodeId& node, const RREdgeId& edge) const {
|
|
/* Assure a valid node id */
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
/* assure a valid edge id */
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
/* find if the node is the src */
|
|
if (node == edge_src_node(edge)) {
|
|
return true; /* confirmed source node*/
|
|
} else {
|
|
return false; /* not a source */
|
|
}
|
|
}
|
|
|
|
/* Check if a node is in the list of sink_nodes of a edge */
|
|
bool RRGraph::validate_node_is_edge_sink(const RRNodeId& node, const RREdgeId& edge) const {
|
|
/* Assure a valid node id */
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
/* assure a valid edge id */
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
/* find if the node is the sink */
|
|
if (node == edge_sink_node(edge)) {
|
|
return true; /* confirmed source node*/
|
|
} else {
|
|
return false; /* not a source */
|
|
}
|
|
}
|
|
|
|
/* This function will check if a node has valid input edges
|
|
* 1. Check the edge ids are valid
|
|
* 2. Check the node is in the list of edge_sink_node
|
|
*/
|
|
bool RRGraph::validate_node_in_edges(const RRNodeId& node) const {
|
|
bool all_valid = true;
|
|
/* Assure a valid node id */
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
/* Check each edge */
|
|
for (auto edge : node_in_edges(node)) {
|
|
/* assure a valid edge id */
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
/* check the node is in the list of edge_sink_node */
|
|
if (true == validate_node_is_edge_sink(node, edge)) {
|
|
continue;
|
|
}
|
|
/* Reach here, it means there is something wrong!
|
|
* Print a warning
|
|
*/
|
|
VTR_LOG_WARN("Edge %d is in the input edge list of node %d while the node is not in edge's sink node list!\n",
|
|
size_t(edge), size_t(node));
|
|
all_valid = false;
|
|
}
|
|
|
|
return all_valid;
|
|
}
|
|
|
|
/* This function will check if a node has valid output edges
|
|
* 1. Check the edge ids are valid
|
|
* 2. Check the node is in the list of edge_source_node
|
|
*/
|
|
bool RRGraph::validate_node_out_edges(const RRNodeId& node) const {
|
|
bool all_valid = true;
|
|
/* Assure a valid node id */
|
|
VTR_ASSERT_SAFE(valid_node_id(node));
|
|
/* Check each edge */
|
|
for (auto edge : node_out_edges(node)) {
|
|
/* assure a valid edge id */
|
|
VTR_ASSERT_SAFE(valid_edge_id(edge));
|
|
/* check the node is in the list of edge_sink_node */
|
|
if (true == validate_node_is_edge_src(node, edge)) {
|
|
continue;
|
|
}
|
|
/* Reach here, it means there is something wrong!
|
|
* Print a warning
|
|
*/
|
|
VTR_LOG_WARN("Edge %d is in the output edge list of node %d while the node is not in edge's source node list!\n",
|
|
size_t(edge), size_t(node));
|
|
all_valid = false;
|
|
}
|
|
|
|
return all_valid;
|
|
}
|
|
|
|
/* check all the edges of a node */
|
|
bool RRGraph::validate_node_edges(const RRNodeId& node) const {
|
|
bool all_valid = true;
|
|
|
|
if (false == validate_node_in_edges(node)) {
|
|
all_valid = false;
|
|
}
|
|
if (false == validate_node_out_edges(node)) {
|
|
all_valid = false;
|
|
}
|
|
|
|
return all_valid;
|
|
}
|
|
|
|
/* check if all the nodes' input edges are valid */
|
|
bool RRGraph::validate_nodes_in_edges() const {
|
|
bool all_valid = true;
|
|
for (size_t id = 0; id < num_nodes_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_node_id(RRNodeId(id))) {
|
|
/* Skip this id */
|
|
continue;
|
|
}
|
|
if (true == validate_node_in_edges(RRNodeId(id))) {
|
|
continue;
|
|
}
|
|
/* Reach here, it means there is something wrong!
|
|
* Print a warning
|
|
*/
|
|
all_valid = false;
|
|
}
|
|
return all_valid;
|
|
}
|
|
|
|
/* check if all the nodes' output edges are valid */
|
|
bool RRGraph::validate_nodes_out_edges() const {
|
|
bool all_valid = true;
|
|
for (size_t id = 0; id < num_nodes_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_node_id(RRNodeId(id))) {
|
|
/* Skip this id */
|
|
continue;
|
|
}
|
|
if (true == validate_node_out_edges(RRNodeId(id))) {
|
|
continue;
|
|
}
|
|
/* Reach here, it means there is something wrong!
|
|
* Print a warning
|
|
*/
|
|
all_valid = false;
|
|
}
|
|
return all_valid;
|
|
}
|
|
|
|
/* check all the edges of every node */
|
|
bool RRGraph::validate_nodes_edges() const {
|
|
bool all_valid = true;
|
|
|
|
if (false == validate_nodes_in_edges()) {
|
|
all_valid = false;
|
|
}
|
|
if (false == validate_nodes_out_edges()) {
|
|
all_valid = false;
|
|
}
|
|
|
|
return all_valid;
|
|
}
|
|
|
|
/* Check if source node of a edge is valid */
|
|
bool RRGraph::validate_edge_src_node(const RREdgeId& edge) const {
|
|
return valid_node_id(edge_src_node(edge));
|
|
}
|
|
|
|
/* Check if sink node of a edge is valid */
|
|
bool RRGraph::validate_edge_sink_node(const RREdgeId& edge) const {
|
|
return valid_node_id(edge_sink_node(edge));
|
|
}
|
|
|
|
/* Check if source nodes of a edge are all valid */
|
|
bool RRGraph::validate_edge_src_nodes() const {
|
|
bool all_valid = true;
|
|
for (size_t id = 0; id < num_edges_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_edge_id(RREdgeId(id))) {
|
|
/* Skip this id */
|
|
continue;
|
|
}
|
|
if (true == validate_edge_src_node(RREdgeId(id))) {
|
|
continue;
|
|
}
|
|
/* Reach here, it means there is something wrong!
|
|
* Print a warning
|
|
*/
|
|
VTR_LOG_WARN("Edge %d has a invalid source node %d!\n",
|
|
id, size_t(edge_src_node(RREdgeId(id))));
|
|
all_valid = false;
|
|
}
|
|
return all_valid;
|
|
}
|
|
|
|
/* Check if source nodes of a edge are all valid */
|
|
bool RRGraph::validate_edge_sink_nodes() const {
|
|
bool all_valid = true;
|
|
for (size_t id = 0; id < num_edges_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_edge_id(RREdgeId(id))) {
|
|
/* Skip this id */
|
|
continue;
|
|
}
|
|
if (true == validate_edge_sink_node(RREdgeId(id))) {
|
|
continue;
|
|
}
|
|
/* Reach here, it means there is something wrong!
|
|
* Print a warning
|
|
*/
|
|
VTR_LOG_WARN("Edge %d has a invalid sink node %d!\n",
|
|
id, size_t(edge_sink_node(RREdgeId(id))));
|
|
all_valid = false;
|
|
}
|
|
return all_valid;
|
|
}
|
|
|
|
/* This function should be used when nodes, edges, switches and segments have been used after
|
|
* We will build the fast_lookup, partition edges and check
|
|
* This function run fundamental and optional checks on internal data
|
|
* Errors are thrown if fundamental checking fails
|
|
* Warnings are thrown if optional checking fails
|
|
*/
|
|
bool RRGraph::validate() const {
|
|
size_t num_err = 0;
|
|
|
|
initialize_fast_node_lookup();
|
|
|
|
/* Validate the sizes of nodes and node-related vectors
|
|
* Validate the sizes of edges and edge-related vectors
|
|
*/
|
|
if (false == validate_sizes()) {
|
|
VTR_LOG_WARN("Fail in validating node- and edge-related vector sizes!\n");
|
|
num_err++;
|
|
}
|
|
|
|
/* Fundamental check */
|
|
if (false == validate_nodes_edges()) {
|
|
VTR_LOG_WARN("Fail in validating edges connected to each node!\n");
|
|
num_err++;
|
|
}
|
|
|
|
if (false == validate_node_segments()) {
|
|
VTR_LOG_WARN("Fail in validating segment IDs of nodes !\n");
|
|
num_err++;
|
|
}
|
|
|
|
if (false == validate_edge_switches()) {
|
|
VTR_LOG_WARN("Fail in validating switch IDs of edges !\n");
|
|
num_err++;
|
|
}
|
|
|
|
/* Error out if there is any fatal errors found */
|
|
if (0 < num_err) {
|
|
VTR_LOG_ERROR("Routing Resource graph is not valid due to %d fatal errors !\n",
|
|
num_err);
|
|
}
|
|
|
|
return (0 == num_err);
|
|
}
|
|
|
|
bool RRGraph::is_dirty() const {
|
|
return dirty_;
|
|
}
|
|
|
|
void RRGraph::set_dirty() {
|
|
dirty_ = true;
|
|
}
|
|
|
|
void RRGraph::clear_dirty() {
|
|
dirty_ = false;
|
|
}
|
|
|
|
/* Reserve a list of nodes */
|
|
void RRGraph::reserve_nodes(const unsigned long& num_nodes) {
|
|
/* Reserve the full set of vectors related to nodes */
|
|
/* Basic information */
|
|
this->node_types_.reserve(num_nodes);
|
|
|
|
this->node_bounding_boxes_.reserve(num_nodes);
|
|
|
|
this->node_capacities_.reserve(num_nodes);
|
|
this->node_ptc_nums_.reserve(num_nodes);
|
|
this->node_cost_indices_.reserve(num_nodes);
|
|
this->node_directions_.reserve(num_nodes);
|
|
this->node_sides_.reserve(num_nodes);
|
|
this->node_Rs_.reserve(num_nodes);
|
|
this->node_Cs_.reserve(num_nodes);
|
|
this->node_rc_data_indices_.reserve(num_nodes);
|
|
this->node_segments_.reserve(num_nodes);
|
|
|
|
/* Edge-related vectors */
|
|
this->node_num_in_edges_.reserve(num_nodes);
|
|
this->node_num_out_edges_.reserve(num_nodes);
|
|
this->node_num_non_configurable_in_edges_.reserve(num_nodes);
|
|
this->node_num_non_configurable_out_edges_.reserve(num_nodes);
|
|
this->node_edges_.reserve(num_nodes);
|
|
}
|
|
|
|
/* Reserve a list of edges */
|
|
void RRGraph::reserve_edges(const unsigned long& num_edges) {
|
|
/* Reserve the full set of vectors related to edges */
|
|
this->edge_src_nodes_.reserve(num_edges);
|
|
this->edge_sink_nodes_.reserve(num_edges);
|
|
this->edge_switches_.reserve(num_edges);
|
|
}
|
|
|
|
/* Reserve a list of switches */
|
|
void RRGraph::reserve_switches(const size_t& num_switches) {
|
|
this->switch_ids_.reserve(num_switches);
|
|
this->switches_.reserve(num_switches);
|
|
}
|
|
|
|
/* Reserve a list of segments */
|
|
void RRGraph::reserve_segments(const size_t& num_segments) {
|
|
this->segment_ids_.reserve(num_segments);
|
|
this->segments_.reserve(num_segments);
|
|
}
|
|
|
|
/* Mutators */
|
|
RRNodeId RRGraph::create_node(const t_rr_type& type) {
|
|
/* Allocate an ID */
|
|
RRNodeId node_id = RRNodeId(num_nodes_);
|
|
/* Expand range of node ids */
|
|
num_nodes_++;
|
|
|
|
/* Initialize the attributes */
|
|
node_types_.push_back(type);
|
|
|
|
node_bounding_boxes_.emplace_back(-1, -1, -1, -1);
|
|
|
|
node_capacities_.push_back(-1);
|
|
node_ptc_nums_.push_back(std::vector<short>(1, -1));
|
|
node_cost_indices_.push_back(-1);
|
|
node_directions_.push_back(NO_DIRECTION);
|
|
node_sides_.push_back(NUM_SIDES);
|
|
node_Rs_.push_back(0.);
|
|
node_Cs_.push_back(0.);
|
|
node_rc_data_indices_.push_back(-1);
|
|
node_segments_.push_back(RRSegmentId::INVALID());
|
|
|
|
node_edges_.emplace_back(); //Initially empty
|
|
|
|
node_num_in_edges_.emplace_back(0);
|
|
node_num_out_edges_.emplace_back(0);
|
|
node_num_non_configurable_in_edges_.emplace_back(0);
|
|
node_num_non_configurable_out_edges_.emplace_back(0);
|
|
|
|
invalidate_fast_node_lookup();
|
|
|
|
VTR_ASSERT(validate_sizes());
|
|
|
|
return node_id;
|
|
}
|
|
|
|
RREdgeId RRGraph::create_edge(const RRNodeId& source, const RRNodeId& sink, const RRSwitchId& switch_id, const bool& fake_switch) {
|
|
VTR_ASSERT(valid_node_id(source));
|
|
VTR_ASSERT(valid_node_id(sink));
|
|
if (false == fake_switch) {
|
|
VTR_ASSERT(valid_switch_id(switch_id));
|
|
}
|
|
|
|
/* Allocate an ID */
|
|
RREdgeId edge_id = RREdgeId(num_edges_);
|
|
/* Expand range of edge ids */
|
|
num_edges_++;
|
|
|
|
/* Initialize the attributes */
|
|
edge_src_nodes_.push_back(source);
|
|
edge_sink_nodes_.push_back(sink);
|
|
edge_switches_.push_back(switch_id);
|
|
|
|
//We do not create the entry in node_edges_ here!
|
|
//For memory efficiency this is done when
|
|
//rebuild_node_edges() is called (i.e. after all
|
|
//edges have been created).
|
|
|
|
VTR_ASSERT(validate_sizes());
|
|
|
|
return edge_id;
|
|
}
|
|
|
|
void RRGraph::set_edge_switch(const RREdgeId& edge, const RRSwitchId& switch_id) {
|
|
VTR_ASSERT(valid_edge_id(edge));
|
|
VTR_ASSERT(valid_switch_id(switch_id));
|
|
edge_switches_[edge] = switch_id;
|
|
}
|
|
|
|
RRSwitchId RRGraph::create_switch(const t_rr_switch_inf& switch_info) {
|
|
//Allocate an ID
|
|
RRSwitchId switch_id = RRSwitchId(switch_ids_.size());
|
|
switch_ids_.push_back(switch_id);
|
|
|
|
switches_.push_back(switch_info);
|
|
|
|
return switch_id;
|
|
}
|
|
|
|
/* Create segment */
|
|
RRSegmentId RRGraph::create_segment(const t_segment_inf& segment_info) {
|
|
//Allocate an ID
|
|
RRSegmentId segment_id = RRSegmentId(segment_ids_.size());
|
|
segment_ids_.push_back(segment_id);
|
|
|
|
segments_.push_back(segment_info);
|
|
|
|
return segment_id;
|
|
}
|
|
|
|
/* This function just marks the node to be removed with an INVALID id
|
|
* It also disconnects and mark the incoming and outcoming edges to be INVALID()
|
|
* And then set the RRGraph as polluted (dirty_flag = true)
|
|
* The compress() function should be called to physically remove the node
|
|
*/
|
|
void RRGraph::remove_node(const RRNodeId& node) {
|
|
//Invalidate all connected edges
|
|
// TODO: consider removal of self-loop edges?
|
|
for (auto edge : node_in_edges(node)) {
|
|
remove_edge(edge);
|
|
}
|
|
for (auto edge : node_out_edges(node)) {
|
|
remove_edge(edge);
|
|
}
|
|
|
|
//Mark node invalid
|
|
invalid_node_ids_.insert(node);
|
|
|
|
//Invalidate the node look-up
|
|
invalidate_fast_node_lookup();
|
|
|
|
set_dirty();
|
|
}
|
|
|
|
/* This function just marks the edge to be removed with an INVALID id
|
|
* And then set the RRGraph as polluted (dirty_flag = true)
|
|
* The compress() function should be called to physically remove the edge
|
|
*/
|
|
void RRGraph::remove_edge(const RREdgeId& edge) {
|
|
RRNodeId src_node = edge_src_node(edge);
|
|
RRNodeId sink_node = edge_sink_node(edge);
|
|
|
|
/* Invalidate node to edge references
|
|
* TODO: consider making this optional (e.g. if called from remove_node)
|
|
*/
|
|
for (size_t i = 0; i < node_num_in_edges_[src_node]; ++i) {
|
|
if (node_edges_[src_node][i] == edge) {
|
|
node_edges_[src_node][i] = RREdgeId::INVALID();
|
|
break;
|
|
}
|
|
}
|
|
for (size_t i = node_num_in_edges_[sink_node]; i < node_num_in_edges_[sink_node] + node_num_out_edges_[sink_node]; ++i) {
|
|
if (node_edges_[sink_node][i] == edge) {
|
|
node_edges_[sink_node][i] = RREdgeId::INVALID();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Mark edge invalid */
|
|
invalid_edge_ids_.insert(edge);
|
|
|
|
set_dirty();
|
|
}
|
|
|
|
void RRGraph::set_node_type(const RRNodeId& node, const t_rr_type& type) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_types_[node] = type;
|
|
}
|
|
|
|
void RRGraph::set_node_xlow(const RRNodeId& node, const short& xlow) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_bounding_boxes_[node].set_xmin(xlow);
|
|
}
|
|
|
|
void RRGraph::set_node_ylow(const RRNodeId& node, const short& ylow) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_bounding_boxes_[node].set_ymin(ylow);
|
|
}
|
|
|
|
void RRGraph::set_node_xhigh(const RRNodeId& node, const short& xhigh) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_bounding_boxes_[node].set_xmax(xhigh);
|
|
}
|
|
|
|
void RRGraph::set_node_yhigh(const RRNodeId& node, const short& yhigh) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_bounding_boxes_[node].set_ymax(yhigh);
|
|
}
|
|
|
|
void RRGraph::set_node_bounding_box(const RRNodeId& node, const vtr::Rect<short>& bb) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_bounding_boxes_[node] = bb;
|
|
}
|
|
|
|
void RRGraph::set_node_capacity(const RRNodeId& node, const short& capacity) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_capacities_[node] = capacity;
|
|
}
|
|
|
|
void RRGraph::set_node_ptc_num(const RRNodeId& node, const short& ptc) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
/* For CHANX and CHANY, we will resize the ptc num to length of the node
|
|
* For other nodes, we will always assign the first element
|
|
*/
|
|
if ((CHANX == node_type(node)) || (CHANY == node_type(node))) {
|
|
if ((size_t)node_length(node) + 1 != node_ptc_nums_[node].size()) {
|
|
node_ptc_nums_[node].resize((size_t)node_length(node) + 1);
|
|
}
|
|
std::fill(node_ptc_nums_[node].begin(), node_ptc_nums_[node].end(), ptc);
|
|
} else {
|
|
VTR_ASSERT(1 == node_ptc_nums_[node].size());
|
|
node_ptc_nums_[node][0] = ptc;
|
|
}
|
|
}
|
|
|
|
void RRGraph::set_node_pin_num(const RRNodeId& node, const short& pin_id) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
VTR_ASSERT_MSG(node_type(node) == IPIN || node_type(node) == OPIN, "Pin number valid only for IPIN/OPIN RR nodes");
|
|
|
|
set_node_ptc_num(node, pin_id);
|
|
}
|
|
|
|
void RRGraph::set_node_track_num(const RRNodeId& node, const short& track_id) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
VTR_ASSERT_MSG(node_type(node) == CHANX || node_type(node) == CHANY, "Track number valid only for CHANX/CHANY RR nodes");
|
|
|
|
set_node_ptc_num(node, track_id);
|
|
}
|
|
|
|
void RRGraph::set_node_class_num(const RRNodeId& node, const short& class_id) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
VTR_ASSERT_MSG(node_type(node) == SOURCE || node_type(node) == SINK, "Class number valid only for SOURCE/SINK RR nodes");
|
|
|
|
set_node_ptc_num(node, class_id);
|
|
}
|
|
|
|
void RRGraph::add_node_track_num(const RRNodeId& node,
|
|
const vtr::Point<size_t>& node_offset,
|
|
const short& track_id) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
VTR_ASSERT_MSG(node_type(node) == CHANX || node_type(node) == CHANY, "Track number valid only for CHANX/CHANY RR nodes");
|
|
|
|
if ((size_t)node_length(node) + 1 != node_ptc_nums_[node].size()) {
|
|
node_ptc_nums_[node].resize((size_t)node_length(node) + 1);
|
|
}
|
|
|
|
size_t offset = node_offset.x() - node_xlow(node) + node_offset.y() - node_ylow(node);
|
|
VTR_ASSERT(offset < node_ptc_nums_[node].size());
|
|
|
|
node_ptc_nums_[node][offset] = track_id;
|
|
}
|
|
|
|
void RRGraph::set_node_cost_index(const RRNodeId& node, const short& cost_index) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
node_cost_indices_[node] = cost_index;
|
|
}
|
|
|
|
void RRGraph::set_node_direction(const RRNodeId& node, const e_direction& direction) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
VTR_ASSERT_MSG(node_type(node) == CHANX || node_type(node) == CHANY, "Direct can only be specified on CHANX/CNAY rr nodes");
|
|
|
|
node_directions_[node] = direction;
|
|
}
|
|
|
|
void RRGraph::set_node_side(const RRNodeId& node, const e_side& side) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
VTR_ASSERT_MSG(node_type(node) == IPIN || node_type(node) == OPIN, "Side can only be specified on IPIN/OPIN rr nodes");
|
|
|
|
node_sides_[node] = side;
|
|
}
|
|
|
|
void RRGraph::set_node_R(const RRNodeId& node, const float& R) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_Rs_[node] = R;
|
|
}
|
|
|
|
void RRGraph::set_node_C(const RRNodeId& node, const float& C) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_Cs_[node] = C;
|
|
}
|
|
|
|
void RRGraph::set_node_rc_data_index(const RRNodeId& node, const short& rc_data_index) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
node_rc_data_indices_[node] = rc_data_index;
|
|
}
|
|
|
|
/*
|
|
* Set a segment id for a node in rr_graph
|
|
*/
|
|
void RRGraph::set_node_segment(const RRNodeId& node, const RRSegmentId& segment_id) {
|
|
VTR_ASSERT(valid_node_id(node));
|
|
|
|
/* Only CHANX and CHANY requires a valid segment id */
|
|
if ((CHANX == node_type(node))
|
|
|| (CHANY == node_type(node))) {
|
|
VTR_ASSERT(valid_segment_id(segment_id));
|
|
}
|
|
|
|
node_segments_[node] = segment_id;
|
|
}
|
|
void RRGraph::rebuild_node_edges() {
|
|
node_edges_.resize(nodes().size());
|
|
node_num_in_edges_.resize(nodes().size(), 0);
|
|
node_num_out_edges_.resize(nodes().size(), 0);
|
|
node_num_non_configurable_in_edges_.resize(nodes().size(), 0);
|
|
node_num_non_configurable_out_edges_.resize(nodes().size(), 0);
|
|
|
|
//Count the number of edges of each type
|
|
for (RREdgeId edge : edges()) {
|
|
if (!edge) continue;
|
|
|
|
RRNodeId src_node = edge_src_node(edge);
|
|
RRNodeId sink_node = edge_sink_node(edge);
|
|
bool config = edge_is_configurable(edge);
|
|
|
|
++node_num_out_edges_[src_node];
|
|
++node_num_in_edges_[sink_node];
|
|
if (!config) {
|
|
++node_num_non_configurable_out_edges_[src_node];
|
|
++node_num_non_configurable_in_edges_[sink_node];
|
|
}
|
|
}
|
|
|
|
//Allocate precisely the correct space for each nodes edge list
|
|
for (RRNodeId node : nodes()) {
|
|
if (!node) continue;
|
|
|
|
node_edges_[node] = std::make_unique<RREdgeId[]>(node_num_in_edges_[node] + node_num_out_edges_[node]);
|
|
}
|
|
|
|
//Insert the edges into the node lists
|
|
{
|
|
vtr::vector<RRNodeId, int> inserted_edge_cnt(nodes().size(), 0);
|
|
for (RREdgeId edge : edges()) {
|
|
RRNodeId src_node = edge_src_node(edge);
|
|
RRNodeId sink_node = edge_sink_node(edge);
|
|
|
|
node_edges_[src_node][inserted_edge_cnt[src_node]++] = edge;
|
|
node_edges_[sink_node][inserted_edge_cnt[sink_node]++] = edge;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
//TODO: Sanity Check remove!
|
|
for (RRNodeId node : nodes()) {
|
|
for (size_t iedge = 0; iedge < node_num_in_edges_[node] + node_num_out_edges_[node]; ++iedge) {
|
|
RREdgeId edge = node_edges_[node][iedge];
|
|
VTR_ASSERT(edge_src_node(edge) == node || edge_sink_node(edge) == node);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//Partition each node's edge lists according to their type to line up with the counts
|
|
|
|
auto is_configurable_edge = [&](const RREdgeId edge) {
|
|
return edge_is_configurable(edge);
|
|
};
|
|
|
|
for (RRNodeId node : nodes()) {
|
|
if (!node) continue;
|
|
|
|
//We partition the nodes first by incoming/outgoing:
|
|
//
|
|
// +---------------------------+-----------------------------+
|
|
// | in | out |
|
|
// +---------------------------+-----------------------------+
|
|
//
|
|
//and then the two subsets by configurability. So the final ordering is:
|
|
//
|
|
// +-----------+---------------+------------+----------------+
|
|
// | in_config | in_non_config | out_config | out_non_config |
|
|
// +-----------+---------------+------------+----------------+
|
|
//
|
|
|
|
//Partition first into incoming/outgoing
|
|
auto is_incoming_edge = [&](const RREdgeId edge) {
|
|
return edge_sink_node(edge) == node;
|
|
};
|
|
/* Use stable_partition to keep the relative order,
|
|
* This is mainly for comparing the RRGraph write with rr_node writer
|
|
* so that it is easy to check consistency
|
|
*/
|
|
std::stable_partition(node_edges_[node].get(),
|
|
node_edges_[node].get() + node_num_in_edges_[node] + node_num_out_edges_[node],
|
|
is_incoming_edge);
|
|
|
|
//Partition incoming by configurable/non-configurable
|
|
std::stable_partition(node_edges_[node].get(),
|
|
node_edges_[node].get() + node_num_in_edges_[node],
|
|
is_configurable_edge);
|
|
|
|
//Partition outgoing by configurable/non-configurable
|
|
std::stable_partition(node_edges_[node].get() + node_num_in_edges_[node],
|
|
node_edges_[node].get() + node_num_in_edges_[node] + node_num_out_edges_[node],
|
|
is_configurable_edge);
|
|
|
|
#if 0
|
|
//TODO: Sanity check remove!
|
|
size_t nedges = node_num_in_edges_[node] + node_num_out_edges_[node];
|
|
for (size_t iedge = 0; iedge < nedges; ++iedge) {
|
|
RREdgeId edge = node_edges_[node][iedge];
|
|
if (iedge < node_num_in_edges_[node]) { //Incoming
|
|
VTR_ASSERT(edge_sink_node(edge) == node);
|
|
if (iedge < node_num_in_edges_[node] - node_num_non_configurable_in_edges_[node]) {
|
|
VTR_ASSERT(edge_is_configurable(edge));
|
|
} else {
|
|
VTR_ASSERT(!edge_is_configurable(edge));
|
|
}
|
|
} else { //Outgoing
|
|
VTR_ASSERT(edge_src_node(edge) == node);
|
|
if (iedge < node_num_in_edges_[node] + node_num_out_edges_[node] - node_num_non_configurable_out_edges_[node]) {
|
|
VTR_ASSERT(edge_is_configurable(edge));
|
|
} else {
|
|
VTR_ASSERT(!edge_is_configurable(edge));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void RRGraph::build_fast_node_lookup() const {
|
|
/* Free the current fast node look-up, we will rebuild a new one here */
|
|
invalidate_fast_node_lookup();
|
|
|
|
/* Get the max (x,y) and then we can resize the ndmatrix */
|
|
vtr::Point<short> max_coord(0, 0);
|
|
for (size_t id = 0; id < num_nodes_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_node_id(RRNodeId(id))) {
|
|
/* Skip this id */
|
|
continue;
|
|
}
|
|
max_coord.set_x(std::max(max_coord.x(), std::max(node_bounding_boxes_[RRNodeId(id)].xmax(), node_bounding_boxes_[RRNodeId(id)].xmin())));
|
|
max_coord.set_y(std::max(max_coord.y(), std::max(node_bounding_boxes_[RRNodeId(id)].ymax(), node_bounding_boxes_[RRNodeId(id)].ymin())));
|
|
}
|
|
node_lookup_.resize({(size_t)max_coord.x() + 1, (size_t)max_coord.y() + 1, NUM_RR_TYPES + 1});
|
|
|
|
for (size_t id = 0; id < num_nodes_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_node_id(RRNodeId(id))) {
|
|
/* Skip this id */
|
|
continue;
|
|
}
|
|
RRNodeId node = RRNodeId(id);
|
|
/* Special for CHANX and CHANY, we should annotate in the look-up
|
|
* for all the (x,y) upto (xhigh, yhigh)
|
|
*/
|
|
size_t x_start = std::min(node_xlow(node), node_xhigh(node));
|
|
size_t y_start = std::min(node_ylow(node), node_yhigh(node));
|
|
std::vector<size_t> node_x(std::abs(node_xlow(node) - node_xhigh(node)) + 1);
|
|
std::vector<size_t> node_y(std::abs(node_ylow(node) - node_yhigh(node)) + 1);
|
|
|
|
std::iota(node_x.begin(), node_x.end(), x_start);
|
|
std::iota(node_y.begin(), node_y.end(), y_start);
|
|
|
|
VTR_ASSERT(size_t(std::max(node_xlow(node), node_xhigh(node))) == node_x.back());
|
|
VTR_ASSERT(size_t(std::max(node_ylow(node), node_yhigh(node))) == node_y.back());
|
|
|
|
size_t itype = node_type(node);
|
|
|
|
for (const size_t& x : node_x) {
|
|
for (const size_t& y : node_y) {
|
|
size_t ptc = node_ptc_num(node);
|
|
/* Routing channel nodes may have different ptc num
|
|
* Find the track ids using the x/y offset
|
|
*/
|
|
if (CHANX == node_type(node)) {
|
|
ptc = node_track_ids(node)[x - node_xlow(node)];
|
|
} else if (CHANY == node_type(node)) {
|
|
ptc = node_track_ids(node)[y - node_ylow(node)];
|
|
}
|
|
|
|
if (ptc >= node_lookup_[x][y][itype].size()) {
|
|
node_lookup_[x][y][itype].resize(ptc + 1);
|
|
}
|
|
|
|
size_t iside = -1;
|
|
if (node_type(node) == OPIN || node_type(node) == IPIN) {
|
|
iside = node_side(node);
|
|
} else {
|
|
iside = NUM_SIDES;
|
|
}
|
|
|
|
if (iside >= node_lookup_[x][y][itype][ptc].size()) {
|
|
node_lookup_[x][y][itype][ptc].resize(iside + 1);
|
|
}
|
|
|
|
//Save node in lookup
|
|
node_lookup_[x][y][itype][ptc][iside] = node;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RRGraph::invalidate_fast_node_lookup() const {
|
|
node_lookup_.clear();
|
|
}
|
|
|
|
bool RRGraph::valid_fast_node_lookup() const {
|
|
return !node_lookup_.empty();
|
|
}
|
|
|
|
void RRGraph::initialize_fast_node_lookup() const {
|
|
if (!valid_fast_node_lookup()) {
|
|
build_fast_node_lookup();
|
|
}
|
|
}
|
|
|
|
bool RRGraph::valid_node_id(const RRNodeId& node) const {
|
|
return (size_t(node) < num_nodes_)
|
|
&& (!invalid_node_ids_.count(node));
|
|
}
|
|
|
|
bool RRGraph::valid_edge_id(const RREdgeId& edge) const {
|
|
return (size_t(edge) < num_edges_)
|
|
&& (!invalid_edge_ids_.count(edge));
|
|
}
|
|
|
|
/* check if a given switch id is valid or not */
|
|
bool RRGraph::valid_switch_id(const RRSwitchId& switch_id) const {
|
|
/* TODO: should we check the index of switch[id] matches ? */
|
|
return size_t(switch_id) < switches_.size();
|
|
}
|
|
|
|
/* check if a given segment id is valid or not */
|
|
bool RRGraph::valid_segment_id(const RRSegmentId& segment_id) const {
|
|
/* TODO: should we check the index of segment[id] matches ? */
|
|
return size_t(segment_id) < segments_.size();
|
|
}
|
|
|
|
/**
|
|
* Internal checking codes to ensure data consistency
|
|
* If you add any internal data to RRGraph and update create_node/edge etc.
|
|
* you need to update the validate_sizes() here to make sure these
|
|
* internal vectors are aligned to the id vectors
|
|
*/
|
|
bool RRGraph::validate_sizes() const {
|
|
return validate_node_sizes()
|
|
&& validate_edge_sizes()
|
|
&& validate_switch_sizes()
|
|
&& validate_segment_sizes();
|
|
}
|
|
|
|
bool RRGraph::validate_node_sizes() const {
|
|
return node_types_.size() == num_nodes_
|
|
&& node_bounding_boxes_.size() == num_nodes_
|
|
&& node_capacities_.size() == num_nodes_
|
|
&& node_ptc_nums_.size() == num_nodes_
|
|
&& node_cost_indices_.size() == num_nodes_
|
|
&& node_directions_.size() == num_nodes_
|
|
&& node_sides_.size() == num_nodes_
|
|
&& node_Rs_.size() == num_nodes_
|
|
&& node_Cs_.size() == num_nodes_
|
|
&& node_segments_.size() == num_nodes_
|
|
&& node_num_non_configurable_in_edges_.size() == num_nodes_
|
|
&& node_num_non_configurable_out_edges_.size() == num_nodes_
|
|
&& node_edges_.size() == num_nodes_;
|
|
}
|
|
|
|
bool RRGraph::validate_edge_sizes() const {
|
|
return edge_src_nodes_.size() == num_edges_
|
|
&& edge_sink_nodes_.size() == num_edges_
|
|
&& edge_switches_.size() == num_edges_;
|
|
}
|
|
|
|
bool RRGraph::validate_switch_sizes() const {
|
|
return switches_.size() == switch_ids_.size();
|
|
}
|
|
|
|
bool RRGraph::validate_segment_sizes() const {
|
|
return segments_.size() == segment_ids_.size();
|
|
}
|
|
|
|
void RRGraph::compress() {
|
|
vtr::vector<RRNodeId, RRNodeId> node_id_map(num_nodes_);
|
|
vtr::vector<RREdgeId, RREdgeId> edge_id_map(num_edges_);
|
|
|
|
build_id_maps(node_id_map, edge_id_map);
|
|
|
|
clean_nodes(node_id_map);
|
|
clean_edges(edge_id_map);
|
|
|
|
rebuild_node_refs(edge_id_map);
|
|
|
|
invalidate_fast_node_lookup();
|
|
|
|
/* Clear invalid node list */
|
|
invalid_node_ids_.clear();
|
|
|
|
/* Clear invalid edge list */
|
|
invalid_edge_ids_.clear();
|
|
|
|
clear_dirty();
|
|
}
|
|
|
|
void RRGraph::build_id_maps(vtr::vector<RRNodeId, RRNodeId>& node_id_map,
|
|
vtr::vector<RREdgeId, RREdgeId>& edge_id_map) {
|
|
/* Build node ids including invalid ids and compress */
|
|
vtr::vector<RRNodeId, RRNodeId> node_ids;
|
|
for (size_t id = 0; id < num_nodes_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_node_id(RRNodeId(id))) {
|
|
/* Give and invalid id */
|
|
node_ids.push_back(RRNodeId::INVALID());
|
|
continue;
|
|
}
|
|
/* Reach here, this is a valid id, push to the edge list */
|
|
node_ids.push_back(RRNodeId(id));
|
|
}
|
|
node_id_map = compress_ids(node_ids);
|
|
|
|
/* Build edge ids including invalid ids and compress */
|
|
vtr::vector<RREdgeId, RREdgeId> edge_ids;
|
|
for (size_t id = 0; id < num_edges_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_edge_id(RREdgeId(id))) {
|
|
/* Give and invalid id */
|
|
edge_ids.push_back(RREdgeId::INVALID());
|
|
continue;
|
|
}
|
|
/* Reach here, this is a valid id, push to the edge list */
|
|
edge_ids.push_back(RREdgeId(id));
|
|
}
|
|
edge_id_map = compress_ids(edge_ids);
|
|
}
|
|
|
|
void RRGraph::clean_nodes(const vtr::vector<RRNodeId, RRNodeId>& node_id_map) {
|
|
num_nodes_ = node_id_map.size();
|
|
|
|
node_types_ = clean_and_reorder_values(node_types_, node_id_map);
|
|
|
|
node_bounding_boxes_ = clean_and_reorder_values(node_bounding_boxes_, node_id_map);
|
|
|
|
node_capacities_ = clean_and_reorder_values(node_capacities_, node_id_map);
|
|
node_ptc_nums_ = clean_and_reorder_values(node_ptc_nums_, node_id_map);
|
|
node_cost_indices_ = clean_and_reorder_values(node_cost_indices_, node_id_map);
|
|
node_directions_ = clean_and_reorder_values(node_directions_, node_id_map);
|
|
node_sides_ = clean_and_reorder_values(node_sides_, node_id_map);
|
|
node_Rs_ = clean_and_reorder_values(node_Rs_, node_id_map);
|
|
node_Cs_ = clean_and_reorder_values(node_Cs_, node_id_map);
|
|
|
|
node_segments_ = clean_and_reorder_values(node_segments_, node_id_map);
|
|
node_num_non_configurable_in_edges_ = clean_and_reorder_values(node_num_non_configurable_in_edges_, node_id_map);
|
|
node_num_non_configurable_out_edges_ = clean_and_reorder_values(node_num_non_configurable_out_edges_, node_id_map);
|
|
node_num_in_edges_ = clean_and_reorder_values(node_num_in_edges_, node_id_map);
|
|
node_num_out_edges_ = clean_and_reorder_values(node_num_out_edges_, node_id_map);
|
|
//node_edges_ = clean_and_reorder_values(node_edges_, node_id_map);
|
|
|
|
VTR_ASSERT(validate_node_sizes());
|
|
}
|
|
|
|
void RRGraph::clean_edges(const vtr::vector<RREdgeId, RREdgeId>& edge_id_map) {
|
|
num_edges_ = edge_id_map.size();
|
|
|
|
edge_src_nodes_ = clean_and_reorder_values(edge_src_nodes_, edge_id_map);
|
|
edge_sink_nodes_ = clean_and_reorder_values(edge_sink_nodes_, edge_id_map);
|
|
edge_switches_ = clean_and_reorder_values(edge_switches_, edge_id_map);
|
|
|
|
VTR_ASSERT(validate_edge_sizes());
|
|
}
|
|
|
|
void RRGraph::rebuild_node_refs(const vtr::vector<RREdgeId, RREdgeId>& edge_id_map) {
|
|
for (size_t id = 0; id < num_nodes_; ++id) {
|
|
/* Try to find if this is an invalid id or not */
|
|
if (!valid_node_id(RRNodeId(id))) {
|
|
/* Skip this id */
|
|
continue;
|
|
}
|
|
RRNodeId node = RRNodeId(id);
|
|
|
|
auto begin = node_edges_[node].get();
|
|
auto end = begin + node_num_in_edges_[node] + node_num_out_edges_[node];
|
|
update_valid_refs(begin, end, edge_id_map);
|
|
|
|
VTR_ASSERT_MSG(all_valid(begin, end), "All Ids should be valid");
|
|
}
|
|
}
|
|
|
|
/* Empty all the vectors related to nodes */
|
|
void RRGraph::clear_nodes() {
|
|
num_nodes_ = 0;
|
|
node_types_.clear();
|
|
node_bounding_boxes_.clear();
|
|
|
|
node_capacities_.clear();
|
|
node_ptc_nums_.clear();
|
|
node_cost_indices_.clear();
|
|
node_directions_.clear();
|
|
node_sides_.clear();
|
|
node_Rs_.clear();
|
|
node_Cs_.clear();
|
|
node_rc_data_indices_.clear();
|
|
node_segments_.clear();
|
|
|
|
node_num_in_edges_.clear();
|
|
node_num_out_edges_.clear();
|
|
node_num_non_configurable_in_edges_.clear();
|
|
node_num_non_configurable_out_edges_.clear();
|
|
|
|
node_edges_.clear();
|
|
|
|
/* clean node_look_up */
|
|
node_lookup_.clear();
|
|
}
|
|
|
|
/* Empty all the vectors related to edges */
|
|
void RRGraph::clear_edges() {
|
|
num_edges_ = 0;
|
|
edge_src_nodes_.clear();
|
|
edge_sink_nodes_.clear();
|
|
edge_switches_.clear();
|
|
}
|
|
|
|
/* Empty all the vectors related to switches */
|
|
void RRGraph::clear_switches() {
|
|
switch_ids_.clear();
|
|
switches_.clear();
|
|
}
|
|
|
|
/* Empty all the vectors related to segments */
|
|
void RRGraph::clear_segments() {
|
|
segment_ids_.clear();
|
|
segments_.clear();
|
|
}
|
|
|
|
/* Clean the rr_graph */
|
|
void RRGraph::clear() {
|
|
clear_nodes();
|
|
clear_edges();
|
|
clear_switches();
|
|
clear_segments();
|
|
|
|
invalidate_fast_node_lookup();
|
|
|
|
/* Clear invalid node list */
|
|
invalid_node_ids_.clear();
|
|
|
|
/* Clear invalid edge list */
|
|
invalid_edge_ids_.clear();
|
|
|
|
clear_dirty();
|
|
}
|