Refactor regex handler in find_machxo2_global_position.
diff --git a/libtrellis/include/RoutingGraph.hpp b/libtrellis/include/RoutingGraph.hpp
index f1c7f1e..bacc6a1 100644
--- a/libtrellis/include/RoutingGraph.hpp
+++ b/libtrellis/include/RoutingGraph.hpp
@@ -6,6 +6,7 @@
#include <vector>
#include <string>
#include <set>
+#include <regex>
#include <boost/functional/hash.hpp>
#include "Chip.hpp"
@@ -166,10 +167,24 @@
// Algorithm to give global nets a unique position in MachXO2 devices.
// ECP5 defers global routing to nextpnr.
+ // Given the tile from where a net was queried, find the actual physical
+ // wire on chip that corresponds to the queried net rather than the
+ // imprecise location stored with the database.
RoutingId find_machxo2_global_position(int row, int col, const std::string &db_name);
// We need access to globals.json to correctly assign global positions.
- // Internal use only- do NOT expose to pytrellis for now.
+ // Internal use only- do NOT expose below methods, members, and types to
+ // pytrellis for now.
MachXO2GlobalsInfo global_data_machxo2;
+ enum GlobalType {
+ CENTER,
+ LEFT_RIGHT,
+ UP_DOWN,
+ BRANCH,
+ OTHER,
+ NONE
+ };
+ // Helper function to put all regexes in one place.
+ GlobalType get_global_type_from_name(const std::string &db_name, smatch &match);
};
}
diff --git a/libtrellis/src/RoutingGraph.cpp b/libtrellis/src/RoutingGraph.cpp
index a63abbf..0fe1753 100644
--- a/libtrellis/src/RoutingGraph.cpp
+++ b/libtrellis/src/RoutingGraph.cpp
@@ -276,6 +276,161 @@
}
RoutingId RoutingGraph::find_machxo2_global_position(int row, int col, const std::string &db_name) {
+ // Globals are given their nominal position, even if they span multiple
+ // tiles, by the following rules (determined by a combination of regexes
+ // on db_name and row/col):
+ smatch m;
+ pair<int, int> center = center_map[make_pair(max_row, max_col)];
+ RoutingId curr_global;
+
+ GlobalType strategy = get_global_type_from_name(db_name, m);
+
+ // All globals in the center tile get a nominal position of the center
+ // tile. We have to use regexes because a number of these connections
+ // in the center mux have config bits spread across multiple tiles
+ // (although few nets actually have routing bits which cross tiles).
+ //
+ // This handles L/R_HPSX as well. DCCs are handled a bit differently
+ // until we can determine they only truly exist in center tile (the row,
+ // col, and db_name will still be enough to distinguish them).
+ if(strategy == GlobalType::CENTER) {
+ // XXX: This is a MachXO2-1200HC-specific hack; VPRXCLKI0's routing
+ // config bits are split across multiple tiles. Arcs do not store data
+ // about their tile position in the routing graph (as that's the DB's
+ // responsibility). Even so, we do _not_ want the same physical arc
+ // that appears in two tiles in the DB appearing twice in the routing
+ // graph. So for now, special-case and remove the second arc at
+ // (12, 9).
+ //
+ // If more than one arc has this problem, we may need to change this
+ // function signature to be device-specific, and find a better way to
+ // store this info along with the routing graph besides special-casing.
+ if((db_name.find("G_VPRXCLKI0") != string::npos) && row == 9 && col == 12)
+ return RoutingId();
+
+ curr_global.id = ident(db_name);
+ curr_global.loc.x = center.second;
+ curr_global.loc.y = center.first;
+ return curr_global;
+
+ // If we found a global emanating from the CENTER MUX, return a L_/R_
+ // global net in the center tile based upon the current tile position
+ // (specifically column).
+ } else if(strategy == GlobalType::LEFT_RIGHT) {
+ assert(row == center.first);
+ // Prefixes only required in the center tile.
+ assert(db_name[0] == 'G');
+
+ std::string db_copy = db_name;
+
+ // Center column tiles connect to the left side of the center mux.
+ if(col <= center.second)
+ db_copy[0] = 'L';
+ else
+ db_copy[0] = 'R';
+
+ curr_global.id = ident(db_copy);
+ curr_global.loc.x = center.second;
+ curr_global.loc.y = center.first;
+ return curr_global;
+
+ // U/D wires get the nominal position of center row, current column.
+ // Both U_/D_ and G_ prefixes are handled here.
+ } else if(strategy == GlobalType::UP_DOWN) {
+ std::string db_copy = db_name;
+ std::vector<int> & ud_conns_in_col = global_data_machxo2.ud_conns[col];
+
+ // First check whether the requested global is in the current column.
+ // If not, no point in continuing.
+ if(std::find(ud_conns_in_col.begin(),
+ ud_conns_in_col.end(),
+ std::stoi(m.str(1))) == ud_conns_in_col.end()) {
+
+ return RoutingId();
+ }
+
+ // Special case the center row, which will have both U/D wires.
+ if(row == center.first) {
+ assert((db_name[0] == 'U') || (db_name[0] == 'D'));
+ } else {
+ // Otherwise choose an U_/D_ wire at nominal position based on
+ // the current tile's row.
+ // Prefixes only required in the center row.
+ assert(db_name[0] == 'G');
+
+ // Center column tiles are considered above the center mux,
+ // despite sharing the same tile.
+ if(row <= center.first)
+ db_copy[0] = 'U';
+ else
+ db_copy[0] = 'D';
+ }
+
+ curr_global.id = ident(db_copy);
+ curr_global.loc.x = col;
+ curr_global.loc.y = center.first;
+ return curr_global;
+
+ // BRANCH wires get nominal position of the row/col where they connect
+ // to U_/D_ routing. We need the global_data_machxo2 struct to figure
+ // out this information.
+ } else if(strategy == GlobalType::BRANCH) {
+ std::vector<int> candidate_cols;
+
+ if(col == 0) {
+ candidate_cols.push_back(0);
+ candidate_cols.push_back(1);
+ } else if(col == 1) {
+ candidate_cols.push_back(0);
+ candidate_cols.push_back(1);
+ candidate_cols.push_back(2);
+ } else if(col == max_col) {
+ candidate_cols.push_back(max_col - 2);
+ candidate_cols.push_back(max_col - 1);
+ candidate_cols.push_back(max_col);
+ } else {
+ candidate_cols.push_back(col - 2);
+ candidate_cols.push_back(col - 1);
+ candidate_cols.push_back(col);
+ candidate_cols.push_back(col + 1);
+ }
+
+ for(auto curr_col : candidate_cols) {
+ std::vector<int> & ud_conns_in_col = global_data_machxo2.ud_conns[curr_col];
+
+ // First check whether the requested global is in the current column.
+ // If not, no point in continuing.
+ if(std::find(ud_conns_in_col.begin(),
+ ud_conns_in_col.end(),
+ std::stoi(m.str(1))) != ud_conns_in_col.end()) {
+
+ curr_global.id = ident(db_name);
+ curr_global.loc.x = curr_col;
+ curr_global.loc.y = row;
+ break;
+ }
+ }
+
+ // One of the candidate columns should have had the correct U/D
+ // connection to route to.
+ assert(curr_global != RoutingId());
+ return curr_global;
+
+ // For OSCH, and DCCs assign nominal position of current requested tile.
+ // DCM only exist in center tile but have their routing spread out
+ // across tiles.
+ } else if(strategy == GlobalType::OTHER) {
+ curr_global.id = ident(db_name);
+ curr_global.loc.x = col;
+ curr_global.loc.y = row;
+ return curr_global;
+ } else {
+ // TODO: Not fuzzed and/or handled yet!
+ return RoutingId();
+ }
+}
+
+RoutingGraph::GlobalType RoutingGraph::get_global_type_from_name(const std::string &db_name, smatch &match) {
// A RoutingId uniquely describes a net on the chip- using a string
// identifier (id- converted to int), and a nominal position (loc).
// Two RoutingIds with the same id and loc represent the same net, so
@@ -283,7 +438,7 @@
// graph properly, given the current tile position and the global's
// identifier.
- // First copy regexs from utils.nets.machxo2, adjusting as necessary for
+ // First copy regexes from utils.nets.machxo2, adjusting as necessary for
// prefixes and regex syntax. Commented-out ones are not ready yet:
// Globals
static const std::regex global_entry(R"(G_VPRX(\d){2}00)", std::regex::optimize);
@@ -351,164 +506,25 @@
// static const std::regex dqsr90(R"(G_DQSR90)", std::regex::optimize);
// static const std::regex qsw90(R"(G_DQSW90)", std::regex::optimize);
- // Globals are given their nominal position, even if they span multiple
- // tiles, by the following rules:
- smatch m;
- pair<int, int> center = center_map[make_pair(max_row, max_col)];
- RoutingId curr_global;
-
- // All globals in the center tile get a nominal position of the center
- // tile. We have to use regexes because a number of these connections
- // in the center mux have config bits spread across multiple tiles
- // (although few nets actually have routing bits which cross tiles).
- //
- // This handles L/R_HPSX as well. DCCs are handled a bit differently
- // until we can determine they only truly exist in center tile (the row,
- // col, and db_name will still be enough to distinguish them).
- if(regex_match(db_name, m, global_entry) ||
- regex_match(db_name, m, global_left_right) ||
- regex_match(db_name, m, center_mux_glb_out) ||
- regex_match(db_name, m, cib_out_to_glb) ||
- regex_match(db_name, m, dcm_sig)) {
-
- // XXX: This is a MachXO2-1200HC-specific hack; VPRXCLKI0's routing
- // config bits are split across multiple tiles. Arcs do not store data
- // about their tile position in the routing graph (as that's the DB's
- // responsibility). Even so, we do _not_ want the same physical arc
- // that appears in two tiles in the DB appearing twice in the routing
- // graph. So for now, special-case and remove the second arc at
- // (12, 9).
- //
- // If more than one arc has this problem, we may need to change this
- // function signature to be device-specific, and find a better way to
- // store this info along with the routing graph besides special-casing.
- if((db_name.find("G_VPRXCLKI0") != string::npos) && row == 9 && col == 12)
- return RoutingId();
-
- curr_global.id = ident(db_name);
- curr_global.loc.x = center.second;
- curr_global.loc.y = center.first;
- return curr_global;
-
- // If we found a global emanating from the CENTER MUX, return a L_/R_
- // global net in the center tile based upon the current tile position
- // (specifically column).
- } else if(regex_match(db_name, m, global_left_right_g)) {
- assert(row == center.first);
- // Prefixes only required in the center tile.
- assert(db_name[0] == 'G');
-
- std::string db_copy = db_name;
-
- // Center column tiles connect to the left side of the center mux.
- if(col <= center.second)
- db_copy[0] = 'L';
- else
- db_copy[0] = 'R';
-
- curr_global.id = ident(db_copy);
- curr_global.loc.x = center.second;
- curr_global.loc.y = center.first;
-
- return curr_global;
-
- // U/D wires get the nominal position of center row, current column.
- // Both U_/D_ and G_ prefixes are handled here.
- } else if(regex_match(db_name, m, global_up_down) ||
- regex_match(db_name, m, global_up_down_g)) {
-
- std::vector<int> & ud_conns_in_col = global_data_machxo2.ud_conns[col];
-
- // First check whether the requested global is in the current column.
- // If not, no point in continuing.
- if(std::find(ud_conns_in_col.begin(), ud_conns_in_col.end(),
- std::stoi(m.str(1))) == ud_conns_in_col.end()) {
- return RoutingId();
- }
-
- // Special case the center row, which will have both U/D wires.
- if(row == center.first) {
- assert((db_name[0] == 'U') || (db_name[0] == 'D'));
-
- curr_global.id = ident(db_name);
- curr_global.loc.x = col;
- curr_global.loc.y = center.first;
- return curr_global;
- } else {
- // Otherwise choose an U_/D_ wire at nominal position based on
- // the current tile's row.
- // Prefixes only required in the center row.
- assert(db_name[0] == 'G');
-
- std::string db_copy = db_name;
-
- // Center column tiles are considered above the center mux,
- // despite sharing the same tile.
- if(row <= center.first)
- db_copy[0] = 'U';
- else
- db_copy[0] = 'D';
-
- curr_global.id = ident(db_copy);
- curr_global.loc.x = col;
- curr_global.loc.y = center.first;
- return curr_global;
- }
- // BRANCH wires get nominal position of the row/col where they connect
- // to U_/D_ routing. We need the global_data_machxo2 struct to figure
- // out this information.
- } else if(regex_match(db_name, m, global_branch)) {
- std::vector<int> candidate_cols;
-
- if(col == 0) {
- candidate_cols.push_back(0);
- candidate_cols.push_back(1);
- } else if(col == 1) {
- candidate_cols.push_back(0);
- candidate_cols.push_back(1);
- candidate_cols.push_back(2);
- } else if(col == max_col) {
- candidate_cols.push_back(max_col - 2);
- candidate_cols.push_back(max_col - 1);
- candidate_cols.push_back(max_col);
- } else {
- candidate_cols.push_back(col - 2);
- candidate_cols.push_back(col - 1);
- candidate_cols.push_back(col);
- candidate_cols.push_back(col + 1);
- }
-
- for(auto curr_col : candidate_cols) {
- std::vector<int> & ud_conns_in_col = global_data_machxo2.ud_conns[curr_col];
-
- // First check whether the requested global is in the current column.
- // If not, no point in continuing.
- if(std::find(ud_conns_in_col.begin(), ud_conns_in_col.end(),
- std::stoi(m.str(1))) != ud_conns_in_col.end()) {
- curr_global.id = ident(db_name);
- curr_global.loc.x = curr_col;
- curr_global.loc.y = row;
- return curr_global;
- }
- }
-
- // One of the candidate columns should have had the correct U/D
- // connection to route to.
- assert(false);
- return RoutingId();
-
- // For OSCH, and DCCs assign nominal position of current requested tile.
- // DCM only exist in center tile but have their routing spread out
- // across tiles.
- } else if(regex_match(db_name, m, dcc_sig) ||
- regex_match(db_name, m, osc_clk)) {
- curr_global.id = ident(db_name);
- curr_global.loc.x = col;
- curr_global.loc.y = row;
- return curr_global;
+ if(regex_match(db_name, match, global_entry) ||
+ regex_match(db_name, match, global_left_right) ||
+ regex_match(db_name, match, center_mux_glb_out) ||
+ regex_match(db_name, match, cib_out_to_glb) ||
+ regex_match(db_name, match, dcm_sig)) {
+ return GlobalType::CENTER;
+ } else if(regex_match(db_name, match, global_left_right_g)) {
+ return GlobalType::LEFT_RIGHT;
+ } else if(regex_match(db_name, match, global_up_down) ||
+ regex_match(db_name, match, global_up_down_g)) {
+ return GlobalType::UP_DOWN;
+ } else if(regex_match(db_name, match, global_branch)) {
+ return GlobalType::BRANCH;
+ } else if(regex_match(db_name, match, dcc_sig) ||
+ regex_match(db_name, match, osc_clk)) {
+ return GlobalType::OTHER;
} else {
// TODO: Not fuzzed and/or handled yet!
- return RoutingId();
+ return GlobalType::NONE;
}
}