From 4ea84e95a91dfc30afc1a904a8834473371e97cb Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Mon, 8 Sep 2025 14:39:37 -0400 Subject: [PATCH 01/11] Add interposer tag parsing This commit adds parsing logic for defining interposer-based architectures. --- doc/src/arch/reference.rst | 26 +++++++ .../interposer_diagram.png | Bin 0 -> 99674 bytes libs/libarchfpga/src/interposer_types.h | 51 ++++++++++++++ libs/libarchfpga/src/physical_types.h | 6 +- libs/libarchfpga/src/read_xml_arch_file.cpp | 65 ++++++++++++------ .../src/read_xml_arch_file_interposer.cpp | 44 ++++++++++++ .../src/read_xml_arch_file_interposer.h | 19 +++++ 7 files changed, 187 insertions(+), 24 deletions(-) create mode 100644 doc/src/arch/scatter_gather_images/interposer_diagram.png create mode 100644 libs/libarchfpga/src/interposer_types.h create mode 100644 libs/libarchfpga/src/read_xml_arch_file_interposer.cpp create mode 100644 libs/libarchfpga/src/read_xml_arch_file_interposer.h diff --git a/doc/src/arch/reference.rst b/doc/src/arch/reference.rst index aa10a43c541..267f8b9df5d 100644 --- a/doc/src/arch/reference.rst +++ b/doc/src/arch/reference.rst @@ -581,6 +581,32 @@ Grid Layout Example Example FPGA grid + +.. arch:tag:: + + :req_param dim: Dimension or axis of the cut. 'X' or 'x' means a horizontal cut while 'Y' or 'y' means a vertical cut. + :req_param loc: Location of the cut. Currently only absolute positions are supported. + + .. note:: Interposers are experimental and are currently not supported by VPR and using the related tags will not actually result in any changes to the flow. + Defines an interposer cut for modelling 2.5D interposer-based architectures. An interposer cut will cut all connections at location 'loc' along the axis 'dim' Leaving the two sides completely unconnected. + To reconnect the two sides, this tag can have multiple tags as children to specify the connection between the two sides. + +.. arch:tag:: + + :req_param sg_name: Name of the scatter-gather pattern to be used for the interdie connection. + :req_param sg_link: Name of the scatter-gather link to be used for the interdie connection. + :req_param offset_start: Starting point of scatter-gather instantiations. + :req_param offset_end: Ending point of scatter-gather instantiations + :req_param offset_increment: Increment/distance between scatter-gather instantiations. + :req_param num: Number of scatter-gather instantiations per switchblock location. + + Defines the interdie wiring between the two sides of the cut. Connectivity is defined using scatter-gather patterns. Starting at 'offset_start' from location of the cut and moving by 'offset_increment' until we reach the location of 'offset_end' away from the cut, 'num' scatter-gather patterns defined by 'sg_name' and 'sg_link' will be instantiated. + Note that these offset points always define the starting point of the scatter-gather pattern's sg_link. + + .. figure:: scatter_gather_images/interposer_diagram.png + + An example of how specifying interposers in VTR works. Connections between the two sides of a cut are first severed. The two sides are then reconnected using scatter_gather patterns. Note that there are 'num' of each pattern at each switchblock locations. + .. _arch_device_info: FPGA Device Information diff --git a/doc/src/arch/scatter_gather_images/interposer_diagram.png b/doc/src/arch/scatter_gather_images/interposer_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..ad0b3646b4ac19d27cf25b105589d4987575e151 GIT binary patch literal 99674 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 U5r+~N5r?QKXk5rTf91FT1?Px# literal 0 HcmV?d00001 diff --git a/libs/libarchfpga/src/interposer_types.h b/libs/libarchfpga/src/interposer_types.h new file mode 100644 index 00000000000..8d45a9bb9b4 --- /dev/null +++ b/libs/libarchfpga/src/interposer_types.h @@ -0,0 +1,51 @@ +#pragma once + +/** + * @file interposer_types.h + * @brief This file contains types used for parsing interposer-related tags such as and + * and converting that information into the device architecture-related data structures. + * + */ + +#include +#include +#include + +/** + * @brief Enum for direction of an interposer cut. X means horizontal cut and Y means vertical cut. + * + */ +enum class e_interposer_cut_dim { + X, + Y +}; + +// Lookup table for converting between a character and an e_interposer_cut_dim +inline const std::unordered_map CHAR_INTERPOSER_DIM_MAP = { + {'X', e_interposer_cut_dim::X}, + {'x', e_interposer_cut_dim::X}, + {'Y', e_interposer_cut_dim::Y}, + {'y', e_interposer_cut_dim::Y}}; + +/** + * @brief Struct containing information of interdire wires i.e. connections between the dies on an interposer + * + */ +struct t_interdie_wire_inf { + std::string sg_name; ///< Name of the scatter-gather pattern to be used for the interdie connection + std::string sg_link; ///< Name of the scatter-gather link to be used for the interdie connection + int offset_start; ///< Starting point of scatter-gather instantiations + int offset_end; ///< Ending point of scatter-gather instantiations + int offset_increment; ///< Increment/distance between scatter-gather instantiations + int num; ///< Number of scatter-gather instantiations per switchblock location +}; + +/** + * @brief Struct containing information of an interposer cut + * + */ +struct t_interposer_cut_inf { + e_interposer_cut_dim dim; ///< Dimension or axis of interposer cut + int loc; ///< Location of the cut on the grid + std::vector interdie_wires; ///< Connectivity specification between the two sides of the cut +}; diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 3418e7ba342..9c8d9dfc135 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -36,6 +36,7 @@ #include #include +#include "interposer_types.h" #include "vtr_ndmatrix.h" #include "vtr_bimap.h" #include "vtr_string_interning.h" @@ -361,13 +362,14 @@ struct t_grid_loc_def { // that come from a common definition. }; -enum GridDefType { +enum class GridDefType { AUTO, FIXED }; struct t_layer_def { - std::vector loc_defs; //The list of block location definitions for this layer specification + std::vector loc_defs; ///< List of block location definitions for this layer specification + std::vector interposer_cuts; ///< List of interposer cuts in this layer }; struct t_grid_def { diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index 676c78d0d35..43a418ae2bc 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -35,19 +35,19 @@ * the two files are swapped on command line. * */ - #include #include -#include #include #include #include +#include #include "logic_types.h" #include "physical_types.h" #include "pugixml.hpp" #include "pugixml_util.hpp" +#include "read_xml_arch_file_interposer.h" #include "read_xml_arch_file_vib.h" #include "vtr_assert.h" #include "vtr_log.h" @@ -71,6 +71,8 @@ #include "read_xml_arch_file_noc_tag.h" #include "read_xml_arch_file_sg.h" +#include "interposer_types.h" + using namespace std::string_literals; using pugiutil::ReqOpt; @@ -2579,7 +2581,7 @@ static t_grid_def process_grid_layout(vtr::string_internment& strings, num_of_avail_layer = get_number_of_layers(layout_type_tag, loc_data); bool has_layer = layout_type_tag.child("layer"); - //Determine the grid specification type + // Determine the grid specification type if (layout_type_tag.name() == std::string("auto_layout")) { expect_only_attributes(layout_type_tag, {"aspect_ratio"}, loc_data); @@ -2597,7 +2599,7 @@ static t_grid_def process_grid_layout(vtr::string_internment& strings, std::string name = get_attribute(layout_type_tag, "name", loc_data).value(); if (name == "auto") { - //We name as 'auto', so don't allow a user to specify it + // We name as 'auto', so don't allow a user to specify it archfpga_throw(loc_data.filename_c_str(), loc_data.line(layout_type_tag), vtr::string_fmt("The name '%s' is reserved for auto-sized layouts; please choose another name", name.c_str()).c_str()); } @@ -2612,27 +2614,26 @@ static t_grid_def process_grid_layout(vtr::string_internment& strings, grid_def.layers.resize(num_of_avail_layer); arch->layer_global_routing.resize(num_of_avail_layer); - //No layer tag is specified (only one die is specified in the arch file) - //Need to process layout_type_tag children to get block types locations in the grid + // No layer tag is specified (only one die is specified in the arch file) + // Need to process layout_type_tag children to get block types locations in the grid if (has_layer) { - std::set seen_die_numbers; //Check that die numbers in the specific layout tag are unique - //One or more than one layer tag is specified - auto layer_tag_specified = layout_type_tag.children("layer"); - for (auto layer_child : layer_tag_specified) { - int die_number; - bool has_global_routing; - //More than one layer tag is specified, meaning that multi-die FPGA is specified in the arch file - //Need to process each tag children to get block types locations for each grid - die_number = get_attribute(layer_child, "die", loc_data).as_int(0); - has_global_routing = get_attribute(layer_child, "has_prog_routing", loc_data, ReqOpt::OPTIONAL).as_bool(true); + std::unordered_set seen_die_numbers; //Check that die numbers in the specific layout tag are unique + for (pugi::xml_node layer_child : layout_type_tag.children("layer")) { + + // More than one layer tag is specified, meaning that multi-die FPGA is specified in the arch file + // Need to process each tag children to get block types locations for each grid + int die_number = get_attribute(layer_child, "die", loc_data).as_int(0); + bool has_global_routing = get_attribute(layer_child, "has_prog_routing", loc_data, ReqOpt::OPTIONAL).as_bool(true); arch->layer_global_routing.at(die_number) = has_global_routing; VTR_ASSERT(die_number >= 0 && die_number < num_of_avail_layer); - auto insert_res = seen_die_numbers.insert(die_number); - VTR_ASSERT_MSG(insert_res.second, "Two different layers with a same die number may have been specified in the Architecture file"); + + // If the die number is not actually inserted in the seen_die_numbers set, it means that it's a duplicate + auto [_, did_insert_in_set] = seen_die_numbers.insert(die_number); + VTR_ASSERT_MSG(did_insert_in_set, "Two different layers with a same die number may have been specified in the Architecture file"); process_block_type_locs(grid_def, die_number, strings, layer_child, loc_data); } } else { - //if only one die is available, then global routing resources must exist in that die + // If only one die is available, then global routing resources must exist in that die int die_number = 0; arch->layer_global_routing.at(die_number) = true; process_block_type_locs(grid_def, die_number, strings, layout_type_tag, loc_data); @@ -2646,9 +2647,29 @@ static void process_block_type_locs(t_grid_def& grid_def, pugi::xml_node layout_block_type_tag, const pugiutil::loc_data& loc_data) { //Process all the block location specifications - for (auto loc_spec_tag : layout_block_type_tag.children()) { - auto loc_type = loc_spec_tag.name(); - auto type_name = get_attribute(loc_spec_tag, "type", loc_data).value(); + for (pugi::xml_node loc_spec_tag : layout_block_type_tag.children()) { + const char* loc_type = loc_spec_tag.name(); + + // There are multiple attributes that are shared by every other tag that interposer + // tags do not have. For this reason we check if loc_spec_tag is an interposer tag + // and switch code paths if it is. + if (loc_type == std::string("interposer_cut")) { + if (grid_def.grid_type == GridDefType::AUTO) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(loc_spec_tag), "Interposers are not currently supported for auto sized devices."); + } + + t_interposer_cut_inf interposer_cut = parse_interposer_cut_tag(loc_spec_tag, loc_data); + + if ((interposer_cut.dim == e_interposer_cut_dim::X && interposer_cut.loc >= grid_def.height) || (interposer_cut.dim == e_interposer_cut_dim::Y && interposer_cut.loc >= grid_def.width)) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(loc_spec_tag), "Interposer cut dimensions are outside of device bounds"); + } + + grid_def.layers.at(die_number).interposer_cuts.push_back(interposer_cut); + continue; + } + + // Continue parsing for non-interposer tags + const char* type_name = get_attribute(loc_spec_tag, "type", loc_data).value(); int priority = get_attribute(loc_spec_tag, "priority", loc_data).as_int(); t_metadata_dict meta = process_meta_data(strings, loc_spec_tag, loc_data); diff --git a/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp b/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp new file mode 100644 index 00000000000..b7caa87c546 --- /dev/null +++ b/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp @@ -0,0 +1,44 @@ +#include "read_xml_arch_file_interposer.h" +#include +#include "interposer_types.h" +#include "read_xml_util.h" +#include "pugixml_util.hpp" +#include "arch_error.h" + +t_interposer_cut_inf parse_interposer_cut_tag(pugi::xml_node interposer_cut_tag, const pugiutil::loc_data& loc_data) { + t_interposer_cut_inf interposer; + + pugiutil::expect_only_attributes(interposer_cut_tag, {"dim", "loc"}, loc_data); + + std::string interposer_dim = pugiutil::get_attribute(interposer_cut_tag, "dim", loc_data).as_string(); + if (interposer_dim.size() != 1 || !CHAR_INTERPOSER_DIM_MAP.contains(interposer_dim[0])) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(interposer_cut_tag), "Interposer tag dimension must be a single character of either X, x, Y or y."); + } + + interposer.dim = CHAR_INTERPOSER_DIM_MAP.at(interposer_dim[0]); + + interposer.loc = pugiutil::get_attribute(interposer_cut_tag, "loc", loc_data).as_int(); + if (interposer.loc < 0) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(interposer_cut_tag), "Interposer location must be positive."); + } + + pugiutil::expect_only_children(interposer_cut_tag, {"interdie_wire"}, loc_data); + + for (pugi::xml_node interdie_wire_tag : interposer_cut_tag.children()) { + const std::vector interdie_wire_attributes = {{"sg", "sg_link", "offset_start", "offset_end", "offset_increment", "num"}}; + pugiutil::expect_only_attributes(interdie_wire_tag, interdie_wire_attributes, loc_data); + + t_interdie_wire_inf interdie_wire; + + interdie_wire.sg_name = pugiutil::get_attribute(interdie_wire_tag, "sg_name", loc_data).as_string(); + interdie_wire.sg_link = pugiutil::get_attribute(interdie_wire_tag, "sg_link", loc_data).as_string(); + interdie_wire.offset_start = pugiutil::get_attribute(interdie_wire_tag, "offset_start", loc_data).as_int(); + interdie_wire.offset_end = pugiutil::get_attribute(interdie_wire_tag, "offset_end", loc_data).as_int(); + interdie_wire.offset_increment = pugiutil::get_attribute(interdie_wire_tag, "offset_increment", loc_data).as_int(); + interdie_wire.num = pugiutil::get_attribute(interdie_wire_tag, "num", loc_data).as_int(); + + interposer.interdie_wires.push_back(interdie_wire); + } + + return interposer; +} diff --git a/libs/libarchfpga/src/read_xml_arch_file_interposer.h b/libs/libarchfpga/src/read_xml_arch_file_interposer.h new file mode 100644 index 00000000000..94e5a5f8c62 --- /dev/null +++ b/libs/libarchfpga/src/read_xml_arch_file_interposer.h @@ -0,0 +1,19 @@ +#pragma once + +/** + * @file read_xml_arch_file_interposer.h + * @brief This file contains functions related to parsing and processing interposer tags in the architecture file + * + */ + +#include "interposer_types.h" +#include "read_xml_util.h" + +/** + * @brief Parse an tag and its children + * + * @param interposer_cut_tag xml_node pointing to the tag + * @param loc_data Points to the location in the architecture file where the parser is reading. Used for priting error messages. + * @return t_interposer_cut_inf with parsed information of the tag + */ +t_interposer_cut_inf parse_interposer_cut_tag(pugi::xml_node interposer_cut_tag, const pugiutil::loc_data& loc_data); From bd53a1b31977cffde2c0d83ba5232a6506ddba03 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Mon, 15 Sep 2025 15:19:18 -0400 Subject: [PATCH 02/11] Change interposer scatter-gather offset to use expressions --- doc/src/arch/reference.rst | 8 ++++---- libs/libarchfpga/src/interposer_types.h | 12 +++++++++--- libs/libarchfpga/src/physical_types.h | 8 +------- .../src/read_xml_arch_file_interposer.cpp | 8 +++++--- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/doc/src/arch/reference.rst b/doc/src/arch/reference.rst index 267f8b9df5d..a374cbeb265 100644 --- a/doc/src/arch/reference.rst +++ b/doc/src/arch/reference.rst @@ -585,13 +585,13 @@ Grid Layout Example .. arch:tag:: :req_param dim: Dimension or axis of the cut. 'X' or 'x' means a horizontal cut while 'Y' or 'y' means a vertical cut. - :req_param loc: Location of the cut. Currently only absolute positions are supported. + :req_param loc: Location of the cut. Cuts are done above or to the right of the tiles at coordinate 'loc'. For example a cut with dim=x and loc=0 would cut the vertical wires above tiles in the 0th row. Currently only integer values are supported. .. note:: Interposers are experimental and are currently not supported by VPR and using the related tags will not actually result in any changes to the flow. Defines an interposer cut for modelling 2.5D interposer-based architectures. An interposer cut will cut all connections at location 'loc' along the axis 'dim' Leaving the two sides completely unconnected. To reconnect the two sides, this tag can have multiple tags as children to specify the connection between the two sides. -.. arch:tag:: +.. arch:tag:: :req_param sg_name: Name of the scatter-gather pattern to be used for the interdie connection. :req_param sg_link: Name of the scatter-gather link to be used for the interdie connection. @@ -601,11 +601,11 @@ Grid Layout Example :req_param num: Number of scatter-gather instantiations per switchblock location. Defines the interdie wiring between the two sides of the cut. Connectivity is defined using scatter-gather patterns. Starting at 'offset_start' from location of the cut and moving by 'offset_increment' until we reach the location of 'offset_end' away from the cut, 'num' scatter-gather patterns defined by 'sg_name' and 'sg_link' will be instantiated. - Note that these offset points always define the starting point of the scatter-gather pattern's sg_link. + Note that these offset points always define the starting point of the scatter-gather pattern's sg_link. offset_start, offset_end and offset_increment can be integer values or expressions involving W and H (device width and height.) .. figure:: scatter_gather_images/interposer_diagram.png - An example of how specifying interposers in VTR works. Connections between the two sides of a cut are first severed. The two sides are then reconnected using scatter_gather patterns. Note that there are 'num' of each pattern at each switchblock locations. + An example of how specifying interposers in VTR works. Connections between the two sides of a cut are first severed after which the two sides are reconnected using scatter_gather patterns. In this example the length of the sg_link wire used is 3. Note that there are 'num' of each pattern at each switchblock location. .. _arch_device_info: diff --git a/libs/libarchfpga/src/interposer_types.h b/libs/libarchfpga/src/interposer_types.h index 8d45a9bb9b4..f8fe0c82bc7 100644 --- a/libs/libarchfpga/src/interposer_types.h +++ b/libs/libarchfpga/src/interposer_types.h @@ -10,6 +10,7 @@ #include #include #include +#include "physical_types.h" /** * @brief Enum for direction of an interposer cut. X means horizontal cut and Y means vertical cut. @@ -34,9 +35,14 @@ inline const std::unordered_map CHAR_INTERPOSER_DIM_ struct t_interdie_wire_inf { std::string sg_name; ///< Name of the scatter-gather pattern to be used for the interdie connection std::string sg_link; ///< Name of the scatter-gather link to be used for the interdie connection - int offset_start; ///< Starting point of scatter-gather instantiations - int offset_end; ///< Ending point of scatter-gather instantiations - int offset_increment; ///< Increment/distance between scatter-gather instantiations + /** + * @brief + * Contains starting and ending point (both inclusive) of scatter-gather instantiations and the increment/distance between the instantiations. + * offset_definition.repeat_expr is not relevant for interdie wires and is not set to anything or used. + * + * Locations defined by this offset definition define the starting point or the gathering point of the SG pattern. The end or scatter point of the SG pattern is defined by the sg_link. + */ + t_grid_loc_spec offset_definition; int num; ///< Number of scatter-gather instantiations per switchblock location }; diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 9c8d9dfc135..ba2a50c3124 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -36,7 +36,6 @@ #include #include -#include "interposer_types.h" #include "vtr_ndmatrix.h" #include "vtr_bimap.h" #include "vtr_string_interning.h" @@ -81,6 +80,7 @@ class t_pb_graph_edge; struct t_cluster_placement_primitive; struct t_arch; enum class e_sb_type; +struct t_interposer_cut_inf; /****************************************************************************/ /* FPGA metadata types */ @@ -255,12 +255,6 @@ typedef enum e_power_estimation_method_ t_power_estimation_method; * from expr_eval.h. */ struct t_grid_loc_spec { - t_grid_loc_spec(std::string start, std::string end, std::string repeat, std::string incr) - : start_expr(std::move(start)) - , end_expr(std::move(end)) - , repeat_expr(std::move(repeat)) - , incr_expr(std::move(incr)) {} - std::string start_expr; //Starting position (inclusive) std::string end_expr; //Ending position (inclusive) diff --git a/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp b/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp index b7caa87c546..b97cf9e55c3 100644 --- a/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp @@ -32,9 +32,11 @@ t_interposer_cut_inf parse_interposer_cut_tag(pugi::xml_node interposer_cut_tag, interdie_wire.sg_name = pugiutil::get_attribute(interdie_wire_tag, "sg_name", loc_data).as_string(); interdie_wire.sg_link = pugiutil::get_attribute(interdie_wire_tag, "sg_link", loc_data).as_string(); - interdie_wire.offset_start = pugiutil::get_attribute(interdie_wire_tag, "offset_start", loc_data).as_int(); - interdie_wire.offset_end = pugiutil::get_attribute(interdie_wire_tag, "offset_end", loc_data).as_int(); - interdie_wire.offset_increment = pugiutil::get_attribute(interdie_wire_tag, "offset_increment", loc_data).as_int(); + + interdie_wire.offset_definition.start_expr = pugiutil::get_attribute(interdie_wire_tag, "offset_start", loc_data).as_string(); + interdie_wire.offset_definition.end_expr = pugiutil::get_attribute(interdie_wire_tag, "offset_end", loc_data).as_string(); + interdie_wire.offset_definition.incr_expr = pugiutil::get_attribute(interdie_wire_tag, "offset_increment", loc_data).as_string(); + interdie_wire.num = pugiutil::get_attribute(interdie_wire_tag, "num", loc_data).as_int(); interposer.interdie_wires.push_back(interdie_wire); From 3459bbebbebb09781c6f1e5e32c004f78980d655 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Mon, 15 Sep 2025 15:21:06 -0400 Subject: [PATCH 03/11] Improve comments --- libs/libarchfpga/src/interposer_types.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/libarchfpga/src/interposer_types.h b/libs/libarchfpga/src/interposer_types.h index f8fe0c82bc7..828e441ffde 100644 --- a/libs/libarchfpga/src/interposer_types.h +++ b/libs/libarchfpga/src/interposer_types.h @@ -51,7 +51,7 @@ struct t_interdie_wire_inf { * */ struct t_interposer_cut_inf { - e_interposer_cut_dim dim; ///< Dimension or axis of interposer cut - int loc; ///< Location of the cut on the grid - std::vector interdie_wires; ///< Connectivity specification between the two sides of the cut + e_interposer_cut_dim dim; ///< Dimension or axis of interposer cut. + int loc; ///< Location of the cut on the grid. Locations start from zero and cuts will happen above or to the right of the tiles at location=loc. + std::vector interdie_wires; ///< Connectivity specification between the two sides of the cut. }; From 18193e923a05eaa984bda2e440e60cddf033d31d Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Mon, 15 Sep 2025 15:31:26 -0400 Subject: [PATCH 04/11] Enhance interposer diagram --- .../interposer_diagram.png | Bin 99674 -> 185232 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/src/arch/scatter_gather_images/interposer_diagram.png b/doc/src/arch/scatter_gather_images/interposer_diagram.png index ad0b3646b4ac19d27cf25b105589d4987575e151..be0b335985d786694e30214e404ba312cb862b59 100644 GIT binary patch literal 185232 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 literal 99674 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 U5r+~N5r?QKXk5rTf91FT1?Px# From 12307454943d037a29c3c5a0e5fe27e01e1eefea Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Mon, 15 Sep 2025 18:47:46 -0400 Subject: [PATCH 05/11] Fix circular dependency between physical_types and interposer_types --- libs/libarchfpga/src/grid_types.h | 131 ++++++++++++++++++++++++ libs/libarchfpga/src/interposer_types.h | 2 +- libs/libarchfpga/src/physical_types.h | 112 +------------------- 3 files changed, 133 insertions(+), 112 deletions(-) create mode 100644 libs/libarchfpga/src/grid_types.h diff --git a/libs/libarchfpga/src/grid_types.h b/libs/libarchfpga/src/grid_types.h new file mode 100644 index 00000000000..cb634e3ca73 --- /dev/null +++ b/libs/libarchfpga/src/grid_types.h @@ -0,0 +1,131 @@ +#pragma once + +/** + * @file grid_types.h + * @brief FPGA grid layout data types + */ + +#include +#include + +// Forward declerations + +struct t_metadata_dict; + +/** + * @brief Grid location specification + * Each member is a formula evaluated in terms of 'W' (device width), + * and 'H' (device height). Formulas can be evaluated using parse_formula() + * from expr_eval.h. + */ +struct t_grid_loc_spec { + std::string start_expr; //Starting position (inclusive) + std::string end_expr; //Ending position (inclusive) + + std::string repeat_expr; //Distance between repeated + // region instances + + std::string incr_expr; //Distance between block instantiations + // with the region +}; + +/* Definition of how to place physical logic block in the grid. + + */ + +/** + * @brief Definition of how to place physical logic block in the grid. + * @details This defines a region of the grid to be set to a specific type + * (provided its priority is high enough to override other blocks). + * + * The diagram below illustrates the layout specification. + * + * +----+ +----+ +----+ + * | | | | | | + * | | | | ... | | + * | | | | | | + * +----+ +----+ +----+ + * + * . . . + * . . . + * . . . + * + * +----+ +----+ +----+ + * | | | | | | + * | | | | ... | | + * | | | | | | + * +----+ +----+ +----+ + * ^ + * | + * repeaty | + * | + * v (endx,endy) + * +----+ +----+ +----+ + * | | | | | | + * | | | | ... | | + * | | | | | | + * +----+ +----+ +----+ + * (startx,starty) + * <--------------> + * repeatx + * + * startx/endx and endx/endy define a rectangular region instances dimensions. + * The region instance is then repeated every repeatx/repeaty (if specified). + * + * Within a particular region instance a block of block_type is laid down every + * incrx/incry units (if not specified defaults to block width/height): + * + * + * * = an instance of block_type within the region + * + * +------------------------------+ + * |* * * *| + * | | + * | | + * | | + * | | + * | | + * |* * * *| + * ^ | | + * | | | + * incry | | | + * | | | + * v | | + * |* * * *| + * +------------------------------+ + * + * <-------> + * incrx + * + * In the above diagram incrx = 10, and incry = 6 + */ +struct t_grid_loc_def { + t_grid_loc_def(std::string block_type_val, int priority_val) + : block_type(std::move(block_type_val)) + , priority(priority_val) + , x("0", "W-1", "max(w+1,W)", "w") //Fill in x direction, no repeat, incr by block width + , y("0", "H-1", "max(h+1,H)", "h") //Fill in y direction, no repeat, incr by block height + {} + + std::string block_type; //The block type name + + int priority = 0; //Priority of the specification. + // In case of conflicting specifications + // the largest priority wins. + + t_grid_loc_spec x; //Horizontal location specification + t_grid_loc_spec y; //Vertical location specification + + // When 1 metadata tag is split among multiple t_grid_loc_def, one + // t_grid_loc_def is arbitrarily chosen to own the metadata, and the other + // t_grid_loc_def point to the owned version. + std::unique_ptr owned_meta; + t_metadata_dict* meta = nullptr; // Metadata for this location definition. This + // metadata may be shared with multiple grid_locs + // that come from a common definition. +}; + +enum class GridDefType { + AUTO, + FIXED +}; \ No newline at end of file diff --git a/libs/libarchfpga/src/interposer_types.h b/libs/libarchfpga/src/interposer_types.h index 828e441ffde..efdfa24be93 100644 --- a/libs/libarchfpga/src/interposer_types.h +++ b/libs/libarchfpga/src/interposer_types.h @@ -10,7 +10,7 @@ #include #include #include -#include "physical_types.h" +#include "grid_types.h" /** * @brief Enum for direction of an interposer cut. X means horizontal cut and Y means vertical cut. diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index ba2a50c3124..8f94e6aa40c 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -48,6 +48,7 @@ #include "vib_inf.h" #include "scatter_gather_types.h" +#include "interposer_types.h" //Forward declarations struct t_clock_network; @@ -249,117 +250,6 @@ typedef enum e_power_estimation_method_ t_power_estimation_method; /*************************************************************************************************/ /* FPGA grid layout data types */ /*************************************************************************************************/ -/* Grid location specification - * Each member is a formula evaluated in terms of 'W' (device width), - * and 'H' (device height). Formulas can be evaluated using parse_formula() - * from expr_eval.h. - */ -struct t_grid_loc_spec { - std::string start_expr; //Starting position (inclusive) - std::string end_expr; //Ending position (inclusive) - - std::string repeat_expr; //Distance between repeated - // region instances - - std::string incr_expr; //Distance between block instantiations - // with the region -}; - -/* Definition of how to place physical logic block in the grid. - * This defines a region of the grid to be set to a specific type - * (provided its priority is high enough to override other blocks). - * - * The diagram below illustrates the layout specification. - * - * +----+ +----+ +----+ - * | | | | | | - * | | | | ... | | - * | | | | | | - * +----+ +----+ +----+ - * - * . . . - * . . . - * . . . - * - * +----+ +----+ +----+ - * | | | | | | - * | | | | ... | | - * | | | | | | - * +----+ +----+ +----+ - * ^ - * | - * repeaty | - * | - * v (endx,endy) - * +----+ +----+ +----+ - * | | | | | | - * | | | | ... | | - * | | | | | | - * +----+ +----+ +----+ - * (startx,starty) - * <--------------> - * repeatx - * - * startx/endx and endx/endy define a rectangular region instances dimensions. - * The region instance is then repeated every repeatx/repeaty (if specified). - * - * Within a particular region instance a block of block_type is laid down every - * incrx/incry units (if not specified defaults to block width/height): - * - * - * * = an instance of block_type within the region - * - * +------------------------------+ - * |* * * *| - * | | - * | | - * | | - * | | - * | | - * |* * * *| - * ^ | | - * | | | - * incry | | | - * | | | - * v | | - * |* * * *| - * +------------------------------+ - * - * <-------> - * incrx - * - * In the above diagram incrx = 10, and incry = 6 - */ -struct t_grid_loc_def { - t_grid_loc_def(std::string block_type_val, int priority_val) - : block_type(std::move(block_type_val)) - , priority(priority_val) - , x("0", "W-1", "max(w+1,W)", "w") //Fill in x direction, no repeat, incr by block width - , y("0", "H-1", "max(h+1,H)", "h") //Fill in y direction, no repeat, incr by block height - {} - - std::string block_type; //The block type name - - int priority = 0; //Priority of the specification. - // In case of conflicting specifications - // the largest priority wins. - - t_grid_loc_spec x; //Horizontal location specification - t_grid_loc_spec y; //Vertical location specification - - // When 1 metadata tag is split among multiple t_grid_loc_def, one - // t_grid_loc_def is arbitrarily chosen to own the metadata, and the other - // t_grid_loc_def point to the owned version. - std::unique_ptr owned_meta; - t_metadata_dict* meta = nullptr; // Metadata for this location definition. This - // metadata may be shared with multiple grid_locs - // that come from a common definition. -}; - -enum class GridDefType { - AUTO, - FIXED -}; struct t_layer_def { std::vector loc_defs; ///< List of block location definitions for this layer specification From 69119865a73efd89d63cb3dcf2b0de651c812461 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Mon, 15 Sep 2025 19:51:24 -0400 Subject: [PATCH 06/11] Fix compile error for t_grid_loc_spec constructor --- libs/libarchfpga/src/grid_types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/libarchfpga/src/grid_types.h b/libs/libarchfpga/src/grid_types.h index cb634e3ca73..2a5ce25d06e 100644 --- a/libs/libarchfpga/src/grid_types.h +++ b/libs/libarchfpga/src/grid_types.h @@ -103,8 +103,8 @@ struct t_grid_loc_def { t_grid_loc_def(std::string block_type_val, int priority_val) : block_type(std::move(block_type_val)) , priority(priority_val) - , x("0", "W-1", "max(w+1,W)", "w") //Fill in x direction, no repeat, incr by block width - , y("0", "H-1", "max(h+1,H)", "h") //Fill in y direction, no repeat, incr by block height + , x{"0", "W-1", "max(w+1,W)", "w"} //Fill in x direction, no repeat, incr by block width + , y{"0", "H-1", "max(h+1,H)", "h"} //Fill in y direction, no repeat, incr by block height {} std::string block_type; //The block type name From 7b887c37c5f3ad9e3687bb55f682db4a21475f87 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Thu, 18 Sep 2025 16:21:55 -0400 Subject: [PATCH 07/11] Add comments and rename GridDefType --- libs/libarchfpga/src/echo_arch.cpp | 4 +- libs/libarchfpga/src/grid_types.h | 58 ++++++++++--------- libs/libarchfpga/src/physical_types.h | 2 +- .../src/read_fpga_interchange_arch.cpp | 2 +- libs/libarchfpga/src/read_xml_arch_file.cpp | 6 +- vpr/src/base/setup_grid.cpp | 10 ++-- vpr/test/test_interchange_device.cpp | 2 +- 7 files changed, 44 insertions(+), 40 deletions(-) diff --git a/libs/libarchfpga/src/echo_arch.cpp b/libs/libarchfpga/src/echo_arch.cpp index 70456a8a204..115616a0edb 100644 --- a/libs/libarchfpga/src/echo_arch.cpp +++ b/libs/libarchfpga/src/echo_arch.cpp @@ -101,10 +101,10 @@ void PrintArchInfo(FILE* Echo, const t_arch* arch) { //Layout fprintf(Echo, "*************************************************\n"); for (const auto& grid_layout : arch->grid_layouts) { - if (grid_layout.grid_type == GridDefType::AUTO) { + if (grid_layout.grid_type == e_grid_def_type::AUTO) { fprintf(Echo, "Layout: '%s' Type: auto Aspect_Ratio: %f\n", grid_layout.name.c_str(), grid_layout.aspect_ratio); } else { - VTR_ASSERT(grid_layout.grid_type == GridDefType::FIXED); + VTR_ASSERT(grid_layout.grid_type == e_grid_def_type::FIXED); fprintf(Echo, "Layout: '%s' Type: fixed Width: %d Height %d\n", grid_layout.name.c_str(), grid_layout.width, grid_layout.height); } } diff --git a/libs/libarchfpga/src/grid_types.h b/libs/libarchfpga/src/grid_types.h index 2a5ce25d06e..7f469e06d38 100644 --- a/libs/libarchfpga/src/grid_types.h +++ b/libs/libarchfpga/src/grid_types.h @@ -13,26 +13,21 @@ struct t_metadata_dict; /** - * @brief Grid location specification + * @brief Grid location specification, used to capture user's intended architecture specification. + * will be later turned into a flattened device grid according to the device's size. * Each member is a formula evaluated in terms of 'W' (device width), * and 'H' (device height). Formulas can be evaluated using parse_formula() * from expr_eval.h. */ struct t_grid_loc_spec { - std::string start_expr; //Starting position (inclusive) - std::string end_expr; //Ending position (inclusive) + std::string start_expr; /// * repeatx * - * startx/endx and endx/endy define a rectangular region instances dimensions. + * startx/endx and endx/endy define a rectangular region instance's dimensions. * The region instance is then repeated every repeatx/repeaty (if specified). * * Within a particular region instance a block of block_type is laid down every @@ -103,29 +98,38 @@ struct t_grid_loc_def { t_grid_loc_def(std::string block_type_val, int priority_val) : block_type(std::move(block_type_val)) , priority(priority_val) - , x{"0", "W-1", "max(w+1,W)", "w"} //Fill in x direction, no repeat, incr by block width - , y{"0", "H-1", "max(h+1,H)", "h"} //Fill in y direction, no repeat, incr by block height + , x{"0", "W-1", "max(w+1,W)", "w"} // Fill in x direction, no repeat, incr by block width + , y{"0", "H-1", "max(h+1,H)", "h"} // Fill in y direction, no repeat, incr by block height {} - std::string block_type; //The block type name + std::string block_type; ///< The block type name - int priority = 0; //Priority of the specification. - // In case of conflicting specifications - // the largest priority wins. + int priority = 0; ///< Priority of the specification. In case of conflicting specifications the largest priority wins. - t_grid_loc_spec x; //Horizontal location specification - t_grid_loc_spec y; //Vertical location specification + t_grid_loc_spec x; ///< Horizontal location specification + t_grid_loc_spec y; ///< Vertical location specification - // When 1 metadata tag is split among multiple t_grid_loc_def, one - // t_grid_loc_def is arbitrarily chosen to own the metadata, and the other - // t_grid_loc_def point to the owned version. + /** + * @brief When 1 metadata tag is split among multiple t_grid_loc_def, one + * t_grid_loc_def is arbitrarily chosen to own the metadata, and the other + * t_grid_loc_def point to the owned version. + * + */ std::unique_ptr owned_meta; - t_metadata_dict* meta = nullptr; // Metadata for this location definition. This - // metadata may be shared with multiple grid_locs - // that come from a common definition. + + /** + * @brief Metadata for this location definition. This + * metadata may be shared with multiple grid_locs + * that come from a common definition. + */ + t_metadata_dict* meta = nullptr; }; -enum class GridDefType { +/** + * @brief Enum for specfying if the architecture grid specification is for an auto sized device (variable size) + * or a fixed size device. + */ +enum class e_grid_def_type { AUTO, FIXED }; \ No newline at end of file diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 8f94e6aa40c..af0aba4a06d 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -257,7 +257,7 @@ struct t_layer_def { }; struct t_grid_def { - GridDefType grid_type = GridDefType::AUTO; //The type of this grid specification + e_grid_def_type grid_type = e_grid_def_type::AUTO; //The type of this grid specification std::string name = ""; //The name of this device diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index d9ad5853570..6268ede1b01 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -2244,7 +2244,7 @@ struct ArchReader { grid_def.width += 2; grid_def.height += 2; - grid_def.grid_type = GridDefType::FIXED; + grid_def.grid_type = e_grid_def_type::FIXED; if (name == "auto") { // At the moment, the interchange specifies fixed-layout only architectures, diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index 43a418ae2bc..92cbb979b16 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -2585,7 +2585,7 @@ static t_grid_def process_grid_layout(vtr::string_internment& strings, if (layout_type_tag.name() == std::string("auto_layout")) { expect_only_attributes(layout_type_tag, {"aspect_ratio"}, loc_data); - grid_def.grid_type = GridDefType::AUTO; + grid_def.grid_type = e_grid_def_type::AUTO; grid_def.aspect_ratio = get_attribute(layout_type_tag, "aspect_ratio", loc_data, ReqOpt::OPTIONAL).as_float(1.); grid_def.name = "auto"; @@ -2593,7 +2593,7 @@ static t_grid_def process_grid_layout(vtr::string_internment& strings, } else if (layout_type_tag.name() == std::string("fixed_layout")) { expect_only_attributes(layout_type_tag, {"width", "height", "name"}, loc_data); - grid_def.grid_type = GridDefType::FIXED; + grid_def.grid_type = e_grid_def_type::FIXED; grid_def.width = get_attribute(layout_type_tag, "width", loc_data).as_int(); grid_def.height = get_attribute(layout_type_tag, "height", loc_data).as_int(); std::string name = get_attribute(layout_type_tag, "name", loc_data).value(); @@ -2654,7 +2654,7 @@ static void process_block_type_locs(t_grid_def& grid_def, // tags do not have. For this reason we check if loc_spec_tag is an interposer tag // and switch code paths if it is. if (loc_type == std::string("interposer_cut")) { - if (grid_def.grid_type == GridDefType::AUTO) { + if (grid_def.grid_type == e_grid_def_type::AUTO) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(loc_spec_tag), "Interposers are not currently supported for auto sized devices."); } diff --git a/vpr/src/base/setup_grid.cpp b/vpr/src/base/setup_grid.cpp index ad8030cd9ec..5d3d03744ec 100644 --- a/vpr/src/base/setup_grid.cpp +++ b/vpr/src/base/setup_grid.cpp @@ -84,7 +84,7 @@ DeviceGrid create_device_grid(const std::string& layout_name, const std::vector< if (layout_name == "auto") { VTR_ASSERT(!grid_layouts.empty()); //Auto-size - if (grid_layouts[0].grid_type == GridDefType::AUTO) { + if (grid_layouts[0].grid_type == e_grid_def_type::AUTO) { //Auto layout of the specified dimensions return build_device_grid(grid_layouts[0], width, height); } else { @@ -152,7 +152,7 @@ static DeviceGrid auto_size_device_grid(const std::vector& grid_layo DeviceGrid grid; auto is_auto_grid_def = [](const t_grid_def& grid_def) { - return grid_def.grid_type == GridDefType::AUTO; + return grid_def.grid_type == e_grid_def_type::AUTO; }; auto auto_layout_itr = std::find_if(grid_layouts.begin(), grid_layouts.end(), is_auto_grid_def); @@ -230,8 +230,8 @@ static DeviceGrid auto_size_device_grid(const std::vector& grid_layo grid_layouts_view.push_back(&layout); } auto area_cmp = [](const t_grid_def* lhs, const t_grid_def* rhs) { - VTR_ASSERT(lhs->grid_type == GridDefType::FIXED); - VTR_ASSERT(rhs->grid_type == GridDefType::FIXED); + VTR_ASSERT(lhs->grid_type == e_grid_def_type::FIXED); + VTR_ASSERT(rhs->grid_type == e_grid_def_type::FIXED); int lhs_area = lhs->width * lhs->height; int rhs_area = rhs->width * rhs->height; @@ -339,7 +339,7 @@ static bool grid_satisfies_instance_counts(const DeviceGrid& grid, const std::ma ///@brief Build the specified device grid static DeviceGrid build_device_grid(const t_grid_def& grid_def, size_t grid_width, size_t grid_height, bool warn_out_of_range, const std::vector& limiting_resources) { - if (grid_def.grid_type == GridDefType::FIXED) { + if (grid_def.grid_type == e_grid_def_type::FIXED) { if (grid_def.width != int(grid_width) || grid_def.height != int(grid_height)) { VPR_FATAL_ERROR(VPR_ERROR_OTHER, "Requested grid size (%zu%zu) does not match fixed device size (%dx%d)", diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp index 324d757830a..f698576d8dd 100644 --- a/vpr/test/test_interchange_device.cpp +++ b/vpr/test/test_interchange_device.cpp @@ -51,7 +51,7 @@ TEST_CASE("read_interchange_layout", "[vpr]") { FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); auto& gd = arch.grid_layouts[0]; - REQUIRE(gd.grid_type == GridDefType::FIXED); + REQUIRE(gd.grid_type == e_grid_def_type::FIXED); REQUIRE(gd.height == 12); REQUIRE(gd.width == 12); From 6d3a21d6c7e8582ed4bf6be51204e9e7d899a734 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Thu, 18 Sep 2025 16:49:34 -0400 Subject: [PATCH 08/11] Add grid specification types to documents --- doc/src/api/vpr/grid.rst | 17 ++++++++++++++++- libs/libarchfpga/src/grid_types.h | 3 +-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/src/api/vpr/grid.rst b/doc/src/api/vpr/grid.rst index 7fa477f2949..901b3a98d6f 100644 --- a/doc/src/api/vpr/grid.rst +++ b/doc/src/api/vpr/grid.rst @@ -7,4 +7,19 @@ DeviceGrid .. doxygenclass:: DeviceGrid :project: vpr - :members: \ No newline at end of file + :members: + +------- +Grid Specification Types +------- + +These types are used to capture user's intended architecture specification and will be later turned into a flattened device grid according to the device's size. + +.. doxygenstruct:: t_grid_loc_spec + :project: vpr + :members: + +.. doxygenstruct:: t_grid_loc_def + :project: vpr + :members: + \ No newline at end of file diff --git a/libs/libarchfpga/src/grid_types.h b/libs/libarchfpga/src/grid_types.h index 7f469e06d38..6c2afd769bc 100644 --- a/libs/libarchfpga/src/grid_types.h +++ b/libs/libarchfpga/src/grid_types.h @@ -13,8 +13,7 @@ struct t_metadata_dict; /** - * @brief Grid location specification, used to capture user's intended architecture specification. - * will be later turned into a flattened device grid according to the device's size. + * @brief Grid location specification * Each member is a formula evaluated in terms of 'W' (device width), * and 'H' (device height). Formulas can be evaluated using parse_formula() * from expr_eval.h. From 0298a690ca063e1bb5510fbc0faeed1cb5449cb9 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Fri, 19 Sep 2025 15:27:37 -0400 Subject: [PATCH 09/11] Add LibArchFPGA tab to doxygen generated docs --- doc/src/api/libarchfpga/grid_types.rst | 14 ++++++++ doc/src/api/libarchfpga/index.rst | 12 +++++++ doc/src/api/libarchfpga/interposer_types.rst | 13 ++++++++ doc/src/api/libarchfpga/physical_types.rst | 33 +++++++++++++++++++ .../api/libarchfpga/scatter_gather_types.rst | 17 ++++++++++ doc/src/api/vpr/grid.rst | 17 +--------- doc/src/index.rst | 1 + 7 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 doc/src/api/libarchfpga/grid_types.rst create mode 100644 doc/src/api/libarchfpga/index.rst create mode 100644 doc/src/api/libarchfpga/interposer_types.rst create mode 100644 doc/src/api/libarchfpga/physical_types.rst create mode 100644 doc/src/api/libarchfpga/scatter_gather_types.rst diff --git a/doc/src/api/libarchfpga/grid_types.rst b/doc/src/api/libarchfpga/grid_types.rst new file mode 100644 index 00000000000..2d119d2711a --- /dev/null +++ b/doc/src/api/libarchfpga/grid_types.rst @@ -0,0 +1,14 @@ +======== +Grid Specification Types +======== + +These types are used to capture user's intended architecture specification and will be later turned into a flattened device grid according to the device's size. + +.. doxygenstruct:: t_grid_loc_spec + :project: vpr + :members: + +.. doxygenstruct:: t_grid_loc_def + :project: vpr + :members: + \ No newline at end of file diff --git a/doc/src/api/libarchfpga/index.rst b/doc/src/api/libarchfpga/index.rst new file mode 100644 index 00000000000..af2000bee41 --- /dev/null +++ b/doc/src/api/libarchfpga/index.rst @@ -0,0 +1,12 @@ +.. _libarchfpga: + +LIBARCHFPGA +======= + +.. toctree:: + :maxdepth: 1 + + physical_types + grid_types + scatter_gather_types + interposer_types \ No newline at end of file diff --git a/doc/src/api/libarchfpga/interposer_types.rst b/doc/src/api/libarchfpga/interposer_types.rst new file mode 100644 index 00000000000..1e54a242c27 --- /dev/null +++ b/doc/src/api/libarchfpga/interposer_types.rst @@ -0,0 +1,13 @@ +======== +Interposer Types +======== + +These types are used to store information about interposer based architectures. + +.. doxygenstruct:: t_interposer_cut_inf + :project: vpr + :members: + +.. doxygenstruct:: t_interdie_wire_inf + :project: vpr + :members: \ No newline at end of file diff --git a/doc/src/api/libarchfpga/physical_types.rst b/doc/src/api/libarchfpga/physical_types.rst new file mode 100644 index 00000000000..de62a90537d --- /dev/null +++ b/doc/src/api/libarchfpga/physical_types.rst @@ -0,0 +1,33 @@ +======== +Physical Types +======== + +These types are used to capture user's intended architecture specification and will be later turned into a flattened device grid according to the device's size. + +.. doxygenstruct:: t_arch + :project: vpr + :members: + +.. doxygenstruct:: t_arch_switch_inf + :project: vpr + :members: + +.. doxygenstruct:: t_segment_inf + :project: vpr + :members: + +.. doxygenstruct:: t_physical_tile_loc + :project: vpr + :members: + +.. doxygenstruct:: t_sub_tile + :project: vpr + :members: + +.. doxygenstruct:: t_layer_def + :project: vpr + :members: + +.. doxygenstruct:: t_grid_def + :project: vpr + :members: diff --git a/doc/src/api/libarchfpga/scatter_gather_types.rst b/doc/src/api/libarchfpga/scatter_gather_types.rst new file mode 100644 index 00000000000..44fe48e24ba --- /dev/null +++ b/doc/src/api/libarchfpga/scatter_gather_types.rst @@ -0,0 +1,17 @@ +======== +Scatter-Gather Types +======== + +These types store information about about scatter-gather patterns + +.. doxygenstruct:: t_scatter_gather_pattern + :project: vpr + :members: + +.. doxygenstruct:: t_sg_link + :project: vpr + :members: + +.. doxygenstruct:: t_sg_location + :project: vpr + :members: \ No newline at end of file diff --git a/doc/src/api/vpr/grid.rst b/doc/src/api/vpr/grid.rst index 901b3a98d6f..7fa477f2949 100644 --- a/doc/src/api/vpr/grid.rst +++ b/doc/src/api/vpr/grid.rst @@ -7,19 +7,4 @@ DeviceGrid .. doxygenclass:: DeviceGrid :project: vpr - :members: - -------- -Grid Specification Types -------- - -These types are used to capture user's intended architecture specification and will be later turned into a flattened device grid according to the device's size. - -.. doxygenstruct:: t_grid_loc_spec - :project: vpr - :members: - -.. doxygenstruct:: t_grid_loc_def - :project: vpr - :members: - \ No newline at end of file + :members: \ No newline at end of file diff --git a/doc/src/index.rst b/doc/src/index.rst index 378e46af087..047c2237dee 100644 --- a/doc/src/index.rst +++ b/doc/src/index.rst @@ -64,6 +64,7 @@ For more specific documentation about VPR see :ref:`vpr`. api/vtrutil/index api/ezgl/index api/vprinternals/index + api/libarchfpga/index Indices and tables ================== From b257a62dd33aa57c7979f912f9ba6086d016744c Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Fri, 19 Sep 2025 16:03:35 -0400 Subject: [PATCH 10/11] Add interposer regression test --- .../src/read_xml_arch_file_interposer.cpp | 2 +- .../interposer/k6_N10_40nm_interposer.xml | 344 ++++++++++++++++++ .../strong_interposer/config/config.txt | 30 ++ .../config/golden_results.txt | 2 + .../vtr_reg_strong/task_list.txt | 3 +- 5 files changed, 379 insertions(+), 2 deletions(-) create mode 100644 vtr_flow/arch/interposer/k6_N10_40nm_interposer.xml create mode 100644 vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_interposer/config/config.txt create mode 100644 vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_interposer/config/golden_results.txt diff --git a/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp b/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp index b97cf9e55c3..f5b9beba193 100644 --- a/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file_interposer.cpp @@ -25,7 +25,7 @@ t_interposer_cut_inf parse_interposer_cut_tag(pugi::xml_node interposer_cut_tag, pugiutil::expect_only_children(interposer_cut_tag, {"interdie_wire"}, loc_data); for (pugi::xml_node interdie_wire_tag : interposer_cut_tag.children()) { - const std::vector interdie_wire_attributes = {{"sg", "sg_link", "offset_start", "offset_end", "offset_increment", "num"}}; + const std::vector interdie_wire_attributes = {{"sg_name", "sg_link", "offset_start", "offset_end", "offset_increment", "num"}}; pugiutil::expect_only_attributes(interdie_wire_tag, interdie_wire_attributes, loc_data); t_interdie_wire_inf interdie_wire; diff --git a/vtr_flow/arch/interposer/k6_N10_40nm_interposer.xml b/vtr_flow/arch/interposer/k6_N10_40nm_interposer.xml new file mode 100644 index 00000000000..e3860430e37 --- /dev/null +++ b/vtr_flow/arch/interposer/k6_N10_40nm_interposer.xml @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 82e-12 + 173e-12 + 261e-12 + 263e-12 + 398e-12 + 397e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_interposer/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_interposer/config/config.txt new file mode 100644 index 00000000000..4fcde29969f --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_interposer/config/config.txt @@ -0,0 +1,30 @@ +############################################## +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/verilog + +# Path to directory of architectures to use +archs_dir=arch/interposer + +# Add circuits to list to sweep +circuit_list_add=ch_intrinsics.v + +# Add architectures to list to sweep +arch_list_add=k6_N10_40nm_interposer.xml + +# Use a fixed size device +circuit_constraint_list_add=(ch_intrinsics.v, device=vtr_homogeneous_extra_small) + +# Parse info and how to parse +parse_file=vpr_standard.txt + +# How to parse QoR info +qor_parse_file=qor_standard.txt + +# Pass requirements +pass_requirements_file=pass_requirements.txt + +# Script parameters +script_params_common = -track_memory_usage diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_interposer/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_interposer/config/golden_results.txt new file mode 100644 index 00000000000..36fbc7dfa6b --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_interposer/config/golden_results.txt @@ -0,0 +1,2 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time initial_placed_wirelength_est placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time initial_placed_CPD_est placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time ap_mem ap_time ap_full_legalizer_mem ap_full_legalizer_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_num_rr_graph_nodes crit_path_num_rr_graph_edges crit_path_collapsed_nodes crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_create_rr_graph_time crit_path_create_intra_cluster_rr_graph_time crit_path_tile_lookahead_computation_time crit_path_router_lookahead_computation_time crit_path_total_timing_analysis_time crit_path_total_sta_time +k6_N10_40nm_interposer.xml ch_intrinsics.v common 1.39 vpr 63.27 MiB -1 -1 0.24 32920 3 0.07 -1 -1 37056 -1 -1 25 99 -1 -1 success v8.0.0-13909-g0298a690c-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 15.2.1 on Linux-6.14.9-300.fc42.x86_64 x86_64 2025-09-19T15:59:53 betzgrp-pcamir.eecg /home/amirpoolad/Dev/vtr-verilog-to-routing/vtr_flow/scripts 64792 99 130 342 472 1 225 254 10 10 100 -1 vtr_homogeneous_extra_small 23.6 MiB 0.06 1395.83 666 26786 4334 19176 3276 63.3 MiB 0.05 0.00 2.06334 1.99047 -111.451 -1.99047 1.99047 0.05 0.000410201 0.000382142 0.0164063 0.0152932 -1 -1 -1 -1 30 1038 11 1.152e+06 450000 194421. 1944.21 0.40 0.139057 0.127114 6972 36796 -1 905 11 476 757 28868 8304 1.89907 1.89907 -121.629 -1.89907 0 0 238537. 2385.37 0.01 0.02 0.02 -1 -1 0.01 0.0143138 0.0133681 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt index 833846ae19e..8f95526f7ce 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt @@ -118,4 +118,5 @@ regression_tests/vtr_reg_strong/strong_3d/3d_cb regression_tests/vtr_reg_strong/strong_3d/3d_sb regression_tests/vtr_reg_strong/strong_xilinx_simple regression_tests/vtr_reg_strong/strong_xilinx_flagship -regression_tests/vtr_reg_strong/strong_scatter_gather \ No newline at end of file +regression_tests/vtr_reg_strong/strong_scatter_gather +regression_tests/vtr_reg_strong/strong_interposer From 7fcb83eb304239557a358442ca59fea3746a138b Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Fri, 19 Sep 2025 19:28:21 -0400 Subject: [PATCH 11/11] Improve LibArchFPGA doxygen documentation --- doc/src/api/libarchfpga/grid_types.rst | 2 +- doc/src/api/libarchfpga/physical_types.rst | 2 +- doc/src/api/libarchfpga/scatter_gather_types.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/src/api/libarchfpga/grid_types.rst b/doc/src/api/libarchfpga/grid_types.rst index 2d119d2711a..39cb5125605 100644 --- a/doc/src/api/libarchfpga/grid_types.rst +++ b/doc/src/api/libarchfpga/grid_types.rst @@ -2,7 +2,7 @@ Grid Specification Types ======== -These types are used to capture user's intended architecture specification and will be later turned into a flattened device grid according to the device's size. +These types are used to capture user's intended grid specification, i.e., which tiles go where in the device. These specifications will be later turned into a flattened device grid according to the device's size. .. doxygenstruct:: t_grid_loc_spec :project: vpr diff --git a/doc/src/api/libarchfpga/physical_types.rst b/doc/src/api/libarchfpga/physical_types.rst index de62a90537d..1dcb6d15a80 100644 --- a/doc/src/api/libarchfpga/physical_types.rst +++ b/doc/src/api/libarchfpga/physical_types.rst @@ -2,7 +2,7 @@ Physical Types ======== -These types are used to capture user's intended architecture specification and will be later turned into a flattened device grid according to the device's size. +These types are used to capture user's intended architecture specification. .. doxygenstruct:: t_arch :project: vpr diff --git a/doc/src/api/libarchfpga/scatter_gather_types.rst b/doc/src/api/libarchfpga/scatter_gather_types.rst index 44fe48e24ba..5b3be6d8bfc 100644 --- a/doc/src/api/libarchfpga/scatter_gather_types.rst +++ b/doc/src/api/libarchfpga/scatter_gather_types.rst @@ -2,7 +2,7 @@ Scatter-Gather Types ======== -These types store information about about scatter-gather patterns +These types store information about about scatter-gather routing patterns .. doxygenstruct:: t_scatter_gather_pattern :project: vpr