diff --git a/docs/source/manual/file_formats/clock_network.rst b/docs/source/manual/file_formats/clock_network.rst index 62e266347..c1fa9c695 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,62 @@ 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 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 + + + + + + + + .. _file_formats_clock_network_switch_point: Switch Point Settings 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 e74ded94f..4ec057d1c 100644 Binary files a/docs/source/manual/file_formats/figures/prog_clk_network_example_2x2_perimeter_cb.png and b/docs/source/manual/file_formats/figures/prog_clk_network_example_2x2_perimeter_cb.png differ 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..a1c16709c 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.cpp +++ b/libs/libclkarchopenfpga/src/base/clock_network.cpp @@ -270,6 +270,54 @@ 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; +} + +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_) { @@ -624,6 +672,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 +765,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 +867,53 @@ 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..9f7fe0fcb 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.h +++ b/libs/libclkarchopenfpga/src/base/clock_network.h @@ -97,6 +97,27 @@ 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 */ ClockLevelId spine_level(const ClockSpineId& spine_id) const; @@ -227,6 +248,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 +339,9 @@ 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..2a1838c07 100644 --- a/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h +++ b/libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h @@ -21,6 +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_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..418e43611 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,41 @@ 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 +394,15 @@ 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..23f54addf 100644 --- a/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp +++ b/libs/libclkarchopenfpga/src/io/write_xml_clock_network.cpp @@ -142,6 +142,41 @@ 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 +201,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); diff --git a/openfpga/src/annotation/append_clock_rr_graph.cpp b/openfpga/src/annotation/append_clock_rr_graph.cpp index 56862ac91..87ee34b41 100644 --- a/openfpga/src/annotation/append_clock_rr_graph.cpp +++ b/openfpga/src/annotation/append_clock_rr_graph.cpp @@ -736,6 +736,67 @@ 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 = + 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); + 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 +873,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); } /******************************************************************** diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index 79cad9ecb..55e096566 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -293,6 +293,70 @@ 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 @@ -427,6 +491,15 @@ 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]; + + 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 */ @@ -449,6 +522,22 @@ 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 */ + 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. */ + } + VTR_LOGV(verbose, "Routed backbone of spine '%s' from (x=%lu, y=%lu) to (x=%lu, " "y=%lu)...\n", 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..d2df1756d --- /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..f7a7dccd5 --- /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..8ae00e08f --- /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 @@ + + + + + + + + + + + +