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; } }