| #include "rr_graph.h" | |
| #include "graph_utils.h" | |
| #include "vtr_util.h" | |
| #include "vpr_error.h" | |
| #include <algorithm> | |
| //Accessors | |
| RRGraph::node_range RRGraph::nodes() const { | |
| return vtr::make_range(node_ids_.begin(), node_ids_.end()); | |
| } | |
| RRGraph::edge_range RRGraph::edges() const { | |
| return vtr::make_range(edge_ids_.begin(), edge_ids_.end()); | |
| } | |
| //Node attributes | |
| t_rr_type RRGraph::node_type(RRNodeId node) const { | |
| VTR_ASSERT(valid_node_id(node)); | |
| return node_types_[node]; | |
| } | |
| short RRGraph::node_xlow(RRNodeId node) const { | |
| return node_bounding_box(node).xmin(); | |
| } | |
| short RRGraph::node_ylow(RRNodeId node) const { | |
| return node_bounding_box(node).ymin(); | |
| } | |
| short RRGraph::node_xhigh(RRNodeId node) const { | |
| return node_bounding_box(node).xmax(); | |
| } | |
| short RRGraph::node_yhigh(RRNodeId node) const { | |
| return node_bounding_box(node).ymax(); | |
| } | |
| short RRGraph::node_length(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(RRNodeId node) const { | |
| VTR_ASSERT(valid_node_id(node)); | |
| return node_bounding_boxes_[node]; | |
| } | |
| short RRGraph::node_fan_in(RRNodeId node) const { | |
| return node_in_edges(node).size(); | |
| } | |
| short RRGraph::node_fan_out(RRNodeId node) const { | |
| return node_out_edges(node).size(); | |
| } | |
| short RRGraph::node_capacity(RRNodeId node) const { | |
| VTR_ASSERT(valid_node_id(node)); | |
| return node_capacities_[node]; | |
| } | |
| short RRGraph::node_ptc_num(RRNodeId node) const { | |
| VTR_ASSERT(valid_node_id(node)); | |
| return node_ptc_nums_[node]; | |
| } | |
| short RRGraph::node_pin_num(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(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(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); | |
| } | |
| short RRGraph::node_cost_index(RRNodeId node) const { | |
| VTR_ASSERT(valid_node_id(node)); | |
| return node_cost_indices_[node]; | |
| } | |
| e_direction RRGraph::node_direction(RRNodeId node) const { | |
| VTR_ASSERT_MSG(node_type(node) == CHANX || node_type(node) == CHANY, "Direction valid only for SOURCE/SINK RR nodes"); | |
| return node_directions_[node]; | |
| } | |
| e_side RRGraph::node_side(RRNodeId node) const { | |
| VTR_ASSERT_MSG(node_type(node) == IPIN || node_type(node) == OPIN, "Direction valid only for IPIN/OPIN RR nodes"); | |
| return node_sides_[node]; | |
| } | |
| float RRGraph::node_R(RRNodeId node) const { | |
| VTR_ASSERT(valid_node_id(node)); | |
| return node_Rs_[node]; | |
| } | |
| float RRGraph::node_C(RRNodeId node) const { | |
| VTR_ASSERT(valid_node_id(node)); | |
| return node_Cs_[node]; | |
| } | |
| RRGraph::edge_range RRGraph::node_out_edges(RRNodeId node) const { | |
| VTR_ASSERT(valid_node_id(node)); | |
| return vtr::make_range(node_out_edges_[node].begin(), node_out_edges_[node].end()); | |
| } | |
| RRGraph::edge_range RRGraph::node_in_edges(RRNodeId node) const { | |
| VTR_ASSERT(valid_node_id(node)); | |
| return vtr::make_range(node_in_edges_[node].begin(), node_in_edges_[node].end()); | |
| } | |
| //Edge attributes | |
| RRNodeId RRGraph::edge_src_node(RREdgeId edge) const { | |
| VTR_ASSERT(valid_edge_id(edge)); | |
| return edge_src_nodes_[edge]; | |
| } | |
| RRNodeId RRGraph::edge_sink_node(RREdgeId edge) const { | |
| VTR_ASSERT(valid_edge_id(edge)); | |
| return edge_sink_nodes_[edge]; | |
| } | |
| RRSwitchId RRGraph::edge_switch(RREdgeId edge) const { | |
| VTR_ASSERT(valid_edge_id(edge)); | |
| return edge_switches_[edge]; | |
| } | |
| RREdgeId RRGraph::find_edge(RRNodeId src_node, RRNodeId sink_node) const { | |
| for (auto edge : node_out_edges(src_node)) { | |
| if (edge_sink_node(edge) == sink_node) { | |
| return edge; | |
| } | |
| } | |
| //Not found | |
| return RREdgeId::INVALID(); | |
| } | |
| //Mutators | |
| RRNodeId RRGraph::create_node(t_rr_type type) { | |
| //Allocate an ID | |
| RRNodeId node_id = RRNodeId(node_ids_.size()); | |
| //Initialize the attributes | |
| node_ids_.push_back(node_id); | |
| node_types_.push_back(type); | |
| node_bounding_boxes_.emplace_back(-1, -1, -1, -1); | |
| node_capacities_.push_back(-1); | |
| node_ptc_nums_.push_back(-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_in_edges_.emplace_back(); //Initially empty | |
| node_out_edges_.emplace_back(); //Initially empty | |
| VTR_ASSERT(validate_sizes()); | |
| return node_id; | |
| } | |
| RREdgeId RRGraph::create_edge(RRNodeId source, RRNodeId sink, RRSwitchId switch_id) { | |
| VTR_ASSERT(valid_node_id(source)); | |
| VTR_ASSERT(valid_node_id(sink)); | |
| //Allocate an ID | |
| RREdgeId edge_id = RREdgeId(edge_ids_.size()); | |
| //Initialize the attributes | |
| edge_ids_.push_back(edge_id); | |
| edge_src_nodes_.push_back(source); | |
| edge_sink_nodes_.push_back(sink); | |
| edge_switches_.push_back(switch_id); | |
| //Add the edge to the nodes | |
| node_out_edges_[source].push_back(edge_id); | |
| node_in_edges_[sink].push_back(edge_id); | |
| VTR_ASSERT(validate_sizes()); | |
| return edge_id; | |
| } | |
| RRSwitchId RRGraph::create_switch(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; | |
| } | |
| void RRGraph::remove_node(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 | |
| node_ids_[node] = RRNodeId::INVALID(); | |
| dirty_ = true; | |
| } | |
| void RRGraph::remove_edge(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_out_edges_[src_node].size(); ++i) { | |
| if (node_out_edges_[src_node][i] == edge) { | |
| node_out_edges_[src_node][i] = RREdgeId::INVALID(); | |
| break; | |
| } | |
| } | |
| for (size_t i = 0; i < node_in_edges_[sink_node].size(); ++i) { | |
| if (node_in_edges_[sink_node][i] == edge) { | |
| node_in_edges_[sink_node][i] = RREdgeId::INVALID(); | |
| break; | |
| } | |
| } | |
| //Mark edge invalid | |
| edge_ids_[edge] = RREdgeId::INVALID(); | |
| dirty_ = true; | |
| } | |
| void RRGraph::set_node_xlow(RRNodeId node, short xlow) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| auto& orig_bb = node_bounding_boxes_[node]; | |
| node_bounding_boxes_[node] = vtr::Rect<short>(xlow, orig_bb.ymin(), orig_bb.xmax(), orig_bb.ymax()); | |
| } | |
| void RRGraph::set_node_ylow(RRNodeId node, short ylow) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| auto& orig_bb = node_bounding_boxes_[node]; | |
| node_bounding_boxes_[node] = vtr::Rect<short>(orig_bb.xmin(), ylow, orig_bb.xmax(), orig_bb.ymax()); | |
| } | |
| void RRGraph::set_node_xhigh(RRNodeId node, short xhigh) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| auto& orig_bb = node_bounding_boxes_[node]; | |
| node_bounding_boxes_[node] = vtr::Rect<short>(orig_bb.xmin(), orig_bb.ymin(), xhigh, orig_bb.ymax()); | |
| } | |
| void RRGraph::set_node_yhigh(RRNodeId node, short yhigh) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| auto& orig_bb = node_bounding_boxes_[node]; | |
| node_bounding_boxes_[node] = vtr::Rect<short>(orig_bb.xmin(), orig_bb.ymin(), orig_bb.xmax(), yhigh); | |
| } | |
| void RRGraph::set_node_bounding_box(RRNodeId node, vtr::Rect<short> bb) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| node_bounding_boxes_[node] = bb; | |
| } | |
| void RRGraph::set_node_capacity(RRNodeId node, short capacity) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| node_capacities_[node] = capacity; | |
| } | |
| void RRGraph::set_node_ptc_num(RRNodeId node, short ptc) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| node_ptc_nums_[node] = ptc; | |
| } | |
| void RRGraph::set_node_pin_num(RRNodeId node, 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(RRNodeId node, 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(RRNodeId node, 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::set_node_cost_index(RRNodeId node, short cost_index) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| node_cost_indices_[node] = cost_index; | |
| } | |
| void RRGraph::set_node_direction(RRNodeId node, 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(RRNodeId node, 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(RRNodeId node, float R) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| node_Rs_[node] = R; | |
| } | |
| void RRGraph::set_node_C(RRNodeId node, float C) { | |
| VTR_ASSERT(valid_node_id(node)); | |
| node_Cs_[node] = C; | |
| } | |
| bool RRGraph::validate() { | |
| bool success = true; | |
| success &= validate_sizes(); | |
| success &= validate_crossrefs(); | |
| success &= validate_invariants(); | |
| return success; | |
| } | |
| bool RRGraph::valid_node_id(RRNodeId node) const { | |
| return size_t(node) < node_ids_.size() && node_ids_[node] == node; | |
| } | |
| bool RRGraph::valid_edge_id(RREdgeId edge) const { | |
| return size_t(edge) < edge_ids_.size() && edge_ids_[edge] == edge; | |
| } | |
| bool RRGraph::validate_sizes() const { | |
| return validate_node_sizes() && validate_edge_sizes(); | |
| } | |
| bool RRGraph::validate_node_sizes() const { | |
| return node_types_.size() == node_ids_.size() | |
| && node_bounding_boxes_.size() == node_ids_.size() | |
| && node_capacities_.size() == node_ids_.size() | |
| && node_ptc_nums_.size() == node_ids_.size() | |
| && node_cost_indices_.size() == node_ids_.size() | |
| && node_directions_.size() == node_ids_.size() | |
| && node_sides_.size() == node_ids_.size() | |
| && node_Rs_.size() == node_ids_.size() | |
| && node_Cs_.size() == node_ids_.size() | |
| && node_in_edges_.size() == node_ids_.size() | |
| && node_out_edges_.size() == node_ids_.size(); | |
| } | |
| bool RRGraph::validate_edge_sizes() const { | |
| return edge_src_nodes_.size() == edge_ids_.size() | |
| && edge_sink_nodes_.size() == edge_ids_.size() | |
| && edge_switches_.size() == edge_ids_.size(); | |
| } | |
| bool RRGraph::validate_crossrefs() const { | |
| bool success = true; | |
| success &= validate_node_edge_crossrefs(); | |
| return success; | |
| } | |
| bool RRGraph::validate_node_edge_crossrefs() const { | |
| //Check Forward (node to edge) | |
| for (auto node : nodes()) { | |
| //For each node edge check that the source/sink are consistent | |
| for (auto edge : node_in_edges(node)) { | |
| auto sink_node = edge_sink_node(edge); | |
| if (sink_node != node) { | |
| std::string msg = vtr::string_fmt("Mismatched node in-edge cross referrence: RR Edge %zu, RR Node != RR Edge Sink: (%zu != %zu)", size_t(edge), size_t(node), size_t(sink_node)); | |
| VPR_THROW(VPR_ERROR_RR_GRAPH, msg.c_str()); | |
| } | |
| } | |
| for (auto edge : node_out_edges(node)) { | |
| auto src_node = edge_src_node(edge); | |
| if (src_node != node) { | |
| std::string msg = vtr::string_fmt("Mismatched node out-edge cross referrence: RR Edge %zu, RR Node != RR Edge Source: (%zu != %zu)", size_t(edge), size_t(node), size_t(src_node)); | |
| VPR_THROW(VPR_ERROR_RR_GRAPH, msg.c_str()); | |
| } | |
| } | |
| } | |
| //Check Backward (edge to node) | |
| for (auto edge : edges()) { | |
| //For each edge, check that the edge exists in the appropriate node in/out edge list | |
| auto src_node = edge_src_node(edge); | |
| auto sink_node = edge_sink_node(edge); | |
| auto src_out_edges = node_out_edges(src_node); | |
| auto sink_in_edges = node_in_edges(sink_node); | |
| auto itr = std::find(src_out_edges.begin(), src_out_edges.end(), edge); | |
| if (itr == src_out_edges.end()) { //Not found | |
| std::string msg = vtr::string_fmt("Mismatched edge node cross reference:" | |
| " RR Edge %zu not found with associated source RR Node %zu", | |
| size_t(edge), size_t(src_node)); | |
| VPR_THROW(VPR_ERROR_RR_GRAPH, msg.c_str()); | |
| } | |
| itr = std::find(sink_in_edges.begin(), sink_in_edges.end(), edge); | |
| if (itr == sink_in_edges.end()) { //Not found | |
| std::string msg = vtr::string_fmt("Mismatched edge node cross reference:" | |
| " RR Edge %zu not found with associated sink RR Node %zu", | |
| size_t(edge), size_t(sink_node)); | |
| VPR_THROW(VPR_ERROR_RR_GRAPH, msg.c_str()); | |
| } | |
| } | |
| return true; | |
| } | |
| bool RRGraph::validate_invariants() const { | |
| bool success = true; | |
| success &= validate_unique_edges_invariant(); | |
| return success; | |
| } | |
| bool RRGraph::validate_unique_edges_invariant() const { | |
| //Check that there is only ever a single (unidirectional) edge between two nodes | |
| std::map<std::pair<RRNodeId,RRNodeId>, std::vector<RREdgeId>> edge_map; | |
| for (auto edge : edges()) { | |
| auto src_node = edge_src_node(edge); | |
| auto sink_node = edge_sink_node(edge); | |
| //Save the edge | |
| edge_map[std::make_pair(src_node, sink_node)].push_back(edge); | |
| } | |
| //Report any multiple edges | |
| for (auto kv : edge_map) { | |
| if (kv.second.size() > 1) { | |
| std::string msg = vtr::string_fmt("Multiple edges found from RR Node %zu -> %zu", | |
| size_t(kv.first.first), size_t(kv.first.second)); | |
| msg += " (Edges: "; | |
| auto mult_edges = kv.second; | |
| for (size_t i = 0; i < mult_edges.size(); ++i) { | |
| msg += std::to_string(size_t(mult_edges[i])); | |
| if (i != mult_edges.size() - 1) { | |
| msg += ", "; | |
| } | |
| } | |
| msg += ")"; | |
| VPR_THROW(VPR_ERROR_RR_GRAPH, msg.c_str()); | |
| } | |
| } | |
| return true; | |
| } | |
| void RRGraph::compress() { | |
| vtr::vector<RRNodeId,RRNodeId> node_id_map(node_ids_.size()); | |
| vtr::vector<RREdgeId,RREdgeId> edge_id_map(edge_ids_.size()); | |
| 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); | |
| dirty_ = false; | |
| } | |
| void RRGraph::build_id_maps(vtr::vector<RRNodeId,RRNodeId>& node_id_map, | |
| vtr::vector<RREdgeId,RREdgeId>& edge_id_map) { | |
| node_id_map = compress_ids(node_ids_); | |
| edge_id_map = compress_ids(edge_ids_); | |
| } | |
| void RRGraph::clean_nodes(const vtr::vector<RRNodeId,RRNodeId>& node_id_map) { | |
| node_ids_ = clean_and_reorder_ids(node_id_map); | |
| 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); | |
| VTR_ASSERT(validate_node_sizes()); | |
| VTR_ASSERT_MSG(are_contiguous(node_ids_), "Ids should be contiguous"); | |
| VTR_ASSERT_MSG(all_valid(node_ids_), "All Ids should be valid"); | |
| } | |
| void RRGraph::clean_edges(const vtr::vector<RREdgeId,RREdgeId>& edge_id_map) { | |
| edge_ids_ = clean_and_reorder_ids(edge_id_map); | |
| 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()); | |
| VTR_ASSERT_MSG(are_contiguous(edge_ids_), "Ids should be contiguous"); | |
| VTR_ASSERT_MSG(all_valid(edge_ids_), "All Ids should be valid"); | |
| } | |
| void RRGraph::rebuild_node_refs(const vtr::vector<RREdgeId,RREdgeId>& edge_id_map) { | |
| for (const auto& node : nodes()) { | |
| node_in_edges_[node] = update_valid_refs(node_in_edges_[node], edge_id_map); | |
| node_out_edges_[node] = update_valid_refs(node_out_edges_[node], edge_id_map); | |
| VTR_ASSERT_MSG(all_valid(node_in_edges_[node]), "All Ids should be valid"); | |
| VTR_ASSERT_MSG(all_valid(node_out_edges_[node]), "All Ids should be valid"); | |
| } | |
| } | |