OpenFPGA/openfpga/src/repack/lb_rr_graph.h

349 lines
14 KiB
C++

/************************************************************************
* This file introduces a class to model a Routing Resource Graph (RRGraph or
*RRG) which is used by packer.
*
* Overview
* ========
* RRGraph aims to describe in a general way how routing resources are connected
* inside a pb_graph
*
* A Routing Resource Graph (RRGraph or RRG) is a directed graph (has many
*cycles), which consists of a number of nodes and edges.
*
* Node
* ----
* Each node represents a routing resource, which could be
* 1. an intermediate node(INTERMEDIATE_NODE), which are input/output pins of
*non-primitive and non-root pb_graph_nodes. It represents a pb_graph_pin that
*exists in a pb_graph
* 2. a virtual source (SOURCE), which are inputs of root pb_graph_nodes or
*outputs of primitive pb_graph_node
* 3. a sink node (SINK), which are outputs of root pb_graph_nodes or inputs or
*primitive pb_graph_node
*
* Edge
* ----
* Each edge represents a connection between two pb_graph_pins
* It represents a pb_graph_edge in a pb_graph
*
* Guidlines on using the LbRRGraph data structure
* =============================================
*
* For those want to access data from RRGraph
* ------------------------------------------
* Some examples for most frequent data query:
*
* // Strongly suggest to use a read-only lb_rr_graph object
* const LbRRGraph& lb_rr_graph;
*
* // Access type of a node with a given node id
* // Get the unique node id that you may get
* // from other data structures or functions
* LbRRNodeId node_id;
* e_lb_rr_type node_type = lb_rr_graph.node_type(node_id);
*
* // Access all the fan-out edges from a given node
* for (const RREdgeId& out_edge_id : rr_graph.node_out_edges(node_id)) {
* // Do something with out_edge
* }
* // If you only want to learn the number of fan-out edges
* size_t num_out_edges = rr_graph.node_fan_out(node_id);
*
* Please refer to the detailed comments on each public accessors
*
* For those want to build/modify a LbRRGraph
* -----------------------------------------
* Do NOT add a builder to this data structure!
* Builders should be kept as free functions that use the public mutators
* We suggest developers to create builders in separated C/C++ source files
* outside the rr_graph header and source files
*
* After build/modify a RRGraph, please do run a fundamental check, a public
*accessor. to ensure that your RRGraph does not include invalid
*nodes/edges/switches/segements as well as connections. The validate() function
*gurantees the consistency between internal data structures, such as the id
*cross-reference between nodes and edges etc., so failing it indicates a fatal
*bug! This is a must-do check!
*
* Example:
* RRGraph lb_rr_graph;
* ... // Building RRGraph
* lb_rr_graph.validate();
*
* Optionally, we strongly recommend developers to run an advance check in
*check_rr_graph() This guarantees legal and routable RRGraph for VPR routers.
*
* This checks for connectivity or other information in the RRGraph that is
*unexpected or unusual in an FPGA, and likely indicates a problem in your graph
*generation. However, if you are intentionally creating an RRGraph with this
*unusual, buts still technically legal, behaviour, you can write your own
*check_rr_graph() with weaker assumptions.
*
* Note: Do NOT modify the coordinate system for nodes, they are designed for
*downstream drawers and routers
*
* For those want to extend RRGraph data structure
* --------------------------------------------------------------------------
* Please avoid modifying any existing public/private accessors/mutators
* in order to keep a stable RRGraph object in the framework
* Developers may add more internal data to RRGraph as well as associate
*accessors/mutators Please update and comment on the added features properly to
*keep this data structure friendly to be extended.
*
* Try to keep your extension within only graph-related internal data to
*RRGraph. In other words, extension is necessary when the new node/edge
*attributes are needed. RRGraph should NOT include other data which are shared
*by other data structures outside. The rr-graph is the single largest data
*structure in VPR, so avoid adding unnecessary information per node or per edge
*to it, as it will impact memory footprint. Instead, using indices to point to
*the outside data source instead of embedding to RRGraph For example: For any
*placement/routing cost related information, try to extend t_rr_indexed_data,
*but not RRGraph For any placement/routing results, try to extend PlaceContext
*and RoutingContext, but not RRGraph
*
* For those want to develop placers or routers
* --------------------------------------------------------------------------
* The RRGraph is designed to be a read-only database/graph, once created.
* Placement and routing should NOT change any attributes of RRGraph.
* Any placement and routing results should be stored in other data structures,
*such as PlaceContext and RoutingContext.
*
* Tracing Cross-Reference
* =======================
* RRGraph is designed to a self-contained data structure as much as possible.
* It includes the switch information (rr_switch) and segment_information
*(rr_segment) which are necessary to build-up any external data structures.
*
* Internal cross-reference
* ------------------------
*
* +--------+ +--------+
* | | node_in_edges | |
* | | node_out_edges | |
* | Node |----------------->| Edge |
* | |<-----------------| |
* | | edge_src_node | |
* +--------+ edge_sink_node +--------+
*
*
* External cross-reference
* ------------------------
* The only cross-reference to outside data structures is the cost_index
* corresponding to the data structure t_rr_index_data
* Details can be found in the definition of t_rr_index_data
* This allows rapid look up by the router of additional information it needs
*for this node, using a flyweight pattern.
*
* +---------+ pb_graph_pin +----------------+
* | RRGraph |---------------->| Pb_graph_node |
* +---------+ pb_graph_edge +----------------+
*
***********************************************************************/
#ifndef LB_RR_GRAPH_OBJ_H
#define LB_RR_GRAPH_OBJ_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!
*/
/* Header files should be included in a sequence */
/* Standard header files required go first */
#include <limits>
#include <vector>
/* Header from vtrutil library */
#include "vtr_range.h"
#include "vtr_vector.h"
/* Header from readarch library */
#include "physical_types.h"
/* Header from vpr library */
#include "lb_rr_graph_fwd.h"
#include "pack_types.h"
/* begin namespace openfpga */
namespace openfpga {
class LbRRGraph {
public: /* Types */
/* Iterators used to create iterator-based loop for
* nodes/edges/switches/segments */
typedef vtr::vector<LbRRNodeId, LbRRNodeId>::const_iterator node_iterator;
typedef vtr::vector<LbRREdgeId, LbRREdgeId>::const_iterator edge_iterator;
/* Ranges used to create range-based loop for nodes/edges/switches/segments */
typedef vtr::Range<node_iterator> node_range;
typedef vtr::Range<edge_iterator> edge_range;
public: /* Constructors */
LbRRGraph();
public: /* Accessors */
/* Aggregates: create range-based loops for nodes/edges/switches/segments
* To iterate over the nodes/edges/switches/segments in a RRGraph,
* using a range-based loop is suggested.
* -----------------------------------------------------------------
* Example: iterate over all the nodes
* // Strongly suggest to use a read-only rr_graph object
* const LbRRGraph& lb_rr_graph;
* for (const LbRRNodeId& node : lb_rr_graph.nodes()) {
* // Do something with each node
* }
*
* for (const LbRREdgeId& edge : lb_rr_graph.edges()) {
* // Do something with each edge
* }
*
*/
node_range nodes() const;
edge_range edges() const;
/* Node-level attributes */
e_lb_rr_type node_type(const LbRRNodeId& node) const;
short node_capacity(const LbRRNodeId& node) const;
t_pb_graph_pin* node_pb_graph_pin(const LbRRNodeId& node) const;
float node_intrinsic_cost(const LbRRNodeId& node) const;
/* Get a list of edge ids, which are incoming edges to a node */
std::vector<LbRREdgeId> node_in_edges(const LbRRNodeId& node) const;
std::vector<LbRREdgeId> node_in_edges(const LbRRNodeId& node,
t_mode* mode) const;
/* Get a list of edge ids, which are outgoing edges from a node */
std::vector<LbRREdgeId> node_out_edges(const LbRRNodeId& node) const;
std::vector<LbRREdgeId> node_out_edges(const LbRRNodeId& node,
t_mode* mode) const;
/* General method to look up a node with type and only pb_graph_pin
* information */
LbRRNodeId find_node(const e_lb_rr_type& type,
const t_pb_graph_pin* pb_graph_pin) const;
/* Method to find special node */
LbRRNodeId ext_source_node() const;
LbRRNodeId ext_sink_node() const;
/* General method to look up a edge with source and sink nodes */
std::vector<LbRREdgeId> find_edge(const LbRRNodeId& src_node,
const LbRRNodeId& sink_node) const;
/* Get the source node which drives a edge */
LbRRNodeId edge_src_node(const LbRREdgeId& edge) const;
/* Get the sink node which a edge ends to */
LbRRNodeId edge_sink_node(const LbRREdgeId& edge) const;
float edge_intrinsic_cost(const LbRREdgeId& edge) const;
t_mode* edge_mode(const LbRREdgeId& edge) const;
public: /* Mutators */
/* Reserve the lists of nodes, edges, switches etc. to be memory efficient.
* This function is mainly used to reserve memory space inside RRGraph,
* when adding a large number of nodes/edge/switches/segments,
* in order to avoid memory fragements
* For example:
* LbRRGraph lb_rr_graph;
* // Add 1000 CHANX nodes to the LbRRGraph object
* rr_graph.reserve_nodes(1000);
* for (size_t i = 0; i < 1000; ++i) {
* rr_graph.create_node(CHANX);
* }
*/
void reserve_nodes(const unsigned long& num_nodes);
void reserve_edges(const unsigned long& num_edges);
/* Add new elements (node, edge, switch, etc.) to RRGraph */
/* Add a node to the RRGraph with a deposited type
* Detailed node-level information should be added using the set_node_*
* functions For example: RRNodeId node = create_node(); set_node_xlow(node,
* 0);
*/
LbRRNodeId create_node(const e_lb_rr_type& type);
/* Create special nodes */
LbRRNodeId create_ext_source_node(const e_lb_rr_type& type);
LbRRNodeId create_ext_sink_node(const e_lb_rr_type& type);
/* Set node-level information */
void set_node_type(const LbRRNodeId& node, const e_lb_rr_type& type);
void set_node_capacity(const LbRRNodeId& node, const short& capacity);
void set_node_pb_graph_pin(const LbRRNodeId& node,
t_pb_graph_pin* pb_graph_pin);
void set_node_intrinsic_cost(const LbRRNodeId& node, const float& cost);
/* Add a edge to the RRGraph, by providing the source and sink node
* This function will automatically create a node and
* configure the nodes and edges in connection
*/
LbRREdgeId create_edge(const LbRRNodeId& source, const LbRRNodeId& sink,
t_mode* mode);
void set_edge_intrinsic_cost(const LbRREdgeId& edge, const float& cost);
public: /* Public validators */
/* Validate is the node id does exist in the RRGraph */
bool valid_node_id(const LbRRNodeId& node) const;
/* Validate is the edge id does exist in the RRGraph */
bool valid_edge_id(const LbRREdgeId& edge) const;
bool validate() const;
bool empty() const;
private: /* Private Validators */
bool validate_node_sizes() const;
bool validate_edge_sizes() const;
bool validate_sizes() const;
bool validate_node_is_edge_src(const LbRRNodeId& node,
const LbRREdgeId& edge) const;
bool validate_node_is_edge_sink(const LbRRNodeId& node,
const LbRREdgeId& edge) const;
bool validate_node_in_edges(const LbRRNodeId& node) const;
bool validate_node_out_edges(const LbRRNodeId& node) const;
bool validate_nodes_in_edges() const;
bool validate_nodes_out_edges() const;
bool validate_nodes_edges() const;
private: /* Internal Data */
/* Node related data */
vtr::vector<LbRRNodeId, LbRRNodeId> node_ids_;
vtr::vector<LbRRNodeId, e_lb_rr_type> node_types_;
vtr::vector<LbRRNodeId, short> node_capacities_;
vtr::vector<LbRRNodeId, t_pb_graph_pin*> node_pb_graph_pins_;
vtr::vector<LbRRNodeId, float> node_intrinsic_costs_;
/* Edges per node is sorted by modes: [<mode_id>][<in_edges...><out_edges>] */
vtr::vector<LbRRNodeId, std::vector<LbRREdgeId>> node_in_edges_;
vtr::vector<LbRRNodeId, std::vector<LbRREdgeId>> node_out_edges_;
/* Edge related data */
/* Range of edge ids, use the unsigned long as
* the number of edges could be >10 times larger than the number of nodes!
*/
vtr::vector<LbRREdgeId, LbRREdgeId> edge_ids_;
vtr::vector<LbRREdgeId, LbRRNodeId> edge_src_nodes_;
vtr::vector<LbRREdgeId, LbRRNodeId> edge_sink_nodes_;
vtr::vector<LbRREdgeId, float> edge_intrinsic_costs_;
vtr::vector<LbRREdgeId, t_mode*> edge_modes_;
/* Fast look-up to search a node by its type, coordinator and ptc_num
* Indexing of fast look-up: [0..NUM_TYPES-1][t_pb_graph_pin*]
*/
typedef std::vector<std::map<const t_pb_graph_pin*, LbRRNodeId>> NodeLookup;
mutable NodeLookup node_lookup_;
/* Special node look-up */
LbRRNodeId ext_source_node_;
LbRRNodeId ext_sink_node_;
};
} /* end namespace openfpga */
#endif