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