Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support intermediate drivers in programmable clock network #1843

Merged
merged 16 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions docs/source/manual/file_formats/clock_network.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ The entry point of a clock tree must be at a valid connection block.
<clock_networks default_segment="<string>" default_tap_switch="<string>" default_driver_switch="<string>">
<clock_network name="<string>" global_port="<int>">
<spine name="<string>" start_x="<int>" start_y="<int>" end_x="<int>" end_y="<int>">
<intermediate_driver x="<int>" y="<int>">
<tap from_pin="<string>" to_pin="<string>"/>
</intermediate_driver>
<switch_point tap="<string>" x="<int>" y="<int>">
<internal_driver from_pin="<string>" to_pin="<string>"/>
</switch_point>
Expand Down Expand Up @@ -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="<int>"

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="<int>"

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="<string>"

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="<string>"

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

<clock_network name="clk_tree_0" global_port="clk[0:1]">
<!-- Some clock spines -->
<spine name="spine0" start_x="1" start_y="1" end_x="2" end_y="1">
<intermediate_driver x="1" y="1">
<tap from_pin="clb.O[0:1]" to_pin="clk[0:0]"/>
</intermediate_driver>
<spine>
</clock_network>


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

<tile name="clb">
<sub_tile name="clb">
<output name="O" num_pins="8"/>
</sub_tile>
</tile>


.. _file_formats_clock_network_switch_point:

Switch Point Settings
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions libs/libclkarchopenfpga/arch/example_internal_drivers.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<clock_networks default_segment="seg_len1" default_tap_switch="fast_switch" default_driver_switch="slow_switch">
<clock_network name="example_network" global_port="clk[0:7]">
<spine name="spine_lvl3" start_x="0" start_y="2" end_x="2" end_y="2">
<intermediate_driver x="1" y="2">
<tap from_pin="clb.O[0:0]" to_pin="clk[0:0]"/>
<tap from_pin="clb.O[1:1]" to_pin="clk[1:1]"/>
<tap from_pin="clb.O[2:3]" to_pin="clk[2:2]"/>
</intermediate_driver>
<switch_point tap="spine_lvl2_upper" x="2" y="2">
<internal_driver from_pin="clb.O[0:3]" to_pin="clk[1:1]"/>
</switch_point>
Expand Down
97 changes: 97 additions & 0 deletions libs/libclkarchopenfpga/src/base/clock_network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,54 @@ vtr::Point<int> ClockNetwork::spine_end_point(
return spine_end_points_[spine_id];
}

std::vector<ClockInternalDriverId> ClockNetwork::spine_intermediate_drivers(
const ClockSpineId& spine_id, const vtr::Point<int>& 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<ClockInternalDriverId>();
}
return result->second;
}

vtr::Point<int> ClockNetwork::spine_intermediate_driver_routing_track_coord(
const ClockSpineId& spine_id, const vtr::Point<int>& coord) const {
vtr::Point<int> 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<ClockInternalDriverId>
ClockNetwork::spine_intermediate_drivers_by_routing_track(
const ClockSpineId& spine_id, const vtr::Point<int>& track_coord) const {
vtr::Point<int> 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_) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<int>& 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) {
Expand Down
28 changes: 28 additions & 0 deletions libs/libclkarchopenfpga/src/base/clock_network.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,27 @@ class ClockNetwork {
std::string spine_name(const ClockSpineId& spine_id) const;
vtr::Point<int> spine_start_point(const ClockSpineId& spine_id) const;
vtr::Point<int> spine_end_point(const ClockSpineId& spine_id) const;
/* Find the intermediate drivers by the SB coordinate */
std::vector<ClockInternalDriverId> spine_intermediate_drivers(
const ClockSpineId& spine_id, const vtr::Point<int>& 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<int> spine_intermediate_driver_routing_track_coord(
const ClockSpineId& spine_id, const vtr::Point<int>& 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<ClockInternalDriverId>
spine_intermediate_drivers_by_routing_track(
const ClockSpineId& spine_id, const vtr::Point<int>& 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;
Expand Down Expand Up @@ -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<int>& 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);
Expand Down Expand Up @@ -314,6 +339,9 @@ class ClockNetwork {
vtr::vector<ClockSpineId, std::vector<vtr::Point<int>>> spine_switch_coords_;
vtr::vector<ClockSpineId, std::vector<std::vector<ClockInternalDriverId>>>
spine_switch_internal_drivers_;
vtr::vector<ClockSpineId,
std::map<std::string, std::vector<ClockInternalDriverId>>>
spine_intermediate_drivers_;
vtr::vector<ClockSpineId, ClockSpineId> spine_parents_;
vtr::vector<ClockSpineId, std::vector<ClockSpineId>> spine_children_;
vtr::vector<ClockSpineId, ClockTreeId> spine_parent_trees_;
Expand Down
9 changes: 9 additions & 0 deletions libs/libclkarchopenfpga/src/io/clock_network_xml_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
69 changes: 68 additions & 1 deletion libs/libclkarchopenfpga/src/io/read_xml_clock_network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,32 @@ static void read_xml_clock_spine_switch_point_internal_driver(
int_driver_to_port_name);
}

/********************************************************************
* Parse XML codes of a <tap> 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<int>& 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 <switch_point> to an object of ClockNetwork
*******************************************************************/
Expand Down Expand Up @@ -229,6 +255,41 @@ static void read_xml_clock_spine_switch_point(
}
}

/********************************************************************
* Parse XML codes of a <driver> 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<int>(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
*******************************************************************/
Expand Down Expand Up @@ -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});
}
}
}
Expand Down
Loading
Loading