From 965ee2190ecab44e0404da19cbe7600d7e526c40 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 17:42:26 -0700 Subject: [PATCH 01/16] [core] support intermediate driver in clock arch --- .../arch/example_internal_drivers.xml | 5 ++ .../src/base/clock_network.cpp | 53 +++++++++++++++ .../src/base/clock_network.h | 7 ++ .../src/io/clock_network_xml_constants.h | 6 ++ .../src/io/read_xml_clock_network.cpp | 65 ++++++++++++++++++- .../src/io/write_xml_clock_network.cpp | 37 +++++++++++ 6 files changed, 172 insertions(+), 1 deletion(-) diff --git a/libs/libclkarchopenfpga/arch/example_internal_drivers.xml b/libs/libclkarchopenfpga/arch/example_internal_drivers.xml index d6435523c..6332695a0 100644 --- a/libs/libclkarchopenfpga/arch/example_internal_drivers.xml +++ b/libs/libclkarchopenfpga/arch/example_internal_drivers.xml @@ -1,6 +1,11 @@ + + + + + diff --git a/libs/libclkarchopenfpga/src/base/clock_network.cpp b/libs/libclkarchopenfpga/src/base/clock_network.cpp index e989e88f4..abeb4154e 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.cpp +++ b/libs/libclkarchopenfpga/src/base/clock_network.cpp @@ -270,6 +270,18 @@ vtr::Point ClockNetwork::spine_end_point( return spine_end_points_[spine_id]; } +std::vector ClockNetwork::spine_intermediate_drivers( + const ClockSpineId& spine_id, const vtr::Point& coord) const { + VTR_ASSERT(valid_spine_id(spine_id)); + /* Convert coord to a unique string */ + std::string coord_str = std::to_string(coord.x()) + std::string(",") + std::to_string(coord.y()); + auto result = spine_intermediate_drivers_[spine_id].find(coord_str); + if (result == spine_intermediate_drivers_[spine_id].end()) { + return std::vector(); + } + return result->second; +} + ClockLevelId ClockNetwork::spine_level(const ClockSpineId& spine_id) const { VTR_ASSERT(valid_spine_id(spine_id)); if (is_dirty_) { @@ -624,6 +636,7 @@ void ClockNetwork::reserve_spines(const size_t& num_spines) { spine_switch_points_.reserve(num_spines); spine_switch_coords_.reserve(num_spines); spine_switch_internal_drivers_.reserve(num_spines); + spine_intermediate_drivers_.reserve(num_spines); spine_parents_.reserve(num_spines); spine_children_.reserve(num_spines); spine_parent_trees_.reserve(num_spines); @@ -716,6 +729,7 @@ ClockSpineId ClockNetwork::create_spine(const std::string& name) { spine_switch_points_.emplace_back(); spine_switch_coords_.emplace_back(); spine_switch_internal_drivers_.emplace_back(); + spine_intermediate_drivers_.emplace_back(); spine_parents_.emplace_back(); spine_children_.emplace_back(); spine_parent_trees_.emplace_back(); @@ -817,6 +831,45 @@ ClockInternalDriverId ClockNetwork::add_spine_switch_point_internal_driver( return int_driver_id; } +ClockInternalDriverId ClockNetwork::add_spine_intermediate_driver( + const ClockSpineId& spine_id, const vtr::Point& coord, + const std::string& int_driver_from_port, + const std::string& int_driver_to_port) { + VTR_ASSERT(valid_spine_id(spine_id)); + /* Convert coord to a unique string */ + std::string coord_str = std::to_string(coord.x()) + std::string(",") + std::to_string(coord.y()); + /* Parse ports */ + PortParser to_pin_parser(int_driver_to_port); + /* Find any existing id for the driver port */ + ClockInternalDriverId int_driver_id_to_add = ClockInternalDriverId(internal_driver_ids_.size()); + for (ClockInternalDriverId int_driver_id : internal_driver_ids_) { + if (internal_driver_from_pins_[int_driver_id] == int_driver_from_port && + internal_driver_to_pins_[int_driver_id] == to_pin_parser.port()) { + int_driver_id_to_add = int_driver_id; + break; + } + } + /* Reaching here, no existing id can be reused, create a new one */ + if (int_driver_id_to_add == ClockInternalDriverId(internal_driver_ids_.size())) { + internal_driver_ids_.push_back(int_driver_id_to_add); + internal_driver_from_pins_.push_back(int_driver_from_port); + internal_driver_to_pins_.push_back(to_pin_parser.port()); + } + /* Add it to existing map, avoid duplicated id */ + auto result = spine_intermediate_drivers_[spine_id].find(coord_str); + if (result == spine_intermediate_drivers_[spine_id].end()) { + spine_intermediate_drivers_[spine_id][coord_str].push_back(int_driver_id_to_add); + } else { + if (std::find(result->second.begin(), result->second.end(), int_driver_id_to_add) == result->second.end()) { + result->second.push_back(int_driver_id_to_add); + } else { + VTR_LOG_WARN("Skip intermediate driver (from_port='%s', to_port='%s') at (%s) as it is duplicated in the clock architecture description file!\n", + int_driver_from_port.c_str(), int_driver_to_port.c_str(), coord_str.c_str()); + } + } + return int_driver_id_to_add; +} + ClockTapId ClockNetwork::add_tree_tap(const ClockTreeId& tree_id, const BasicPort& from_port, const std::string& to_port) { diff --git a/libs/libclkarchopenfpga/src/base/clock_network.h b/libs/libclkarchopenfpga/src/base/clock_network.h index 2f1a09592..6e6194cef 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.h +++ b/libs/libclkarchopenfpga/src/base/clock_network.h @@ -97,6 +97,8 @@ class ClockNetwork { std::string spine_name(const ClockSpineId& spine_id) const; vtr::Point spine_start_point(const ClockSpineId& spine_id) const; vtr::Point spine_end_point(const ClockSpineId& spine_id) const; + std::vector spine_intermediate_drivers(const ClockSpineId& spine_id, const vtr::Point& coord) const; + /* Return the level where the spine locates in the multi-layer clock tree * structure */ ClockLevelId spine_level(const ClockSpineId& spine_id) const; @@ -227,6 +229,10 @@ class ClockNetwork { const ClockSpineId& spine_id, const ClockSwitchPointId& switch_point_id, const std::string& internal_driver_from_port, const std::string& internal_driver_to_port); + ClockInternalDriverId add_spine_intermediate_driver( + const ClockSpineId& spine_id, const vtr::Point& coord, + const std::string& internal_driver_from_port, + const std::string& internal_driver_to_port); ClockTapId add_tree_tap(const ClockTreeId& tree_id, const BasicPort& from_port, const std::string& to_port); @@ -314,6 +320,7 @@ class ClockNetwork { vtr::vector>> spine_switch_coords_; vtr::vector>> spine_switch_internal_drivers_; + vtr::vector>> spine_intermediate_drivers_; vtr::vector spine_parents_; vtr::vector> spine_children_; vtr::vector spine_parent_trees_; diff --git a/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h b/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h index 65e828573..8dc4aac92 100644 --- a/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h +++ b/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h @@ -21,6 +21,12 @@ constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_END_X = "end_x"; constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_END_Y = "end_y"; constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_TYPE = "type"; constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_DIRECTION = "direction"; +constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME = "intermediate_driver"; +constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_TAP_NODE_NAME = "tap"; +constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_X = "x"; +constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_Y = "y"; +constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_FROM_PIN = "from_pin"; +constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_TO_PIN = "to_pin"; constexpr const char* XML_CLOCK_SPINE_SWITCH_POINT_NODE_NAME = "switch_point"; constexpr const char* XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_NODE_NAME = "internal_driver"; diff --git a/libs/libclkarchopenfpga/src/io/read_xml_clock_network.cpp b/libs/libclkarchopenfpga/src/io/read_xml_clock_network.cpp index 46f74641d..32195274a 100644 --- a/libs/libclkarchopenfpga/src/io/read_xml_clock_network.cpp +++ b/libs/libclkarchopenfpga/src/io/read_xml_clock_network.cpp @@ -178,6 +178,32 @@ static void read_xml_clock_spine_switch_point_internal_driver( int_driver_to_port_name); } +/******************************************************************** + * Parse XML codes of a to an object of ClockNetwork + *******************************************************************/ +static void read_xml_clock_spine_intermediate_driver_tap( + pugi::xml_node& xml_int_driver, const pugiutil::loc_data& loc_data, + ClockNetwork& clk_ntwk, const ClockSpineId& spine_id, + const vtr::Point& spine_coord) { + if (!clk_ntwk.valid_spine_id(spine_id)) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_int_driver), + "Invalid id of a clock spine!\n"); + } + + std::string int_driver_from_port_name = + get_attribute( + xml_int_driver, + XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_FROM_PIN, loc_data) + .as_string(); + std::string int_driver_to_port_name = + get_attribute(xml_int_driver, + XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_TO_PIN, + loc_data) + .as_string(); + clk_ntwk.add_spine_intermediate_driver(spine_id, spine_coord, int_driver_from_port_name, + int_driver_to_port_name); +} + /******************************************************************** * Parse XML codes of a to an object of ClockNetwork *******************************************************************/ @@ -229,6 +255,38 @@ static void read_xml_clock_spine_switch_point( } } +/******************************************************************** + * Parse XML codes of a to an object of ClockNetwork + *******************************************************************/ +static void read_xml_clock_spine_intermediate_driver( + pugi::xml_node& xml_driver, const pugiutil::loc_data& loc_data, + ClockNetwork& clk_ntwk, const ClockSpineId& spine_id) { + if (!clk_ntwk.valid_spine_id(spine_id)) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_driver), + "Invalid id of a clock spine!\n"); + } + + int tap_x = get_attribute(xml_driver, + XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_X, loc_data) + .as_int(); + int tap_y = get_attribute(xml_driver, + XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_Y, loc_data) + .as_int(); + + /* Add internal drivers if possible */ + for (pugi::xml_node xml_int_driver : xml_driver.children()) { + /* Error out if the XML child has an invalid name! */ + if (xml_int_driver.name() == + std::string(XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_TAP_NODE_NAME)) { + read_xml_clock_spine_intermediate_driver_tap( + xml_int_driver, loc_data, clk_ntwk, spine_id, vtr::Point(tap_x, tap_y)); + } else { + bad_tag(xml_int_driver, loc_data, xml_driver, + {XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_TAP_NODE_NAME}); + } + } +} + /******************************************************************** * Convert string to the enumerate of model type *******************************************************************/ @@ -333,9 +391,14 @@ static void read_xml_clock_spine(pugi::xml_node& xml_spine, std::string(XML_CLOCK_SPINE_SWITCH_POINT_NODE_NAME)) { read_xml_clock_spine_switch_point(xml_switch_point, loc_data, clk_ntwk, spine_id); + } else if (xml_switch_point.name() == + std::string(XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME)) { + read_xml_clock_spine_intermediate_driver(xml_switch_point, loc_data, clk_ntwk, + spine_id); + } else { bad_tag(xml_switch_point, loc_data, xml_spine, - {XML_CLOCK_SPINE_SWITCH_POINT_NODE_NAME}); + {XML_CLOCK_SPINE_SWITCH_POINT_NODE_NAME, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME}); } } } diff --git a/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp b/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp index 1d9937141..dfcdaa41d 100644 --- a/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp +++ b/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp @@ -142,6 +142,39 @@ static int write_xml_clock_spine_switch_point( return 0; } +static int write_xml_clock_spine_intermediate_drivers( + std::fstream& fp, const ClockNetwork& clk_ntwk, const ClockSpineId& spine_id, + const vtr::Point& coord) { + std::vector int_drivers = clk_ntwk.spine_intermediate_drivers(spine_id, coord); + if (int_drivers.empty()) { + return 0; + } + openfpga::write_tab_to_file(fp, 3); + fp << "<" << XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME << ""; + + write_xml_attribute(fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_X, coord.x()); + write_xml_attribute(fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_Y, coord.y()); + + for (ClockInternalDriverId int_driver_id : int_drivers) { + openfpga::write_tab_to_file(fp, 4); + fp << "<" << XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_TAP_NODE_NAME; + write_xml_attribute( + fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_FROM_PIN, + clk_ntwk.internal_driver_from_pin(int_driver_id).c_str()); + write_xml_attribute( + fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_TO_PIN, + clk_ntwk.internal_driver_to_pin(int_driver_id) + .to_verilog_string() + .c_str()); + fp << "/>" + << "\n"; + } + fp << "\n"; + + return 0; +} + + static int write_xml_clock_spine(std::fstream& fp, const ClockNetwork& clk_ntwk, const ClockSpineId& spine_id) { openfpga::write_tab_to_file(fp, 2); @@ -166,6 +199,10 @@ static int write_xml_clock_spine(std::fstream& fp, const ClockNetwork& clk_ntwk, fp << ">" << "\n"; + for (const vtr::Point& coord : clk_ntwk.spine_coordinates(spine_id)) { + write_xml_clock_spine_intermediate_drivers(fp, clk_ntwk, spine_id, coord); + } + for (const ClockSwitchPointId& switch_point_id : clk_ntwk.spine_switch_points(spine_id)) { write_xml_clock_spine_switch_point(fp, clk_ntwk, spine_id, switch_point_id); From 1332d426c7f3d50875d690a551e703ba7dcabed6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 17:42:53 -0700 Subject: [PATCH 02/16] [core] code format --- .../src/base/clock_network.cpp | 25 ++++++++----- .../src/base/clock_network.h | 9 +++-- .../src/io/clock_network_xml_constants.h | 9 +++-- .../src/io/read_xml_clock_network.cpp | 36 ++++++++++--------- .../src/io/write_xml_clock_network.cpp | 20 ++++++----- 5 files changed, 60 insertions(+), 39 deletions(-) diff --git a/libs/libclkarchopenfpga/src/base/clock_network.cpp b/libs/libclkarchopenfpga/src/base/clock_network.cpp index abeb4154e..d70ed59a8 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.cpp +++ b/libs/libclkarchopenfpga/src/base/clock_network.cpp @@ -274,7 +274,8 @@ std::vector ClockNetwork::spine_intermediate_drivers( const ClockSpineId& spine_id, const vtr::Point& coord) const { VTR_ASSERT(valid_spine_id(spine_id)); /* Convert coord to a unique string */ - std::string coord_str = std::to_string(coord.x()) + std::string(",") + std::to_string(coord.y()); + std::string coord_str = + std::to_string(coord.x()) + std::string(",") + std::to_string(coord.y()); auto result = spine_intermediate_drivers_[spine_id].find(coord_str); if (result == spine_intermediate_drivers_[spine_id].end()) { return std::vector(); @@ -837,11 +838,13 @@ ClockInternalDriverId ClockNetwork::add_spine_intermediate_driver( const std::string& int_driver_to_port) { VTR_ASSERT(valid_spine_id(spine_id)); /* Convert coord to a unique string */ - std::string coord_str = std::to_string(coord.x()) + std::string(",") + std::to_string(coord.y()); + std::string coord_str = + std::to_string(coord.x()) + std::string(",") + std::to_string(coord.y()); /* Parse ports */ PortParser to_pin_parser(int_driver_to_port); /* Find any existing id for the driver port */ - ClockInternalDriverId int_driver_id_to_add = ClockInternalDriverId(internal_driver_ids_.size()); + ClockInternalDriverId int_driver_id_to_add = + ClockInternalDriverId(internal_driver_ids_.size()); for (ClockInternalDriverId int_driver_id : internal_driver_ids_) { if (internal_driver_from_pins_[int_driver_id] == int_driver_from_port && internal_driver_to_pins_[int_driver_id] == to_pin_parser.port()) { @@ -850,7 +853,8 @@ ClockInternalDriverId ClockNetwork::add_spine_intermediate_driver( } } /* Reaching here, no existing id can be reused, create a new one */ - if (int_driver_id_to_add == ClockInternalDriverId(internal_driver_ids_.size())) { + if (int_driver_id_to_add == + ClockInternalDriverId(internal_driver_ids_.size())) { internal_driver_ids_.push_back(int_driver_id_to_add); internal_driver_from_pins_.push_back(int_driver_from_port); internal_driver_to_pins_.push_back(to_pin_parser.port()); @@ -858,13 +862,18 @@ ClockInternalDriverId ClockNetwork::add_spine_intermediate_driver( /* Add it to existing map, avoid duplicated id */ auto result = spine_intermediate_drivers_[spine_id].find(coord_str); if (result == spine_intermediate_drivers_[spine_id].end()) { - spine_intermediate_drivers_[spine_id][coord_str].push_back(int_driver_id_to_add); + spine_intermediate_drivers_[spine_id][coord_str].push_back( + int_driver_id_to_add); } else { - if (std::find(result->second.begin(), result->second.end(), int_driver_id_to_add) == result->second.end()) { + if (std::find(result->second.begin(), result->second.end(), + int_driver_id_to_add) == result->second.end()) { result->second.push_back(int_driver_id_to_add); } else { - VTR_LOG_WARN("Skip intermediate driver (from_port='%s', to_port='%s') at (%s) as it is duplicated in the clock architecture description file!\n", - int_driver_from_port.c_str(), int_driver_to_port.c_str(), coord_str.c_str()); + VTR_LOG_WARN( + "Skip intermediate driver (from_port='%s', to_port='%s') at (%s) as it " + "is duplicated in the clock architecture description file!\n", + int_driver_from_port.c_str(), int_driver_to_port.c_str(), + coord_str.c_str()); } } return int_driver_id_to_add; diff --git a/libs/libclkarchopenfpga/src/base/clock_network.h b/libs/libclkarchopenfpga/src/base/clock_network.h index 6e6194cef..1ee5dabcc 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.h +++ b/libs/libclkarchopenfpga/src/base/clock_network.h @@ -97,8 +97,9 @@ class ClockNetwork { std::string spine_name(const ClockSpineId& spine_id) const; vtr::Point spine_start_point(const ClockSpineId& spine_id) const; vtr::Point spine_end_point(const ClockSpineId& spine_id) const; - std::vector spine_intermediate_drivers(const ClockSpineId& spine_id, const vtr::Point& coord) const; - + std::vector spine_intermediate_drivers( + const ClockSpineId& spine_id, const vtr::Point& coord) const; + /* Return the level where the spine locates in the multi-layer clock tree * structure */ ClockLevelId spine_level(const ClockSpineId& spine_id) const; @@ -320,7 +321,9 @@ class ClockNetwork { vtr::vector>> spine_switch_coords_; vtr::vector>> spine_switch_internal_drivers_; - vtr::vector>> spine_intermediate_drivers_; + vtr::vector>> + spine_intermediate_drivers_; vtr::vector spine_parents_; vtr::vector> spine_children_; vtr::vector spine_parent_trees_; diff --git a/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h b/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h index 8dc4aac92..2a1838c07 100644 --- a/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h +++ b/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h @@ -21,12 +21,15 @@ constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_END_X = "end_x"; constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_END_Y = "end_y"; constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_TYPE = "type"; constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_DIRECTION = "direction"; -constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME = "intermediate_driver"; +constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME = + "intermediate_driver"; constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_TAP_NODE_NAME = "tap"; constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_X = "x"; constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_Y = "y"; -constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_FROM_PIN = "from_pin"; -constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_TO_PIN = "to_pin"; +constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_FROM_PIN = + "from_pin"; +constexpr const char* XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_TO_PIN = + "to_pin"; constexpr const char* XML_CLOCK_SPINE_SWITCH_POINT_NODE_NAME = "switch_point"; constexpr const char* XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_NODE_NAME = "internal_driver"; diff --git a/libs/libclkarchopenfpga/src/io/read_xml_clock_network.cpp b/libs/libclkarchopenfpga/src/io/read_xml_clock_network.cpp index 32195274a..418e43611 100644 --- a/libs/libclkarchopenfpga/src/io/read_xml_clock_network.cpp +++ b/libs/libclkarchopenfpga/src/io/read_xml_clock_network.cpp @@ -191,17 +191,17 @@ static void read_xml_clock_spine_intermediate_driver_tap( } std::string int_driver_from_port_name = - get_attribute( - xml_int_driver, - XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_FROM_PIN, loc_data) + get_attribute(xml_int_driver, + XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_FROM_PIN, + loc_data) .as_string(); std::string int_driver_to_port_name = get_attribute(xml_int_driver, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_TO_PIN, loc_data) .as_string(); - clk_ntwk.add_spine_intermediate_driver(spine_id, spine_coord, int_driver_from_port_name, - int_driver_to_port_name); + clk_ntwk.add_spine_intermediate_driver( + spine_id, spine_coord, int_driver_from_port_name, int_driver_to_port_name); } /******************************************************************** @@ -266,12 +266,14 @@ static void read_xml_clock_spine_intermediate_driver( "Invalid id of a clock spine!\n"); } - int tap_x = get_attribute(xml_driver, - XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_X, loc_data) - .as_int(); - int tap_y = get_attribute(xml_driver, - XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_Y, loc_data) - .as_int(); + int tap_x = + get_attribute(xml_driver, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_X, + loc_data) + .as_int(); + int tap_y = + get_attribute(xml_driver, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_Y, + loc_data) + .as_int(); /* Add internal drivers if possible */ for (pugi::xml_node xml_int_driver : xml_driver.children()) { @@ -279,7 +281,8 @@ static void read_xml_clock_spine_intermediate_driver( if (xml_int_driver.name() == std::string(XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_TAP_NODE_NAME)) { read_xml_clock_spine_intermediate_driver_tap( - xml_int_driver, loc_data, clk_ntwk, spine_id, vtr::Point(tap_x, tap_y)); + xml_int_driver, loc_data, clk_ntwk, spine_id, + vtr::Point(tap_x, tap_y)); } else { bad_tag(xml_int_driver, loc_data, xml_driver, {XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_TAP_NODE_NAME}); @@ -392,13 +395,14 @@ static void read_xml_clock_spine(pugi::xml_node& xml_spine, read_xml_clock_spine_switch_point(xml_switch_point, loc_data, clk_ntwk, spine_id); } else if (xml_switch_point.name() == - std::string(XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME)) { - read_xml_clock_spine_intermediate_driver(xml_switch_point, loc_data, clk_ntwk, - spine_id); + std::string(XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME)) { + read_xml_clock_spine_intermediate_driver(xml_switch_point, loc_data, + clk_ntwk, spine_id); } else { bad_tag(xml_switch_point, loc_data, xml_spine, - {XML_CLOCK_SPINE_SWITCH_POINT_NODE_NAME, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME}); + {XML_CLOCK_SPINE_SWITCH_POINT_NODE_NAME, + XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME}); } } } diff --git a/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp b/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp index dfcdaa41d..23f54addf 100644 --- a/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp +++ b/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp @@ -145,15 +145,18 @@ static int write_xml_clock_spine_switch_point( static int write_xml_clock_spine_intermediate_drivers( std::fstream& fp, const ClockNetwork& clk_ntwk, const ClockSpineId& spine_id, const vtr::Point& coord) { - std::vector int_drivers = clk_ntwk.spine_intermediate_drivers(spine_id, coord); + std::vector int_drivers = + clk_ntwk.spine_intermediate_drivers(spine_id, coord); if (int_drivers.empty()) { return 0; } openfpga::write_tab_to_file(fp, 3); fp << "<" << XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_NODE_NAME << ""; - write_xml_attribute(fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_X, coord.x()); - write_xml_attribute(fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_Y, coord.y()); + write_xml_attribute(fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_X, + coord.x()); + write_xml_attribute(fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_Y, + coord.y()); for (ClockInternalDriverId int_driver_id : int_drivers) { openfpga::write_tab_to_file(fp, 4); @@ -161,11 +164,11 @@ static int write_xml_clock_spine_intermediate_drivers( write_xml_attribute( fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_FROM_PIN, clk_ntwk.internal_driver_from_pin(int_driver_id).c_str()); - write_xml_attribute( - fp, XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_TO_PIN, - clk_ntwk.internal_driver_to_pin(int_driver_id) - .to_verilog_string() - .c_str()); + write_xml_attribute(fp, + XML_CLOCK_SPINE_INTERMEDIATE_DRIVER_ATTRIBUTE_TO_PIN, + clk_ntwk.internal_driver_to_pin(int_driver_id) + .to_verilog_string() + .c_str()); fp << "/>" << "\n"; } @@ -174,7 +177,6 @@ static int write_xml_clock_spine_intermediate_drivers( return 0; } - static int write_xml_clock_spine(std::fstream& fp, const ClockNetwork& clk_ntwk, const ClockSpineId& spine_id) { openfpga::write_tab_to_file(fp, 2); From e8957b6fd8a8d5f3fcc21b6b615d59e613a587e4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 19:07:16 -0700 Subject: [PATCH 03/16] [core] enable clock intermediate driver in rrgraph buildup --- .../src/annotation/append_clock_rr_graph.cpp | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/openfpga/src/annotation/append_clock_rr_graph.cpp b/openfpga/src/annotation/append_clock_rr_graph.cpp index 56862ac91..cc9dbe60d 100644 --- a/openfpga/src/annotation/append_clock_rr_graph.cpp +++ b/openfpga/src/annotation/append_clock_rr_graph.cpp @@ -736,6 +736,79 @@ static int add_rr_graph_opin2clk_edges( return CMD_EXEC_SUCCESS; } +/******************************************************************** + * Add edges between OPIN of programmable blocks and clock routing tracks + * Note that such edges only occur at the intermeidate points of spines + * Different from add_rr_graph_opin2clk_edges(), we follow the clock spines + *here By expanding on intermediate points, internal drivers will be added + *******************************************************************/ +static int add_rr_graph_opin2clk_intermediate_edges( + RRGraphBuilder& rr_graph_builder, size_t& num_edges_to_create, + const RRClockSpatialLookup& clk_rr_lookup, const RRGraphView& rr_graph_view, + const DeviceGrid& grids, const size_t& layer, const ClockNetwork& clk_ntwk, + const bool& verbose) { + size_t edge_count = 0; + for (ClockTreeId clk_tree : clk_ntwk.trees()) { + for (ClockSpineId ispine : clk_ntwk.spines(clk_tree)) { + VTR_LOGV(verbose, "Finding internal drivers on spine '%s'...\n", + clk_ntwk.spine_name(ispine).c_str()); + for (auto ipin : clk_ntwk.pins(clk_tree)) { + for (const vtr::Point& coord : clk_ntwk.spine_coordinates(ispine)) { + if (clk_ntwk.spine_intermediate_drivers(ispine, coord).empty()) { + continue; + } + size_t curr_edge_count = edge_count; + /* Get the rr node of destination spine */ + Direction des_spine_direction = clk_ntwk.spine_direction(ispine); + ClockLevelId des_spine_level = clk_ntwk.spine_level(ispine); + vtr::Point des_coord; + /* des node depends on the type of routing track and direction. But it should be a starting point at the current SB[x][y] */ + if (des_spine_direction == Direction::INC && clk_ntwk.spine_track_type(ispine) == CHANX) { + des_coord.set_x(coord.x() + 1); + des_coord.set_y(coord.y()); + } + if (des_spine_direction == Direction::DEC && clk_ntwk.spine_track_type(ispine) == CHANX) { + des_coord.set_x(coord.x()); + des_coord.set_y(coord.y()); + } + if (des_spine_direction == Direction::INC && clk_ntwk.spine_track_type(ispine) == CHANY) { + des_coord.set_x(coord.x()); + des_coord.set_y(coord.y() + 1); + } + if (des_spine_direction == Direction::DEC && clk_ntwk.spine_track_type(ispine) == CHANY) { + des_coord.set_x(coord.x()); + des_coord.set_y(coord.y()); + } + RRNodeId des_node = clk_rr_lookup.find_node( + des_coord.x(), des_coord.y(), clk_tree, des_spine_level, ipin, + des_spine_direction, verbose); + if (!rr_graph_view.valid_node(des_node)) { + continue; + } + /* Walk through each qualified OPIN, build edges */ + std::vector int_driver_ids = clk_ntwk.spine_intermediate_drivers(ispine, coord); + for (RRNodeId src_node : find_clock_opin2track_node( + grids, rr_graph_view, layer, coord, clk_ntwk, ipin, + int_driver_ids, verbose)) { + /* Create edges */ + VTR_ASSERT(rr_graph_view.valid_node(des_node)); + rr_graph_builder.create_edge( + src_node, des_node, clk_ntwk.default_driver_switch(), false); + edge_count++; + } + VTR_LOGV(verbose, "\tWill add %lu edges from OPINs as intermediate drivers at (x=%lu, y=%lu)\n", + edge_count - curr_edge_count, des_coord.x(), des_coord.y()); + } + } + } + } + /* Allocate edges */ + rr_graph_builder.build_edges(true); + num_edges_to_create += edge_count; + return CMD_EXEC_SUCCESS; +} + + /******************************************************************** * Add edges to interconnect clock nodes * Walk through the routing tracks in each connection block (driver nodes) @@ -812,6 +885,9 @@ static void add_rr_graph_clock_edges( add_rr_graph_opin2clk_edges(rr_graph_builder, num_edges_to_create, clk_rr_lookup, rr_graph_view, grids, layer, clk_ntwk, verbose); + add_rr_graph_opin2clk_intermediate_edges(rr_graph_builder, num_edges_to_create, + clk_rr_lookup, rr_graph_view, grids, layer, + clk_ntwk, verbose); } /******************************************************************** From f87e095558996ce51f7972c89059b9a522d32ac7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 19:22:39 -0700 Subject: [PATCH 04/16] [core] support intermediate driver in clock routing --- .../src/annotation/append_clock_rr_graph.cpp | 12 +---- .../src/annotation/route_clock_rr_graph.cpp | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/openfpga/src/annotation/append_clock_rr_graph.cpp b/openfpga/src/annotation/append_clock_rr_graph.cpp index cc9dbe60d..6fe98875c 100644 --- a/openfpga/src/annotation/append_clock_rr_graph.cpp +++ b/openfpga/src/annotation/append_clock_rr_graph.cpp @@ -761,24 +761,14 @@ static int add_rr_graph_opin2clk_intermediate_edges( /* Get the rr node of destination spine */ Direction des_spine_direction = clk_ntwk.spine_direction(ispine); ClockLevelId des_spine_level = clk_ntwk.spine_level(ispine); - vtr::Point des_coord; + vtr::Point des_coord(coord.x(), coord.y()); /* des node depends on the type of routing track and direction. But it should be a starting point at the current SB[x][y] */ if (des_spine_direction == Direction::INC && clk_ntwk.spine_track_type(ispine) == CHANX) { des_coord.set_x(coord.x() + 1); - des_coord.set_y(coord.y()); - } - if (des_spine_direction == Direction::DEC && clk_ntwk.spine_track_type(ispine) == CHANX) { - des_coord.set_x(coord.x()); - des_coord.set_y(coord.y()); } if (des_spine_direction == Direction::INC && clk_ntwk.spine_track_type(ispine) == CHANY) { - des_coord.set_x(coord.x()); des_coord.set_y(coord.y() + 1); } - if (des_spine_direction == Direction::DEC && clk_ntwk.spine_track_type(ispine) == CHANY) { - des_coord.set_x(coord.x()); - des_coord.set_y(coord.y()); - } RRNodeId des_node = clk_rr_lookup.find_node( des_coord.x(), des_coord.y(), clk_tree, des_spine_level, ipin, des_spine_direction, verbose); diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index 79cad9ecb..2ff70bfc5 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -449,6 +449,50 @@ static int rec_expand_and_route_clock_spine( des_spine_direction, verbose); VTR_ASSERT(rr_graph.valid_node(src_node)); VTR_ASSERT(rr_graph.valid_node(des_node)); + + /* Internal drivers may appear at the intermediate. Check if there are + * any defined and related rr_node found as incoming edges. If the + * global net is mapped to the internal driver, use it as the previous + * node */ + size_t use_int_driver = 0; + if (!clk_ntwk.spine_intermediate_drivers(curr_spine, des_coord) + .empty() && + tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { + for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { + RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); + if (OPIN != rr_graph.node_type(opin_node)) { + continue; + } + if (rr_node_gnets[opin_node] != tree2clk_pin_map.at(curr_pin)) { + continue; + } + /* This is the opin node we need, use it as the internal driver */ + vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, + opin_node); + vpr_routing_annotation.set_rr_node_net(opin_node, + tree2clk_pin_map.at(curr_pin)); + vpr_routing_annotation.set_rr_node_net(des_node, + tree2clk_pin_map.at(curr_pin)); + use_int_driver++; + VTR_LOGV(verbose, + "Routed intermediate point of spine '%s' at " + "(%lu, %lu) using internal driver\n", + clk_ntwk.spine_name(curr_spine).c_str(), des_coord.x(), + des_coord.y()); + } + } + if (use_int_driver > 1) { + VTR_LOG_ERROR( + "Found %lu internal drivers for the intermediate point (%lu, %lu) for " + "spine '%s'!\n Expect only 1!\n", + use_int_driver, des_coord.x(), des_coord.y(), + clk_ntwk.spine_name(curr_spine).c_str()); + return CMD_EXEC_FATAL_ERROR; + } + if (use_int_driver == 1) { + continue; /* Used internal driver, early pass. */ + } + VTR_LOGV(verbose, "Routed backbone of spine '%s' from (x=%lu, y=%lu) to (x=%lu, " "y=%lu)...\n", From 2bb87ea278fc2d9b5fe77a917d77b5fcdb1cf7c6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 19:23:14 -0700 Subject: [PATCH 05/16] [core] code format --- .../src/annotation/append_clock_rr_graph.cpp | 30 +++++++++++-------- .../src/annotation/route_clock_rr_graph.cpp | 3 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/openfpga/src/annotation/append_clock_rr_graph.cpp b/openfpga/src/annotation/append_clock_rr_graph.cpp index 6fe98875c..d118c38f1 100644 --- a/openfpga/src/annotation/append_clock_rr_graph.cpp +++ b/openfpga/src/annotation/append_clock_rr_graph.cpp @@ -753,7 +753,8 @@ static int add_rr_graph_opin2clk_intermediate_edges( VTR_LOGV(verbose, "Finding internal drivers on spine '%s'...\n", clk_ntwk.spine_name(ispine).c_str()); for (auto ipin : clk_ntwk.pins(clk_tree)) { - for (const vtr::Point& coord : clk_ntwk.spine_coordinates(ispine)) { + for (const vtr::Point& coord : + clk_ntwk.spine_coordinates(ispine)) { if (clk_ntwk.spine_intermediate_drivers(ispine, coord).empty()) { continue; } @@ -762,13 +763,16 @@ static int add_rr_graph_opin2clk_intermediate_edges( Direction des_spine_direction = clk_ntwk.spine_direction(ispine); ClockLevelId des_spine_level = clk_ntwk.spine_level(ispine); vtr::Point des_coord(coord.x(), coord.y()); - /* des node depends on the type of routing track and direction. But it should be a starting point at the current SB[x][y] */ - if (des_spine_direction == Direction::INC && clk_ntwk.spine_track_type(ispine) == CHANX) { + /* des node depends on the type of routing track and direction. But it + * should be a starting point at the current SB[x][y] */ + if (des_spine_direction == Direction::INC && + clk_ntwk.spine_track_type(ispine) == CHANX) { des_coord.set_x(coord.x() + 1); - } - if (des_spine_direction == Direction::INC && clk_ntwk.spine_track_type(ispine) == CHANY) { + } + if (des_spine_direction == Direction::INC && + clk_ntwk.spine_track_type(ispine) == CHANY) { des_coord.set_y(coord.y() + 1); - } + } RRNodeId des_node = clk_rr_lookup.find_node( des_coord.x(), des_coord.y(), clk_tree, des_spine_level, ipin, des_spine_direction, verbose); @@ -776,7 +780,8 @@ static int add_rr_graph_opin2clk_intermediate_edges( continue; } /* Walk through each qualified OPIN, build edges */ - std::vector int_driver_ids = clk_ntwk.spine_intermediate_drivers(ispine, coord); + std::vector int_driver_ids = + clk_ntwk.spine_intermediate_drivers(ispine, coord); for (RRNodeId src_node : find_clock_opin2track_node( grids, rr_graph_view, layer, coord, clk_ntwk, ipin, int_driver_ids, verbose)) { @@ -786,7 +791,9 @@ static int add_rr_graph_opin2clk_intermediate_edges( src_node, des_node, clk_ntwk.default_driver_switch(), false); edge_count++; } - VTR_LOGV(verbose, "\tWill add %lu edges from OPINs as intermediate drivers at (x=%lu, y=%lu)\n", + VTR_LOGV(verbose, + "\tWill add %lu edges from OPINs as intermediate drivers at " + "(x=%lu, y=%lu)\n", edge_count - curr_edge_count, des_coord.x(), des_coord.y()); } } @@ -798,7 +805,6 @@ static int add_rr_graph_opin2clk_intermediate_edges( return CMD_EXEC_SUCCESS; } - /******************************************************************** * Add edges to interconnect clock nodes * Walk through the routing tracks in each connection block (driver nodes) @@ -875,9 +881,9 @@ static void add_rr_graph_clock_edges( add_rr_graph_opin2clk_edges(rr_graph_builder, num_edges_to_create, clk_rr_lookup, rr_graph_view, grids, layer, clk_ntwk, verbose); - add_rr_graph_opin2clk_intermediate_edges(rr_graph_builder, num_edges_to_create, - clk_rr_lookup, rr_graph_view, grids, layer, - clk_ntwk, verbose); + add_rr_graph_opin2clk_intermediate_edges( + rr_graph_builder, num_edges_to_create, clk_rr_lookup, rr_graph_view, grids, + layer, clk_ntwk, verbose); } /******************************************************************** diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index 2ff70bfc5..fd4ae69b9 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -455,8 +455,7 @@ static int rec_expand_and_route_clock_spine( * global net is mapped to the internal driver, use it as the previous * node */ size_t use_int_driver = 0; - if (!clk_ntwk.spine_intermediate_drivers(curr_spine, des_coord) - .empty() && + if (!clk_ntwk.spine_intermediate_drivers(curr_spine, des_coord).empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); From ed33b62a60c696df3c88feafe9175f6eae121d7d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 19:27:40 -0700 Subject: [PATCH 06/16] [test] add new tests to validate intermediate drivers in clock --- .../regression_test_scripts/basic_reg_test.sh | 1 + ...k_arch_1clk_1rst_2layer_int_driver_clk.xml | 37 ++++++++++++ ...k_arch_1clk_1rst_2layer_int_driver_rst.xml | 35 ++++++++++++ .../config/pin_constraints_clk_cond.xml | 9 +++ .../config/pin_constraints_rst_cond.xml | 8 +++ .../config/repack_pin_constraints.xml | 4 ++ .../config/task.conf | 57 +++++++++++++++++++ .../config/vpr_constraint_clk_cond.xml | 12 ++++ .../config/vpr_constraint_rst_cond.xml | 12 ++++ 9 files changed, 175 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_clk.xml create mode 100644 openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml create mode 100644 openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/pin_constraints_clk_cond.xml create mode 100644 openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/pin_constraints_rst_cond.xml create mode 100644 openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/repack_pin_constraints.xml create mode 100644 openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml create mode 100644 openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_rst_cond.xml diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index c75e4634d..e5dd8fa57 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -262,6 +262,7 @@ run-task basic_tests/clock_network/homo_1clock_1reset_2layer_on_lut_pb_pin_fixup run-task basic_tests/clock_network/homo_1clock_1reset_2layer_syntax $@ run-task basic_tests/clock_network/homo_1clock_1reset_2layer_disable_unused_spines $@ run-task basic_tests/clock_network/homo_1clock_1reset_2layer_internal_driver $@ +run-task basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver $@ echo -e "Testing configuration chain of a K4N4 FPGA using .blif generated by yosys+verific"; run-task basic_tests/verific_test $@ diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_clk.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_clk.xml new file mode 100644 index 000000000..270ca7f33 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_clk.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml new file mode 100644 index 000000000..b0f8d5333 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/pin_constraints_clk_cond.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/pin_constraints_clk_cond.xml new file mode 100644 index 000000000..903c4bfac --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/pin_constraints_clk_cond.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/pin_constraints_rst_cond.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/pin_constraints_rst_cond.xml new file mode 100644 index 000000000..55e49733f --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/pin_constraints_rst_cond.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/repack_pin_constraints.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/repack_pin_constraints.xml new file mode 100644 index 000000000..06a125111 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/repack_pin_constraints.xml @@ -0,0 +1,4 @@ + + + + diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/task.conf b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/task.conf new file mode 100644 index 000000000..a4841a1ae --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/task.conf @@ -0,0 +1,57 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# Configuration file for running experiments +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs +# Each job execute fpga_flow script on combination of architecture & benchmark +# timeout_each_job is timeout for each job +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + +[GENERAL] +run_engine=openfpga_shell +power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml +power_analysis = false +spice_output=false +verilog_output=true +timeout_each_job = 3*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/example_clkntwk_int_driver_no_ace_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_N4_fracff_40nm_Ntwk1clk1rst2lvl_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +openfpga_repack_constraints_file=${PATH:TASK_DIR}/config/repack_pin_constraints.xml +openfpga_vpr_device_layout=2x2 +openfpga_vpr_route_chan_width=32 +openfpga_clock_arch_file=${PATH:TASK_DIR}/config/clk_arch_1clk_1rst_2layer_int_driver.xml +openfpga_verilog_testbench_port_mapping=--explicit_port_mapping +openfpga_route_clock_options= +openfpga_vpr_constraint_file=${PATH:TASK_DIR}/config/vpr_constraint_clk_cond.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N4_tileable_fracff_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/clk_cond/clk_cond.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/rst_cond/rst_cond.v + +[SYNTHESIS_PARAM] +# Yosys script parameters +bench_yosys_cell_sim_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/openfpga_dff_sim.v +bench_yosys_dff_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/openfpga_dff_map.v +bench_read_verilog_options_common = -nolatches +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_dff_flow.ys +bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys;${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_rewrite_flow.ys + +bench0_top = clk_cond +bench0_openfpga_pin_constraints_file = ${PATH:TASK_DIR}/config/pin_constraints_clk_cond.xml +bench0_openfpga_vpr_constraint_file=${PATH:TASK_DIR}/config/vpr_constraint_clk_cond.xml +bench0_openfpga_clock_arch_file=${PATH:TASK_DIR}/config/clk_arch_1clk_1rst_2layer_int_driver_clk.xml + +bench1_top = rst_cond +bench1_openfpga_pin_constraints_file = ${PATH:TASK_DIR}/config/pin_constraints_rst_cond.xml +bench1_openfpga_vpr_constraint_file=${PATH:TASK_DIR}/config/vpr_constraint_rst_cond.xml +bench1_openfpga_clock_arch_file=${PATH:TASK_DIR}/config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml new file mode 100644 index 000000000..f9b30b022 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_rst_cond.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_rst_cond.xml new file mode 100644 index 000000000..3c6f27aef --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_rst_cond.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + From 6d3d36626e3c350181bd5a47e432fe188cd287c2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 19:29:47 -0700 Subject: [PATCH 07/16] [test] typo --- .../config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml index b0f8d5333..d2df1756d 100644 --- a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/clk_arch_1clk_1rst_2layer_int_driver_rst.xml @@ -17,10 +17,10 @@ - + - - + + From 6551ca81e54cf5782b781e313d76edcd9bc0a833 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 19:48:02 -0700 Subject: [PATCH 08/16] [core] debugging --- .../src/annotation/route_clock_rr_graph.cpp | 46 +++++++++++++++++++ .../config/vpr_constraint_clk_cond.xml | 4 +- .../config/vpr_constraint_rst_cond.xml | 2 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index fd4ae69b9..e60da7fb3 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -427,6 +427,52 @@ static int rec_expand_and_route_clock_spine( * The skip condition may cause this. */ /* Skip the first stop */ if (icoord == spine_coords.size() - 1) { + vtr::Point des_coord = spine_coords[icoord]; + Direction des_spine_direction = clk_ntwk.spine_direction(curr_spine); + ClockLevelId des_spine_level = clk_ntwk.spine_level(curr_spine); + RRNodeId des_node = clk_rr_lookup.find_node( + des_coord.x(), des_coord.y(), clk_tree, des_spine_level, curr_pin, + des_spine_direction, verbose); + VTR_ASSERT(rr_graph.valid_node(des_node)); + + /* Internal drivers may appear at the intermediate. Check if there are + * any defined and related rr_node found as incoming edges. If the + * global net is mapped to the internal driver, use it as the previous + * node */ + size_t use_int_driver = 0; + if (!clk_ntwk.spine_intermediate_drivers(curr_spine, des_coord).empty() && + tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { + for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { + RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); + if (OPIN != rr_graph.node_type(opin_node)) { + continue; + } + if (rr_node_gnets[opin_node] != tree2clk_pin_map.at(curr_pin)) { + continue; + } + /* This is the opin node we need, use it as the internal driver */ + vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, + opin_node); + vpr_routing_annotation.set_rr_node_net(opin_node, + tree2clk_pin_map.at(curr_pin)); + vpr_routing_annotation.set_rr_node_net(des_node, + tree2clk_pin_map.at(curr_pin)); + use_int_driver++; + VTR_LOGV(verbose, + "Routed intermediate point of spine '%s' at " + "(%lu, %lu) using internal driver\n", + clk_ntwk.spine_name(curr_spine).c_str(), des_coord.x(), + des_coord.y()); + } + } + if (use_int_driver > 1) { + VTR_LOG_ERROR( + "Found %lu internal drivers for the intermediate point (%lu, %lu) for " + "spine '%s'!\n Expect only 1!\n", + use_int_driver, des_coord.x(), des_coord.y(), + clk_ntwk.spine_name(curr_spine).c_str()); + return CMD_EXEC_FATAL_ERROR; + } continue; } /* Connect only when next stop is used */ diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml index f9b30b022..b80e7299c 100644 --- a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml @@ -2,11 +2,11 @@ - + - + diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_rst_cond.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_rst_cond.xml index 3c6f27aef..8ae00e08f 100644 --- a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_rst_cond.xml +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_rst_cond.xml @@ -2,7 +2,7 @@ - + From 33a253da3dcd72a16670375e11192e1e0195cb3e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 22:20:41 -0700 Subject: [PATCH 09/16] [core] fixed the bug --- .../src/annotation/route_clock_rr_graph.cpp | 28 +++++++++++++++++-- .../config/vpr_constraint_clk_cond.xml | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index e60da7fb3..fe5e6a9fc 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -440,8 +440,20 @@ static int rec_expand_and_route_clock_spine( * global net is mapped to the internal driver, use it as the previous * node */ size_t use_int_driver = 0; - if (!clk_ntwk.spine_intermediate_drivers(curr_spine, des_coord).empty() && + vtr::Point int_driver_coord(des_coord.x(), des_coord.y()); + if (des_spine_direction == Direction::INC && + clk_ntwk.spine_track_type(curr_spine) == CHANX) { + int_driver_coord.set_x(des_coord.x() - 1); + } + if (des_spine_direction == Direction::INC && + clk_ntwk.spine_track_type(curr_spine) == CHANY) { + int_driver_coord.set_y(des_coord.y() - 1); + } + + if (!clk_ntwk.spine_intermediate_drivers(curr_spine, int_driver_coord).empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { + VTR_LOGV(verbose, "Finding intermediate drivers at (%d, %d) for spine '%s'\n", + des_coord.x(), des_coord.y(), clk_ntwk.spine_name(curr_spine).c_str()); for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); if (OPIN != rr_graph.node_type(opin_node)) { @@ -501,8 +513,20 @@ static int rec_expand_and_route_clock_spine( * global net is mapped to the internal driver, use it as the previous * node */ size_t use_int_driver = 0; - if (!clk_ntwk.spine_intermediate_drivers(curr_spine, des_coord).empty() && + vtr::Point int_driver_coord(des_coord.x(), des_coord.y()); + if (des_spine_direction == Direction::INC && + clk_ntwk.spine_track_type(curr_spine) == CHANX) { + int_driver_coord.set_x(des_coord.x() - 1); + } + if (des_spine_direction == Direction::INC && + clk_ntwk.spine_track_type(curr_spine) == CHANY) { + int_driver_coord.set_y(des_coord.y() - 1); + } + + if (!clk_ntwk.spine_intermediate_drivers(curr_spine, int_driver_coord).empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { + VTR_LOGV(verbose, "Finding intermediate drivers at (%d, %d) for spine '%s'\n", + des_coord.x(), des_coord.y(), clk_ntwk.spine_name(curr_spine).c_str()); for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); if (OPIN != rr_graph.node_type(opin_node)) { diff --git a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml index b80e7299c..f7a7dccd5 100644 --- a/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml +++ b/openfpga_flow/tasks/basic_tests/clock_network/homo_1clock_1reset_2layer_intermediate_driver/config/vpr_constraint_clk_cond.xml @@ -6,7 +6,7 @@ - + From 4e85a6f4145614bcac63e08fc6faf7ae0e1c213c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 20 Sep 2024 22:37:05 -0700 Subject: [PATCH 10/16] [core] code format --- .../src/annotation/route_clock_rr_graph.cpp | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index fe5e6a9fc..428dddc12 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -450,10 +450,13 @@ static int rec_expand_and_route_clock_spine( int_driver_coord.set_y(des_coord.y() - 1); } - if (!clk_ntwk.spine_intermediate_drivers(curr_spine, int_driver_coord).empty() && + if (!clk_ntwk.spine_intermediate_drivers(curr_spine, int_driver_coord) + .empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { - VTR_LOGV(verbose, "Finding intermediate drivers at (%d, %d) for spine '%s'\n", - des_coord.x(), des_coord.y(), clk_ntwk.spine_name(curr_spine).c_str()); + VTR_LOGV(verbose, + "Finding intermediate drivers at (%d, %d) for spine '%s'\n", + des_coord.x(), des_coord.y(), + clk_ntwk.spine_name(curr_spine).c_str()); for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); if (OPIN != rr_graph.node_type(opin_node)) { @@ -479,7 +482,8 @@ static int rec_expand_and_route_clock_spine( } if (use_int_driver > 1) { VTR_LOG_ERROR( - "Found %lu internal drivers for the intermediate point (%lu, %lu) for " + "Found %lu internal drivers for the intermediate point (%lu, %lu) " + "for " "spine '%s'!\n Expect only 1!\n", use_int_driver, des_coord.x(), des_coord.y(), clk_ntwk.spine_name(curr_spine).c_str()); @@ -523,10 +527,12 @@ static int rec_expand_and_route_clock_spine( int_driver_coord.set_y(des_coord.y() - 1); } - if (!clk_ntwk.spine_intermediate_drivers(curr_spine, int_driver_coord).empty() && + if (!clk_ntwk.spine_intermediate_drivers(curr_spine, int_driver_coord) + .empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { - VTR_LOGV(verbose, "Finding intermediate drivers at (%d, %d) for spine '%s'\n", - des_coord.x(), des_coord.y(), clk_ntwk.spine_name(curr_spine).c_str()); + VTR_LOGV( + verbose, "Finding intermediate drivers at (%d, %d) for spine '%s'\n", + des_coord.x(), des_coord.y(), clk_ntwk.spine_name(curr_spine).c_str()); for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); if (OPIN != rr_graph.node_type(opin_node)) { From 26cda624d30f26a32d26d788721ab4d2b5890d61 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 21 Sep 2024 13:31:30 -0700 Subject: [PATCH 11/16] [doc] add new syntax about clock network --- .../manual/file_formats/clock_network.rst | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/docs/source/manual/file_formats/clock_network.rst b/docs/source/manual/file_formats/clock_network.rst index 62e266347..1f075a3d4 100644 --- a/docs/source/manual/file_formats/clock_network.rst +++ b/docs/source/manual/file_formats/clock_network.rst @@ -27,6 +27,9 @@ The entry point of a clock tree must be at a valid connection block. + + + @@ -179,6 +182,59 @@ where a horizental clock spine ``spine0`` is defined which spans from (1, 1) to .. note:: We only support clock spines in horizental and vertical directions. Diagonal clock spine is not supported! +.. _file_formats_clock_network_intermediate_driver: + +Intermediate Driver +^^^^^^^^^^^^^^^^^^^ + +The following syntax are applicable to the XML definition tagged by ``intermediate_driver`` +Note that a number of intermediate drivers can be defined under each clock spine ``spine``. + +.. option:: x="" + + The coordinate X where the intermediate driver should occur on the spine. Must be a valid coordinate within the range of the current clock spine and the clock spine to be tapped. + +.. option:: y="" + + The coordinate Y where the intermediate driver should occur on the spine. Must be a valid coordinate within the range of the current clock spine and the clock spine to be tapped. + +.. note:: The intermeidate driver is different than the internal driver (see details in :ref:`file_formats_clock_network_switch_point`). Intermediate driver may occur in any mid points of a spine, while internal driver occurs **ONLY** on the switch points between spines. + +Under each intermediate driver, a number of tap points can be specified. +For each tap point, outputs of neighbouring programmable blocks are allowed to drive the spine through syntax ``tap``. + +.. option:: from_pin="" + + Define the pin of a programmable block as an internal driver to a clock network. The pin must be a valid pin defined in the VPR architecture description file. + +.. option:: to_pin="" + + Define the source pin of a clock network. The pin must be a valid pin of the global ports defined in the tile_annotation part of OpenFPGA architecture description file. + +For example, + +.. code-block:: xml + + + + + + + + + + +where the clock routing can be driven at (x=1,y=1) by the output pins ``O[0:3]`` of tile ``clb`` in a VPR architecture description file: + +.. code-block:: xml + + + + + + + + .. _file_formats_clock_network_switch_point: Switch Point Settings From af09120a521161a461de3d65d96b6802002b46de Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 21 Sep 2024 13:35:42 -0700 Subject: [PATCH 12/16] [doc] update fig --- .../manual/file_formats/clock_network.rst | 5 ++++- ...g_clk_network_example_2x2_perimeter_cb.png | Bin 82100 -> 74048 bytes 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/manual/file_formats/clock_network.rst b/docs/source/manual/file_formats/clock_network.rst index 1f075a3d4..c1fa9c695 100644 --- a/docs/source/manual/file_formats/clock_network.rst +++ b/docs/source/manual/file_formats/clock_network.rst @@ -224,7 +224,10 @@ For example, -where the clock routing can be driven at (x=1,y=1) by the output pins ``O[0:3]`` of tile ``clb`` in a VPR architecture description file: + +where clock spine ``spine0`` will be driven by other programmable blocks at (1, 1), as highlighted in purple in the :numref:`fig_prog_clock_network_example_2x2_perimeter_cb` + +To be specific, the clock routing can be driven at (x=1,y=1) by the output pins ``O[0:3]`` of tile ``clb`` in a VPR architecture description file: .. code-block:: xml diff --git a/docs/source/manual/file_formats/figures/prog_clk_network_example_2x2_perimeter_cb.png b/docs/source/manual/file_formats/figures/prog_clk_network_example_2x2_perimeter_cb.png index e74ded94f30eeec1f701efa94d2872c34670cada..4ec057d1c12d34e2b40f6a655445d9e1c656fec2 100644 GIT binary patch literal 74048 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

4kZ|1;0R)<5Wf1or7Vl3Wu{7@? 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%0As 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%0AV}8 z@HVlDX)|wuYl>iwJBIYvz93r63GIm6OBXiFi-;lvNL10|LpTtU>!G3_*JaoLc%0IA z?fmc>!WP(8c&+Y$!DWK{-7o*pg%0%ezgKlxB zZa+$dC-vr!>F{-SVRv>i#=6Y6e0fCZTVbz+~ucukVv)!z<7SHPlJqd|3b zpIC_yoERNiW)$`$dvVn^y~^Bv(u>zhM4ebVjd&ICMSuuv#*Iz{Hf)KeskCCf;NdVK z-+37Z&!RKi|INco9Fp4>1A$jGWoglXiaC2}2FUELsfGb+@@;xf`rd}`1nM}Hw9Ikxq1-S-+00l7Z*K7C4uwTdscwXg*}_~OFPoP`^CniphVA|&^N+V)`xLGDW7 z?HqyFkcH5Ul}8TS8)l6^1DYfL%(RNT5oxTqHED*8%#7tc6|N3bs-xq+ugtgj{Y;}A zADH{OUX##g6`=Xsnm)1|qpuYHeo)F=+xRG78b2;b6Ee-)3(>C=iT~v?0GS`@9?|q{ zIjb0QTZmht$>V|*wtt+5mSI)*dy|XstHTtgX!3uYMt+SQqG9mTMR<%+L|X+D>Cd?K zu>cA#bJ&OC;3~e=ax%(~+3fr$qUcq6V7nmwZ@=qTx*(oN@V;cLx;52WJ^fF_c%xt0 zJ!Tzn`M>>MC|uwD{xtoMtHW14(Y22MK=UH)SHl)dyiIuj<@cG_Ufzf&Z?95c8IC4s z75b-He7?Iqi`^0}CzTO_I9p}LUvVcOW6@no$dVgH)7o?G^4ShdD(2Oi$_U>-H+gaG z@`(A%ZVC6%f7-Gt=5SjGQ8D6q+P%+G?tTBIi4AsFrOD9-|FqH>gjZ$vv6vV6FHP#O z^rZ8S-faJeElYo~VuyH=%8-0zI6|paXzCxfbQSKaSQUa%2b(Ta0Dgic+odAs>!*ZDUAYHo=Cs z0eX)=mZ*v0$oO$I1$jR7u!Ahji=y};_a(a^03z17{cx$m?JkY-sD2|GaO{2tcR;v0 zAFefk-cBEG87-woo z*KnERmyO)QuV9U?_n>z|Mm~1A7a@at=EEi6Xuhv%VOFp{2eiY$=}*s>Uk5Z?5NPR| zgKtDp_saqNnhAq0ko1`WYamI0M+EM2ZrP9Y8kgOo2ujN-c+WO+u8IZa55nqlc4Dcn zO&W992VE|lV5jTG!OR|6Zn+%(GjSaei@g#JDiocOyy{aj6J}n4kaS^MT zH6qmLUO5bR3%}h=YTBXV}>-moe`Ym$cVWJIjJvUPngrd13)J1&U+Ho zv)+qOP%@vKWirwpe!O#~8)OYwbl4d`t=qfUB=rE{(2T(UBg|oo~{KaiSuB!8blguZUnI@mqeT{w* zbdUaTA~=mLX_S)P2^G6hSUq$irsN+J(@4~E}Dx)(h4o_?p z<_zq^t;sS%^%=P0yN}9n?05b@MR@l)RFI9we3OQtDe2z)VIhUVN##=Y9+VMJwg~pw zo}^taUGFpU<=Hz;mRxkY;@y7MiTw&q?yerlOtE(EF|rc_nXIRXd!dyzuX185Qa zpIRghiNXk>&GXpB6#QXtik6{*rZ^GT;T%tY^9JGa?~;qgtJ=J&Ox^(qz-z{wgTbopZSS)o=y8j~{Ws-%T2{#HSOO#YT?U zko_tOQxZGM?c&4b(+bLS2v%UOgj4n2h{K5Z4UuE_mWCI8dqfH^+pMINt@EgNdSN2^ zx22zJtT{4+@6d<2s;@KRn1!m%WVW7*jP2U50M#ZJaUr~#FqYP#u%sf6xCgNOp(JU? z;1P*W-jRS|vv8dox@R`wxn>%QCLwu}avCnOy1-@>w^09}<^yQ_i6vihsOr9V_;z{G zyf2mAyF?qFp6qpNep69-#cKp#M$MgEpzZH17++|Ivy#n}bw)KkiLevwUQ)GAa_XtvvilCOt$ zG~=RAxX-H2l)`m74?crw16j99#IZ9d~ELLs&QFRL7KLN;HxZS(S2be1Q$ z$DuvN1*b`3b>}Qs2gMBIXx?F0Q8u}HiyjF|?dPqv6#k|dXP$5n1+~bYQ`jHHWc1kX zJW{Mk;-~s%pV-|8`Z9RPMgymHLC)&mAV1{f7C@_2+gSgR&*ic1bC%@KdpZxq_$RGG zX>BQ+M4R8@tM&q)+ECgswz`tM>?%#Anah+1cMPN)8iWbt^CR?#bWdF&sl{4|C0~%JPV10+Q=V6`2-7D+P zP+y33V(L8`9(e^z_dNSgAed=SLXOa$W6R?OxTAO+NAP=A*M|vWbbuITPf1c05gp~e zpJcLZ?9~K1JlL$?HE033elH$_(>b>-e0Zn(=TOS+1!Q}En1PumGS%Z>F5v0!)cZl7 zmYoMqv(Kc#m=4x*Lo_bkv-<=pf+vs~*ddpQpMz)lC?7770!kCL(gOMtn3GcIz?Evv zC||i(Jt`oF3i8)TzApxLM7|~8|I{t`ho02i^9|>H9{T9cOfJ7sdvj-5ahF4m(+&zz zf(wuHnyzl^VcuP)&7!eKihynjn7$IFl__z(f#`CLRYqY5v--e&!W5hWh^_nGAi^K0 zc90{zl12KXVs7v zDcA>@PYZ%!2GJX?&~=lCm`n59YO6)Pe)3q*K8le}LEQ zpeYfWqBl3_zo=E?l=qmT?Ng;EU&6zrAi807Yi6Rekw5@SUVK zFk<%x;GGtj7}4J1HysbQvNHgUtuH+PJc7y=Qz-B}16zd4N=sQa*L|Ek2h#u^p5vCQ zF31V3@uoM$Ij&-19=9+C?@ta$dd0(_&h}c+ zbPL{Y(DYoueyE?=H4kHHUV`b+4Yk#9xNz0!~qbH>^DAPU=C4Ud1jxMuB- z5NqVkuZff;k{V7T_I4a5EOdpUFHk9|w7SER4$(I>d)gfN#1WL+8G_{5cMfmORr2in&CjM>SVtB}xjc`YxnpCxX*@?n4~Qz`0`a z2|gxGlfKpxTYM|DW&3x1l2uRJDsHQ<83i6n%XFzzdbuv-1uY(tzZ{@bRoncbMx%(= zZ3pBbg_Y{C`@F-k{{1?(`N18wmzrq!j${4M^?Phl33vxbS7@kTDRJ1s4OVKC?fdzF zdTZVgcgu?#p-u;Gugb z7cFtEtHDa^yStK>vkXbbSx(WMxn4!s!n~IJpb7Nl@a3``48jdPOc{ek-d2MFfcDfxhnr3dc4NS7`xsni<ZCbYLdoDFph{@VR;hf@sf1QxwG^n+ENh zd)Lr^4)^KZw2hD=i048Eg|wdpt}Vz@&~({kRteT_t3l9%-UO+`l3uRnTtR*u%K;wNB0L1Q~4Xk!4m@G^u5V^u+27Y7*)-KA2V=^5rt z=QGpc(6hPBXJLJVY7eg*Q1YgO#{aur+%IBXSW6<>ckjgTZzh?6n6VVzyTsJZ5tU+Z0I+4-C3qbD4m1rRPQbL z(z2g}(?Iu28)*j@vUnU_xLwtouh~D;M>I}YLjkC`IX6!sZKrp*KQk<(llr?I4Qu^ z8lu+6MefCOyKx;ffSg^>Q79Ri3xL;TP5tefb6*BJXWWzv&PJ+C=R3}t0~S0NPpFYsrh7L zZ59f&U*5|%gh)bx$v67KfXCb#upgQHxpW0!jKN5bcYU7CIRa-Xyb_%*RNABf>D z^dUcziEp{&k#lTpJ{k!2jkl#p9C@G+T|F~;HTkxFdhd`$bn+Mb1Wq;+jX02ftn)I3swpZAextjKA1mta2P0n$iKOSM5 zSHJgkiaok|`EHm~k3edM%Z{lmv>zk^?sz`2fXc3V(3m9ig>o7}Q8E4!ywJih#fs9Y zv&r~9_Xp)=6Z@yn1NP~`=S@xmlhSLdW()9lp<7E^?B&qVDWqFFh_8ok<`rpZP9^xf zsfLI73mbfIShqfrZlg?dr`?(-mrobgPr$&h0fyBdDXv9Y^_sp`0`E|bHsBl#ejp+y zNy1C;<$6eaGBGt7;>Pm6iPE4JE7feKy!fhb%waXv?svG)SHBgialpNoNYrK_2Ojvx z00yhB1M-&R@r+?3-eI((`vmaA)AueY81I44(A~8}5CSK2Yv>XJRXL@NV=9b+x2ZqY zJNoyt_LY@~>lu4yj~v1&`ohMOG)VmJs0^f2#3e8L#n_V&2GAl&E| z1P2`7CNj=zm?&KpocyeKSVHKgVOmoSo+6IfI^fFk79|uib>;xW6dZDlj%CV1TFw51 z!Ltacz2i7Pw3=8dG}->L&yTgDMAY8OMSAc?d=*5a{wX;!smk$ zzXyP0U!5Kr0(rllHL>W!on%TX;*PtA(vc%ej;z7KMmr20Y}+r{ZYMX#r-=DlEwUXp zg*ke5egjF#%s2JENy209A2i zH4wHGbrDmTKYt?9%k2yuf*Ct;V&J<=IW^-zM3J739P1wd*(dT_V$(xka_^KheY+>aZHH z6H;VomkEiDbc!G6&)Tk|+$Mn@q2e5pC;yXKjGK4cmAmtAk>^VjaMys}5yhWrv;P(0 zX>6wP|0;ODO^C^;-`}7mx_y-N#7x2W|G1n#6O#W+BmV!_B>lf)IO;=;PSuQqfZX%B!ytm(XR`B0`D=Lca;tvYCXs{DaSoVqkCZYiVEA$%Ha%?o1Fk)FZ1W$b zn;|B1P#pD3MA-Cqe)4kT-#+J!wuM~sg)(6C6gona{(o%NpJxbwa5sK>4x31NRtYwY zJ9e~wCclF|hX$Yvc|x$+?`hm-&9 z!%})+MC^wH_9uLdqY;HxfDhmP{BQ!^_s7ZkNklVmgo#(;T^Qth)ZYjgkPlRr@Jbww zI0=vY+lQq$YrEgpVb|zzB4cngqWou|s%8FYUjzjvul|kjiov#!c3Ut8o~a-4H=;*x zL2>9Lu!RMIhA6kYf1|*?*lU+}2QL9d3;%JPi-#ze_*;jiz%Rze&#{}|)eZ9I z0FqVhzqF5{H98~6C+VVm=Cx3cj%bvH()3@b`hk8V81u36tr<{9C2uN8yf-1ThGGD8 z)VZH2s2j^ct~YkyW})xv~+0jxXShPJT)fah{6{iIzwJB0KdV{19c^Lb}e25<>;K* zU%DSx@jV4|Q7(l5jS)}e7=Cs$JA#LLZ-Ff`DGfi*8bc(GN9XrH;~%w_xcWEa*UU+Q zDI;*^cF!LD^-SHhO$K(;k7$JqFcgTyW!km*M{(=!3)0nY-{5IYaUvD3YA||F$LqDd zGoQ%$s?ezZx(nVmkzrCK>DKc+^#?K|p%6MzVcfY{6tG6Okk)V*>mMp7cDz$&910*XuFretCxI= z;a=uV|4(&i`4`m}wS5qflynG*ap+DFkQgMTL+Kc#6_r$SDCsT{Bt{xZNd<>;=om^u zx?35BaE6$<=l6M@f8l;{pO@#w`RudKcU^m*Yp-?ALsGZiy`XEKc~>B*Zk!NL?BhEw z9B_^PA6VASHz9V)Z0>F;MUkIcKp|XO!9#Utods}u_qyZg>utz}rhxGgTtz@zh;38H z&6%Z*1(GGOQybbx2Ao@9qYJfN)V|MzA)P&=#G(DW4iA!_uxUjdx$N_-UiahC=S%M? zUi4hI{LCxDY-r>?SVw%a`wY8cGdVFm{@Sp911DC9@a->LVO_WvBGu-aNGdy!Ee^?#DTG%s|FiM zH*oL64Pk6UBn(vK+;}|&7?xX6f$xM-f_4wfPolfx<>;UI7(R8=S4?EACT#Rh+F$3H ziDQY0TUoU{$&m9S>?Ufu!?PCgj&E^gu(cDQB&SVR#4Ls??e)Uips39vo`0AH4*|(< zp&c>uq%A#z&JX?t;baI$)R!TC_p3FxUYdzv4Oj>AMcWN_n& z|K4EPQ+M{FF+X=85~ePJV!m;~H4!WW$0=Me&=eECk9>wkeJe>B zJ_$QhS}t1WTT_$LN!M7q0tmQ#gQs*|ion0#7Tf%@`0iCmN|T4(934%4=t%+VpIc*l z`7-5GJ2CTJQ-F$4o_Lv0!A>X{8aHlOB8(qn;hk{Z>#4WSJ1rIOw_6{2RhufuJ;R#L zK&jY%tbQL~5PPk9-um)7Z;p@ikuAPF#zIm2TLTjTWgsPF9?nnOsl5AXS6Id4B}KUb5mrTy_|bBg z+7~*u+=6v`0xH|+gzA||y!2{T7ls1xuCv~dToNLyoej1O^Ytk{jd;Uc}1?3>N|G1lNtV1e`EJici3T8`vhl1zvEotxb ztMMRcZRc~xO1JW69YeBBu%*r^^rX0ZW+pE@wQ2 z(AMnxh{8>dJ?1G7T^acYgs2)`JuGHTU(xt4nngs-Vw~&#|K^Um0t<{q1jHLru4AX@ zhtVV+f9EV|d64sy?A{A<$j{(?>`d(OKUUG2ih8ckBa=xp1Kkp;m;X9VNEi{v)6{SA zk3sxScOyuu>2z3%aTpscaRi=@X{!=zaPm$FpsXFf$Jc74D~#Sl~NC= zcK@@sjs&Ay^O+X!rFuEk0BSYNha0bu8Ysysg*ys?$Yre_dTK?G_s&dC>dR-ALjY~rtUh9w& zlQt?U!H_P?9=}KRYM;6JLW`GAd8RKpoP?Fq*L-d!yv-^-O@%-#k{w$3p*3f1ceISp zMQRny@2RQT7rnV#_EtghbCT;0%7=+T_e}g|OQ}ib9lC$3x7y*hmW;}$4k;OZFs


>coGD2)KPW%3tMJxE({( zQ~FoZ*B6?~?b9#5QduTMCL>7|HDw74$jXN(NS3ir;9gX{t1{URbjf0>;iZPDxzThNiud)hLyHB5 zhf@B9JrH>XrHNL07U0X3Tm9x-X@uN!+So8t-ih3k=7p*CtqFg{;#_u6(q#S`>LfHv zDlEX3?qC(r6Se&tszmKOtolbc5iwBG^woM0%~|bfwV#J7@*L52*AtcBbn`tXeYNjH z6G|emMW&w0%#>EC)U#0769MmBp&H$Hq=9>{ELpRKyQc~OBHqwNTg;>u#yA3sX-_6pC6jSLPCra50rE zbWFZ2DKUMo)53Q6H6 zT7hc+PTP~biwr+f)ogiENwjpf(r0wL|}aAcSh*yLA0!0Lc9c0NKd+`Pf0gnIP)e#ap#oVL0|53n_NtxR65>q zg_PE{7I4mVp8dATYZM5}wo++&maOv1GLax<4QKsrs~(C_wbO=egK8NVSyQ(06F(&L8|pQ4ru$ffoPG_S zcKq!YGBgDO$hf(BOWLfui+)AWXTa_IKkjAFJmt_qen?a6R7$0j5TQcPQulmG=<3(b zFgh!W4=A?&qLN*^3q89|+`yGqY*+Lt;li^WlDL~|(Qh1dzkSBqIqg{#)&wGBn?iRG zCL)6pqdRg=9D}Z*doN;IC_LIZWIU5jt5pEqAj1EDzVRLpI_Bi=3=E)=#jN9>w)12Y ze+TW7R=JBw+p<-(>r&jsK?^?k0{>f>r@$!6u z!%{M1d8uZ1^^uL{e|Y3p4^U0!o@4mw#YXij5|cKe%WOVwq7YH6F`T_bE>pgwOah73 z8K#RjP5>WjoRXKC@j5j6Y}IyWix2d&aE`e8*WA2m!0V@BoRG4BPw&)4tk+Kk*gAO__m`=c9DMGLdlXOH_v7_V#mWKRW< z>t2tts)kbpFW-qWBuE9xq{}Q^ZsfyCM~BOH>D(#%1^a;xDl88AEKb{rOnP|B;{ejr z;$dNWPJz(iFWhs_L(BsX&nnhYv++iEPVP#U**IKi$Ue#qeAtNl(-c9S8PH0aflTX| zYtY?EqVp7x5%rU;c5W5Q0d&aWs>@Gl2NBnh?v4gPR3L=wNdQ@aIv|Y-i=x zL{T5L_>;SA;2BjSD)y-DXX>@GtKEk8XZgu(Nnu2p@@=DEI{X^7 zGrBNKXv=sRZI=1!4ne^PBfGV`l$rljnH|EAnjx^cHTLo?#&R}>YCX!B;?QU0r2$|1 zcgH{x*Dp7(|0E@Ja_TKzmVX2x=MgNs*A9D=j!Z?4S)?LXr{pSTpoWq~$X!T8XWEwi zZ&WuhE(POlYf?7*^}aL!pd2^v(kJ&-y(_mXLh%-u}4sGR^U|f z8$8v_y;2qNYV_X=@@yVmojOj*HH6-egrIk7mlFSe6M3{}(RRu+w6^lJCYXt!)IVzV zG8F28kn!f6ginRuA2{@)Qjy5pLRbU(9n7`Rt5s2%;m?OJbBa9&81=UCYPiNs31;-B zlNG!uAJ# zo-|o7Sif^Fwko?aDoz$xT91CeCcKYQmGuZLa(p-)2qLT&F8a#a9OOL%%2F{Tlj+yi z)xRcYJD5)Gy}g&OAVt`lb2wv7`&d~Vt zym<|g7<(|I#Gfu>zn*x&oJ!dFnDHkA4rl~+v@>fn@G6(4>DS7+xWnYu5iq6lm*(c{ zQt}T8cf=JXX^mRvwHFR^jI&pZ>u*at5)`6(Iz3L|f_P=0;M=bkV)EraSpTYMjZS0c z{X+c6(3~C4l~UV)akYI}mi+mQKXFhe(#-f|jFs(Aj4 z1^*(aGX64?Q)`v$A$<6o0LJB4t|{gYq?+rla}*fGi_a^i07^DE%fRvKH;hiW18zwE zh@%hyj0`rN)o4!HOp>Igp~*jyCM*p&3SJY+PxvzS$W_A7J_UMWtWZmuPC&`6 zP(yP`!B)C+6AMjD&5?DLRLr>nzh}Uzdh>M2{y#Q4bs4(^T`D9nUXUiSxmj^xRv5!G z$@t;?m4uYSW0kWP%B4bssiv-=4psDqlTN}8=|GsPrGtME@zbff*KdTPD^2Mqs|CIQ z%6KlH%-)lf!G(r6Jp9l+06RFdy_&GtB7Pi8v&@Qp?ANb7T~pw#1?H>1b=F+t@HDG& zN>GtULkqY@GMrmwT}&qg(KteN*_1FpMzfqlIdvDN@<@y2zinOCr-t0$~ zKj%?QleBbz>{plY!0dbXIw^QSA^+NwL$Jfhshl)8ub2vE#!d7Q_huI^Qoqj5z`)C% zoU8ZSz0uf6tFW7dcB{uvJG+hFTb&R5_oSwt`LJPjCr;QagkQ{dUP8LC4|iZ*{A^FU zHmmXMWkX$`fo6~QnAIwoX)Fu|V;zAaEyA__mubOa4;JykM}Gb9L!RdOJb?yTLv>M# zMeq%UDMU;Vu7$ioy)tbeq zZ~u7DRM^|3hQA!IA7rXrS^1sLomPGw@dB3IxwkkMk4>DnqLnUyo03C-CvJ=Q`V2Lr zeZVb>qwwKBllva5=^q&u!nGRr zp@Juf+-60=IcuC9Rv8xcf%Z$LEXXu^7V#0*T+#Bl32P|NKPL0kpy|bDJ)@gK6 z=a_T^T~lO4-xf08SWqO|1wk$9HI=#QQT3!vNz>*+E(@e@o+U1X^%yNF4D z{ed07@kFe#Bv82M4MD-^1ATx1d-NL@Xc?yts!D4W?IZ*Sv0jhZL~e>{K$Gkn8dH2Jnfy5nqNq+ zmmZku2o1P=&&@F!bgQ_HMSe|!b*wZd?XJ<^CnwXfKX;$s0dje*+lY9WDaz(*xF}y` z=M2vng4)>1NSPQnN4C_BxWDlQJKA5%(kl%ixyaYtqF-A4^Bkh%GwPd094=NmE0m3A zen%65U7U*Uo!k@1k|RtOZM~SY(@QiZ#c^cnAmpcKPRP|o%VNwbW*W3k z9J-E$YA?n2m>z|y(Bt-~!lcgGK5t6dI5XLz2{kVuiSU27N^i^aXhqRWOCJT4-iGjB z6H|h=?qCKE{j?gBC74wF1ft-?_)IqyeLlkbu_D3HW<(GyUxFL#h}m=}*u}j4>^xz_ zu>_y{B*1M;V(mY}`_}(Ep_8`iup`Fvf5im<-?!_XcOW;_cwl*Mx3YE*NdXbz(9ty1 Js8e%@{2xe7fI9#H From 9e461284d01a628cf2b7a6dc54ba504d67a6517c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 21 Sep 2024 21:38:32 -0700 Subject: [PATCH 13/16] [core] standardize API for clock network intermeidate drivers --- .../src/base/clock_network.cpp | 33 +++++++++++++++++++ .../src/base/clock_network.h | 7 +++- .../src/annotation/append_clock_rr_graph.cpp | 12 +------ .../src/annotation/route_clock_rr_graph.cpp | 24 ++------------ 4 files changed, 42 insertions(+), 34 deletions(-) diff --git a/libs/libclkarchopenfpga/src/base/clock_network.cpp b/libs/libclkarchopenfpga/src/base/clock_network.cpp index d70ed59a8..7c11c4541 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.cpp +++ b/libs/libclkarchopenfpga/src/base/clock_network.cpp @@ -283,6 +283,39 @@ std::vector ClockNetwork::spine_intermediate_drivers( return result->second; } +vtr::Point ClockNetwork::spine_intermediate_driver_routing_track_coord(const ClockSpineId& spine_id, const vtr::Point& coord) const { + vtr::Point des_coord(coord.x(), coord.y()); + Direction des_spine_direction = spine_direction(spine_id); + /* des node depends on the type of routing track and direction. But it + * should be a starting point at the current SB[x][y] */ + if (des_spine_direction == Direction::INC && + spine_track_type(spine_id) == CHANX) { + des_coord.set_x(coord.x() + 1); + } + if (des_spine_direction == Direction::INC && + spine_track_type(spine_id) == CHANY) { + des_coord.set_y(coord.y() + 1); + } + return des_coord; +} + +std::vector ClockNetwork::spine_intermediate_drivers_by_routing_track( + const ClockSpineId& spine_id, const vtr::Point& track_coord) const { + vtr::Point des_coord(track_coord.x(), track_coord.y()); + Direction des_spine_direction = spine_direction(spine_id); + /* des node depends on the type of routing track and direction. But it + * should be a starting point at the current SB[x][y] */ + if (des_spine_direction == Direction::INC && + spine_track_type(spine_id) == CHANX) { + des_coord.set_x(track_coord.x() - 1); + } + if (des_spine_direction == Direction::INC && + spine_track_type(spine_id) == CHANY) { + des_coord.set_y(track_coord.y() - 1); + } + return spine_intermediate_drivers(spine_id, des_coord); +} + ClockLevelId ClockNetwork::spine_level(const ClockSpineId& spine_id) const { VTR_ASSERT(valid_spine_id(spine_id)); if (is_dirty_) { diff --git a/libs/libclkarchopenfpga/src/base/clock_network.h b/libs/libclkarchopenfpga/src/base/clock_network.h index 1ee5dabcc..494d17220 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.h +++ b/libs/libclkarchopenfpga/src/base/clock_network.h @@ -97,8 +97,14 @@ class ClockNetwork { std::string spine_name(const ClockSpineId& spine_id) const; vtr::Point spine_start_point(const ClockSpineId& spine_id) const; vtr::Point spine_end_point(const ClockSpineId& spine_id) const; + /* Find the intermediate drivers by the SB coordinate */ std::vector spine_intermediate_drivers( const ClockSpineId& spine_id, const vtr::Point& coord) const; + /* Find the coordinate of routing track which the intermediate driver will driver. Note that the coordinate may be different than the coordinate of intermeidate driver. One of the exceptions lies in the CHANX with INC direction, which starts actually on the routing tracks on the right side of a SB, resulting in x -> x + 1. Another exception is on the CHANY with INC direction, which starts actually on the routing tracks on the top side of a SB, resulting in y - > y + 1. This function is to provide an official conversion the coordinates. */ + vtr::Point spine_intermediate_driver_routing_track_coord(const ClockSpineId& spine_id, const vtr::Point& coord) const; + /* Find the intermediate drivers by the routing track starting point. Note that the routing track starting point may be different from the SB coordinate. See the exceptions in the spine_intermediate_driver_track_coord() */ + std::vector spine_intermediate_drivers_by_routing_track( + const ClockSpineId& spine_id, const vtr::Point& track_coord) const; /* Return the level where the spine locates in the multi-layer clock tree * structure */ @@ -273,7 +279,6 @@ class ClockNetwork { /* Validate the internal data. Required to ensure clean data before usage. If * validation is successful, is_valid() will return true */ bool validate() const; - private: /* Public invalidators/validators */ /* Ensure tree data is clean. All the spines are valid, and switch points are * valid */ diff --git a/openfpga/src/annotation/append_clock_rr_graph.cpp b/openfpga/src/annotation/append_clock_rr_graph.cpp index d118c38f1..f45480b00 100644 --- a/openfpga/src/annotation/append_clock_rr_graph.cpp +++ b/openfpga/src/annotation/append_clock_rr_graph.cpp @@ -762,17 +762,7 @@ static int add_rr_graph_opin2clk_intermediate_edges( /* Get the rr node of destination spine */ Direction des_spine_direction = clk_ntwk.spine_direction(ispine); ClockLevelId des_spine_level = clk_ntwk.spine_level(ispine); - vtr::Point des_coord(coord.x(), coord.y()); - /* des node depends on the type of routing track and direction. But it - * should be a starting point at the current SB[x][y] */ - if (des_spine_direction == Direction::INC && - clk_ntwk.spine_track_type(ispine) == CHANX) { - des_coord.set_x(coord.x() + 1); - } - if (des_spine_direction == Direction::INC && - clk_ntwk.spine_track_type(ispine) == CHANY) { - des_coord.set_y(coord.y() + 1); - } + vtr::Point des_coord = clk_ntwk.spine_intermediate_driver_routing_track_coord(ispine, coord); RRNodeId des_node = clk_rr_lookup.find_node( des_coord.x(), des_coord.y(), clk_tree, des_spine_level, ipin, des_spine_direction, verbose); diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index 428dddc12..78f32eb65 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -440,17 +440,7 @@ static int rec_expand_and_route_clock_spine( * global net is mapped to the internal driver, use it as the previous * node */ size_t use_int_driver = 0; - vtr::Point int_driver_coord(des_coord.x(), des_coord.y()); - if (des_spine_direction == Direction::INC && - clk_ntwk.spine_track_type(curr_spine) == CHANX) { - int_driver_coord.set_x(des_coord.x() - 1); - } - if (des_spine_direction == Direction::INC && - clk_ntwk.spine_track_type(curr_spine) == CHANY) { - int_driver_coord.set_y(des_coord.y() - 1); - } - - if (!clk_ntwk.spine_intermediate_drivers(curr_spine, int_driver_coord) + if (!clk_ntwk.spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) .empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { VTR_LOGV(verbose, @@ -517,17 +507,7 @@ static int rec_expand_and_route_clock_spine( * global net is mapped to the internal driver, use it as the previous * node */ size_t use_int_driver = 0; - vtr::Point int_driver_coord(des_coord.x(), des_coord.y()); - if (des_spine_direction == Direction::INC && - clk_ntwk.spine_track_type(curr_spine) == CHANX) { - int_driver_coord.set_x(des_coord.x() - 1); - } - if (des_spine_direction == Direction::INC && - clk_ntwk.spine_track_type(curr_spine) == CHANY) { - int_driver_coord.set_y(des_coord.y() - 1); - } - - if (!clk_ntwk.spine_intermediate_drivers(curr_spine, int_driver_coord) + if (!clk_ntwk.spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) .empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { VTR_LOGV( From 415fd9a8fa9d89c2cd8868e908abf1f210edc367 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 21 Sep 2024 21:39:30 -0700 Subject: [PATCH 14/16] [core] code format --- .../src/base/clock_network.cpp | 10 +++++---- .../src/base/clock_network.h | 21 +++++++++++++++---- .../src/annotation/append_clock_rr_graph.cpp | 4 +++- .../src/annotation/route_clock_rr_graph.cpp | 6 ++++-- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/libs/libclkarchopenfpga/src/base/clock_network.cpp b/libs/libclkarchopenfpga/src/base/clock_network.cpp index 7c11c4541..a1c16709c 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.cpp +++ b/libs/libclkarchopenfpga/src/base/clock_network.cpp @@ -283,7 +283,8 @@ std::vector ClockNetwork::spine_intermediate_drivers( return result->second; } -vtr::Point ClockNetwork::spine_intermediate_driver_routing_track_coord(const ClockSpineId& spine_id, const vtr::Point& coord) const { +vtr::Point ClockNetwork::spine_intermediate_driver_routing_track_coord( + const ClockSpineId& spine_id, const vtr::Point& coord) const { vtr::Point des_coord(coord.x(), coord.y()); Direction des_spine_direction = spine_direction(spine_id); /* des node depends on the type of routing track and direction. But it @@ -299,8 +300,9 @@ vtr::Point ClockNetwork::spine_intermediate_driver_routing_track_coord(cons return des_coord; } -std::vector ClockNetwork::spine_intermediate_drivers_by_routing_track( - const ClockSpineId& spine_id, const vtr::Point& track_coord) const { +std::vector +ClockNetwork::spine_intermediate_drivers_by_routing_track( + const ClockSpineId& spine_id, const vtr::Point& track_coord) const { vtr::Point des_coord(track_coord.x(), track_coord.y()); Direction des_spine_direction = spine_direction(spine_id); /* des node depends on the type of routing track and direction. But it @@ -313,7 +315,7 @@ std::vector ClockNetwork::spine_intermediate_drivers_by_r spine_track_type(spine_id) == CHANY) { des_coord.set_y(track_coord.y() - 1); } - return spine_intermediate_drivers(spine_id, des_coord); + return spine_intermediate_drivers(spine_id, des_coord); } ClockLevelId ClockNetwork::spine_level(const ClockSpineId& spine_id) const { diff --git a/libs/libclkarchopenfpga/src/base/clock_network.h b/libs/libclkarchopenfpga/src/base/clock_network.h index 494d17220..9f7fe0fcb 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.h +++ b/libs/libclkarchopenfpga/src/base/clock_network.h @@ -100,10 +100,22 @@ class ClockNetwork { /* Find the intermediate drivers by the SB coordinate */ std::vector spine_intermediate_drivers( const ClockSpineId& spine_id, const vtr::Point& coord) const; - /* Find the coordinate of routing track which the intermediate driver will driver. Note that the coordinate may be different than the coordinate of intermeidate driver. One of the exceptions lies in the CHANX with INC direction, which starts actually on the routing tracks on the right side of a SB, resulting in x -> x + 1. Another exception is on the CHANY with INC direction, which starts actually on the routing tracks on the top side of a SB, resulting in y - > y + 1. This function is to provide an official conversion the coordinates. */ - vtr::Point spine_intermediate_driver_routing_track_coord(const ClockSpineId& spine_id, const vtr::Point& coord) const; - /* Find the intermediate drivers by the routing track starting point. Note that the routing track starting point may be different from the SB coordinate. See the exceptions in the spine_intermediate_driver_track_coord() */ - std::vector spine_intermediate_drivers_by_routing_track( + /* Find the coordinate of routing track which the intermediate driver will + * driver. Note that the coordinate may be different than the coordinate of + * intermeidate driver. One of the exceptions lies in the CHANX with INC + * direction, which starts actually on the routing tracks on the right side of + * a SB, resulting in x -> x + 1. Another exception is on the CHANY with INC + * direction, which starts actually on the routing tracks on the top side of a + * SB, resulting in y - > y + 1. This function is to provide an official + * conversion the coordinates. */ + vtr::Point spine_intermediate_driver_routing_track_coord( + const ClockSpineId& spine_id, const vtr::Point& coord) const; + /* Find the intermediate drivers by the routing track starting point. Note + * that the routing track starting point may be different from the SB + * coordinate. See the exceptions in the + * spine_intermediate_driver_track_coord() */ + std::vector + spine_intermediate_drivers_by_routing_track( const ClockSpineId& spine_id, const vtr::Point& track_coord) const; /* Return the level where the spine locates in the multi-layer clock tree @@ -279,6 +291,7 @@ class ClockNetwork { /* Validate the internal data. Required to ensure clean data before usage. If * validation is successful, is_valid() will return true */ bool validate() const; + private: /* Public invalidators/validators */ /* Ensure tree data is clean. All the spines are valid, and switch points are * valid */ diff --git a/openfpga/src/annotation/append_clock_rr_graph.cpp b/openfpga/src/annotation/append_clock_rr_graph.cpp index f45480b00..87ee34b41 100644 --- a/openfpga/src/annotation/append_clock_rr_graph.cpp +++ b/openfpga/src/annotation/append_clock_rr_graph.cpp @@ -762,7 +762,9 @@ static int add_rr_graph_opin2clk_intermediate_edges( /* Get the rr node of destination spine */ Direction des_spine_direction = clk_ntwk.spine_direction(ispine); ClockLevelId des_spine_level = clk_ntwk.spine_level(ispine); - vtr::Point des_coord = clk_ntwk.spine_intermediate_driver_routing_track_coord(ispine, coord); + vtr::Point des_coord = + clk_ntwk.spine_intermediate_driver_routing_track_coord(ispine, + coord); RRNodeId des_node = clk_rr_lookup.find_node( des_coord.x(), des_coord.y(), clk_tree, des_spine_level, ipin, des_spine_direction, verbose); diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index 78f32eb65..3ad73ecc4 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -440,7 +440,8 @@ static int rec_expand_and_route_clock_spine( * global net is mapped to the internal driver, use it as the previous * node */ size_t use_int_driver = 0; - if (!clk_ntwk.spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) + if (!clk_ntwk + .spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) .empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { VTR_LOGV(verbose, @@ -507,7 +508,8 @@ static int rec_expand_and_route_clock_spine( * global net is mapped to the internal driver, use it as the previous * node */ size_t use_int_driver = 0; - if (!clk_ntwk.spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) + if (!clk_ntwk + .spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) .empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { VTR_LOGV( From f009180bbf473b95c1445f81bed51775222c19b1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 21 Sep 2024 21:53:53 -0700 Subject: [PATCH 15/16] [core] refactor --- .../src/annotation/route_clock_rr_graph.cpp | 160 ++++++++---------- 1 file changed, 74 insertions(+), 86 deletions(-) diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index 3ad73ecc4..a697ec534 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -293,6 +293,72 @@ static int route_spine_taps( return CMD_EXEC_SUCCESS; } + +/******************************************************************** + * Recursively route a clock spine on an existing routing resource graph + *******************************************************************/ +static int route_spine_intermediate_drivers( + VprRoutingAnnotation& vpr_routing_annotation, + const RRGraphView& rr_graph, const RRClockSpatialLookup& clk_rr_lookup, + const vtr::vector& rr_node_gnets, + const std::map& tree2clk_pin_map, + const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, + const ClockSpineId& curr_spine, const ClockTreePinId& curr_pin, + const vtr::Point& des_coord, const bool& verbose) { + Direction des_spine_direction = clk_ntwk.spine_direction(curr_spine); + ClockLevelId des_spine_level = clk_ntwk.spine_level(curr_spine); + RRNodeId des_node = clk_rr_lookup.find_node( + des_coord.x(), des_coord.y(), clk_tree, des_spine_level, curr_pin, + des_spine_direction, verbose); + VTR_ASSERT(rr_graph.valid_node(des_node)); + + /* Internal drivers may appear at the intermediate. Check if there are + * any defined and related rr_node found as incoming edges. If the + * global net is mapped to the internal driver, use it as the previous + * node */ + size_t use_int_driver = 0; + if (!clk_ntwk + .spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) + .empty() && + tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { + VTR_LOGV(verbose, + "Finding intermediate drivers at (%d, %d) for spine '%s'\n", + des_coord.x(), des_coord.y(), + clk_ntwk.spine_name(curr_spine).c_str()); + for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { + RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); + if (OPIN != rr_graph.node_type(opin_node)) { + continue; + } + if (rr_node_gnets[opin_node] != tree2clk_pin_map.at(curr_pin)) { + continue; + } + /* This is the opin node we need, use it as the internal driver */ + vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, + opin_node); + vpr_routing_annotation.set_rr_node_net(opin_node, + tree2clk_pin_map.at(curr_pin)); + vpr_routing_annotation.set_rr_node_net(des_node, + tree2clk_pin_map.at(curr_pin)); + use_int_driver++; + VTR_LOGV(verbose, + "Routed intermediate point of spine '%s' at " + "(%lu, %lu) using internal driver\n", + clk_ntwk.spine_name(curr_spine).c_str(), des_coord.x(), + des_coord.y()); + } + } + if (use_int_driver > 1) { + VTR_LOG_ERROR( + "Found %lu internal drivers for the intermediate point (%lu, %lu) " + "for " + "spine '%s'!\n Expect only 1!\n", + use_int_driver, des_coord.x(), des_coord.y(), + clk_ntwk.spine_name(curr_spine).c_str()); + } + return use_int_driver; +} + /******************************************************************** * Recursively route a clock spine on an existing routing resource graph * The strategy is to route spine one by one @@ -428,58 +494,13 @@ static int rec_expand_and_route_clock_spine( /* Skip the first stop */ if (icoord == spine_coords.size() - 1) { vtr::Point des_coord = spine_coords[icoord]; - Direction des_spine_direction = clk_ntwk.spine_direction(curr_spine); - ClockLevelId des_spine_level = clk_ntwk.spine_level(curr_spine); - RRNodeId des_node = clk_rr_lookup.find_node( - des_coord.x(), des_coord.y(), clk_tree, des_spine_level, curr_pin, - des_spine_direction, verbose); - VTR_ASSERT(rr_graph.valid_node(des_node)); - /* Internal drivers may appear at the intermediate. Check if there are - * any defined and related rr_node found as incoming edges. If the - * global net is mapped to the internal driver, use it as the previous - * node */ - size_t use_int_driver = 0; - if (!clk_ntwk - .spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) - .empty() && - tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { - VTR_LOGV(verbose, - "Finding intermediate drivers at (%d, %d) for spine '%s'\n", - des_coord.x(), des_coord.y(), - clk_ntwk.spine_name(curr_spine).c_str()); - for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { - RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); - if (OPIN != rr_graph.node_type(opin_node)) { - continue; - } - if (rr_node_gnets[opin_node] != tree2clk_pin_map.at(curr_pin)) { - continue; - } - /* This is the opin node we need, use it as the internal driver */ - vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, - opin_node); - vpr_routing_annotation.set_rr_node_net(opin_node, - tree2clk_pin_map.at(curr_pin)); - vpr_routing_annotation.set_rr_node_net(des_node, - tree2clk_pin_map.at(curr_pin)); - use_int_driver++; - VTR_LOGV(verbose, - "Routed intermediate point of spine '%s' at " - "(%lu, %lu) using internal driver\n", - clk_ntwk.spine_name(curr_spine).c_str(), des_coord.x(), - des_coord.y()); - } - } + int use_int_driver = route_spine_intermediate_drivers(vpr_routing_annotation, + rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, + clk_ntwk, clk_tree, curr_spine, curr_pin, des_coord, verbose); if (use_int_driver > 1) { - VTR_LOG_ERROR( - "Found %lu internal drivers for the intermediate point (%lu, %lu) " - "for " - "spine '%s'!\n Expect only 1!\n", - use_int_driver, des_coord.x(), des_coord.y(), - clk_ntwk.spine_name(curr_spine).c_str()); return CMD_EXEC_FATAL_ERROR; - } + } continue; } /* Connect only when next stop is used */ @@ -507,45 +528,12 @@ static int rec_expand_and_route_clock_spine( * any defined and related rr_node found as incoming edges. If the * global net is mapped to the internal driver, use it as the previous * node */ - size_t use_int_driver = 0; - if (!clk_ntwk - .spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) - .empty() && - tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { - VTR_LOGV( - verbose, "Finding intermediate drivers at (%d, %d) for spine '%s'\n", - des_coord.x(), des_coord.y(), clk_ntwk.spine_name(curr_spine).c_str()); - for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { - RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); - if (OPIN != rr_graph.node_type(opin_node)) { - continue; - } - if (rr_node_gnets[opin_node] != tree2clk_pin_map.at(curr_pin)) { - continue; - } - /* This is the opin node we need, use it as the internal driver */ - vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, - opin_node); - vpr_routing_annotation.set_rr_node_net(opin_node, - tree2clk_pin_map.at(curr_pin)); - vpr_routing_annotation.set_rr_node_net(des_node, - tree2clk_pin_map.at(curr_pin)); - use_int_driver++; - VTR_LOGV(verbose, - "Routed intermediate point of spine '%s' at " - "(%lu, %lu) using internal driver\n", - clk_ntwk.spine_name(curr_spine).c_str(), des_coord.x(), - des_coord.y()); - } - } + int use_int_driver = route_spine_intermediate_drivers(vpr_routing_annotation, + rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, + clk_ntwk, clk_tree, curr_spine, curr_pin, des_coord, verbose); if (use_int_driver > 1) { - VTR_LOG_ERROR( - "Found %lu internal drivers for the intermediate point (%lu, %lu) for " - "spine '%s'!\n Expect only 1!\n", - use_int_driver, des_coord.x(), des_coord.y(), - clk_ntwk.spine_name(curr_spine).c_str()); return CMD_EXEC_FATAL_ERROR; - } + } if (use_int_driver == 1) { continue; /* Used internal driver, early pass. */ } From c52610959c6304de76ac94efdf18a3564a40c27c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 21 Sep 2024 21:54:37 -0700 Subject: [PATCH 16/16] [core] code format --- .../src/annotation/route_clock_rr_graph.cpp | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index a697ec534..55e096566 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -293,13 +293,12 @@ static int route_spine_taps( return CMD_EXEC_SUCCESS; } - /******************************************************************** * Recursively route a clock spine on an existing routing resource graph *******************************************************************/ static int route_spine_intermediate_drivers( - VprRoutingAnnotation& vpr_routing_annotation, - const RRGraphView& rr_graph, const RRClockSpatialLookup& clk_rr_lookup, + VprRoutingAnnotation& vpr_routing_annotation, const RRGraphView& rr_graph, + const RRClockSpatialLookup& clk_rr_lookup, const vtr::vector& rr_node_gnets, const std::map& tree2clk_pin_map, const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, @@ -321,10 +320,9 @@ static int route_spine_intermediate_drivers( .spine_intermediate_drivers_by_routing_track(curr_spine, des_coord) .empty() && tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) { - VTR_LOGV(verbose, - "Finding intermediate drivers at (%d, %d) for spine '%s'\n", - des_coord.x(), des_coord.y(), - clk_ntwk.spine_name(curr_spine).c_str()); + VTR_LOGV( + verbose, "Finding intermediate drivers at (%d, %d) for spine '%s'\n", + des_coord.x(), des_coord.y(), clk_ntwk.spine_name(curr_spine).c_str()); for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); if (OPIN != rr_graph.node_type(opin_node)) { @@ -495,12 +493,13 @@ static int rec_expand_and_route_clock_spine( if (icoord == spine_coords.size() - 1) { vtr::Point des_coord = spine_coords[icoord]; - int use_int_driver = route_spine_intermediate_drivers(vpr_routing_annotation, - rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, - clk_ntwk, clk_tree, curr_spine, curr_pin, des_coord, verbose); + int use_int_driver = route_spine_intermediate_drivers( + vpr_routing_annotation, rr_graph, clk_rr_lookup, rr_node_gnets, + tree2clk_pin_map, clk_ntwk, clk_tree, curr_spine, curr_pin, des_coord, + verbose); if (use_int_driver > 1) { return CMD_EXEC_FATAL_ERROR; - } + } continue; } /* Connect only when next stop is used */ @@ -528,12 +527,13 @@ static int rec_expand_and_route_clock_spine( * any defined and related rr_node found as incoming edges. If the * global net is mapped to the internal driver, use it as the previous * node */ - int use_int_driver = route_spine_intermediate_drivers(vpr_routing_annotation, - rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, - clk_ntwk, clk_tree, curr_spine, curr_pin, des_coord, verbose); + int use_int_driver = route_spine_intermediate_drivers( + vpr_routing_annotation, rr_graph, clk_rr_lookup, rr_node_gnets, + tree2clk_pin_map, clk_ntwk, clk_tree, curr_spine, curr_pin, des_coord, + verbose); if (use_int_driver > 1) { return CMD_EXEC_FATAL_ERROR; - } + } if (use_int_driver == 1) { continue; /* Used internal driver, early pass. */ }