diff --git a/CMakeLists.txt b/CMakeLists.txt index 91683479942..5649ad83c41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ option(VTR_ENABLE_SANITIZE "Enable address/leak/undefined-behaviour sanitizers ( option(VTR_ENABLE_PROFILING "Enable performance profiler (gprof)" OFF) option(VTR_ENABLE_COVERAGE "Enable code coverage tracking (gcov)" OFF) option(VTR_ENABLE_DEBUG_LOGGING "Enable debug logging" OFF) +option(VTR_ENABLE_VERSION "Enable version number up-to-date during compilation" ON) option(VTR_ENABLE_VERBOSE "Enable increased debug verbosity" OFF) option(SPEC_CPU "Enable SPEC CPU v8 support" OFF) diff --git a/doc/src/vpr/command_line_usage.rst b/doc/src/vpr/command_line_usage.rst index f589969d8ef..6ce335eb15a 100644 --- a/doc/src/vpr/command_line_usage.rst +++ b/doc/src/vpr/command_line_usage.rst @@ -358,13 +358,13 @@ Use the options below to override this default naming behaviour. .. seealso:: :ref:`Routing Resource XML File `. -.. option:: --read_vpr_constraints +.. option:: --read_vpr_constraints ::...: - Reads the :ref:`floorplanning constraints ` that packing and placement must respect from the specified XML file. + Reads the :ref:`floorplanning constraints ` that packing and placement must respect from the specified XML file; and/or reads the :ref:`global route constraints ` that router must respect from the specified XML file. Multiple files are allowed and should be seperated with a colomn char. .. option:: --write_vpr_constraints - Writes out new :ref:`floorplanning constraints ` based on current placement to the specified XML file. + Writes out new :ref:`floorplanning constraints ` based on current placement to the specified XML file; and/or writes out new :ref:`global route constraints ` based on current global routecounstraints to the specified XML file. Note that a single combined file is written to even there are multiple input constraint files read in. .. option:: --read_router_lookahead diff --git a/doc/src/vpr/route_constraints.rst b/doc/src/vpr/route_constraints.rst new file mode 100644 index 00000000000..fe921cd5a23 --- /dev/null +++ b/doc/src/vpr/route_constraints.rst @@ -0,0 +1,44 @@ + +VPR Route Constraints +========================= +.. _vpr_constraints_file: +VPR supports running flows with route constraints. Route constraints are set on global signals to specify if they should be routed or not. For example, a user may want to route a specific internal clock even clock modeling option is set to not route it. + +.. note:: The constraint specified in this file overrides the setting of option "--clock_modeling" if it is specified. A message will be issued in such case: "Route constraint(s) detected and will override clock modeling setting". + +The route constraints should be specified by the user using an XML constraints file format, as described in the section below. + +A Constraints File Example +-------------------------- + +.. code-block:: xml + :caption: An example of a route constraints file in XML format. + :linenos: + + + + + + + + + + +.. _end: + +.. note:: The "route_model" in constraint specified in this file only support "ideal" and "route" only. + +Constraints File Format +----------------------- + +VPR has a specific XML format which must be used when creating a route constraints file. The purpose of this constraints file is to specify + +#. The signals that should be constrained for routing +#. The route model for such signals + +The file is passed as an input to VPR when running with route constraints. When the file is read in, its information is used to guide VPR route or not route such signals. + +.. note:: Use the VPR option :vpr:option:`--read_vpr_constraints` to specify the VPR route constraints file that is to be loaded. + +.. note:: Wildcard names of signals are supported to specify a list of signals. The wildcard expression should follow the C/C++ regexpr rule. + diff --git a/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp b/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp index 9383257cecb..4f35b27b7a5 100644 --- a/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp +++ b/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp @@ -202,7 +202,7 @@ namespace Catch { FatalConditionHandler::FatalConditionHandler() { assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); if (altStackSize == 0) { - altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); + altStackSize = std::max(static_cast(/*SIGSTKSZ*/32768), minStackSizeForErrors); } altStackMem = new char[altStackSize](); } diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 00553f1caf2..b64a48d6333 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -166,8 +166,11 @@ enum e_pin_type { enum e_interconnect { COMPLETE_INTERC = 1, DIRECT_INTERC = 2, - MUX_INTERC = 3 + MUX_INTERC = 3, + NUM_INTERC_TYPES /* Invalid type */ }; +/* String version of interconnect types. Use for debugging messages */ +constexpr std::array INTERCONNECT_TYPE_STRING = {{"unknown", "complete", "direct", "mux"}}; /* Orientations. */ enum e_side : unsigned char { @@ -1468,7 +1471,7 @@ enum e_directionality { BI_DIRECTIONAL }; /* X_AXIS: Data that describes an x-directed wire segment (CHANX) * - * Y_AXIS: Data that describes an y-directed wire segment (CHANY) * + * Y_AXIS: Data that describes an y-directed wire segment (CHANY) * * BOTH_AXIS: Data that can be applied to both x-directed and y-directed wire segment */ enum e_parallel_axis { X_AXIS, @@ -1513,7 +1516,7 @@ enum e_Fc_type { * Cmetal: Capacitance of a routing track, per unit logic block length. * * Rmetal: Resistance of a routing track, per unit logic block length. * * (UDSD by AY) drivers: How do signals driving a routing track connect to * - * the track? + * the track? * seg_index: The index of the segment as stored in the appropriate Segs list* * Upon loading the architecture, we use this field to keep track * * the segment's index in the unified segment_inf vector. This is * @@ -1569,12 +1572,12 @@ constexpr std::array SWITCH_T /* Constant/Reserved names for switches in architecture XML * Delayless switch: - * The zero-delay switch created by VPR internally + * The zero-delay switch created by VPR internally * This is a special switch just to ease CAD algorithms * It is mainly used in - * - the edges between SOURCE and SINK nodes in routing resource graphs + * - the edges between SOURCE and SINK nodes in routing resource graphs * - the edges in CLB-to-CLB connections (defined by in arch XML) - * + * */ constexpr const char* VPR_DELAYLESS_SWITCH_NAME = "__vpr_delayless_switch__"; @@ -1910,12 +1913,18 @@ struct t_arch { char* architecture_id; //Secure hash digest of the architecture file to uniquely identify this architecture + /* Xifan Tang: options for tileable routing architectures */ + bool tileable; + bool through_channel; + t_chan_width_dist Chans; enum e_switch_block_type SBType; + enum e_switch_block_type SBSubType; std::vector switchblocks; float R_minW_nmos; float R_minW_pmos; int Fs; + int subFs; float grid_logic_tile_area; std::vector Segments; t_arch_switch_inf* Switches = nullptr; diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index d32ca8cb790..f9cde596a01 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -119,6 +119,11 @@ static std::pair ProcessPinString(pugi::xml_node Locations, T type, const char* pin_loc_string, const pugiutil::loc_data& loc_data); +template +static std::pair ProcessInstanceString(pugi::xml_node Locations, + T type, + const char* pin_loc_string, + const pugiutil::loc_data& loc_data); /* Process XML hierarchy */ static void ProcessTiles(pugi::xml_node Node, @@ -616,10 +621,15 @@ static void LoadPinLoc(pugi::xml_node Locations, &sub_tile, token.c_str(), loc_data); - + /* Get the offset in the capacity range */ + auto capacity_range = ProcessInstanceString(Locations, + &sub_tile, + token.c_str(), + loc_data); + VTR_ASSERT(0 <= capacity_range.first && capacity_range.second < sub_tile_capacity); for (int pin_num = pin_range.first; pin_num < pin_range.second; ++pin_num) { VTR_ASSERT(pin_num < (int)sub_tile.sub_tile_to_tile_pin_indices.size() / sub_tile_capacity); - for (int capacity = 0; capacity < sub_tile_capacity; ++capacity) { + for (int capacity = capacity_range.first; capacity <= capacity_range.second; ++capacity) { int sub_tile_pin_index = pin_num + capacity * sub_tile.num_phy_pins / sub_tile_capacity; int physical_pin_index = sub_tile.sub_tile_to_tile_pin_indices[sub_tile_pin_index]; type->pinloc[width][height][side][physical_pin_index] = true; @@ -646,6 +656,99 @@ static void LoadPinLoc(pugi::xml_node Locations, } } +/* Parse the string to extract instance range, e.g., io[4:7] -> (4, 7) + * If no instance range is explicitly defined, we assume the range of type capacity, i.e., (0, capacity - 1) */ +template +static std::pair ProcessInstanceString(pugi::xml_node Locations, + T type, + const char* pin_loc_string, + const pugiutil::loc_data& loc_data) { + int num_tokens; + auto tokens = GetTokensFromString(pin_loc_string, &num_tokens); + + int token_index = 0; + auto token = tokens[token_index]; + + if (token.type != TOKEN_STRING || 0 != strcmp(token.data, type->name)) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Wrong physical type name of the port: %s\n", pin_loc_string); + } + + token_index++; + token = tokens[token_index]; + + int first_inst = 0; + int last_inst = type->capacity.total() - 1; + + /* If there is a dot, such as io.input[0:3], it indicates the full range of the capacity, the default value should be returned */ + if (token.type == TOKEN_DOT) { + freeTokens(tokens, num_tokens); + return std::make_pair(first_inst, last_inst); + } + + /* If the string contains index for capacity range, e.g., io[3:3].in[0:5], we skip the capacity range here. */ + if (token.type != TOKEN_OPEN_SQUARE_BRACKET) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No open square bracket present: %s\n", pin_loc_string); + } + + token_index++; + token = tokens[token_index]; + + if (token.type != TOKEN_INT) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No integer to indicate least significant instance index: %s\n", pin_loc_string); + } + + first_inst = vtr::atoi(token.data); + + token_index++; + token = tokens[token_index]; + + // Single pin is specified + if (token.type != TOKEN_COLON) { + if (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No closing bracket: %s\n", pin_loc_string); + } + + token_index++; + + if (token_index != num_tokens) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "instance of pin location should be completed, but more tokens are present: %s\n", pin_loc_string); + } + + freeTokens(tokens, num_tokens); + return std::make_pair(first_inst, first_inst); + } + + token_index++; + token = tokens[token_index]; + + if (token.type != TOKEN_INT) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No integer to indicate most significant instance index: %s\n", pin_loc_string); + } + + last_inst = vtr::atoi(token.data); + + token_index++; + token = tokens[token_index]; + + if (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No closed square bracket: %s\n", pin_loc_string); + } + + if (first_inst > last_inst) { + std::swap(first_inst, last_inst); + } + + freeTokens(tokens, num_tokens); + return std::make_pair(first_inst, last_inst); +} + template static std::pair ProcessPinString(pugi::xml_node Locations, T type, @@ -665,6 +768,20 @@ static std::pair ProcessPinString(pugi::xml_node Locations, token_index++; token = tokens[token_index]; + /* If the string contains index for capacity range, e.g., io[3:3].in[0:5], we skip the capacity range here. */ + if (token.type == TOKEN_OPEN_SQUARE_BRACKET) { + while (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { + token_index++; + token = tokens[token_index]; + if (token_index == num_tokens) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Found an open '[' but miss close ']' of the port: %s\n", pin_loc_string); + } + } + token_index++; + token = tokens[token_index]; + } + if (token.type != TOKEN_DOT) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No dot is present to separate type name and port name: %s\n", pin_loc_string); @@ -2389,7 +2506,10 @@ static void ProcessLayout(pugi::xml_node layout_tag, t_arch* arch, const pugiuti VTR_ASSERT(layout_tag.name() == std::string("layout")); //Expect no attributes on - expect_only_attributes(layout_tag, {}, loc_data); + //expect_only_attributes(layout_tag, {}, loc_data); + + arch->tileable = get_attribute(layout_tag, "tileable", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->through_channel = get_attribute(layout_tag, "through_channel", loc_data, ReqOpt::OPTIONAL).as_bool(false); //Count the number of or tags size_t auto_layout_cnt = 0; @@ -2737,8 +2857,9 @@ static void ProcessDevice(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& // tag Cur = get_single_child(Node, "switch_block", loc_data); - expect_only_attributes(Cur, {"type", "fs"}, loc_data); + expect_only_attributes(Cur, {"type", "fs", "sub_type", "sub_fs"}, loc_data); Prop = get_attribute(Cur, "type", loc_data).value(); + /* Parse attribute 'type', representing the major connectivity pattern for switch blocks */ if (strcmp(Prop, "wilton") == 0) { arch->SBType = WILTON; } else if (strcmp(Prop, "universal") == 0) { @@ -2752,9 +2873,31 @@ static void ProcessDevice(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Unknown property %s for switch block type x\n", Prop); } + /* Parse attribute 'sub_type', representing the minor connectivity pattern for switch blocks + * If not specified, the 'sub_type' is the same as major type + * This option is only valid for tileable routing resource graph builder + * Note that sub_type does not support custom switch block pattern!!! + * If 'sub_type' is specified, the custom switch block for 'type' is not allowed! + */ + std::string sub_type_str = get_attribute(Cur, "sub_type", loc_data, BoolToReqOpt(false)).as_string(""); + if (!sub_type_str.empty()) { + if (sub_type_str == std::string("wilton")) { + arch->SBSubType = WILTON; + } else if (sub_type_str == std::string("universal")) { + arch->SBSubType = UNIVERSAL; + } else if (sub_type_str == std::string("subset")) { + arch->SBSubType = SUBSET; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), + "Unknown property %s for switch block subtype x\n", sub_type_str.c_str()); + } + } else { + arch->SBSubType = arch->SBType; + } ReqOpt CUSTOM_SWITCHBLOCK_REQD = BoolToReqOpt(!custom_switch_block); arch->Fs = get_attribute(Cur, "fs", loc_data, CUSTOM_SWITCHBLOCK_REQD).as_int(3); + arch->subFs = get_attribute(Cur, "sub_fs", loc_data, BoolToReqOpt(false)).as_int(arch->Fs); Cur = get_single_child(Node, "default_fc", loc_data, ReqOpt::OPTIONAL); if (Cur) { @@ -3310,19 +3453,30 @@ static void ProcessPinLocations(pugi::xml_node Locations, //Verify that all top-level pins have had their locations specified - //Record all the specified pins - std::map> port_pins_with_specified_locations; + //Record all the specified pins, (capacity, port_name, index) + std::map>> port_pins_with_specified_locations; for (int w = 0; w < PhysicalTileType->width; ++w) { for (int h = 0; h < PhysicalTileType->height; ++h) { for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { for (auto token : pin_locs->assignments[sub_tile_index][w][h][side]) { InstPort inst_port(token.c_str()); - //A pin specification should contain only the block name, and not any instace count information + //A pin specification may contain instance count, but should be in the range of capacity + int inst_lsb = 0; + int inst_msb = PhysicalTileType->capacity - 1; if (inst_port.instance_low_index() != InstPort::UNSPECIFIED || inst_port.instance_high_index() != InstPort::UNSPECIFIED) { - archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), - "Pin location specification '%s' should not contain an instance range (should only be the block name)", - token.c_str()); + /* Extract range numbers */ + inst_lsb = inst_port.instance_low_index(); + inst_msb = inst_port.instance_high_index(); + if (inst_lsb > inst_msb) { + std::swap(inst_lsb, inst_msb); + } + /* Check if we have a valid range */ + if (inst_lsb < 0 || inst_msb > PhysicalTileType->capacity - 1) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Pin location specification '%s' contain an out-of-range instance. Expect [%d:%d]", + token.c_str(), 0, PhysicalTileType->capacity - 1); + } } //Check that the block name matches @@ -3359,9 +3513,11 @@ static void ProcessPinLocations(pugi::xml_node Locations, VTR_ASSERT(pin_low_idx >= 0); VTR_ASSERT(pin_high_idx >= 0); - for (int ipin = pin_low_idx; ipin <= pin_high_idx; ++ipin) { - //Record that the pin has it's location specified - port_pins_with_specified_locations[inst_port.port_name()].insert(ipin); + for (int iinst = inst_lsb; iinst <= inst_msb; ++iinst) { + for (int ipin = pin_low_idx; ipin <= pin_high_idx; ++ipin) { + //Record that the pin has it's location specified + port_pins_with_specified_locations[iinst][inst_port.port_name()].insert(ipin); + } } } } @@ -3369,13 +3525,15 @@ static void ProcessPinLocations(pugi::xml_node Locations, } //Check for any pins missing location specs - for (const auto& port : SubTile->ports) { - for (int ipin = 0; ipin < port.num_pins; ++ipin) { - if (!port_pins_with_specified_locations[port.name].count(ipin)) { - //Missing - archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), - "Pin '%s.%s[%d]' has no pin location specificed (a location is required for pattern=\"custom\")", - SubTile->name, port.name, ipin); + for (int iinst = 0; iinst < PhysicalTileType->capacity; ++iinst) { + for (const auto& port : SubTile->ports) { + for (int ipin = 0; ipin < port.num_pins; ++ipin) { + if (!port_pins_with_specified_locations[iinst][port.name].count(ipin)) { + //Missing + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Pin '%s[%d].%s[%d]' has no pin location specificed (a location is required for pattern=\"custom\")", + SubTile->name, iinst, port.name, ipin); + } } } } diff --git a/libs/librrgraph/src/base/rr_graph_builder.cpp b/libs/librrgraph/src/base/rr_graph_builder.cpp index 535e027ca9f..e1b24083159 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.cpp +++ b/libs/librrgraph/src/base/rr_graph_builder.cpp @@ -1,3 +1,4 @@ +#include "vtr_assert.h" #include "vtr_log.h" #include "rr_graph_builder.h" #include "vtr_time.h" @@ -7,7 +8,10 @@ //#include "globals.h" -RRGraphBuilder::RRGraphBuilder() {} +RRGraphBuilder::RRGraphBuilder() { + is_edge_dirty_ = true; + is_incoming_edge_dirty_ = true; +} t_rr_graph_storage& RRGraphBuilder::rr_nodes() { return node_storage_; @@ -25,6 +29,14 @@ MetadataStorage>& RRGraphBuilder::rr_edge_metadata() return rr_edge_metadata_; } +vtr::vector>& RRGraphBuilder::node_in_edge_storage() { + return node_in_edges_; +} + +vtr::vector>& RRGraphBuilder::node_ptc_storage() { + return node_ptc_nums_; +} + void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { t_rr_type node_type = node_storage_.node_type(node); short node_ptc_num = node_storage_.node_ptc_num(node); @@ -59,13 +71,43 @@ void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { } } +RRNodeId RRGraphBuilder::create_node(int x, int y, t_rr_type type, int ptc, e_side side) { + e_side node_side = SIDES[0]; + /* Only OPIN and IPIN nodes have sides, otherwise force to use a default side */ + if (OPIN == type || IPIN == type) { + node_side = side; + } + node_storage_.emplace_back(); + node_ptc_nums_.emplace_back(); + RRNodeId new_node = RRNodeId(node_storage_.size() - 1); + node_storage_.set_node_type(new_node, type); + node_storage_.set_node_coordinates(new_node, x, y, x, y); + node_storage_.set_node_ptc_num(new_node, ptc); + if (OPIN == type || IPIN == type) { + node_storage_.add_node_side(new_node, node_side); + } + /* Special for CHANX, being consistent with the rule in find_node() */ + if (CHANX == type) { + node_lookup_.add_node(new_node, y, x, type, ptc, node_side); + } else { + node_lookup_.add_node(new_node, x, y, type, ptc, node_side); + } + + return new_node; +} + void RRGraphBuilder::clear() { node_lookup_.clear(); node_storage_.clear(); + node_in_edges_.clear(); + node_ptc_nums_.clear(); rr_node_metadata_.clear(); rr_edge_metadata_.clear(); rr_segments_.clear(); rr_switch_inf_.clear(); + edges_to_build_.clear(); + is_edge_dirty_ = true; + is_incoming_edge_dirty_ = true; } void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_nodes_algorithm, @@ -137,3 +179,96 @@ void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_ std::get<2>(edge)); }); } + +void RRGraphBuilder::create_edge(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch) { + edges_to_build_.emplace_back(src, dest, size_t(edge_switch)); + is_edge_dirty_ = true; /* Adding a new edge revokes the flag */ + is_incoming_edge_dirty_ = true; +} + +void RRGraphBuilder::build_edges(const bool& uniquify) { + if (uniquify) { + std::sort(edges_to_build_.begin(), edges_to_build_.end()); + edges_to_build_.erase(std::unique(edges_to_build_.begin(), edges_to_build_.end()), edges_to_build_.end()); + } + alloc_and_load_edges(&edges_to_build_); + edges_to_build_.clear(); + is_edge_dirty_ = false; +} + +void RRGraphBuilder::build_in_edges() { + VTR_ASSERT(validate()); + node_in_edges_.clear(); + node_in_edges_.resize(node_storage_.size()); + + for (RRNodeId src_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + for (auto iedge : node_storage_.edges(src_node)) { + VTR_ASSERT(src_node == node_storage_.edge_source_node(node_storage_.edge_id(src_node, iedge))); + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, iedge)); + node_in_edges_[des_node].push_back(node_storage_.edge_id(src_node, iedge)); + } + } + is_incoming_edge_dirty_ = false; +} + +std::vector RRGraphBuilder::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (is_incoming_edge_dirty_) { + VTR_LOG_ERROR("Incoming edges are not built yet in routing resource graph. Please call build_in_edges()."); + return std::vector(); + } + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +void RRGraphBuilder::add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id) { + VTR_ASSERT(size_t(node) < node_storage_.size()); + VTR_ASSERT(size_t(node) < node_ptc_nums_.size()); + VTR_ASSERT_MSG(node_storage_.node_type(node) == CHANX || node_storage_.node_type(node) == CHANY, "Track number valid only for CHANX/CHANY RR nodes"); + + size_t node_length = std::abs(node_storage_.node_xhigh(node) - node_storage_.node_xlow(node)) + + std::abs(node_storage_.node_yhigh(node) - node_storage_.node_ylow(node)); + if (node_length + 1 != node_ptc_nums_[node].size()) { + node_ptc_nums_[node].resize(node_length + 1); + } + + size_t offset = node_offset.x() - node_storage_.node_xlow(node) + node_offset.y() - node_storage_.node_ylow(node); + VTR_ASSERT(offset < node_ptc_nums_[node].size()); + + node_ptc_nums_[node][offset] = track_id; +} + +void RRGraphBuilder::add_track_node_to_lookup(RRNodeId node) { + VTR_ASSERT_MSG(node_storage_.node_type(node) == CHANX || node_storage_.node_type(node) == CHANY, "Update track node look-up is only valid to CHANX/CHANY nodes"); + + /* Compute the track id based on the (x, y) coordinate */ + size_t x_start = std::min(node_storage_.node_xlow(node), node_storage_.node_xhigh(node)); + size_t y_start = std::min(node_storage_.node_ylow(node), node_storage_.node_yhigh(node)); + std::vector node_x(std::abs(node_storage_.node_xlow(node) - node_storage_.node_xhigh(node)) + 1); + std::vector node_y(std::abs(node_storage_.node_ylow(node) - node_storage_.node_yhigh(node)) + 1); + + std::iota(node_x.begin(), node_x.end(), x_start); + std::iota(node_y.begin(), node_y.end(), y_start); + + VTR_ASSERT(size_t(std::max(node_storage_.node_xlow(node), node_storage_.node_xhigh(node))) == node_x.back()); + VTR_ASSERT(size_t(std::max(node_storage_.node_ylow(node), node_storage_.node_yhigh(node))) == node_y.back()); + + for (const size_t& x : node_x) { + for (const size_t& y : node_y) { + size_t ptc = node_storage_.node_ptc_num(node); + /* Routing channel nodes may have different ptc num + * Find the track ids using the x/y offset + * FIXME: Special case on assigning CHANX (x,y) should be changed to a natural way! + */ + if (CHANX == node_storage_.node_type(node)) { + ptc = node_ptc_nums_[node][x - node_storage_.node_xlow(node)]; + node_lookup_.add_node(node, y, x, CHANX, ptc); + } else if (CHANY == node_storage_.node_type(node)) { + ptc = node_ptc_nums_[node][y - node_storage_.node_ylow(node)]; + node_lookup_.add_node(node, x, y, CHANY, ptc); + } + } + } +} diff --git a/libs/librrgraph/src/base/rr_graph_builder.h b/libs/librrgraph/src/base/rr_graph_builder.h index f1777355f07..089a0c393da 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.h +++ b/libs/librrgraph/src/base/rr_graph_builder.h @@ -16,6 +16,7 @@ #include "rr_graph_storage.h" #include "rr_spatial_lookup.h" #include "metadata_storage.h" +#include "rr_edge.h" class RRGraphBuilder { /* -- Constructors -- */ @@ -44,6 +45,10 @@ class RRGraphBuilder { MetadataStorage& rr_node_metadata(); /** @brief Return a writable object for the meta data on the edge */ MetadataStorage>& rr_edge_metadata(); + /** @brief Return a writable object fo the incoming edge storage */ + vtr::vector>& node_in_edge_storage(); + /** @brief Return a writable object of the node ptc storage (for tileable routing resource graph) */ + vtr::vector>& node_ptc_storage(); /** @brief Return the size for rr_node_metadata */ inline size_t rr_node_metadata_size() const { @@ -114,6 +119,10 @@ class RRGraphBuilder { inline void set_node_type(RRNodeId id, t_rr_type type) { node_storage_.set_node_type(id, type); } + /** @brief Create a new rr_node in the node storage and register it to the node look-up. + * Return a valid node id if succeed. Otherwise, return an invalid id. + */ + RRNodeId create_node(int x, int y, t_rr_type type, int ptc, e_side side = NUM_SIDES); /** * @brief Add an existing rr_node in the node storage to the node look-up * @@ -189,7 +198,16 @@ class RRGraphBuilder { node_storage_.set_node_track_num(id, new_track_num); } - /** @brief set_ node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ + /** @brief Add a track id for a given node base on the offset in coordinate, applicable only to CHANX and CHANY nodes. + * This API is used by tileable routing resource graph generator, which requires each routing track has a different + * track id depending their location in FPGA fabric. + */ + void add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id); + + /** @brief Update the node_lookup for a track node. This is applicable to tileable routing graph */ + void add_track_node_to_lookup(RRNodeId node); + + /** @brief set_node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ inline void set_node_class_num(RRNodeId id, int new_class_num) { node_storage_.set_node_class_num(id, new_class_num); } @@ -199,6 +217,24 @@ class RRGraphBuilder { node_storage_.set_node_direction(id, new_direction); } + /** @brief Add a new edge to the cache of edges to be built + * .. note:: This will not add an edge to storage! You need to call build_edges() after all the edges are cached! */ + void create_edge(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch); + + /** @brief Allocate and build actual edges in storage. + * Once called, the cached edges will be uniquified and added to routing resource nodes, + * while the cache will be empty once build-up is accomplished */ + void build_edges(const bool& uniquify = true); + + /** @brief Allocate and build incoming edges for each node. + * By default, no incoming edges are kept in storage, to be memory efficient */ + void build_in_edges(); + + /** @brief Return incoming edges for a given routing resource node + * Require build_in_edges() to be called first + */ + std::vector node_in_edges(RRNodeId node) const; + /** @brief Reserve the lists of edges to be memory efficient. * This function is mainly used to reserve memory space inside RRGraph, * when adding a large number of edges in order to avoid memory fragements */ @@ -259,6 +295,9 @@ class RRGraphBuilder { return node_storage_.count_rr_switches(arch_switch_inf, arch_switch_fanins); } + /* Unlock storage; required to modify an routing resource graph after edge is read */ + inline void unlock_storage() { node_storage_.edges_read_ = false; node_storage_.partitioned_ = false; node_storage_.clear_node_first_edge();} + /** @brief Reserve the lists of nodes, edges, switches etc. to be memory efficient. * This function is mainly used to reserve memory space inside RRGraph, * when adding a large number of nodes/edge/switches/segments, @@ -282,13 +321,13 @@ class RRGraphBuilder { rr_switch_inf_.resize(size); } - /** @brief Validate that edge data is partitioned correctly + /** @brief Validate that edge data is partitioned correctly. Also there are no edges left to be built! * @note This function is used to validate the correctness of the routing resource graph in terms * of graph attributes. Strongly recommend to call it when you finish the building a routing resource * graph. If you need more advance checks, which are related to architecture features, you should * consider to use the check_rr_graph() function or build your own check_rr_graph() function. */ inline bool validate() const { - return node_storage_.validate(rr_switch_inf_); + return node_storage_.validate(rr_switch_inf_) && edges_to_build_.empty(); } /** @brief Sorts edge data such that configurable edges appears before @@ -335,6 +374,14 @@ class RRGraphBuilder { /* Fast look-up for rr nodes */ RRSpatialLookup node_lookup_; + /* A cache for edge-related information, required to build edges for routing resource nodes. + * It is used when building a routing resource graph by considering memory efficiency. + * It will be clear up after calling build_edges(). + * + * .. warning:: This is a temporary data which is used to collect edges to be built for nodes + */ + t_rr_edge_info_set edges_to_build_; + /** Wire segment types in RR graph * - Each rr_segment contains the detailed information of a routing track, which is denoted by a node in CHANX or CHANY type. * - We use a fly-weight data structure here, in the same philosophy as the rr_indexed_data. See detailed explanation in the t_segment_inf data structure @@ -348,6 +395,29 @@ class RRGraphBuilder { /* Detailed information about the switches, which are used in the RRGraph */ vtr::vector rr_switch_inf_; + /** A list of incoming edges for each routing resource node. This can be built optionally, as required by applications. + * By default, it is empty! Call build_in_edges() to construct it!!! */ + vtr::vector> node_in_edges_; + + /* Extra ptc number for each routing resource node. This is required by tileable routing resource graph. + * In a tileable routing architecture, routing tracks, e.g., CHANX and CHANY, follows a staggered organization. + * Hence, a routing track may appear in different routing channels, representing different ptc/track id. + * Here is an illustrative example of a X-direction routing track (CHANX) in INC direction, which is organized in staggered way. + * + * Coord(x,y) (1,0) (2,0) (3,0) (4,0) Another track (node) + * ptc=0 ------> ------> + * \ / + * ptc=1 ------> / + * \ / + * ptc=2 ------> / + * \ / + * ptc=3 -------> + * ^ ^ + * | | + * starting point ending point + */ + vtr::vector> node_ptc_nums_; + /** .. warning:: The Metadata should stay as an independent data structure than rest of the internal data, * e.g., node_lookup! */ /* Metadata is an extra data on rr-nodes and edges, respectively, that is not used by vpr @@ -372,6 +442,11 @@ class RRGraphBuilder { * value: map of */ MetadataStorage> rr_edge_metadata_; + + /** @brief a flag to mark the status of edge storage + * dirty means that the edge storage is not complete, should call related APIs to build */ + bool is_edge_dirty_; + bool is_incoming_edge_dirty_; }; #endif diff --git a/libs/librrgraph/src/base/rr_graph_fwd.h b/libs/librrgraph/src/base/rr_graph_fwd.h index fee69b34cd2..15199830642 100644 --- a/libs/librrgraph/src/base/rr_graph_fwd.h +++ b/libs/librrgraph/src/base/rr_graph_fwd.h @@ -1,5 +1,5 @@ -#ifndef RR_GRAPH_OBJ_FWD_H -#define RR_GRAPH_OBJ_FWD_H +#ifndef RR_GRAPH_FWD_H +#define RR_GRAPH_FWD_H #include "vtr_strong_id.h" /*************************************************************** @@ -11,8 +11,6 @@ //Forward declaration class t_rr_graph_storage; -class RRGraph; - struct rr_node_id_tag; struct rr_edge_id_tag; struct rr_indexed_data_id_tag; diff --git a/libs/librrgraph/src/base/rr_graph_storage.cpp b/libs/librrgraph/src/base/rr_graph_storage.cpp index 94ca29b7636..ee453d99f8b 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.cpp +++ b/libs/librrgraph/src/base/rr_graph_storage.cpp @@ -379,7 +379,10 @@ void t_rr_graph_storage::assign_first_edges() { bool t_rr_graph_storage::verify_first_edges() const { size_t num_edges = edge_src_node_.size(); - VTR_ASSERT(node_first_edge_[RRNodeId(node_storage_.size())] == RREdgeId(num_edges)); + if (node_first_edge_[RRNodeId(node_storage_.size())] != RREdgeId(num_edges)) { + VTR_LOG("node first edge is '%lu' while expected edge id is '%lu'\n", size_t(node_first_edge_[RRNodeId(node_storage_.size())]), num_edges); + VTR_ASSERT(node_first_edge_[RRNodeId(node_storage_.size())] == RREdgeId(num_edges)); + } // Each edge should belong with the edge range defined by // [node_first_edge_[src_node], node_first_edge_[src_node+1]). @@ -564,6 +567,11 @@ t_edge_size t_rr_graph_storage::num_non_configurable_edges(RRNodeId node, const return num_edges(node) - num_configurable_edges(node, rr_switches); } +bool t_rr_graph_storage::edge_is_configurable(RREdgeId edge, const vtr::vector& rr_switches) const { + auto iswitch = edge_switch(edge); + return rr_switches[RRSwitchId(iswitch)].configurable(); +} + bool t_rr_graph_storage::edge_is_configurable(RRNodeId id, t_edge_size iedge, const vtr::vector& rr_switches) const { auto iswitch = edge_switch(id, iedge); return rr_switches[RRSwitchId(iswitch)].configurable(); @@ -629,21 +637,21 @@ void t_rr_graph_storage::set_node_ptc_num(RRNodeId id, int new_ptc_num) { } void t_rr_graph_storage::set_node_pin_num(RRNodeId id, int new_pin_num) { if (node_type(id) != IPIN && node_type(id) != OPIN) { - VTR_LOG_ERROR("Attempted to set RR node 'pin_num' for non-IPIN/OPIN type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'pin_num' for non-IPIN/OPIN type '%s'\n", node_type_string(id)); } node_ptc_[id].ptc_.pin_num = new_pin_num; } void t_rr_graph_storage::set_node_track_num(RRNodeId id, int new_track_num) { if (node_type(id) != CHANX && node_type(id) != CHANY) { - VTR_LOG_ERROR("Attempted to set RR node 'track_num' for non-CHANX/CHANY type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'track_num' for non-CHANX/CHANY type '%s'\n", node_type_string(id)); } node_ptc_[id].ptc_.track_num = new_track_num; } void t_rr_graph_storage::set_node_class_num(RRNodeId id, int new_class_num) { if (node_type(id) != SOURCE && node_type(id) != SINK) { - VTR_LOG_ERROR("Attempted to set RR node 'class_num' for non-SOURCE/SINK type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'class_num' for non-SOURCE/SINK type '%s'\n", node_type_string(id)); } node_ptc_[id].ptc_.class_num = new_class_num; } @@ -658,7 +666,7 @@ static int get_node_pin_num( RRNodeId id) { auto node_type = node_storage[id].type_; if (node_type != IPIN && node_type != OPIN) { - VTR_LOG_ERROR("Attempted to access RR node 'pin_num' for non-IPIN/OPIN type '%s'", rr_node_typename[node_type]); + VTR_LOG_ERROR("Attempted to access RR node 'pin_num' for non-IPIN/OPIN type '%s'\n", rr_node_typename[node_type]); } return node_ptc[id].ptc_.pin_num; } @@ -669,7 +677,7 @@ static int get_node_track_num( RRNodeId id) { auto node_type = node_storage[id].type_; if (node_type != CHANX && node_type != CHANY) { - VTR_LOG_ERROR("Attempted to access RR node 'track_num' for non-CHANX/CHANY type '%s'", rr_node_typename[node_type]); + VTR_LOG_ERROR("Attempted to access RR node 'track_num' for non-CHANX/CHANY type '%s'\n", rr_node_typename[node_type]); } return node_ptc[id].ptc_.track_num; } @@ -680,7 +688,7 @@ static int get_node_class_num( RRNodeId id) { auto node_type = node_storage[id].type_; if (node_type != SOURCE && node_type != SINK) { - VTR_LOG_ERROR("Attempted to access RR node 'class_num' for non-SOURCE/SINK type '%s'", rr_node_typename[node_type]); + VTR_LOG_ERROR("Attempted to access RR node 'class_num' for non-SOURCE/SINK type '%s'\n", rr_node_typename[node_type]); } return node_ptc[id].ptc_.class_num; } @@ -730,7 +738,7 @@ void t_rr_graph_storage::set_node_coordinates(RRNodeId id, short x1, short y1, s void t_rr_graph_storage::set_node_cost_index(RRNodeId id, RRIndexedDataId new_cost_index) { auto& node = node_storage_[id]; if ((size_t)new_cost_index >= std::numeric_limits::max()) { - VTR_LOG_ERROR("Attempted to set cost_index_ %zu above cost_index storage max value.", + VTR_LOG_ERROR("Attempted to set cost_index_ %zu above cost_index storage max value.\n", new_cost_index); } node.cost_index_ = (size_t)new_cost_index; @@ -747,19 +755,19 @@ void t_rr_graph_storage::set_node_capacity(RRNodeId id, short new_capacity) { void t_rr_graph_storage::set_node_direction(RRNodeId id, Direction new_direction) { if (node_type(id) != CHANX && node_type(id) != CHANY) { - VTR_LOG_ERROR("Attempted to set RR node 'direction' for non-channel type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'direction' for non-channel type '%s'\n", node_type_string(id)); } node_storage_[id].dir_side_.direction = new_direction; } void t_rr_graph_storage::add_node_side(RRNodeId id, e_side new_side) { if (node_type(id) != IPIN && node_type(id) != OPIN) { - VTR_LOG_ERROR("Attempted to set RR node 'side' for non-channel type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'side' for non-channel type '%s'\n", node_type_string(id)); } std::bitset side_bits = node_storage_[id].dir_side_.sides; side_bits[size_t(new_side)] = true; if (side_bits.to_ulong() > CHAR_MAX) { - VTR_LOG_ERROR("Invalid side '%s' to be added to rr node %u", SIDE_STRING[new_side], size_t(id)); + VTR_LOG_ERROR("Invalid side '%s' to be added to rr node %u\n", SIDE_STRING[new_side], size_t(id)); } node_storage_[id].dir_side_.sides = static_cast(side_bits.to_ulong()); } diff --git a/libs/librrgraph/src/base/rr_graph_storage.h b/libs/librrgraph/src/base/rr_graph_storage.h index 6d150c02641..4db0a12f199 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.h +++ b/libs/librrgraph/src/base/rr_graph_storage.h @@ -63,13 +63,13 @@ struct alignas(16) t_rr_node_data { t_rr_type type_ = NUM_RR_TYPES; /* The character is a hex number which is a 4-bit truth table for node sides - * The 4-bits in serial represent 4 sides on which a node could appear - * It follows a fixed sequence, which is (LEFT, BOTTOM, RIGHT, TOP) whose indices are (3, 2, 1, 0) + * The 4-bits in serial represent 4 sides on which a node could appear + * It follows a fixed sequence, which is (LEFT, BOTTOM, RIGHT, TOP) whose indices are (3, 2, 1, 0) * - When a node appears on a given side, it is set to "1" * - When a node does not appear on a given side, it is set to "0" * For example, - * - '1' means '0001' in hex number, which means the node appears on TOP - * - 'A' means '1100' in hex number, which means the node appears on LEFT and BOTTOM sides, + * - '1' means '0001' in hex number, which means the node appears on TOP + * - 'A' means '1100' in hex number, which means the node appears on LEFT and BOTTOM sides, */ union { Direction direction; //Valid only for CHANX/CHANY @@ -252,6 +252,7 @@ class t_rr_graph_storage { * - num_non_configurable_edges(RRNodeId) * - edge_id(RRNodeId, t_edge_size) * - edge_sink_node(RRNodeId, t_edge_size) + * - edge_source_node(RRNodeId, t_edge_size) * - edge_switch(RRNodeId, t_edge_size) * * Only call these methods after partition_edges has been invoked. */ @@ -269,6 +270,7 @@ class t_rr_graph_storage { t_edge_size num_edges(const RRNodeId& id) const { return size_t(last_edge(id)) - size_t(first_edge(id)); } + bool edge_is_configurable(RREdgeId edge, const vtr::vector& rr_switches) const; bool edge_is_configurable(RRNodeId id, t_edge_size iedge, const vtr::vector& rr_switches) const; t_edge_size num_configurable_edges(RRNodeId node, const vtr::vector& rr_switches) const; t_edge_size num_non_configurable_edges(RRNodeId node, const vtr::vector& rr_switches) const; @@ -316,6 +318,11 @@ class t_rr_graph_storage { return edge_dest_node_[edge]; } + // Get the source node for the specified edge. + RRNodeId edge_source_node(const RREdgeId& edge) const { + return edge_src_node_[edge]; + } + // Call the `apply` function with the edge id, source, and sink nodes of every edge. void for_each_edge(std::function apply) const { for (size_t i = 0; i < edge_dest_node_.size(); i++) { @@ -332,6 +339,11 @@ class t_rr_graph_storage { return edge_sink_node(edge_id(id, iedge)); } + // Get the source node for the iedge'th edge from specified RRNodeId. + RRNodeId edge_source_node(const RRNodeId& id, t_edge_size iedge) const { + return edge_source_node(edge_id(id, iedge)); + } + // Get the switch used for the specified edge. short edge_switch(const RREdgeId& edge) const { return edge_switch_[edge]; @@ -621,6 +633,7 @@ class t_rr_graph_storage { return side_tt[size_t(side)]; } + public: inline void clear_node_first_edge() { node_first_edge_.clear(); } diff --git a/libs/librrgraph/src/base/rr_graph_utils.h b/libs/librrgraph/src/base/rr_graph_utils.h index 0725bcd0cf9..1aa58acc0b4 100644 --- a/libs/librrgraph/src/base/rr_graph_utils.h +++ b/libs/librrgraph/src/base/rr_graph_utils.h @@ -5,8 +5,8 @@ * the function declaration */ #include -#include "rr_graph_fwd.h" #include "rr_node_types.h" +#include "rr_graph_obj.h" #include "rr_graph_view.h" struct t_pin_chain_node { @@ -48,4 +48,4 @@ vtr::vector> get_fan_in_list(const RRGraphView& int seg_index_of_cblock(const RRGraphView& rr_graph, t_rr_type from_rr_type, int to_node); int seg_index_of_sblock(const RRGraphView& rr_graph, int from_node, int to_node); -#endif \ No newline at end of file +#endif diff --git a/libs/librrgraph/src/base/rr_graph_view.cpp b/libs/librrgraph/src/base/rr_graph_view.cpp index 0ddc5445d42..225af722e5b 100644 --- a/libs/librrgraph/src/base/rr_graph_view.cpp +++ b/libs/librrgraph/src/base/rr_graph_view.cpp @@ -1,3 +1,4 @@ +#include #include "rr_graph_view.h" #include "rr_node.h" #include "physical_types.h" @@ -9,7 +10,9 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, const vtr::vector& rr_indexed_data, const std::vector& rr_rc_data, const vtr::vector& rr_segments, - const vtr::vector& rr_switch_inf) + const vtr::vector& rr_switch_inf, + const vtr::vector>& node_in_edges, + const vtr::vector>& node_ptc_nums) : node_storage_(node_storage) , node_lookup_(node_lookup) , rr_node_metadata_(rr_node_metadata) @@ -17,5 +20,117 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, , rr_indexed_data_(rr_indexed_data) , rr_rc_data_(rr_rc_data) , rr_segments_(rr_segments) - , rr_switch_inf_(rr_switch_inf) { + , rr_switch_inf_(rr_switch_inf) + , node_in_edges_(node_in_edges) + , node_ptc_nums_(node_ptc_nums) { } + +std::vector RRGraphView::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +std::vector RRGraphView::node_configurable_in_edges(RRNodeId node) const { + /* Note: This is not efficient in runtime, should sort edges by configurability when allocating the array! */ + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (rr_switch_inf_[edge_switch(edge)].configurable()) { + ret_edges.push_back(edge); + } + } + return ret_edges; +} + +std::vector RRGraphView::node_non_configurable_in_edges(RRNodeId node) const { + /* Note: This is not efficient in runtime, should sort edges by configurability when allocating the array! */ + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (!rr_switch_inf_[edge_switch(edge)].configurable()) { + ret_edges.push_back(edge); + } + } + return ret_edges; +} + +std::vector RRGraphView::find_edges(const RRNodeId& src_node, const RRNodeId& des_node) const { + std::vector edge_list; + for (auto iedge : node_out_edges(src_node)) { + if (edge_sink_node(RREdgeId(iedge)) == des_node) { + edge_list.push_back(RREdgeId(iedge)); + } + } + return edge_list; +} + +RRSegmentId RRGraphView::node_segment(RRNodeId node) const { + RRIndexedDataId cost_index = node_cost_index(node); + return RRSegmentId(rr_indexed_data_[cost_index].seg_index); +} + +size_t RRGraphView::in_edges_count() const { + size_t edge_count = 0; + for (auto edge_list : node_in_edges_) { + edge_count += edge_list.size(); + } + return edge_count; +} + +bool RRGraphView::validate_in_edges() const { + size_t num_err = 0; + /* For each edge, validate that + * - The source node is in the fan-in edge list of the destination node + * - The sink node is in the fan-out edge list of the source node + */ + for (RRNodeId curr_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + /* curr_node ---> des_node + * <-?- check if the incoming edge is correct or not + */ + for (auto iedge : node_storage_.edges(curr_node)) { + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(curr_node, iedge)); + std::vector des_fanin_nodes; + for (auto next_edge : node_in_edges(des_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_source_node(next_edge); + des_fanin_nodes.push_back(prev_edge_des_node); + } + if (des_fanin_nodes.end() == std::find(des_fanin_nodes.begin(), des_fanin_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-in edges of Node '%s', while does drive it in its fan-out list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(des_node).c_str()); + num_err++; + } + } + /* src_node -?-> curr_node + * <--- check if the fan-out edge is correct or not + */ + for (auto iedge : node_in_edges(curr_node)) { + RRNodeId src_node = node_storage_.edge_source_node(iedge); + std::vector src_fanout_nodes; + for (auto prev_edge : node_storage_.edges(src_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, prev_edge)); + src_fanout_nodes.push_back(prev_edge_des_node); + } + if (src_fanout_nodes.end() == std::find(src_fanout_nodes.begin(), src_fanout_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-out edges of Node '%s', while does exist in its fan-in list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(src_node).c_str()); + num_err++; + } + } + } + if (num_err) { + VTR_LOG_ERROR("Found %ld errors when validating incoming edges for routing resource graph\n", num_err); + return false; + } + return true; +} + + diff --git a/libs/librrgraph/src/base/rr_graph_view.h b/libs/librrgraph/src/base/rr_graph_view.h index cdae9ebe5de..fa7171fac85 100644 --- a/libs/librrgraph/src/base/rr_graph_view.h +++ b/libs/librrgraph/src/base/rr_graph_view.h @@ -25,9 +25,9 @@ * - This avoids massive changes for each client on using the APIs * as each frame view provides adhoc APIs for each client * - * TODO: more compact frame views will be created, e.g., + * TODO: more compact frame views will be created, e.g., * - a mini frame view: contains only node and edges, representing the connectivity of the graph - * - a geometry frame view: an extended mini frame view with node-level attributes, + * - a geometry frame view: an extended mini frame view with node-level attributes, * in particular geometry information (type, x, y etc). * */ @@ -42,32 +42,34 @@ class RRGraphView { const vtr::vector& rr_indexed_data, const std::vector& rr_rc_data, const vtr::vector& rr_segments, - const vtr::vector& rr_switch_inf); + const vtr::vector& rr_switch_inf, + const vtr::vector>& node_in_edges, + const vtr::vector>& node_ptc_nums); /* Disable copy constructors and copy assignment operator - * This is to avoid accidental copy because it could be an expensive operation considering that the + * This is to avoid accidental copy because it could be an expensive operation considering that the * memory footprint of the data structure could ~ Gb - * Using the following syntax, we prohibit accidental 'pass-by-value' which can be immediately caught + * Using the following syntax, we prohibit accidental 'pass-by-value' which can be immediately caught * by compiler */ RRGraphView(const RRGraphView&) = delete; void operator=(const RRGraphView&) = delete; /* -- Accessors -- */ - /* TODO: The accessors may be turned into private later if they are replacable by 'questionin' + /* TODO: The accessors may be turned into private later if they are replacable by 'questionin' * kind of accessors */ public: /* Aggregates: create range-based loops for nodes - * To iterate over the nodes in a RRGraph, - * using a range-based loop is suggested. - * ----------------------------------------------------------------- - * Example: iterate over all the nodes - * // Strongly suggest to use a read-only rr_graph object - * const RRGraph& rr_graph; - * for (const RRNodeId& node : rr_graph.nodes()) { - * // Do something with each node - * } + * To iterate over the nodes in a RRGraph, + * using a range-based loop is suggested. + * ----------------------------------------------------------------- + * Example: iterate over all the nodes + * // Strongly suggest to use a read-only rr_graph object + * const RRGraph& rr_graph; + * for (const RRNodeId& node : rr_graph.nodes()) { + * // Do something with each node + * } */ inline vtr::StrongIdRange nodes() const { return vtr::StrongIdRange(RRNodeId(0), RRNodeId(num_nodes())); @@ -188,7 +190,7 @@ class RRGraphView { && (node_xhigh(node) == -1) && (node_yhigh(node) == -1)); } - /** @brief Check if two routing resource nodes are adjacent (must be a CHANX and a CHANY). + /** @brief Check if two routing resource nodes are adjacent (must be a CHANX and a CHANY). * This function is used for error checking; it checks if two nodes are physically adjacent (could be connected) based on their geometry. * It does not check the routing edges to see if they are, in fact, possible to connect in the current routing graph. * This function is inlined for runtime optimization. */ @@ -203,7 +205,7 @@ class RRGraphView { return true; } - /** @brief Check if node is within bounding box. + /** @brief Check if node is within bounding box. * To return true, the RRNode must be completely contained within the specified bounding box, with the edges of the bounding box being inclusive. *This function is inlined for runtime optimization. */ inline bool node_is_inside_bounding_box(RRNodeId node, vtr::Rect bounding_box) const { @@ -300,17 +302,39 @@ class RRGraphView { inline short edge_switch(RRNodeId id, t_edge_size iedge) const { return node_storage_.edge_switch(id, iedge); } + inline RRSwitchId edge_switch(RREdgeId edge) const { + return RRSwitchId(node_storage_.edge_switch(edge)); + } + /** @brief Get the source node for the iedge'th edge from specified RRNodeId. + * This method should generally not be used, and instead first_edge and + * last_edge should be used.*/ + inline RRNodeId edge_src_node(RREdgeId edge) const { + return node_storage_.edge_source_node(edge); + } /** @brief Get the destination node for the iedge'th edge from specified RRNodeId. * This method should generally not be used, and instead first_edge and * last_edge should be used.*/ inline RRNodeId edge_sink_node(RRNodeId id, t_edge_size iedge) const { return node_storage_.edge_sink_node(id, iedge); } + inline RRNodeId edge_sink_node(RREdgeId edge) const { + return node_storage_.edge_sink_node(edge); + } + + /** @brief Get the source node for the iedge'th edge from specified RRNodeId. + * This method should generally not be used, and instead first_edge and + * last_edge should be used.*/ + inline RRNodeId edge_source_node(RRNodeId id, t_edge_size iedge) const { + return node_storage_.edge_source_node(id, iedge); + } /** @brief Detect if the edge is a configurable edge (controlled by a programmable routing multipler or a tri-state switch). */ inline bool edge_is_configurable(RRNodeId id, t_edge_size iedge) const { return node_storage_.edge_is_configurable(id, iedge, rr_switch_inf_); } + inline bool edge_is_configurable(RREdgeId edge) const { + return node_storage_.edge_is_configurable(edge, rr_switch_inf_); + } /** @brief Get the number of configurable edges. This function is inlined for runtime optimization. */ inline t_edge_size num_configurable_edges(RRNodeId node) const { @@ -322,22 +346,28 @@ class RRGraphView { return node_storage_.num_non_configurable_edges(node, rr_switch_inf_); } - /** @brief A configurable edge represents a programmable switch between routing resources, which could be + /** @brief A configurable edge represents a programmable switch between routing resources, which could be * a multiplexer * a tri-state buffer - * a pass gate + * a pass gate * This API gets ID range for configurable edges. This function is inlined for runtime optimization. */ inline edge_idx_range configurable_edges(RRNodeId node) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(node_storage_.num_edges(node) - num_non_configurable_edges(node))); } + inline edge_idx_range node_configurable_out_edges(RRNodeId node) const { + return configurable_edges(node); + } - /** @brief A non-configurable edge represents a hard-wired connection between routing resources, which could be + /** @brief A non-configurable edge represents a hard-wired connection between routing resources, which could be * a non-configurable buffer that can not be turned off * a short metal connection that can not be turned off * This API gets ID range for non-configurable edges. This function is inlined for runtime optimization. */ inline edge_idx_range non_configurable_edges(RRNodeId node) const { return vtr::make_range(edge_idx_iterator(node_storage_.num_edges(node) - num_non_configurable_edges(node)), edge_idx_iterator(num_edges(node))); } + inline edge_idx_range node_non_configurable_out_edges(RRNodeId node) const { + return non_configurable_edges(node); + } /** @brief Get outgoing edges for a node. * This API is designed to enable range-based loop to walk through the outgoing edges of a node @@ -351,32 +381,38 @@ class RRGraphView { inline edge_idx_range edges(const RRNodeId& id) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); } + inline edge_idx_range node_out_edges(const RRNodeId& id) const { + return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); + } + + /** @brief find the edges between two nodes */ + std::vector find_edges(const RRNodeId& src_node, const RRNodeId& des_node) const; /** @brief Get the number of edges. This function is inlined for runtime optimization. */ inline t_edge_size num_edges(RRNodeId node) const { return node_storage_.num_edges(node); } - /** @brief The ptc_num carries different meanings for different node types - * (true in VPR RRG that is currently supported, may not be true in customized RRG) - * CHANX or CHANY: the track id in routing channels - * OPIN or IPIN: the index of pins in the logic block data structure - * SOURCE and SINK: the class id of a pin (indicating logic equivalence of pins) in the logic block data structure - * @note - * This API is very powerful and developers should not use it unless it is necessary, - * e.g the node type is unknown. If the node type is known, the more specific routines, `node_pin_num()`, + /** @brief The ptc_num carries different meanings for different node types + * (true in VPR RRG that is currently supported, may not be true in customized RRG) + * CHANX or CHANY: the track id in routing channels + * OPIN or IPIN: the index of pins in the logic block data structure + * SOURCE and SINK: the class id of a pin (indicating logic equivalence of pins) in the logic block data structure + * @note + * This API is very powerful and developers should not use it unless it is necessary, + * e.g the node type is unknown. If the node type is known, the more specific routines, `node_pin_num()`, * `node_track_num()`and `node_class_num()`, for different types of nodes should be used.*/ inline int node_ptc_num(RRNodeId node) const { return node_storage_.node_ptc_num(node); } - /** @brief Get the pin num of a routing resource node. This is designed for logic blocks, + /** @brief Get the pin num of a routing resource node. This is designed for logic blocks, * which are IPIN and OPIN nodes. This function is inlined for runtime optimization. */ inline int node_pin_num(RRNodeId node) const { return node_storage_.node_pin_num(node); } - /** @brief Get the track num of a routing resource node. This is designed for routing tracks, + /** @brief Get the track num of a routing resource node. This is designed for routing tracks, * which are CHANX and CHANY nodes. This function is inlined for runtime optimization. */ inline int node_track_num(RRNodeId node) const { return node_storage_.node_track_num(node); @@ -392,6 +428,17 @@ class RRGraphView { RRIndexedDataId node_cost_index(RRNodeId node) const { return node_storage_.node_cost_index(node); } + + /** @brief Get the segment id which a routing resource node represents. Only applicable to nodes whose type is CHANX or CHANY */ + RRSegmentId node_segment(RRNodeId node) const; + + /** @brief Return incoming edges for a given routing resource node + * Require build_in_edges() to be called first + */ + std::vector node_in_edges(RRNodeId node) const; + std::vector node_configurable_in_edges(RRNodeId node) const; + std::vector node_non_configurable_in_edges(RRNodeId node) const; + /** @brief Return detailed routing segment information with a given id* @note The routing segments here may not be exactly same as those defined in architecture file. They have been * adapted to fit the context of routing resource graphs. */ @@ -455,7 +502,7 @@ class RRGraphView { } public: /* Validators */ - /** brief Validate that edge data is partitioned correctly + /** @brief Validate that edge data is partitioned correctly * @note This function is used to validate the correctness of the routing resource graph in terms * of graph attributes. Strongly recommend to call it when you finish the building a routing resource * graph. If you need more advance checks, which are related to architecture features, you should @@ -464,6 +511,21 @@ class RRGraphView { return node_storage_.validate_node(node_id, rr_switch_inf_); } + /** @brief Check if the node id is a valid one in storage */ + inline bool valid_node(RRNodeId node_id) const { + return size_t(node_id) < node_storage_.size(); + } + + /** @brief Check if the switch is a valid one in storage */ + inline bool valid_switch(RRSwitchId switch_id) const { + return (size_t(switch_id) < rr_switch_inf_.size()); + } + + /** @brief Validate if all the fan-in edge lists are valid */ + bool validate_in_edges() const; + /** @brief Count the number of incoming edges for all the nodes */ + size_t in_edges_count() const; + /* -- Internal data storage -- */ /* Note: only read-only object or data structures are allowed!!! */ private: @@ -498,13 +560,20 @@ class RRGraphView { /* rr_indexed_data_ and rr_segments_ are needed to lookup the segment information in node_coordinate_to_string() */ const vtr::vector& rr_indexed_data_; - /* RC data for nodes. This is a flyweight data */ + /* RC data for nodes. This is a flyweight data */ const std::vector& rr_rc_data_; /* Segment info for rr nodes */ const vtr::vector& rr_segments_; /* switch info for rr nodes */ const vtr::vector& rr_switch_inf_; + + /** A list of incoming edges for each routing resource node. This can be built optionally, as required by applications. + * By default, it is empty! Call build_in_edges() to construct it!!! */ + const vtr::vector>& node_in_edges_; + + /** A list of extra ptc numbers for each routing resource node. See details in RRGraphBuilder class */ + const vtr::vector>& node_ptc_nums_; }; #endif diff --git a/libs/librrgraph/src/base/rr_spatial_lookup.cpp b/libs/librrgraph/src/base/rr_spatial_lookup.cpp index 6f6bae475d3..7818a5254e8 100644 --- a/libs/librrgraph/src/base/rr_spatial_lookup.cpp +++ b/libs/librrgraph/src/base/rr_spatial_lookup.cpp @@ -1,4 +1,5 @@ #include "vtr_assert.h" +#include "vtr_log.h" #include "rr_spatial_lookup.h" RRSpatialLookup::RRSpatialLookup() { diff --git a/libs/libvtrcapnproto/CMakeLists.txt b/libs/libvtrcapnproto/CMakeLists.txt index cd97b0ec4f1..0f66a10856a 100644 --- a/libs/libvtrcapnproto/CMakeLists.txt +++ b/libs/libvtrcapnproto/CMakeLists.txt @@ -32,7 +32,7 @@ capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS ) if (VPR_ENABLE_INTERCHANGE) - set(IC_DIR ${CMAKE_SOURCE_DIR}/libs/EXTERNAL/libinterchange/interchange) + set(IC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../EXTERNAL/libinterchange/interchange) set(CAPNPC_SRC_PREFIX ${IC_DIR}) find_program(WGET wget REQUIRED) diff --git a/libs/libvtrutil/CMakeLists.txt b/libs/libvtrutil/CMakeLists.txt index 78187dfd153..51b29eb5abb 100644 --- a/libs/libvtrutil/CMakeLists.txt +++ b/libs/libvtrutil/CMakeLists.txt @@ -61,24 +61,28 @@ endif() # 2) The custom command depends on the touched version input file and generates the processed # version file, with updated values. The custom command uses the configure_version.cmake # script to generate the up-to-date vtr_version.cpp -add_custom_target(version ALL - COMMAND ${CMAKE_COMMAND} -E touch ${VTR_VERSION_FILE_IN}) - -add_custom_command(OUTPUT ${VTR_VERSION_FILE_OUT} - COMMAND ${CMAKE_COMMAND} - -D IN_FILE=${VTR_VERSION_FILE_IN} - -D OUT_FILE=${VTR_VERSION_FILE_OUT} - -D VTR_VERSION_MAJOR=${VTR_VERSION_MAJOR} - -D VTR_VERSION_MINOR=${VTR_VERSION_MINOR} - -D VTR_VERSION_PATCH=${VTR_VERSION_PATCH} - -D VTR_VERSION_PRERELEASE=${VTR_VERSION_PRERELEASE} - -D VTR_COMPILER_INFO=${VTR_COMPILER_INFO} - -D VTR_BUILD_INFO=${VTR_BUILD_INFO} - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/configure_version.cmake - MAIN_DEPENDENCY ${VTR_VERSION_FILE_IN} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - VERBATIM) - +if (VTR_ENABLE_VERSION) + add_custom_target(version ALL + COMMAND ${CMAKE_COMMAND} -E touch ${VTR_VERSION_FILE_IN}) + + add_custom_command(OUTPUT ${VTR_VERSION_FILE_OUT} + COMMAND ${CMAKE_COMMAND} + -D IN_FILE=${VTR_VERSION_FILE_IN} + -D OUT_FILE=${VTR_VERSION_FILE_OUT} + -D VTR_VERSION_MAJOR=${VTR_VERSION_MAJOR} + -D VTR_VERSION_MINOR=${VTR_VERSION_MINOR} + -D VTR_VERSION_PATCH=${VTR_VERSION_PATCH} + -D VTR_VERSION_PRERELEASE=${VTR_VERSION_PRERELEASE} + -D VTR_COMPILER_INFO=${VTR_COMPILER_INFO} + -D VTR_BUILD_INFO=${VTR_BUILD_INFO} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/configure_version.cmake + MAIN_DEPENDENCY ${VTR_VERSION_FILE_IN} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM) +else() +# Just copy the input file to output file with version number + configure_file(${VTR_VERSION_FILE_IN} ${VTR_VERSION_FILE_OUT}) +endif() # # Source files and library @@ -98,7 +102,9 @@ target_include_directories(libvtrutil PUBLIC ${LIB_INCLUDE_DIRS}) set_target_properties(libvtrutil PROPERTIES PREFIX "") #Avoid extra 'lib' prefix #Ensure version is always up to date by requiring version to be run first -add_dependencies(libvtrutil version) +if (VTR_ENABLE_VERSION) + add_dependencies(libvtrutil version) +endif() #Specify link-time dependancies target_link_libraries(libvtrutil diff --git a/libs/libvtrutil/src/vtr_geometry.h b/libs/libvtrutil/src/vtr_geometry.h index 3685c308653..1e11f87e1d7 100644 --- a/libs/libvtrutil/src/vtr_geometry.h +++ b/libs/libvtrutil/src/vtr_geometry.h @@ -61,6 +61,9 @@ bool operator!=(const RectUnion& lhs, const RectUnion& rhs); template class Point { public: //Constructors + // below is to create a no argument constructor for libopenfpga/libopenfpgautil/src/openfpga_pb_parser.cpp + // need to figure out a better solution to avoid change this in libs from vtr + Point(); Point(T x_val, T y_val) noexcept; public: //Accessors diff --git a/libs/libvtrutil/src/vtr_geometry.tpp b/libs/libvtrutil/src/vtr_geometry.tpp index 2010700fc50..c3e25dcf8a9 100644 --- a/libs/libvtrutil/src/vtr_geometry.tpp +++ b/libs/libvtrutil/src/vtr_geometry.tpp @@ -3,6 +3,11 @@ namespace vtr { * Point */ +template +Point::Point() { + //pass +} + template Point::Point(T x_val, T y_val) noexcept : x_(x_val) diff --git a/libs/libvtrutil/src/vtr_logic.h b/libs/libvtrutil/src/vtr_logic.h index 30d44c4a6b9..b43ee49291e 100644 --- a/libs/libvtrutil/src/vtr_logic.h +++ b/libs/libvtrutil/src/vtr_logic.h @@ -16,6 +16,8 @@ constexpr int FALSE = 0; constexpr int TRUE = 1; +#include + namespace vtr { /** @@ -25,9 +27,12 @@ enum class LogicValue { FALSE = 0, TRUE = 1, DONT_CARE = 2, - UNKOWN = 3 + UNKOWN = 3, + NUM_LOGIC_VALUE_TYPES }; +constexpr std::array LOGIC_VALUE_STRING = {{"false", "true", "don't care", "unknown"}}; + } // namespace vtr #endif diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 33bd010346e..6145c037fa0 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -384,9 +384,11 @@ static void SetupSwitches(const t_arch& Arch, static void SetupRoutingArch(const t_arch& Arch, t_det_routing_arch* RoutingArch) { RoutingArch->switch_block_type = Arch.SBType; + RoutingArch->switch_block_subtype = Arch.SBSubType; RoutingArch->R_minW_nmos = Arch.R_minW_nmos; RoutingArch->R_minW_pmos = Arch.R_minW_pmos; RoutingArch->Fs = Arch.Fs; + RoutingArch->subFs = Arch.subFs; RoutingArch->directionality = BI_DIRECTIONAL; if (Arch.Segments.size()) { RoutingArch->directionality = Arch.Segments[0].directionality; @@ -394,6 +396,10 @@ static void SetupRoutingArch(const t_arch& Arch, /* copy over the switch block information */ RoutingArch->switchblocks = Arch.switchblocks; + + /* Copy the tileable routing setting */ + RoutingArch->tileable = Arch.tileable; + RoutingArch->through_channel = Arch.through_channel; } static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) { @@ -421,6 +427,11 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->router_algorithm = Options.RouterAlgorithm; RouterOpts->fixed_channel_width = Options.RouteChanWidth; RouterOpts->min_channel_width_hint = Options.min_route_chan_width_hint; + + //TODO document these? + RouterOpts->trim_empty_channels = false; /* DEFAULT */ + RouterOpts->trim_obs_channels = false; /* DEFAULT */ + RouterOpts->read_rr_edge_metadata = Options.read_rr_edge_metadata; RouterOpts->reorder_rr_graph_nodes_algorithm = Options.reorder_rr_graph_nodes_algorithm; RouterOpts->reorder_rr_graph_nodes_threshold = Options.reorder_rr_graph_nodes_threshold; @@ -685,6 +696,8 @@ static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysi analysis_opts.timing_update_type = Options.timing_update_type; analysis_opts.write_timing_summary = Options.write_timing_summary; + + analysis_opts.skip_sync_clustering_and_routing_results = Options.skip_sync_clustering_and_routing_results; } static void SetupPowerOpts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch) { diff --git a/vpr/src/base/ShowSetup.cpp b/vpr/src/base/ShowSetup.cpp index ba8e9d74cc2..f05dd6f1c72 100644 --- a/vpr/src/base/ShowSetup.cpp +++ b/vpr/src/base/ShowSetup.cpp @@ -319,6 +319,8 @@ static void ShowRouterOpts(const t_router_opts& RouterOpts) { VPR_FATAL_ERROR(VPR_ERROR_UNKNOWN, "Unknown check_route value\n"); } + VTR_LOG("RouterOpts.trim_empty_chan: %s\n", (RouterOpts.trim_empty_channels ? "true" : "false")); + VTR_LOG("RouterOpts.trim_obs_chan: %s\n", (RouterOpts.trim_obs_channels ? "true" : "false")); VTR_LOG("RouterOpts.acc_fac: %f\n", RouterOpts.acc_fac); VTR_LOG("RouterOpts.bb_factor: %d\n", RouterOpts.bb_factor); VTR_LOG("RouterOpts.bend_cost: %f\n", RouterOpts.bend_cost); diff --git a/vpr/src/base/gen/vpr_constraints_uxsdcxx.h b/vpr/src/base/gen/vpr_constraints_uxsdcxx.h index 3248c338281..24e12ef884c 100644 --- a/vpr/src/base/gen/vpr_constraints_uxsdcxx.h +++ b/vpr/src/base/gen/vpr_constraints_uxsdcxx.h @@ -4,13 +4,14 @@ * https://github.com/duck2/uxsdcxx * Modify only if your build process doesn't involve regenerating this file. * - * Cmdline: uxsdcxx.py vpr_constraints.xsd - * Input file: /home/khalid88/Documents/uxsdcxx/vpr_constraints.xsd - * md5sum of input file: 6b6011a6e6446347b234da82e517422e + * Cmdline: uxsdcxx.py /home/tao/works/dev/route_constraints/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * Input file: /home/tao/works/dev/route_constraints/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * md5sum of input file: 9f078b743b3fa356a41cf239f150a599 */ #include + #include #include #include @@ -25,8 +26,6 @@ #include "pugixml.hpp" #include "vpr_constraints_uxsdcxx_interface.h" -#include "region.h" - /* All uxsdcxx functions and structs live in this namespace. */ namespace uxsd { @@ -34,827 +33,1251 @@ namespace uxsd { * Internal function for getting line and column number from file based on * byte offset. */ -inline void get_line_number(const char* filename, std::ptrdiff_t offset, int* line, int* col); +inline void get_line_number(const char *filename, std::ptrdiff_t offset, int * line, int * col); -[[noreturn]] inline void noreturn_report(const std::function* report_error, const char* msg) { +[[noreturn]] inline void noreturn_report(const std::function * report_error, const char *msg) { (*report_error)(msg); throw std::runtime_error("Unreachable!"); } /* Declarations for internal load functions for the complex types. */ -template -inline void load_add_atom(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); -template -inline void load_add_region(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); -inline void load_add_region_required_attributes(const pugi::xml_node& root, int* x_high, int* x_low, int* y_high, int* y_low, const std::function* report_error); -template -inline void load_partition(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); -template -inline void load_partition_list(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); -template -inline void load_vpr_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); +template +inline void load_add_atom(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); +template +inline void load_add_region(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); +inline void load_add_region_required_attributes(const pugi::xml_node &root, int * x_high, int * x_low, int * y_high, int * y_low, const std::function * report_error); +template +inline void load_partition(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); +template +inline void load_partition_list(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); +template +inline void load_set_global_signal(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); +template +inline void load_global_route_constraints(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); +template +inline void load_assignment(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); +template +inline void load_packer_constraints(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); +template +inline void load_vpr_constraints(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); /* Declarations for internal write functions for the complex types. */ -template -inline void write_partition(T& in, std::ostream& os, const void* data, void* iter); -template -inline void write_partition_list(T& in, std::ostream& os, const void* data, void* iter); -template -inline void write_vpr_constraints(T& in, std::ostream& os, const void* data, void* iter); +template +inline void write_partition(T &in, std::ostream &os, const void *data, void *iter); +template +inline void write_partition_list(T &in, std::ostream &os, const void *data, void *iter); +template +inline void write_global_route_constraints(T &in, std::ostream &os, const void *data, void *iter); +template +inline void write_packer_constraints(T &in, std::ostream &os, const void *data, void *iter); +template +inline void write_vpr_constraints(T &in, std::ostream &os, const void *data, void *iter); /* Load function for the root element. */ -template -inline void load_vpr_constraints_xml(T& out, Context& context, const char* filename, std::istream& is) { - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load(is); - if (!result) { - int line, col; - get_line_number(filename, result.offset, &line, &col); - std::stringstream msg; - msg << "Unable to load XML file '" << filename << "', "; - msg << result.description() << " (line: " << line; - msg << " col: " << col << ")"; - out.error_encountered(filename, line, msg.str().c_str()); - } - ptrdiff_t offset_debug = 0; - std::function report_error = [filename, &out, &offset_debug](const char* message) { - int line, col; - get_line_number(filename, offset_debug, &line, &col); - out.error_encountered(filename, line, message); - // If error_encountered didn't throw, throw now to unwind. - throw std::runtime_error(message); - }; - out.start_load(&report_error); - - for (pugi::xml_node node = doc.first_child(); node; node = node.next_sibling()) { - if (std::strcmp(node.name(), "vpr_constraints") == 0) { - /* If errno is set up to this point, it messes with strtol errno checking. */ - errno = 0; - load_vpr_constraints(node, out, context, &report_error, &offset_debug); - } else { - offset_debug = node.offset_debug(); - report_error(("Invalid root-level element " + std::string(node.name())).c_str()); - } - } - out.finish_load(); +template +inline void load_vpr_constraints_xml(T &out, Context &context, const char * filename, std::istream &is){ + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load(is); + if(!result) { + int line, col; + get_line_number(filename, result.offset, &line, &col); + std::stringstream msg; + msg << "Unable to load XML file '" << filename << "', "; + msg << result.description() << " (line: " << line; + msg << " col: " << col << ")"; out.error_encountered(filename, line, msg.str().c_str()); + } + ptrdiff_t offset_debug = 0; + std::function report_error = [filename, &out, &offset_debug](const char * message) { + int line, col; + get_line_number(filename, offset_debug, &line, &col); + out.error_encountered(filename, line, message); + // If error_encountered didn't throw, throw now to unwind. + throw std::runtime_error(message); + }; + out.start_load(&report_error); + + for(pugi::xml_node node= doc.first_child(); node; node = node.next_sibling()){ + if(std::strcmp(node.name(), "vpr_constraints") == 0){ + /* If errno is set up to this point, it messes with strtol errno checking. */ + errno = 0; + load_vpr_constraints(node, out, context, &report_error, &offset_debug); + } else { + offset_debug = node.offset_debug(); + report_error(("Invalid root-level element " + std::string(node.name())).c_str()); + } + } + out.finish_load(); } /* Write function for the root element. */ -template -inline void write_vpr_constraints_xml(T& in, Context& context, std::ostream& os) { - in.start_write(); - os << "\n"; - write_vpr_constraints(in, os, context); - os << "\n"; - in.finish_write(); +template +inline void write_vpr_constraints_xml(T &in, Context &context, std::ostream &os){ + in.start_write(); + os << "\n"; + write_vpr_constraints(in, os, context); + os << "\n"; + in.finish_write(); } + typedef const uint32_t __attribute__((aligned(1))) triehash_uu32; typedef const uint64_t __attribute__((aligned(1))) triehash_uu64; static_assert(alignof(triehash_uu32) == 1, "Unaligned 32-bit access not found."); static_assert(alignof(triehash_uu64) == 1, "Unaligned 64-bit access not found."); #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define onechar(c, s, l) (((uint64_t)(c)) << (s)) +#define onechar(c, s, l) (((uint64_t)(c)) << (s)) #else -# define onechar(c, s, l) (((uint64_t)(c)) << (l - 8 - s)) +#define onechar(c, s, l) (((uint64_t)(c)) << (l-8-s)) #endif /* Tokens for attribute and node names. */ -enum class atok_t_add_atom { NAME_PATTERN }; -constexpr const char* atok_lookup_t_add_atom[] = {"name_pattern"}; - -enum class atok_t_add_region { SUBTILE, - X_HIGH, - X_LOW, - Y_HIGH, - Y_LOW }; -constexpr const char* atok_lookup_t_add_region[] = {"subtile", "x_high", "x_low", "y_high", "y_low"}; - -enum class gtok_t_partition { ADD_ATOM, - ADD_REGION }; -constexpr const char* gtok_lookup_t_partition[] = {"add_atom", "add_region"}; -enum class atok_t_partition { NAME }; -constexpr const char* atok_lookup_t_partition[] = {"name"}; - -enum class gtok_t_partition_list { PARTITION }; -constexpr const char* gtok_lookup_t_partition_list[] = {"partition"}; -enum class gtok_t_vpr_constraints { PARTITION_LIST }; -constexpr const char* gtok_lookup_t_vpr_constraints[] = {"partition_list"}; -enum class atok_t_vpr_constraints { TOOL_NAME }; -constexpr const char* atok_lookup_t_vpr_constraints[] = {"tool_name"}; +enum class atok_t_add_atom {NAME_PATTERN}; +constexpr const char *atok_lookup_t_add_atom[] = {"name_pattern"}; + + +enum class atok_t_add_region {SUBTILE, X_HIGH, X_LOW, Y_HIGH, Y_LOW}; +constexpr const char *atok_lookup_t_add_region[] = {"subtile", "x_high", "x_low", "y_high", "y_low"}; + +enum class gtok_t_partition {ADD_ATOM, ADD_REGION}; +constexpr const char *gtok_lookup_t_partition[] = {"add_atom", "add_region"}; +enum class atok_t_partition {NAME}; +constexpr const char *atok_lookup_t_partition[] = {"name"}; + +enum class gtok_t_partition_list {PARTITION}; +constexpr const char *gtok_lookup_t_partition_list[] = {"partition"}; + +enum class atok_t_set_global_signal {NAME, ROUTE_MODEL, TYPE}; +constexpr const char *atok_lookup_t_set_global_signal[] = {"name", "route_model", "type"}; + +enum class gtok_t_global_route_constraints {SET_GLOBAL_SIGNAL}; +constexpr const char *gtok_lookup_t_global_route_constraints[] = {"set_global_signal"}; + +enum class atok_t_assignment {NET, PIN}; +constexpr const char *atok_lookup_t_assignment[] = {"net", "pin"}; + +enum class gtok_t_packer_constraints {ASSIGNMENT}; +constexpr const char *gtok_lookup_t_packer_constraints[] = {"assignment"}; +enum class gtok_t_vpr_constraints {PARTITION_LIST, GLOBAL_ROUTE_CONSTRAINTS, PACKER_CONSTRAINTS}; +constexpr const char *gtok_lookup_t_vpr_constraints[] = {"partition_list", "global_route_constraints", "packer_constraints"}; +enum class atok_t_vpr_constraints {TOOL_NAME}; +constexpr const char *atok_lookup_t_vpr_constraints[] = {"tool_name"}; + /* Internal lexers. These convert the PugiXML node names to input tokens. */ -inline atok_t_add_atom lex_attr_t_add_atom(const char* in, const std::function* report_error) { - unsigned int len = strlen(in); - switch (len) { - case 12: - switch (*((triehash_uu64*)&in[0])) { - case onechar('n', 0, 64) | onechar('a', 8, 64) | onechar('m', 16, 64) | onechar('e', 24, 64) | onechar('_', 32, 64) | onechar('p', 40, 64) | onechar('a', 48, 64) | onechar('t', 56, 64): - switch (*((triehash_uu32*)&in[8])) { - case onechar('t', 0, 32) | onechar('e', 8, 32) | onechar('r', 16, 32) | onechar('n', 24, 32): - return atok_t_add_atom::NAME_PATTERN; - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); -} - -inline atok_t_add_region lex_attr_t_add_region(const char* in, const std::function* report_error) { - unsigned int len = strlen(in); - switch (len) { - case 5: - switch (*((triehash_uu32*)&in[0])) { - case onechar('x', 0, 32) | onechar('_', 8, 32) | onechar('l', 16, 32) | onechar('o', 24, 32): - switch (in[4]) { - case onechar('w', 0, 8): - return atok_t_add_region::X_LOW; - break; - default: - break; - } - break; - case onechar('y', 0, 32) | onechar('_', 8, 32) | onechar('l', 16, 32) | onechar('o', 24, 32): - switch (in[4]) { - case onechar('w', 0, 8): - return atok_t_add_region::Y_LOW; - break; - default: - break; - } - break; - default: - break; - } - break; - case 6: - switch (*((triehash_uu32*)&in[0])) { - case onechar('x', 0, 32) | onechar('_', 8, 32) | onechar('h', 16, 32) | onechar('i', 24, 32): - switch (in[4]) { - case onechar('g', 0, 8): - switch (in[5]) { - case onechar('h', 0, 8): - return atok_t_add_region::X_HIGH; - break; - default: - break; - } - break; - default: - break; - } - break; - case onechar('y', 0, 32) | onechar('_', 8, 32) | onechar('h', 16, 32) | onechar('i', 24, 32): - switch (in[4]) { - case onechar('g', 0, 8): - switch (in[5]) { - case onechar('h', 0, 8): - return atok_t_add_region::Y_HIGH; - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - break; - case 7: - switch (*((triehash_uu32*)&in[0])) { - case onechar('s', 0, 32) | onechar('u', 8, 32) | onechar('b', 16, 32) | onechar('t', 24, 32): - switch (in[4]) { - case onechar('i', 0, 8): - switch (in[5]) { - case onechar('l', 0, 8): - switch (in[6]) { - case onechar('e', 0, 8): - return atok_t_add_region::SUBTILE; - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); -} - -inline gtok_t_partition lex_node_t_partition(const char* in, const std::function* report_error) { - unsigned int len = strlen(in); - switch (len) { - case 8: - switch (*((triehash_uu64*)&in[0])) { - case onechar('a', 0, 64) | onechar('d', 8, 64) | onechar('d', 16, 64) | onechar('_', 24, 64) | onechar('a', 32, 64) | onechar('t', 40, 64) | onechar('o', 48, 64) | onechar('m', 56, 64): - return gtok_t_partition::ADD_ATOM; - break; - default: - break; - } - break; - case 10: - switch (*((triehash_uu64*)&in[0])) { - case onechar('a', 0, 64) | onechar('d', 8, 64) | onechar('d', 16, 64) | onechar('_', 24, 64) | onechar('r', 32, 64) | onechar('e', 40, 64) | onechar('g', 48, 64) | onechar('i', 56, 64): - switch (in[8]) { - case onechar('o', 0, 8): - switch (in[9]) { - case onechar('n', 0, 8): - return gtok_t_partition::ADD_REGION; - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); -} -inline atok_t_partition lex_attr_t_partition(const char* in, const std::function* report_error) { - unsigned int len = strlen(in); - switch (len) { - case 4: - switch (*((triehash_uu32*)&in[0])) { - case onechar('n', 0, 32) | onechar('a', 8, 32) | onechar('m', 16, 32) | onechar('e', 24, 32): - return atok_t_partition::NAME; - break; - default: - break; - } - break; - default: - break; - } - noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); -} - -inline gtok_t_partition_list lex_node_t_partition_list(const char* in, const std::function* report_error) { - unsigned int len = strlen(in); - switch (len) { - case 9: - switch (*((triehash_uu64*)&in[0])) { - case onechar('p', 0, 64) | onechar('a', 8, 64) | onechar('r', 16, 64) | onechar('t', 24, 64) | onechar('i', 32, 64) | onechar('t', 40, 64) | onechar('i', 48, 64) | onechar('o', 56, 64): - switch (in[8]) { - case onechar('n', 0, 8): - return gtok_t_partition_list::PARTITION; - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); -} - -inline gtok_t_vpr_constraints lex_node_t_vpr_constraints(const char* in, const std::function* report_error) { - unsigned int len = strlen(in); - switch (len) { - case 14: - switch (*((triehash_uu64*)&in[0])) { - case onechar('p', 0, 64) | onechar('a', 8, 64) | onechar('r', 16, 64) | onechar('t', 24, 64) | onechar('i', 32, 64) | onechar('t', 40, 64) | onechar('i', 48, 64) | onechar('o', 56, 64): - switch (*((triehash_uu32*)&in[8])) { - case onechar('n', 0, 32) | onechar('_', 8, 32) | onechar('l', 16, 32) | onechar('i', 24, 32): - switch (in[12]) { - case onechar('s', 0, 8): - switch (in[13]) { - case onechar('t', 0, 8): - return gtok_t_vpr_constraints::PARTITION_LIST; - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); -} -inline atok_t_vpr_constraints lex_attr_t_vpr_constraints(const char* in, const std::function* report_error) { - unsigned int len = strlen(in); - switch (len) { - case 9: - switch (*((triehash_uu64*)&in[0])) { - case onechar('t', 0, 64) | onechar('o', 8, 64) | onechar('o', 16, 64) | onechar('l', 24, 64) | onechar('_', 32, 64) | onechar('n', 40, 64) | onechar('a', 48, 64) | onechar('m', 56, 64): - switch (in[8]) { - case onechar('e', 0, 8): - return atok_t_vpr_constraints::TOOL_NAME; - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); +inline atok_t_add_atom lex_attr_t_add_atom(const char *in, const std::function * report_error){ + unsigned int len = strlen(in); + switch(len){ + case 12: + switch(*((triehash_uu64*)&in[0])){ + case onechar('n', 0, 64) | onechar('a', 8, 64) | onechar('m', 16, 64) | onechar('e', 24, 64) | onechar('_', 32, 64) | onechar('p', 40, 64) | onechar('a', 48, 64) | onechar('t', 56, 64): + switch(*((triehash_uu32*)&in[8])){ + case onechar('t', 0, 32) | onechar('e', 8, 32) | onechar('r', 16, 32) | onechar('n', 24, 32): + return atok_t_add_atom::NAME_PATTERN; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); } -/** - * Internal error function for xs:choice and xs:sequence validators. - */ -[[noreturn]] inline void dfa_error(const char* wrong, const int* states, const char* const* lookup, int len, const std::function* report_error); +inline atok_t_add_region lex_attr_t_add_region(const char *in, const std::function * report_error){ + unsigned int len = strlen(in); + switch(len){ + case 5: + switch(*((triehash_uu32*)&in[0])){ + case onechar('x', 0, 32) | onechar('_', 8, 32) | onechar('l', 16, 32) | onechar('o', 24, 32): + switch(in[4]){ + case onechar('w', 0, 8): + return atok_t_add_region::X_LOW; + break; + default: break; + } + break; + case onechar('y', 0, 32) | onechar('_', 8, 32) | onechar('l', 16, 32) | onechar('o', 24, 32): + switch(in[4]){ + case onechar('w', 0, 8): + return atok_t_add_region::Y_LOW; + break; + default: break; + } + break; + default: break; + } + break; + case 6: + switch(*((triehash_uu32*)&in[0])){ + case onechar('x', 0, 32) | onechar('_', 8, 32) | onechar('h', 16, 32) | onechar('i', 24, 32): + switch(in[4]){ + case onechar('g', 0, 8): + switch(in[5]){ + case onechar('h', 0, 8): + return atok_t_add_region::X_HIGH; + break; + default: break; + } + break; + default: break; + } + break; + case onechar('y', 0, 32) | onechar('_', 8, 32) | onechar('h', 16, 32) | onechar('i', 24, 32): + switch(in[4]){ + case onechar('g', 0, 8): + switch(in[5]){ + case onechar('h', 0, 8): + return atok_t_add_region::Y_HIGH; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + case 7: + switch(*((triehash_uu32*)&in[0])){ + case onechar('s', 0, 32) | onechar('u', 8, 32) | onechar('b', 16, 32) | onechar('t', 24, 32): + switch(in[4]){ + case onechar('i', 0, 8): + switch(in[5]){ + case onechar('l', 0, 8): + switch(in[6]){ + case onechar('e', 0, 8): + return atok_t_add_region::SUBTILE; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); +} + +inline gtok_t_partition lex_node_t_partition(const char *in, const std::function *report_error){ + unsigned int len = strlen(in); + switch(len){ + case 8: + switch(*((triehash_uu64*)&in[0])){ + case onechar('a', 0, 64) | onechar('d', 8, 64) | onechar('d', 16, 64) | onechar('_', 24, 64) | onechar('a', 32, 64) | onechar('t', 40, 64) | onechar('o', 48, 64) | onechar('m', 56, 64): + return gtok_t_partition::ADD_ATOM; + break; + default: break; + } + break; + case 10: + switch(*((triehash_uu64*)&in[0])){ + case onechar('a', 0, 64) | onechar('d', 8, 64) | onechar('d', 16, 64) | onechar('_', 24, 64) | onechar('r', 32, 64) | onechar('e', 40, 64) | onechar('g', 48, 64) | onechar('i', 56, 64): + switch(in[8]){ + case onechar('o', 0, 8): + switch(in[9]){ + case onechar('n', 0, 8): + return gtok_t_partition::ADD_REGION; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); +} +inline atok_t_partition lex_attr_t_partition(const char *in, const std::function * report_error){ + unsigned int len = strlen(in); + switch(len){ + case 4: + switch(*((triehash_uu32*)&in[0])){ + case onechar('n', 0, 32) | onechar('a', 8, 32) | onechar('m', 16, 32) | onechar('e', 24, 32): + return atok_t_partition::NAME; + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); +} + +inline gtok_t_partition_list lex_node_t_partition_list(const char *in, const std::function *report_error){ + unsigned int len = strlen(in); + switch(len){ + case 9: + switch(*((triehash_uu64*)&in[0])){ + case onechar('p', 0, 64) | onechar('a', 8, 64) | onechar('r', 16, 64) | onechar('t', 24, 64) | onechar('i', 32, 64) | onechar('t', 40, 64) | onechar('i', 48, 64) | onechar('o', 56, 64): + switch(in[8]){ + case onechar('n', 0, 8): + return gtok_t_partition_list::PARTITION; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); +} + +inline atok_t_set_global_signal lex_attr_t_set_global_signal(const char *in, const std::function * report_error){ + unsigned int len = strlen(in); + switch(len){ + case 4: + switch(*((triehash_uu32*)&in[0])){ + case onechar('n', 0, 32) | onechar('a', 8, 32) | onechar('m', 16, 32) | onechar('e', 24, 32): + return atok_t_set_global_signal::NAME; + break; + case onechar('t', 0, 32) | onechar('y', 8, 32) | onechar('p', 16, 32) | onechar('e', 24, 32): + return atok_t_set_global_signal::TYPE; + break; + default: break; + } + break; + case 11: + switch(*((triehash_uu64*)&in[0])){ + case onechar('r', 0, 64) | onechar('o', 8, 64) | onechar('u', 16, 64) | onechar('t', 24, 64) | onechar('e', 32, 64) | onechar('_', 40, 64) | onechar('m', 48, 64) | onechar('o', 56, 64): + switch(in[8]){ + case onechar('d', 0, 8): + switch(in[9]){ + case onechar('e', 0, 8): + switch(in[10]){ + case onechar('l', 0, 8): + return atok_t_set_global_signal::ROUTE_MODEL; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); +} + +inline gtok_t_global_route_constraints lex_node_t_global_route_constraints(const char *in, const std::function *report_error){ + unsigned int len = strlen(in); + switch(len){ + case 17: + switch(*((triehash_uu64*)&in[0])){ + case onechar('s', 0, 64) | onechar('e', 8, 64) | onechar('t', 16, 64) | onechar('_', 24, 64) | onechar('g', 32, 64) | onechar('l', 40, 64) | onechar('o', 48, 64) | onechar('b', 56, 64): + switch(*((triehash_uu64*)&in[8])){ + case onechar('a', 0, 64) | onechar('l', 8, 64) | onechar('_', 16, 64) | onechar('s', 24, 64) | onechar('i', 32, 64) | onechar('g', 40, 64) | onechar('n', 48, 64) | onechar('a', 56, 64): + switch(in[16]){ + case onechar('l', 0, 8): + return gtok_t_global_route_constraints::SET_GLOBAL_SIGNAL; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); +} + +inline atok_t_assignment lex_attr_t_assignment(const char *in, const std::function * report_error){ + unsigned int len = strlen(in); + switch(len){ + case 3: + switch(in[0]){ + case onechar('n', 0, 8): + switch(in[1]){ + case onechar('e', 0, 8): + switch(in[2]){ + case onechar('t', 0, 8): + return atok_t_assignment::NET; + break; + default: break; + } + break; + default: break; + } + break; + case onechar('p', 0, 8): + switch(in[1]){ + case onechar('i', 0, 8): + switch(in[2]){ + case onechar('n', 0, 8): + return atok_t_assignment::PIN; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); +} + +inline gtok_t_packer_constraints lex_node_t_packer_constraints(const char *in, const std::function *report_error){ + unsigned int len = strlen(in); + switch(len){ + case 10: + switch(*((triehash_uu64*)&in[0])){ + case onechar('a', 0, 64) | onechar('s', 8, 64) | onechar('s', 16, 64) | onechar('i', 24, 64) | onechar('g', 32, 64) | onechar('n', 40, 64) | onechar('m', 48, 64) | onechar('e', 56, 64): + switch(in[8]){ + case onechar('n', 0, 8): + switch(in[9]){ + case onechar('t', 0, 8): + return gtok_t_packer_constraints::ASSIGNMENT; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); +} + +inline gtok_t_vpr_constraints lex_node_t_vpr_constraints(const char *in, const std::function *report_error){ + unsigned int len = strlen(in); + switch(len){ + case 14: + switch(*((triehash_uu64*)&in[0])){ + case onechar('p', 0, 64) | onechar('a', 8, 64) | onechar('r', 16, 64) | onechar('t', 24, 64) | onechar('i', 32, 64) | onechar('t', 40, 64) | onechar('i', 48, 64) | onechar('o', 56, 64): + switch(*((triehash_uu32*)&in[8])){ + case onechar('n', 0, 32) | onechar('_', 8, 32) | onechar('l', 16, 32) | onechar('i', 24, 32): + switch(in[12]){ + case onechar('s', 0, 8): + switch(in[13]){ + case onechar('t', 0, 8): + return gtok_t_vpr_constraints::PARTITION_LIST; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + case 18: + switch(*((triehash_uu64*)&in[0])){ + case onechar('p', 0, 64) | onechar('a', 8, 64) | onechar('c', 16, 64) | onechar('k', 24, 64) | onechar('e', 32, 64) | onechar('r', 40, 64) | onechar('_', 48, 64) | onechar('c', 56, 64): + switch(*((triehash_uu64*)&in[8])){ + case onechar('o', 0, 64) | onechar('n', 8, 64) | onechar('s', 16, 64) | onechar('t', 24, 64) | onechar('r', 32, 64) | onechar('a', 40, 64) | onechar('i', 48, 64) | onechar('n', 56, 64): + switch(in[16]){ + case onechar('t', 0, 8): + switch(in[17]){ + case onechar('s', 0, 8): + return gtok_t_vpr_constraints::PACKER_CONSTRAINTS; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + case 24: + switch(*((triehash_uu64*)&in[0])){ + case onechar('g', 0, 64) | onechar('l', 8, 64) | onechar('o', 16, 64) | onechar('b', 24, 64) | onechar('a', 32, 64) | onechar('l', 40, 64) | onechar('_', 48, 64) | onechar('r', 56, 64): + switch(*((triehash_uu64*)&in[8])){ + case onechar('o', 0, 64) | onechar('u', 8, 64) | onechar('t', 16, 64) | onechar('e', 24, 64) | onechar('_', 32, 64) | onechar('c', 40, 64) | onechar('o', 48, 64) | onechar('n', 56, 64): + switch(*((triehash_uu64*)&in[16])){ + case onechar('s', 0, 64) | onechar('t', 8, 64) | onechar('r', 16, 64) | onechar('a', 24, 64) | onechar('i', 32, 64) | onechar('n', 40, 64) | onechar('t', 48, 64) | onechar('s', 56, 64): + return gtok_t_vpr_constraints::GLOBAL_ROUTE_CONSTRAINTS; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); +} +inline atok_t_vpr_constraints lex_attr_t_vpr_constraints(const char *in, const std::function * report_error){ + unsigned int len = strlen(in); + switch(len){ + case 9: + switch(*((triehash_uu64*)&in[0])){ + case onechar('t', 0, 64) | onechar('o', 8, 64) | onechar('o', 16, 64) | onechar('l', 24, 64) | onechar('_', 32, 64) | onechar('n', 40, 64) | onechar('a', 48, 64) | onechar('m', 56, 64): + switch(in[8]){ + case onechar('e', 0, 8): + return atok_t_vpr_constraints::TOOL_NAME; + break; + default: break; + } + break; + default: break; + } + break; + default: break; + } + noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); +} /** - * Internal error function for xs:all validators. + * Internal error function for xs:choice and xs:sequence validators. */ -template -[[noreturn]] inline void all_error(std::bitset gstate, const char* const* lookup, const std::function* report_error); +[[noreturn]] inline void dfa_error(const char *wrong, const int *states, const char * const *lookup, int len, const std::function * report_error); /** * Internal error function for attribute validators. */ template -[[noreturn]] inline void attr_error(std::bitset astate, const char* const* lookup, const std::function* report_error); +[[noreturn]] inline void attr_error(std::bitset astate, const char * const *lookup, const std::function * report_error); + /* Internal loading functions, which validate and load a PugiXML DOM tree into memory. */ -inline int load_int(const char* in, const std::function* report_error) { - int out; - // global variable, must set to 0 before using it to avoid changed by other errors - errno = 0; - out = std::strtol(in, NULL, 10); - if (errno != 0) - noreturn_report(report_error, ("Invalid value `" + std::string(in) + "` when loading into a int.").c_str()); - return out; -} -inline void load_add_region_required_attributes(const pugi::xml_node& root, int* x_high, int* x_low, int* y_high, int* y_low, const std::function* report_error) { - std::bitset<5> astate = 0; - for (pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()) { - atok_t_add_region in = lex_attr_t_add_region(attr.name(), report_error); - if (astate[(int)in] == 0) - astate[(int)in] = 1; - else - noreturn_report(report_error, ("Duplicate attribute " + std::string(attr.name()) + " in .").c_str()); - switch (in) { - case atok_t_add_region::SUBTILE: - /* Attribute subtile set after element init */ - break; - case atok_t_add_region::X_HIGH: - *x_high = load_int(attr.value(), report_error); - break; - case atok_t_add_region::X_LOW: - *x_low = load_int(attr.value(), report_error); - break; - case atok_t_add_region::Y_HIGH: - *y_high = load_int(attr.value(), report_error); - break; - case atok_t_add_region::Y_LOW: - *y_low = load_int(attr.value(), report_error); - break; - default: - break; /* Not possible. */ - } - } - std::bitset<5> test_astate = astate | std::bitset<5>(0b00001); - if (!test_astate.all()) attr_error(test_astate, atok_lookup_t_add_region, report_error); +inline int load_int(const char *in, const std::function * report_error){ + int out; + out = std::strtol(in, NULL, 10); + if(errno != 0) + noreturn_report(report_error, ("Invalid value `" + std::string(in) + "` when loading into a int.").c_str()); + return out; +} +inline void load_add_region_required_attributes(const pugi::xml_node &root, int * x_high, int * x_low, int * y_high, int * y_low, const std::function * report_error){ + std::bitset<5> astate = 0; + for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ + atok_t_add_region in = lex_attr_t_add_region(attr.name(), report_error); + if(astate[(int)in] == 0) astate[(int)in] = 1; + else noreturn_report(report_error, ("Duplicate attribute " + std::string(attr.name()) + " in .").c_str()); + switch(in){ + case atok_t_add_region::SUBTILE: + /* Attribute subtile set after element init */ + break; + case atok_t_add_region::X_HIGH: + *x_high = load_int(attr.value(), report_error); + break; + case atok_t_add_region::X_LOW: + *x_low = load_int(attr.value(), report_error); + break; + case atok_t_add_region::Y_HIGH: + *y_high = load_int(attr.value(), report_error); + break; + case atok_t_add_region::Y_LOW: + *y_low = load_int(attr.value(), report_error); + break; + default: break; /* Not possible. */ + } + } + std::bitset<5> test_astate = astate | std::bitset<5>(0b00001); + if(!test_astate.all()) attr_error(test_astate, atok_lookup_t_add_region, report_error); } template -inline void load_add_atom(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { - (void)root; - (void)out; - (void)context; - (void)report_error; - // Update current file offset in case an error is encountered. - *offset_debug = root.offset_debug(); - - for (pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()) { - atok_t_add_atom in = lex_attr_t_add_atom(attr.name(), report_error); - switch (in) { - case atok_t_add_atom::NAME_PATTERN: - out.set_add_atom_name_pattern(attr.value(), context); - break; - default: - break; /* Not possible. */ - } - } - - if (root.first_child().type() == pugi::node_element) - noreturn_report(report_error, "Unexpected child element in ."); +inline void load_add_atom(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug){ + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ + atok_t_add_atom in = lex_attr_t_add_atom(attr.name(), report_error); + switch(in){ + case atok_t_add_atom::NAME_PATTERN: + out.set_add_atom_name_pattern(attr.value(), context); + break; + default: break; /* Not possible. */ + } + } + + if(root.first_child().type() == pugi::node_element) + noreturn_report(report_error, "Unexpected child element in ."); + } template -inline void load_add_region(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { - (void)root; - (void)out; - (void)context; - (void)report_error; - // Update current file offset in case an error is encountered. - *offset_debug = root.offset_debug(); - - for (pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()) { - atok_t_add_region in = lex_attr_t_add_region(attr.name(), report_error); - switch (in) { - case atok_t_add_region::SUBTILE: - out.set_add_region_subtile(load_int(attr.value(), report_error), context); - break; - case atok_t_add_region::X_HIGH: - /* Attribute x_high is already set */ - break; - case atok_t_add_region::X_LOW: - /* Attribute x_low is already set */ - break; - case atok_t_add_region::Y_HIGH: - /* Attribute y_high is already set */ - break; - case atok_t_add_region::Y_LOW: - /* Attribute y_low is already set */ - break; - default: - break; /* Not possible. */ - } - } - - if (root.first_child().type() == pugi::node_element) - noreturn_report(report_error, "Unexpected child element in ."); +inline void load_add_region(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug){ + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ + atok_t_add_region in = lex_attr_t_add_region(attr.name(), report_error); + switch(in){ + case atok_t_add_region::SUBTILE: + out.set_add_region_subtile(load_int(attr.value(), report_error), context); + break; + case atok_t_add_region::X_HIGH: + /* Attribute x_high is already set */ + break; + case atok_t_add_region::X_LOW: + /* Attribute x_low is already set */ + break; + case atok_t_add_region::Y_HIGH: + /* Attribute y_high is already set */ + break; + case atok_t_add_region::Y_LOW: + /* Attribute y_low is already set */ + break; + default: break; /* Not possible. */ + } + } + + if(root.first_child().type() == pugi::node_element) + noreturn_report(report_error, "Unexpected child element in ."); + } constexpr int NUM_T_PARTITION_STATES = 2; constexpr const int NUM_T_PARTITION_INPUTS = 2; constexpr int gstate_t_partition[NUM_T_PARTITION_STATES][NUM_T_PARTITION_INPUTS] = { - {0, 0}, - {0, 0}, + {0, 0}, + {0, 0}, }; template -inline void load_partition(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { - (void)root; - (void)out; - (void)context; - (void)report_error; - // Update current file offset in case an error is encountered. - *offset_debug = root.offset_debug(); - - for (pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()) { - atok_t_partition in = lex_attr_t_partition(attr.name(), report_error); - switch (in) { - case atok_t_partition::NAME: - out.set_partition_name(attr.value(), context); - break; - default: - break; /* Not possible. */ - } - } - - // Preallocate arrays by counting child nodes (if any) - size_t add_atom_count = 0; - size_t add_region_count = 0; - { - int next, state = 1; - for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { - *offset_debug = node.offset_debug(); - gtok_t_partition in = lex_node_t_partition(node.name(), report_error); - next = gstate_t_partition[state][(int)in]; - if (next == -1) - dfa_error(gtok_lookup_t_partition[(int)in], gstate_t_partition[state], gtok_lookup_t_partition, 2, report_error); - state = next; - switch (in) { - case gtok_t_partition::ADD_ATOM: - add_atom_count += 1; - break; - case gtok_t_partition::ADD_REGION: - add_region_count += 1; - break; - default: - break; /* Not possible. */ - } - } - - out.preallocate_partition_add_atom(context, add_atom_count); - out.preallocate_partition_add_region(context, add_region_count); - } - int next, state = 1; - for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { - *offset_debug = node.offset_debug(); - gtok_t_partition in = lex_node_t_partition(node.name(), report_error); - next = gstate_t_partition[state][(int)in]; - if (next == -1) - dfa_error(gtok_lookup_t_partition[(int)in], gstate_t_partition[state], gtok_lookup_t_partition, 2, report_error); - state = next; - switch (in) { - case gtok_t_partition::ADD_ATOM: { - auto child_context = out.add_partition_add_atom(context); - load_add_atom(node, out, child_context, report_error, offset_debug); - out.finish_partition_add_atom(child_context); - } break; - case gtok_t_partition::ADD_REGION: { - int add_region_x_high; - memset(&add_region_x_high, 0, sizeof(add_region_x_high)); - int add_region_x_low; - memset(&add_region_x_low, 0, sizeof(add_region_x_low)); - int add_region_y_high; - memset(&add_region_y_high, 0, sizeof(add_region_y_high)); - int add_region_y_low; - memset(&add_region_y_low, 0, sizeof(add_region_y_low)); - load_add_region_required_attributes(node, &add_region_x_high, &add_region_x_low, &add_region_y_high, &add_region_y_low, report_error); - auto child_context = out.add_partition_add_region(context, add_region_x_high, add_region_x_low, add_region_y_high, add_region_y_low); - load_add_region(node, out, child_context, report_error, offset_debug); - out.finish_partition_add_region(child_context); - } break; - default: - break; /* Not possible. */ - } - } - if (state != 0) dfa_error("end of input", gstate_t_partition[state], gtok_lookup_t_partition, 2, report_error); +inline void load_partition(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug){ + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ + atok_t_partition in = lex_attr_t_partition(attr.name(), report_error); + switch(in){ + case atok_t_partition::NAME: + out.set_partition_name(attr.value(), context); + break; + default: break; /* Not possible. */ + } + } + + // Preallocate arrays by counting child nodes (if any) + size_t add_atom_count = 0; + size_t add_region_count = 0; + { + int next, state=1; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_partition in = lex_node_t_partition(node.name(), report_error); + next = gstate_t_partition[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_partition[(int)in], gstate_t_partition[state], gtok_lookup_t_partition, 2, report_error); + state = next; + switch(in) { + case gtok_t_partition::ADD_ATOM: + add_atom_count += 1; + break; + case gtok_t_partition::ADD_REGION: + add_region_count += 1; + break; + default: break; /* Not possible. */ + } + } + + out.preallocate_partition_add_atom(context, add_atom_count); + out.preallocate_partition_add_region(context, add_region_count); + } + int next, state=1; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()){ + *offset_debug = node.offset_debug(); + gtok_t_partition in = lex_node_t_partition(node.name(), report_error); + next = gstate_t_partition[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_partition[(int)in], gstate_t_partition[state], gtok_lookup_t_partition, 2, report_error); + state = next; + switch(in){ + case gtok_t_partition::ADD_ATOM: + { + auto child_context = out.add_partition_add_atom(context); + load_add_atom(node, out, child_context, report_error, offset_debug); + out.finish_partition_add_atom(child_context); + } + break; + case gtok_t_partition::ADD_REGION: + { + int add_region_x_high; + memset(&add_region_x_high, 0, sizeof(add_region_x_high)); + int add_region_x_low; + memset(&add_region_x_low, 0, sizeof(add_region_x_low)); + int add_region_y_high; + memset(&add_region_y_high, 0, sizeof(add_region_y_high)); + int add_region_y_low; + memset(&add_region_y_low, 0, sizeof(add_region_y_low)); + load_add_region_required_attributes(node, &add_region_x_high, &add_region_x_low, &add_region_y_high, &add_region_y_low, report_error); + auto child_context = out.add_partition_add_region(context, add_region_x_high, add_region_x_low, add_region_y_high, add_region_y_low); + load_add_region(node, out, child_context, report_error, offset_debug); + out.finish_partition_add_region(child_context); + } + break; + default: break; /* Not possible. */ + } + } + if(state != 0) dfa_error("end of input", gstate_t_partition[state], gtok_lookup_t_partition, 2, report_error); + } constexpr int NUM_T_PARTITION_LIST_STATES = 2; constexpr const int NUM_T_PARTITION_LIST_INPUTS = 1; constexpr int gstate_t_partition_list[NUM_T_PARTITION_LIST_STATES][NUM_T_PARTITION_LIST_INPUTS] = { - {0}, - {0}, + {0}, + {0}, }; template -inline void load_partition_list(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { - (void)root; - (void)out; - (void)context; - (void)report_error; - // Update current file offset in case an error is encountered. - *offset_debug = root.offset_debug(); - - if (root.first_attribute()) - noreturn_report(report_error, "Unexpected attribute in ."); - - // Preallocate arrays by counting child nodes (if any) - size_t partition_count = 0; - { - int next, state = 1; - for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { - *offset_debug = node.offset_debug(); - gtok_t_partition_list in = lex_node_t_partition_list(node.name(), report_error); - next = gstate_t_partition_list[state][(int)in]; - if (next == -1) - dfa_error(gtok_lookup_t_partition_list[(int)in], gstate_t_partition_list[state], gtok_lookup_t_partition_list, 1, report_error); - state = next; - switch (in) { - case gtok_t_partition_list::PARTITION: - partition_count += 1; - break; - default: - break; /* Not possible. */ - } - } - - out.preallocate_partition_list_partition(context, partition_count); - } - int next, state = 1; - for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { - *offset_debug = node.offset_debug(); - gtok_t_partition_list in = lex_node_t_partition_list(node.name(), report_error); - next = gstate_t_partition_list[state][(int)in]; - if (next == -1) - dfa_error(gtok_lookup_t_partition_list[(int)in], gstate_t_partition_list[state], gtok_lookup_t_partition_list, 1, report_error); - state = next; - switch (in) { - case gtok_t_partition_list::PARTITION: { - auto child_context = out.add_partition_list_partition(context); - load_partition(node, out, child_context, report_error, offset_debug); - out.finish_partition_list_partition(child_context); - } break; - default: - break; /* Not possible. */ - } - } - if (state != 0) dfa_error("end of input", gstate_t_partition_list[state], gtok_lookup_t_partition_list, 1, report_error); +inline void load_partition_list(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug){ + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + if(root.first_attribute()) + noreturn_report(report_error, "Unexpected attribute in ."); + + // Preallocate arrays by counting child nodes (if any) + size_t partition_count = 0; + { + int next, state=1; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_partition_list in = lex_node_t_partition_list(node.name(), report_error); + next = gstate_t_partition_list[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_partition_list[(int)in], gstate_t_partition_list[state], gtok_lookup_t_partition_list, 1, report_error); + state = next; + switch(in) { + case gtok_t_partition_list::PARTITION: + partition_count += 1; + break; + default: break; /* Not possible. */ + } + } + + out.preallocate_partition_list_partition(context, partition_count); + } + int next, state=1; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()){ + *offset_debug = node.offset_debug(); + gtok_t_partition_list in = lex_node_t_partition_list(node.name(), report_error); + next = gstate_t_partition_list[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_partition_list[(int)in], gstate_t_partition_list[state], gtok_lookup_t_partition_list, 1, report_error); + state = next; + switch(in){ + case gtok_t_partition_list::PARTITION: + { + auto child_context = out.add_partition_list_partition(context); + load_partition(node, out, child_context, report_error, offset_debug); + out.finish_partition_list_partition(child_context); + } + break; + default: break; /* Not possible. */ + } + } + if(state != 0) dfa_error("end of input", gstate_t_partition_list[state], gtok_lookup_t_partition_list, 1, report_error); + } template -inline void load_vpr_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { - (void)root; - (void)out; - (void)context; - (void)report_error; - // Update current file offset in case an error is encountered. - *offset_debug = root.offset_debug(); - - for (pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()) { - atok_t_vpr_constraints in = lex_attr_t_vpr_constraints(attr.name(), report_error); - switch (in) { - case atok_t_vpr_constraints::TOOL_NAME: - out.set_vpr_constraints_tool_name(attr.value(), context); - break; - default: - break; /* Not possible. */ - } - } - - std::bitset<1> gstate = 0; - for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { - *offset_debug = node.offset_debug(); - gtok_t_vpr_constraints in = lex_node_t_vpr_constraints(node.name(), report_error); - if (gstate[(int)in] == 0) - gstate[(int)in] = 1; - else - noreturn_report(report_error, ("Duplicate element " + std::string(node.name()) + " in .").c_str()); - switch (in) { - case gtok_t_vpr_constraints::PARTITION_LIST: { - auto child_context = out.init_vpr_constraints_partition_list(context); - load_partition_list(node, out, child_context, report_error, offset_debug); - out.finish_vpr_constraints_partition_list(child_context); - } break; - default: - break; /* Not possible. */ - } - } - std::bitset<1> test_gstate = gstate | std::bitset<1>(0b0); - if (!test_gstate.all()) all_error(test_gstate, gtok_lookup_t_vpr_constraints, report_error); +inline void load_set_global_signal(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug){ + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ + atok_t_set_global_signal in = lex_attr_t_set_global_signal(attr.name(), report_error); + switch(in){ + case atok_t_set_global_signal::NAME: + out.set_set_global_signal_name(attr.value(), context); + break; + case atok_t_set_global_signal::ROUTE_MODEL: + out.set_set_global_signal_route_model(attr.value(), context); + break; + case atok_t_set_global_signal::TYPE: + out.set_set_global_signal_type(attr.value(), context); + break; + default: break; /* Not possible. */ + } + } + + if(root.first_child().type() == pugi::node_element) + noreturn_report(report_error, "Unexpected child element in ."); + } -/* Internal writing functions, which uxsdcxx uses to write out a class. */ +constexpr int NUM_T_GLOBAL_ROUTE_CONSTRAINTS_STATES = 2; +constexpr const int NUM_T_GLOBAL_ROUTE_CONSTRAINTS_INPUTS = 1; +constexpr int gstate_t_global_route_constraints[NUM_T_GLOBAL_ROUTE_CONSTRAINTS_STATES][NUM_T_GLOBAL_ROUTE_CONSTRAINTS_INPUTS] = { + {0}, + {0}, +}; template -inline void write_partition(T& in, std::ostream& os, Context& context) { - (void)in; - (void)os; - (void)context; - { - for (size_t i = 0, n = in.num_partition_add_atom(context); i < n; i++) { - auto child_context = in.get_partition_add_atom(i, context); - os << "\n"; - } - } - { - for (size_t i = 0, n = in.num_partition_add_region(context); i < n; i++) { - auto child_context = in.get_partition_add_region(i, context); - os << "\n"; - } - } +inline void load_global_route_constraints(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug){ + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + if(root.first_attribute()) + noreturn_report(report_error, "Unexpected attribute in ."); + + // Preallocate arrays by counting child nodes (if any) + size_t set_global_signal_count = 0; + { + int next, state=1; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_global_route_constraints in = lex_node_t_global_route_constraints(node.name(), report_error); + next = gstate_t_global_route_constraints[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_global_route_constraints[(int)in], gstate_t_global_route_constraints[state], gtok_lookup_t_global_route_constraints, 1, report_error); + state = next; + switch(in) { + case gtok_t_global_route_constraints::SET_GLOBAL_SIGNAL: + set_global_signal_count += 1; + break; + default: break; /* Not possible. */ + } + } + + out.preallocate_global_route_constraints_set_global_signal(context, set_global_signal_count); + } + int next, state=1; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()){ + *offset_debug = node.offset_debug(); + gtok_t_global_route_constraints in = lex_node_t_global_route_constraints(node.name(), report_error); + next = gstate_t_global_route_constraints[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_global_route_constraints[(int)in], gstate_t_global_route_constraints[state], gtok_lookup_t_global_route_constraints, 1, report_error); + state = next; + switch(in){ + case gtok_t_global_route_constraints::SET_GLOBAL_SIGNAL: + { + auto child_context = out.add_global_route_constraints_set_global_signal(context); + load_set_global_signal(node, out, child_context, report_error, offset_debug); + out.finish_global_route_constraints_set_global_signal(child_context); + } + break; + default: break; /* Not possible. */ + } + } + if(state != 0) dfa_error("end of input", gstate_t_global_route_constraints[state], gtok_lookup_t_global_route_constraints, 1, report_error); + } template -inline void write_partition_list(T& in, std::ostream& os, Context& context) { - (void)in; - (void)os; - (void)context; - { - for (size_t i = 0, n = in.num_partition_list_partition(context); i < n; i++) { - auto child_context = in.get_partition_list_partition(i, context); - os << ""; - write_partition(in, os, child_context); - os << "\n"; - } - } +inline void load_assignment(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug){ + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ + atok_t_assignment in = lex_attr_t_assignment(attr.name(), report_error); + switch(in){ + case atok_t_assignment::NET: + out.set_assignment_net(attr.value(), context); + break; + case atok_t_assignment::PIN: + out.set_assignment_pin(attr.value(), context); + break; + default: break; /* Not possible. */ + } + } + + if(root.first_child().type() == pugi::node_element) + noreturn_report(report_error, "Unexpected child element in ."); + } +constexpr int NUM_T_PACKER_CONSTRAINTS_STATES = 2; +constexpr const int NUM_T_PACKER_CONSTRAINTS_INPUTS = 1; +constexpr int gstate_t_packer_constraints[NUM_T_PACKER_CONSTRAINTS_STATES][NUM_T_PACKER_CONSTRAINTS_INPUTS] = { + {0}, + {0}, +}; template -inline void write_vpr_constraints(T& in, std::ostream& os, Context& context) { - (void)in; - (void)os; - (void)context; - { - auto child_context = in.get_vpr_constraints_partition_list(context); - os << "\n"; - write_partition_list(in, os, child_context); - os << "\n"; - } -} +inline void load_packer_constraints(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug){ + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + if(root.first_attribute()) + noreturn_report(report_error, "Unexpected attribute in ."); + + // Preallocate arrays by counting child nodes (if any) + size_t assignment_count = 0; + { + int next, state=1; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_packer_constraints in = lex_node_t_packer_constraints(node.name(), report_error); + next = gstate_t_packer_constraints[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_packer_constraints[(int)in], gstate_t_packer_constraints[state], gtok_lookup_t_packer_constraints, 1, report_error); + state = next; + switch(in) { + case gtok_t_packer_constraints::ASSIGNMENT: + assignment_count += 1; + break; + default: break; /* Not possible. */ + } + } + + out.preallocate_packer_constraints_assignment(context, assignment_count); + } + int next, state=1; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()){ + *offset_debug = node.offset_debug(); + gtok_t_packer_constraints in = lex_node_t_packer_constraints(node.name(), report_error); + next = gstate_t_packer_constraints[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_packer_constraints[(int)in], gstate_t_packer_constraints[state], gtok_lookup_t_packer_constraints, 1, report_error); + state = next; + switch(in){ + case gtok_t_packer_constraints::ASSIGNMENT: + { + auto child_context = out.add_packer_constraints_assignment(context); + load_assignment(node, out, child_context, report_error, offset_debug); + out.finish_packer_constraints_assignment(child_context); + } + break; + default: break; /* Not possible. */ + } + } + if(state != 0) dfa_error("end of input", gstate_t_packer_constraints[state], gtok_lookup_t_packer_constraints, 1, report_error); -inline void dfa_error(const char* wrong, const int* states, const char* const* lookup, int len, const std::function* report_error) { - std::vector expected; - for (int i = 0; i < len; i++) { - if (states[i] != -1) expected.push_back(lookup[i]); - } +} - std::string expected_or = expected[0]; - for (unsigned int i = 1; i < expected.size(); i++) - expected_or += std::string(" or ") + expected[i]; +constexpr int NUM_T_VPR_CONSTRAINTS_STATES = 1; +constexpr const int NUM_T_VPR_CONSTRAINTS_INPUTS = 3; +constexpr int gstate_t_vpr_constraints[NUM_T_VPR_CONSTRAINTS_STATES][NUM_T_VPR_CONSTRAINTS_INPUTS] = { + {0, 0, 0}, +}; +template +inline void load_vpr_constraints(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug){ + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ + atok_t_vpr_constraints in = lex_attr_t_vpr_constraints(attr.name(), report_error); + switch(in){ + case atok_t_vpr_constraints::TOOL_NAME: + out.set_vpr_constraints_tool_name(attr.value(), context); + break; + default: break; /* Not possible. */ + } + } + + // Preallocate arrays by counting child nodes (if any) + size_t partition_list_count = 0; + size_t global_route_constraints_count = 0; + size_t packer_constraints_count = 0; + { + int next, state=0; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_vpr_constraints in = lex_node_t_vpr_constraints(node.name(), report_error); + next = gstate_t_vpr_constraints[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_vpr_constraints[(int)in], gstate_t_vpr_constraints[state], gtok_lookup_t_vpr_constraints, 3, report_error); + state = next; + switch(in) { + case gtok_t_vpr_constraints::PARTITION_LIST: + partition_list_count += 1; + break; + case gtok_t_vpr_constraints::GLOBAL_ROUTE_CONSTRAINTS: + global_route_constraints_count += 1; + break; + case gtok_t_vpr_constraints::PACKER_CONSTRAINTS: + packer_constraints_count += 1; + break; + default: break; /* Not possible. */ + } + } + + out.preallocate_vpr_constraints_partition_list(context, partition_list_count); + out.preallocate_vpr_constraints_global_route_constraints(context, global_route_constraints_count); + out.preallocate_vpr_constraints_packer_constraints(context, packer_constraints_count); + } + int next, state=0; + for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()){ + *offset_debug = node.offset_debug(); + gtok_t_vpr_constraints in = lex_node_t_vpr_constraints(node.name(), report_error); + next = gstate_t_vpr_constraints[state][(int)in]; + if(next == -1) + dfa_error(gtok_lookup_t_vpr_constraints[(int)in], gstate_t_vpr_constraints[state], gtok_lookup_t_vpr_constraints, 3, report_error); + state = next; + switch(in){ + case gtok_t_vpr_constraints::PARTITION_LIST: + { + auto child_context = out.add_vpr_constraints_partition_list(context); + load_partition_list(node, out, child_context, report_error, offset_debug); + out.finish_vpr_constraints_partition_list(child_context); + } + break; + case gtok_t_vpr_constraints::GLOBAL_ROUTE_CONSTRAINTS: + { + auto child_context = out.add_vpr_constraints_global_route_constraints(context); + load_global_route_constraints(node, out, child_context, report_error, offset_debug); + out.finish_vpr_constraints_global_route_constraints(child_context); + } + break; + case gtok_t_vpr_constraints::PACKER_CONSTRAINTS: + { + auto child_context = out.add_vpr_constraints_packer_constraints(context); + load_packer_constraints(node, out, child_context, report_error, offset_debug); + out.finish_vpr_constraints_packer_constraints(child_context); + } + break; + default: break; /* Not possible. */ + } + } + if(state != 0) dfa_error("end of input", gstate_t_vpr_constraints[state], gtok_lookup_t_vpr_constraints, 3, report_error); - noreturn_report(report_error, ("Expected " + expected_or + ", found " + std::string(wrong)).c_str()); } -template -inline void all_error(std::bitset gstate, const char* const* lookup, const std::function* report_error) { - std::vector missing; - for (unsigned int i = 0; i < N; i++) { - if (gstate[i] == 0) missing.push_back(lookup[i]); - } - - std::string missing_and = missing[0]; - for (unsigned int i = 1; i < missing.size(); i++) - missing_and += std::string(", ") + missing[i]; - noreturn_report(report_error, ("Didn't find required elements " + missing_and + ".").c_str()); +/* Internal writing functions, which uxsdcxx uses to write out a class. */ +template +inline void write_partition(T &in, std::ostream &os, Context &context){ + (void)in; + (void)os; + (void)context; + { + for(size_t i=0, n=in.num_partition_add_atom(context); i\n"; + } + } + { + for(size_t i=0, n=in.num_partition_add_region(context); i\n"; + } + } } -template -inline void attr_error(std::bitset astate, const char* const* lookup, const std::function* report_error) { - std::vector missing; - for (unsigned int i = 0; i < N; i++) { - if (astate[i] == 0) missing.push_back(lookup[i]); - } - - std::string missing_and = missing[0]; - for (unsigned int i = 1; i < missing.size(); i++) - missing_and += std::string(", ") + missing[i]; +template +inline void write_partition_list(T &in, std::ostream &os, Context &context){ + (void)in; + (void)os; + (void)context; + { + for(size_t i=0, n=in.num_partition_list_partition(context); i"; + write_partition(in, os, child_context); + os << "\n"; + } + } +} - noreturn_report(report_error, ("Didn't find required attributes " + missing_and + ".").c_str()); +template +inline void write_global_route_constraints(T &in, std::ostream &os, Context &context){ + (void)in; + (void)os; + (void)context; + { + for(size_t i=0, n=in.num_global_route_constraints_set_global_signal(context); i\n"; + } + } } -inline void get_line_number(const char* filename, std::ptrdiff_t target_offset, int* line, int* col) { - std::unique_ptr f(fopen(filename, "rb"), fclose); +template +inline void write_packer_constraints(T &in, std::ostream &os, Context &context){ + (void)in; + (void)os; + (void)context; + { + for(size_t i=0, n=in.num_packer_constraints_assignment(context); i\n"; + } + } +} - if (!f) { - throw std::runtime_error(std::string("Failed to open file") + filename); - } +template +inline void write_vpr_constraints(T &in, std::ostream &os, Context &context){ + (void)in; + (void)os; + (void)context; + { + for(size_t i=0, n=in.num_vpr_constraints_partition_list(context); i\n"; + write_partition_list(in, os, child_context); + os << "\n"; + } + } + { + for(size_t i=0, n=in.num_vpr_constraints_global_route_constraints(context); i\n"; + write_global_route_constraints(in, os, child_context); + os << "\n"; + } + } + { + for(size_t i=0, n=in.num_vpr_constraints_packer_constraints(context); i\n"; + write_packer_constraints(in, os, child_context); + os << "\n"; + } + } +} - int current_line = 1; - std::ptrdiff_t offset = 0; - std::ptrdiff_t last_line_offset = 0; - std::ptrdiff_t current_line_offset = 0; +inline void dfa_error(const char *wrong, const int *states, const char * const *lookup, int len, const std::function * report_error){ + std::vector expected; + for(int i=0; i 0) { - for (std::size_t i = 0; i < size; ++i) { - if (buffer[i] == '\n') { - current_line += 1; - last_line_offset = current_line_offset; - current_line_offset = offset + i; + noreturn_report(report_error, ("Expected " + expected_or + ", found " + std::string(wrong)).c_str()); +} - if (target_offset < current_line_offset) { - if (target_offset < last_line_offset) { - throw std::runtime_error("Assertion violation"); - } +template +inline void attr_error(std::bitset astate, const char * const *lookup, const std::function * report_error){ + std::vector missing; + for(unsigned int i=0; i f(fopen(filename, "rb"), fclose); + + if (!f) { + throw std::runtime_error(std::string("Failed to open file") + filename); + } + + int current_line = 1; + std::ptrdiff_t offset = 0; + std::ptrdiff_t last_line_offset = 0; + std::ptrdiff_t current_line_offset = 0; + + char buffer[1024]; + std::size_t size; + + while ((size = fread(buffer, 1, sizeof(buffer), f.get())) > 0) { + for (std::size_t i = 0; i < size; ++i) { + if (buffer[i] == '\n') { + current_line += 1; + last_line_offset = current_line_offset; + current_line_offset = offset + i; + + if(target_offset < current_line_offset) { + if(target_offset < last_line_offset) { + throw std::runtime_error("Assertion violation"); + } + + *line = current_line - 1; + *col = target_offset - last_line_offset; + return; + } + } + } + + offset += size; + } + + *line = current_line; + *col = target_offset - current_line_offset; } + } /* namespace uxsd */ diff --git a/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h b/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h index 6da8558b84d..9e6ab7bb31a 100644 --- a/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h +++ b/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h @@ -4,13 +4,14 @@ * https://github.com/duck2/uxsdcxx * Modify only if your build process doesn't involve regenerating this file. * - * Cmdline: uxsdcxx.py vpr_constraints.xsd - * Input file: /home/khalid88/Documents/uxsdcxx/vpr_constraints.xsd - * md5sum of input file: 6b6011a6e6446347b234da82e517422e + * Cmdline: uxsdcxx.py /home/tao/works/dev/route_constraints/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * Input file: /home/tao/works/dev/route_constraints/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * md5sum of input file: 9f078b743b3fa356a41cf239f150a599 */ #include + /* All uxsdcxx functions and structs live in this namespace. */ #include @@ -20,101 +21,174 @@ namespace uxsd { /* Base class for the schema. */ struct DefaultVprConstraintsContextTypes { - using AddAtomReadContext = void*; - using AddRegionReadContext = void*; - using PartitionReadContext = void*; - using PartitionListReadContext = void*; - using VprConstraintsReadContext = void*; - using AddAtomWriteContext = void*; - using AddRegionWriteContext = void*; - using PartitionWriteContext = void*; - using PartitionListWriteContext = void*; - using VprConstraintsWriteContext = void*; +using AddAtomReadContext = void *; + using AddRegionReadContext = void *; + using PartitionReadContext = void *; + using PartitionListReadContext = void *; + using SetGlobalSignalReadContext = void *; + using GlobalRouteConstraintsReadContext = void *; + using AssignmentReadContext = void *; + using PackerConstraintsReadContext = void *; + using VprConstraintsReadContext = void *; +using AddAtomWriteContext = void *; + using AddRegionWriteContext = void *; + using PartitionWriteContext = void *; + using PartitionListWriteContext = void *; + using SetGlobalSignalWriteContext = void *; + using GlobalRouteConstraintsWriteContext = void *; + using AssignmentWriteContext = void *; + using PackerConstraintsWriteContext = void *; + using VprConstraintsWriteContext = void *; }; -template +template class VprConstraintsBase { - public: - virtual ~VprConstraintsBase() {} - virtual void start_load(const std::function* report_error) = 0; - virtual void finish_load() = 0; - virtual void start_write() = 0; - virtual void finish_write() = 0; - virtual void error_encountered(const char* file, int line, const char* message) = 0; - /** Generated for complex type "add_atom": - * - * - * - */ - virtual inline const char* get_add_atom_name_pattern(typename ContextTypes::AddAtomReadContext& ctx) = 0; - virtual inline void set_add_atom_name_pattern(const char* name_pattern, typename ContextTypes::AddAtomWriteContext& ctx) = 0; - - /** Generated for complex type "add_region": - * - * - * - * - * - * - * - */ - virtual inline int get_add_region_subtile(typename ContextTypes::AddRegionReadContext& ctx) = 0; - virtual inline void set_add_region_subtile(int subtile, typename ContextTypes::AddRegionWriteContext& ctx) = 0; - virtual inline int get_add_region_x_high(typename ContextTypes::AddRegionReadContext& ctx) = 0; - virtual inline int get_add_region_x_low(typename ContextTypes::AddRegionReadContext& ctx) = 0; - virtual inline int get_add_region_y_high(typename ContextTypes::AddRegionReadContext& ctx) = 0; - virtual inline int get_add_region_y_low(typename ContextTypes::AddRegionReadContext& ctx) = 0; - - /** Generated for complex type "partition": - * - * - * - * - * - * - * - * - * - */ - virtual inline const char* get_partition_name(typename ContextTypes::PartitionReadContext& ctx) = 0; - virtual inline void set_partition_name(const char* name, typename ContextTypes::PartitionWriteContext& ctx) = 0; - virtual inline void preallocate_partition_add_atom(typename ContextTypes::PartitionWriteContext& ctx, size_t size) = 0; - virtual inline typename ContextTypes::AddAtomWriteContext add_partition_add_atom(typename ContextTypes::PartitionWriteContext& ctx) = 0; - virtual inline void finish_partition_add_atom(typename ContextTypes::AddAtomWriteContext& ctx) = 0; - virtual inline size_t num_partition_add_atom(typename ContextTypes::PartitionReadContext& ctx) = 0; - virtual inline typename ContextTypes::AddAtomReadContext get_partition_add_atom(int n, typename ContextTypes::PartitionReadContext& ctx) = 0; - virtual inline void preallocate_partition_add_region(typename ContextTypes::PartitionWriteContext& ctx, size_t size) = 0; - virtual inline typename ContextTypes::AddRegionWriteContext add_partition_add_region(typename ContextTypes::PartitionWriteContext& ctx, int x_high, int x_low, int y_high, int y_low) = 0; - virtual inline void finish_partition_add_region(typename ContextTypes::AddRegionWriteContext& ctx) = 0; - virtual inline size_t num_partition_add_region(typename ContextTypes::PartitionReadContext& ctx) = 0; - virtual inline typename ContextTypes::AddRegionReadContext get_partition_add_region(int n, typename ContextTypes::PartitionReadContext& ctx) = 0; - - /** Generated for complex type "partition_list": - * - * - * - * - * - */ - virtual inline void preallocate_partition_list_partition(typename ContextTypes::PartitionListWriteContext& ctx, size_t size) = 0; - virtual inline typename ContextTypes::PartitionWriteContext add_partition_list_partition(typename ContextTypes::PartitionListWriteContext& ctx) = 0; - virtual inline void finish_partition_list_partition(typename ContextTypes::PartitionWriteContext& ctx) = 0; - virtual inline size_t num_partition_list_partition(typename ContextTypes::PartitionListReadContext& ctx) = 0; - virtual inline typename ContextTypes::PartitionReadContext get_partition_list_partition(int n, typename ContextTypes::PartitionListReadContext& ctx) = 0; - - /** Generated for complex type "vpr_constraints": - * - * - * - * - * - * - */ - virtual inline const char* get_vpr_constraints_tool_name(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; - virtual inline void set_vpr_constraints_tool_name(const char* tool_name, typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; - virtual inline typename ContextTypes::PartitionListWriteContext init_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; - virtual inline void finish_vpr_constraints_partition_list(typename ContextTypes::PartitionListWriteContext& ctx) = 0; - virtual inline typename ContextTypes::PartitionListReadContext get_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; +public: + virtual ~VprConstraintsBase() {} + virtual void start_load(const std::function *report_error) = 0; + virtual void finish_load() = 0; + virtual void start_write() = 0; + virtual void finish_write() = 0; + virtual void error_encountered(const char * file, int line, const char *message) = 0; + /** Generated for complex type "add_atom": + * + * + * + */ + virtual inline const char * get_add_atom_name_pattern(typename ContextTypes::AddAtomReadContext &ctx) = 0; + virtual inline void set_add_atom_name_pattern(const char * name_pattern, typename ContextTypes::AddAtomWriteContext &ctx) = 0; + + /** Generated for complex type "add_region": + * + * + * + * + * + * + * + */ + virtual inline int get_add_region_subtile(typename ContextTypes::AddRegionReadContext &ctx) = 0; + virtual inline void set_add_region_subtile(int subtile, typename ContextTypes::AddRegionWriteContext &ctx) = 0; + virtual inline int get_add_region_x_high(typename ContextTypes::AddRegionReadContext &ctx) = 0; + virtual inline int get_add_region_x_low(typename ContextTypes::AddRegionReadContext &ctx) = 0; + virtual inline int get_add_region_y_high(typename ContextTypes::AddRegionReadContext &ctx) = 0; + virtual inline int get_add_region_y_low(typename ContextTypes::AddRegionReadContext &ctx) = 0; + + /** Generated for complex type "partition": + * + * + * + * + * + * + * + * + * + */ + virtual inline const char * get_partition_name(typename ContextTypes::PartitionReadContext &ctx) = 0; + virtual inline void set_partition_name(const char * name, typename ContextTypes::PartitionWriteContext &ctx) = 0; + virtual inline void preallocate_partition_add_atom(typename ContextTypes::PartitionWriteContext &ctx, size_t size) = 0; + virtual inline typename ContextTypes::AddAtomWriteContext add_partition_add_atom(typename ContextTypes::PartitionWriteContext &ctx) = 0; + virtual inline void finish_partition_add_atom(typename ContextTypes::AddAtomWriteContext &ctx) = 0; + virtual inline size_t num_partition_add_atom(typename ContextTypes::PartitionReadContext &ctx) = 0; + virtual inline typename ContextTypes::AddAtomReadContext get_partition_add_atom(int n, typename ContextTypes::PartitionReadContext &ctx) = 0; + virtual inline void preallocate_partition_add_region(typename ContextTypes::PartitionWriteContext &ctx, size_t size) = 0; + virtual inline typename ContextTypes::AddRegionWriteContext add_partition_add_region(typename ContextTypes::PartitionWriteContext &ctx, int x_high, int x_low, int y_high, int y_low) = 0; + virtual inline void finish_partition_add_region(typename ContextTypes::AddRegionWriteContext &ctx) = 0; + virtual inline size_t num_partition_add_region(typename ContextTypes::PartitionReadContext &ctx) = 0; + virtual inline typename ContextTypes::AddRegionReadContext get_partition_add_region(int n, typename ContextTypes::PartitionReadContext &ctx) = 0; + + /** Generated for complex type "partition_list": + * + * + * + * + * + */ + virtual inline void preallocate_partition_list_partition(typename ContextTypes::PartitionListWriteContext &ctx, size_t size) = 0; + virtual inline typename ContextTypes::PartitionWriteContext add_partition_list_partition(typename ContextTypes::PartitionListWriteContext &ctx) = 0; + virtual inline void finish_partition_list_partition(typename ContextTypes::PartitionWriteContext &ctx) = 0; + virtual inline size_t num_partition_list_partition(typename ContextTypes::PartitionListReadContext &ctx) = 0; + virtual inline typename ContextTypes::PartitionReadContext get_partition_list_partition(int n, typename ContextTypes::PartitionListReadContext &ctx) = 0; + + /** Generated for complex type "set_global_signal": + * + * + * + * + * + */ + virtual inline const char * get_set_global_signal_name(typename ContextTypes::SetGlobalSignalReadContext &ctx) = 0; + virtual inline void set_set_global_signal_name(const char * name, typename ContextTypes::SetGlobalSignalWriteContext &ctx) = 0; + virtual inline const char * get_set_global_signal_route_model(typename ContextTypes::SetGlobalSignalReadContext &ctx) = 0; + virtual inline void set_set_global_signal_route_model(const char * route_model, typename ContextTypes::SetGlobalSignalWriteContext &ctx) = 0; + virtual inline const char * get_set_global_signal_type(typename ContextTypes::SetGlobalSignalReadContext &ctx) = 0; + virtual inline void set_set_global_signal_type(const char * type, typename ContextTypes::SetGlobalSignalWriteContext &ctx) = 0; + + /** Generated for complex type "global_route_constraints": + * + * + * + * + * + */ + virtual inline void preallocate_global_route_constraints_set_global_signal(typename ContextTypes::GlobalRouteConstraintsWriteContext &ctx, size_t size) = 0; + virtual inline typename ContextTypes::SetGlobalSignalWriteContext add_global_route_constraints_set_global_signal(typename ContextTypes::GlobalRouteConstraintsWriteContext &ctx) = 0; + virtual inline void finish_global_route_constraints_set_global_signal(typename ContextTypes::SetGlobalSignalWriteContext &ctx) = 0; + virtual inline size_t num_global_route_constraints_set_global_signal(typename ContextTypes::GlobalRouteConstraintsReadContext &ctx) = 0; + virtual inline typename ContextTypes::SetGlobalSignalReadContext get_global_route_constraints_set_global_signal(int n, typename ContextTypes::GlobalRouteConstraintsReadContext &ctx) = 0; + + /** Generated for complex type "assignment": + * + * + * + * + */ + virtual inline const char * get_assignment_net(typename ContextTypes::AssignmentReadContext &ctx) = 0; + virtual inline void set_assignment_net(const char * net, typename ContextTypes::AssignmentWriteContext &ctx) = 0; + virtual inline const char * get_assignment_pin(typename ContextTypes::AssignmentReadContext &ctx) = 0; + virtual inline void set_assignment_pin(const char * pin, typename ContextTypes::AssignmentWriteContext &ctx) = 0; + + /** Generated for complex type "packer_constraints": + * + * + * + * + * + */ + virtual inline void preallocate_packer_constraints_assignment(typename ContextTypes::PackerConstraintsWriteContext &ctx, size_t size) = 0; + virtual inline typename ContextTypes::AssignmentWriteContext add_packer_constraints_assignment(typename ContextTypes::PackerConstraintsWriteContext &ctx) = 0; + virtual inline void finish_packer_constraints_assignment(typename ContextTypes::AssignmentWriteContext &ctx) = 0; + virtual inline size_t num_packer_constraints_assignment(typename ContextTypes::PackerConstraintsReadContext &ctx) = 0; + virtual inline typename ContextTypes::AssignmentReadContext get_packer_constraints_assignment(int n, typename ContextTypes::PackerConstraintsReadContext &ctx) = 0; + + /** Generated for complex type "vpr_constraints": + * + * + * + * + * + * + * + * + */ + virtual inline const char * get_vpr_constraints_tool_name(typename ContextTypes::VprConstraintsReadContext &ctx) = 0; + virtual inline void set_vpr_constraints_tool_name(const char * tool_name, typename ContextTypes::VprConstraintsWriteContext &ctx) = 0; + virtual inline void preallocate_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsWriteContext &ctx, size_t size) = 0; + virtual inline typename ContextTypes::PartitionListWriteContext add_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsWriteContext &ctx) = 0; + virtual inline void finish_vpr_constraints_partition_list(typename ContextTypes::PartitionListWriteContext &ctx) = 0; + virtual inline size_t num_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsReadContext &ctx) = 0; + virtual inline typename ContextTypes::PartitionListReadContext get_vpr_constraints_partition_list(int n, typename ContextTypes::VprConstraintsReadContext &ctx) = 0; + virtual inline void preallocate_vpr_constraints_global_route_constraints(typename ContextTypes::VprConstraintsWriteContext &ctx, size_t size) = 0; + virtual inline typename ContextTypes::GlobalRouteConstraintsWriteContext add_vpr_constraints_global_route_constraints(typename ContextTypes::VprConstraintsWriteContext &ctx) = 0; + virtual inline void finish_vpr_constraints_global_route_constraints(typename ContextTypes::GlobalRouteConstraintsWriteContext &ctx) = 0; + virtual inline size_t num_vpr_constraints_global_route_constraints(typename ContextTypes::VprConstraintsReadContext &ctx) = 0; + virtual inline typename ContextTypes::GlobalRouteConstraintsReadContext get_vpr_constraints_global_route_constraints(int n, typename ContextTypes::VprConstraintsReadContext &ctx) = 0; + virtual inline void preallocate_vpr_constraints_packer_constraints(typename ContextTypes::VprConstraintsWriteContext &ctx, size_t size) = 0; + virtual inline typename ContextTypes::PackerConstraintsWriteContext add_vpr_constraints_packer_constraints(typename ContextTypes::VprConstraintsWriteContext &ctx) = 0; + virtual inline void finish_vpr_constraints_packer_constraints(typename ContextTypes::PackerConstraintsWriteContext &ctx) = 0; + virtual inline size_t num_vpr_constraints_packer_constraints(typename ContextTypes::VprConstraintsReadContext &ctx) = 0; + virtual inline typename ContextTypes::PackerConstraintsReadContext get_vpr_constraints_packer_constraints(int n, typename ContextTypes::VprConstraintsReadContext &ctx) = 0; }; } /* namespace uxsd */ diff --git a/vpr/src/base/packer_constraint.cpp b/vpr/src/base/packer_constraint.cpp new file mode 100644 index 00000000000..b5cbdc44904 --- /dev/null +++ b/vpr/src/base/packer_constraint.cpp @@ -0,0 +1,33 @@ +#include "packer_constraint.h" + +PackerConstraint::PackerConstraint() { + net_name_ = std::string(""); + pin_name_ = std::string(""); + is_valid_ = false; +} + +void PackerConstraint::set_net_name(std::string name) { + net_name_ = name; + return; +} + +std::string PackerConstraint::net_name() const { + return net_name_; +} + +void PackerConstraint::set_pin_name(std::string name) { + pin_name_ = name; + return; +} + +std::string PackerConstraint::pin_name() const { + return net_name_; +} + +void PackerConstraint::set_is_valid(bool value) { + is_valid_ = value; +} + +bool PackerConstraint::is_valid() const { + return is_valid_; +} diff --git a/vpr/src/base/packer_constraint.h b/vpr/src/base/packer_constraint.h new file mode 100644 index 00000000000..d480d8586a7 --- /dev/null +++ b/vpr/src/base/packer_constraint.h @@ -0,0 +1,54 @@ +#ifndef PACKER_CONSTRAINT_H +#define PACKER_CONSTRAINT_H + +#include "vpr_types.h" + +/** + * @file + * @brief This file defines the PackerConstraint class. + */ + +class PackerConstraint { + public: + /** + * @brief Constructor for the PackerConstraint class, sets member variables to invalid values + */ + PackerConstraint(); + + /** + * @brief get net name + */ + std::string net_name() const; + + /** + * @brief set net name + */ + void set_net_name(std::string); + + /** + * @brief get pin name + */ + std::string pin_name() const; + + /** + * @brief set pin name + */ + void set_pin_name(std::string); + + /** + * @brief set is valid + */ + void set_is_valid(bool); + + /** + * @brief get is valid + */ + bool is_valid() const; + + private: + std::string net_name_; + std::string pin_name_; + bool is_valid_; +}; + +#endif /* PACKER_CONSTRAINT_H */ diff --git a/vpr/src/base/place_and_route.cpp b/vpr/src/base/place_and_route.cpp index d12a203390c..4a5808bd118 100644 --- a/vpr/src/base/place_and_route.cpp +++ b/vpr/src/base/place_and_route.cpp @@ -97,6 +97,10 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, graph_directionality = GRAPH_BIDIR; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch->directionality == UNI_DIRECTIONAL && det_routing_arch->tileable) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } diff --git a/vpr/src/base/read_blif.cpp b/vpr/src/base/read_blif.cpp index 2425a18d239..326216896f4 100644 --- a/vpr/src/base/read_blif.cpp +++ b/vpr/src/base/read_blif.cpp @@ -719,18 +719,15 @@ bool is_binary_param(const std::string& param) { } bool is_real_param(const std::string& param) { - const std::string chars = "012345678."; - /* Must be non-empty */ if (param.empty()) { return false; } - /* The string mustn't contain any other chars that the expected ones */ - for (size_t i = 0; i < param.length(); ++i) { - if (chars.find(param[i]) == std::string::npos) { - return false; - } + /* The string must match the regular expression */ + static const std::regex real_number_expr("[+-]?([0-9]*\\.[0-9]+)|([0-9]+\\.[0-9]*)"); + if (!std::regex_match(param, real_number_expr)) { + return false; } /* This is a real number param */ diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index d0870e54591..6d097db9c1d 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -2639,6 +2639,13 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .help("Writes implemented design final timing summary to the specified JSON, XML or TXT file.") .show_in(argparse::ShowIn::HELP_ONLY); + analysis_grp.add_argument(args.skip_sync_clustering_and_routing_results, "--skip_sync_clustering_and_routing_results") + .help( + "Select to skip the synchronization on clustering results based on routing optimization results." + "Note that when this sync-up is disabled, clustering results may be wrong (leading to incorrect bitstreams)!") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + auto& power_grp = parser.add_argument_group("power analysis options"); power_grp.add_argument(args.do_power, "--power") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index d1e9e1e1d61..838b1ce0045 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -232,6 +232,7 @@ struct t_options { argparse::ArgValue post_synth_netlist_unconn_input_handling; argparse::ArgValue post_synth_netlist_unconn_output_handling; argparse::ArgValue write_timing_summary; + argparse::ArgValue skip_sync_clustering_and_routing_results; }; argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& args); diff --git a/vpr/src/base/route_constraint.cpp b/vpr/src/base/route_constraint.cpp new file mode 100644 index 00000000000..4ed76deb108 --- /dev/null +++ b/vpr/src/base/route_constraint.cpp @@ -0,0 +1,43 @@ +#include "route_constraint.h" + +RouteConstraint::RouteConstraint() { + net_name_ = std::string(""); + net_type_ = std::string(""); + route_method_ = std::string(""); + is_valid_ = false; +} + +void RouteConstraint::set_net_name(std::string name) { + net_name_ = name; + return; +} + +std::string RouteConstraint::net_name() const { + return net_name_; +} + +void RouteConstraint::set_net_type(std::string type) { + net_type_ = type; + return; +} + +std::string RouteConstraint::net_type() const { + return net_type_; +} + +void RouteConstraint::set_route_model(std::string route_method) { + route_method_ = route_method; + return; +} + +std::string RouteConstraint::route_model() const { + return route_method_; +} + +void RouteConstraint::set_is_valid(bool value) { + is_valid_ = value; +} + +bool RouteConstraint::is_valid() const { + return is_valid_; +} diff --git a/vpr/src/base/route_constraint.h b/vpr/src/base/route_constraint.h new file mode 100644 index 00000000000..58b2839d060 --- /dev/null +++ b/vpr/src/base/route_constraint.h @@ -0,0 +1,65 @@ +#ifndef ROUTE_CONSTRAINT_H +#define ROUTE_CONSTRAINT_H + +#include "vpr_types.h" + +/** + * @file + * @brief This file defines the RouteConstraint class. + */ + +class RouteConstraint { + public: + /** + * @brief Constructor for the RouteConstraint class, sets member variables to invalid values + */ + RouteConstraint(); + + /** + * @brief get net name + */ + std::string net_name() const; + + /** + * @brief set net name + */ + void set_net_name(std::string); + + /** + * @brief get net type + */ + std::string net_type() const; + + /** + * @brief set net type + */ + void set_net_type(std::string); + + /** + * @brief get route model + */ + std::string route_model() const; + + /** + * @brief set route model + */ + void set_route_model(std::string); + + /** + * @brief set is valid + */ + void set_is_valid(bool); + + /** + * @brief get is valid + */ + bool is_valid() const; + + private: + std::string net_name_; + std::string net_type_; + std::string route_method_; + bool is_valid_; +}; + +#endif /* ROUTE_CONSTRAINT_H */ diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 6ad16110946..f33c7743d61 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -345,10 +345,15 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a } } - //Initialize vpr floorplanning constraints + //Initialize vpr floorplanning and routing constraints auto& filename_opts = vpr_setup->FileNameOpts; if (!filename_opts.read_vpr_constraints_file.empty()) { - load_vpr_constraints_file(filename_opts.read_vpr_constraints_file.c_str()); + load_vpr_constraints_files(filename_opts.read_vpr_constraints_file.c_str()); + + // give a notificaiton on routing constraints overiding clock modeling + if (g_vpr_ctx.routing().constraints.get_route_constraint_num() && options->clock_modeling.provenance() == argparse::Provenance::SPECIFIED) { + VTR_LOG_WARN("Route constraint(s) detected and will override clock modeling setting.\n"); + } } fflush(stdout); @@ -398,6 +403,9 @@ bool vpr_flow(t_vpr_setup& vpr_setup, t_arch& arch) { vpr_analysis_flow(router_net_list, vpr_setup, arch, route_status, is_flat); } + // write out constratins + write_vpr_constraints(vpr_setup); + //close the graphics vpr_close_graphics(vpr_setup); @@ -780,6 +788,11 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, //Assume successful route_status = RouteStatus(true, -1); } else { //Do or load + + // apply route constraints + // use mutable routing context here to apply change on the constraints when binding the constraint to real net + apply_route_constraints(g_vpr_ctx.mutable_routing().constraints); + int chan_width = router_opts.fixed_channel_width; NetPinsMatrix net_delay; @@ -1003,6 +1016,9 @@ void vpr_create_rr_graph(t_vpr_setup& vpr_setup, const t_arch& arch, int chan_wi } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + if ((UNI_DIRECTIONAL == det_routing_arch->directionality) && (true == det_routing_arch->tileable)) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } } t_chan_width chan_width = init_chan(chan_width_fac, arch.Chans, graph_directionality); @@ -1327,7 +1343,8 @@ bool vpr_analysis_flow(const Netlist<>& net_list, VTR_LOG("*****************************************************************************************\n"); } - /* If routing is successful, apply post-routing annotations + /* If routing is successful and users do not force to skip the sync-up, + * - apply post-routing annotations * - apply logic block pin fix-up * * Note: @@ -1336,20 +1353,24 @@ bool vpr_analysis_flow(const Netlist<>& net_list, */ if (!is_flat) { if (route_status.success()) { - sync_netlists_to_routing(net_list, - g_vpr_ctx.device(), - g_vpr_ctx.mutable_atom(), - g_vpr_ctx.atom().lookup, - g_vpr_ctx.mutable_clustering(), - g_vpr_ctx.placement(), - g_vpr_ctx.routing(), - vpr_setup.PackerOpts.pack_verbosity > 2, - is_flat); - - std::string post_routing_packing_output_file_name = vpr_setup.PackerOpts.output_file + ".post_routing"; - write_packing_results_to_xml(vpr_setup.PackerOpts.global_clocks, - Arch.architecture_id, - post_routing_packing_output_file_name.c_str()); + if (!analysis_opts.skip_sync_clustering_and_routing_results) { + sync_netlists_to_routing(net_list, + g_vpr_ctx.device(), + g_vpr_ctx.mutable_atom(), + g_vpr_ctx.atom().lookup, + g_vpr_ctx.mutable_clustering(), + g_vpr_ctx.placement(), + g_vpr_ctx.routing(), + vpr_setup.PackerOpts.pack_verbosity > 2, + is_flat); + + std::string post_routing_packing_output_file_name = vpr_setup.PackerOpts.output_file + ".post_routing"; + write_packing_results_to_xml(vpr_setup.PackerOpts.global_clocks, + Arch.architecture_id, + post_routing_packing_output_file_name.c_str()); + } else { + VTR_LOG_WARN("Sychronization between packing and routing results is not applied due to users select to skip it\n"); + } } else { VTR_LOG_WARN("Sychronization between packing and routing results is not applied due to illegal circuit implementation\n"); } diff --git a/vpr/src/base/vpr_constraints.cpp b/vpr/src/base/vpr_constraints.cpp index 95c7e7b7358..b99b79dea81 100644 --- a/vpr/src/base/vpr_constraints.cpp +++ b/vpr/src/base/vpr_constraints.cpp @@ -1,5 +1,8 @@ #include "vpr_constraints.h" #include "partition.h" +#include "route_constraint.h" +#include "packer_constraint.h" +#include void VprConstraints::add_constrained_atom(const AtomBlockId blk_id, const PartitionId part_id) { auto got = constrained_atoms.find(blk_id); @@ -58,6 +61,128 @@ PartitionRegion VprConstraints::get_partition_pr(PartitionId part_id) { return pr; } +void VprConstraints::add_route_constraint(RouteConstraint rc) { + route_constraints_.insert({rc.net_name(), rc}); + return; +} + +const RouteConstraint VprConstraints::get_route_constraint_by_net_name(std::string net_name) { + RouteConstraint rc; + auto const& rc_itr = route_constraints_.find(net_name); + if (rc_itr == route_constraints_.end()) { + // try regexp + bool found_thru_regex = false; + for (auto constraint : route_constraints_) { + if (std::regex_match(net_name, std::regex(constraint.first))) { + rc = constraint.second; + + // mark as invalid so write constraint function will not write constraint + // of regexpr name + // instead a matched constraint is inserted in + constraint.second.set_is_valid(false); + rc.set_net_name(net_name); + rc.set_is_valid(true); + route_constraints_.insert({net_name, rc}); + + found_thru_regex = true; + break; + } + } + if (!found_thru_regex) { + rc.set_net_name("INVALID"); + rc.set_net_type("INVALID"); + rc.set_route_model("INVALID"); + rc.set_is_valid(false); + } + } else { + rc = rc_itr->second; + } + return rc; +} + +const RouteConstraint VprConstraints::get_route_constraint_by_idx(std::size_t idx) const { + RouteConstraint rc; + if ((route_constraints_.size() == 0) || (idx > route_constraints_.size() - 1)) { + rc.set_net_name("INVALID"); + rc.set_net_type("INVALID"); + rc.set_route_model("INVALID"); + rc.set_is_valid(false); + } else { + std::size_t i = 0; + for (auto const& rc_itr : route_constraints_) { + if (i == idx) { + rc = rc_itr.second; + break; + } + } + } + return rc; +} + +int VprConstraints::get_route_constraint_num(void) const { + return route_constraints_.size(); +} + +void VprConstraints::add_packer_constraint(PackerConstraint rc) { + packer_constraints_.insert({rc.net_name(), rc}); + return; +} + +const PackerConstraint VprConstraints::get_packer_constraint_by_net_name(std::string net_name) { + PackerConstraint pc; + auto const& pc_itr = packer_constraints_.find(net_name); + if (pc_itr == packer_constraints_.end()) { + // try regexp + bool found_thru_regex = false; + for (auto constraint : packer_constraints_) { + if (std::regex_match(net_name, std::regex(constraint.first))) { + pc = constraint.second; + + // mark as invalid so write constraint function will not write constraint + // of regexpr name + // instead a matched constraint is inserted in + constraint.second.set_is_valid(false); + pc.set_net_name(net_name); + pc.set_is_valid(true); + packer_constraints_.insert({net_name, pc}); + + found_thru_regex = true; + break; + } + } + if (!found_thru_regex) { + pc.set_net_name("INVALID"); + pc.set_pin_name("INVALID"); + pc.set_is_valid(false); + } + } else { + pc = pc_itr->second; + } + return pc; +} + +const PackerConstraint VprConstraints::get_packer_constraint_by_idx(std::size_t idx) const { + PackerConstraint pc; + if ((packer_constraints_.size() == 0) || (idx > packer_constraints_.size() - 1)) { + pc.set_net_name("INVALID"); + pc.set_pin_name("INVALID"); + pc.set_is_valid(false); + } else { + std::size_t i = 0; + for (auto const& pc_itr : packer_constraints_) { + if (i == idx) { + pc = pc_itr.second; + break; + } + } + } + return pc; +} + +int VprConstraints::get_packer_constraint_num(void) const { + return packer_constraints_.size(); +} + void print_constraints(FILE* fp, VprConstraints constraints) { Partition temp_part; std::vector atoms; diff --git a/vpr/src/base/vpr_constraints.h b/vpr/src/base/vpr_constraints.h index fd3f64842a4..eaf4ac20446 100644 --- a/vpr/src/base/vpr_constraints.h +++ b/vpr/src/base/vpr_constraints.h @@ -5,6 +5,8 @@ #include "vpr_utils.h" #include "partition.h" #include "partition_region.h" +#include "route_constraint.h" +#include "packer_constraint.h" /** * @file @@ -87,6 +89,62 @@ class VprConstraints { */ PartitionRegion get_partition_pr(PartitionId part_id); + /** + * @brief add route constraint + * + * @param net_name the route constraint + */ + void add_route_constraint(RouteConstraint rc); + + /** + * @brief returns route constraint by index + * + * @param index the constraint index + */ + const RouteConstraint get_route_constraint_by_idx(std::size_t index) const; + + /** + * @brief returns route constraint of a specific net + * + * @param net_name the net name + */ + const RouteConstraint get_route_constraint_by_net_name(std::string net_name); + + /** + * @brief returns number of route constraints + * + * @param void + */ + int get_route_constraint_num(void) const; + + /** + * @brief add packer constraint + * + * @param net_name the packer constraint + */ + void add_packer_constraint(PackerConstraint rc); + + /** + * @brief returns packer constraint by index + * + * @param index the constraint index + */ + const PackerConstraint get_packer_constraint_by_idx(std::size_t index) const; + + /** + * @brief returns packer constraint of a specific net + * + * @param net_name the net name + */ + const PackerConstraint get_packer_constraint_by_net_name(std::string net_name); + + /** + * @brief returns number of packer constraints + * + * @param void + */ + int get_packer_constraint_num(void) const; + private: /** * Store all constrained atoms @@ -97,6 +155,17 @@ class VprConstraints { * Store all partitions */ vtr::vector partitions; + + /** + * store all route constraints + */ + std::unordered_map route_constraints_; + + /** + * store all packer constraints + */ + std::unordered_map packer_constraints_; + }; ///@brief used to print floorplanning constraints data from a VprConstraints object diff --git a/vpr/src/base/vpr_constraints.xsd b/vpr/src/base/vpr_constraints.xsd index 406e2abcda4..ee81318f600 100644 --- a/vpr/src/base/vpr_constraints.xsd +++ b/vpr/src/base/vpr_constraints.xsd @@ -59,16 +59,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + diff --git a/vpr/src/base/vpr_constraints_reader.cpp b/vpr/src/base/vpr_constraints_reader.cpp index 8e69b7b42b4..1140e5269c6 100644 --- a/vpr/src/base/vpr_constraints_reader.cpp +++ b/vpr/src/base/vpr_constraints_reader.cpp @@ -10,31 +10,60 @@ #include #include "vpr_constraints_reader.h" +#include "vtr_token.h" -void load_vpr_constraints_file(const char* read_vpr_constraints_name) { - vtr::ScopedStartFinishTimer timer("Loading VPR constraints file"); +void load_vpr_constraints_files(const char* read_vpr_constraints_name) { + vtr::ScopedStartFinishTimer timer("Loading VPR constraints file(s)"); VprConstraintsSerializer reader; - if (vtr::check_file_name_extension(read_vpr_constraints_name, ".xml")) { - try { - std::ifstream file(read_vpr_constraints_name); - void* context; - uxsd::load_vpr_constraints_xml(reader, context, read_vpr_constraints_name, file); - } catch (pugiutil::XmlError& e) { - vpr_throw(VPR_ERROR_ROUTE, read_vpr_constraints_name, e.line(), "%s", e.what()); + // file name from arguments could be a serial of files, seperated by colon ":" + // at this point, caller has already checked that the required constraint file name is not emtpy + int num_tokens = 0, num_file_read = 0; + bool found_file = false; + t_token* tokens = GetTokensFromString(read_vpr_constraints_name, &num_tokens); + std::string in_tokens(""); + for (int i = 0; i < num_tokens; i++) { + if ((tokens[i].type == TOKEN_COLON)) { // end of one input file + found_file = true; + } else if (i == num_tokens - 1) { // end of inputs, append token anyway + in_tokens += std::string(tokens[i].data); + found_file = true; + } else { + in_tokens += std::string(tokens[i].data); + } + if (found_file) { + const char* file_name = in_tokens.c_str(); + if (vtr::check_file_name_extension(file_name, ".xml")) { + try { + std::ifstream file(file_name); + void* context; + uxsd::load_vpr_constraints_xml(reader, context, file_name, file); + } catch (pugiutil::XmlError& e) { + vpr_throw(VPR_ERROR_ROUTE, file_name, e.line(), "%s", e.what()); + } + } else { + VTR_LOG_WARN( + "VPR constraints file '%s' may be in incorrect format. " + "Expecting .xml format. Not reading file.\n", + file_name); + } + in_tokens.clear(); + num_file_read++; + found_file = false; } - } else { - VTR_LOG_WARN( - "VPR constraints file '%s' may be in incorrect format. " - "Expecting .xml format. Not reading file.\n", - read_vpr_constraints_name); } + VTR_LOG("Read in '%d' constraint file(s) successfully.\n", num_file_read); + freeTokens(tokens, num_tokens); //Update the floorplanning constraints in the floorplanning constraints context auto& floorplanning_ctx = g_vpr_ctx.mutable_floorplanning(); floorplanning_ctx.constraints = reader.constraints_; + // update vpr constraints for routing + auto& routing_ctx = g_vpr_ctx.mutable_routing(); + routing_ctx.constraints = reader.constraints_; + VprConstraints ctx_constraints = floorplanning_ctx.constraints; if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_VPR_CONSTRAINTS)) { diff --git a/vpr/src/base/vpr_constraints_reader.h b/vpr/src/base/vpr_constraints_reader.h index 95fcc988607..60aefc25b5b 100644 --- a/vpr/src/base/vpr_constraints_reader.h +++ b/vpr/src/base/vpr_constraints_reader.h @@ -5,6 +5,6 @@ #ifndef VPR_CONSTRAINTS_READER_H_ #define VPR_CONSTRAINTS_READER_H_ -void load_vpr_constraints_file(const char* read_vpr_constraints_name); +void load_vpr_constraints_files(const char* read_vpr_constraints_name); #endif /* VPR_CONSTRAINTS_READER_H_ */ diff --git a/vpr/src/base/vpr_constraints_serializer.h b/vpr/src/base/vpr_constraints_serializer.h index 4007b7c5c3b..8256e711126 100644 --- a/vpr/src/base/vpr_constraints_serializer.h +++ b/vpr/src/base/vpr_constraints_serializer.h @@ -2,6 +2,8 @@ #define VPR_CONSTRAINTS_SERIALIZER_H_ #include "region.h" +#include "route_constraint.h" +#include "packer_constraint.h" #include "vpr_constraints.h" #include "partition.h" #include "partition_region.h" @@ -69,11 +71,19 @@ struct VprConstraintsContextTypes : public uxsd::DefaultVprConstraintsContextTyp using AddRegionReadContext = Region; using PartitionReadContext = partition_info; using PartitionListReadContext = void*; + using SetGlobalSignalReadContext = RouteConstraint; + using GlobalRouteConstraintsReadContext = void*; + using AssignmentReadContext = PackerConstraint; + using AssignmentWriteContext = void*; + using PackerConstraintsReadContext = void*; + using PackerConstraintsWriteContext = void*; using VprConstraintsReadContext = void*; using AddAtomWriteContext = void*; using AddRegionWriteContext = void*; using PartitionWriteContext = void*; using PartitionListWriteContext = void*; + using SetGlobalSignalWriteContext = void*; + using GlobalRouteConstraintsWriteContext = void*; using VprConstraintsWriteContext = void*; }; @@ -300,11 +310,110 @@ class VprConstraintsSerializer final : public uxsd::VprConstraintsBase + * + * + * + * + */ + virtual inline const char* get_set_global_signal_name(RouteConstraint& rc) final { + temp_name_string_ = rc.net_name(); + return temp_name_string_.c_str(); + } + virtual inline void set_set_global_signal_name(const char* name, void*& /*ctx*/) final { + std::string net_name = std::string(name); + loaded_route_constraint.set_net_name(net_name); + return; + } + virtual inline const char* get_set_global_signal_route_model(RouteConstraint& rc) final { + temp_name_string_ = rc.route_model(); + return temp_name_string_.c_str(); + } + virtual inline void set_set_global_signal_route_model(const char* route_model, void*& /*ctx*/) final { + loaded_route_constraint.set_route_model(std::string(route_model)); + loaded_route_constraint.set_is_valid(true); + } + virtual inline const char* get_set_global_signal_type(RouteConstraint& rc) final { + temp_name_string_ = rc.net_type(); + return temp_name_string_.c_str(); + } + virtual inline void set_set_global_signal_type(const char* type, void*& /*ctx*/) final { + loaded_route_constraint.set_net_type(std::string(type)); + } + + /** Generated for complex type "global_route_constraints": + * + * + * + * + * + */ + virtual inline void preallocate_global_route_constraints_set_global_signal(void*& /*ctx*/, size_t /*size*/) final {} + virtual inline void* add_global_route_constraints_set_global_signal(void*& /*ctx*/) final { + return nullptr; + } + virtual inline void finish_global_route_constraints_set_global_signal(void*& /*ctx*/) final { + constraints_.add_route_constraint(loaded_route_constraint); + } + virtual inline size_t num_global_route_constraints_set_global_signal(void*& /*ctx*/) final { + return constraints_.get_route_constraint_num(); + } + virtual inline RouteConstraint get_global_route_constraints_set_global_signal(int n, void*& /*ctx*/) final { + return constraints_.get_route_constraint_by_idx((std::size_t)n); + } + + /** Generated for complex type "assignment": + * + * + * + * + */ + virtual inline const char * get_assignment_net(PackerConstraint& pc) final { + temp_name_string_ = pc.net_name(); + return temp_name_string_.c_str(); + } + virtual inline void set_assignment_net(const char * net, void* &/*ctx*/) final { + std::string net_name = std::string(net); + loaded_packer_constraint.set_net_name(net_name); + } + virtual inline const char * get_assignment_pin(PackerConstraint& pc) final { + temp_name_string_ = pc.pin_name(); + return temp_name_string_.c_str(); + } + virtual inline void set_assignment_pin(const char *pin, void *& /*ctx*/) final { + std::string pin_name = std::string(pin); + loaded_packer_constraint.set_pin_name(pin_name); + loaded_packer_constraint.set_is_valid(true); + } + + /** Generated for complex type "packer_constraints": + * + * + * + * + * + */ + virtual inline void preallocate_packer_constraints_assignment(void*& /*ctx*/, size_t /*size*/) final {} + virtual inline void* add_packer_constraints_assignment(void*& /*ctx*/) final { + return nullptr; + } + virtual inline void finish_packer_constraints_assignment(void*& /*ctx*/) final { + constraints_.add_packer_constraint(loaded_packer_constraint); + } + virtual inline size_t num_packer_constraints_assignment(void*& /*ctx*/) final { + return constraints_.get_packer_constraint_num(); + } + virtual inline PackerConstraint get_packer_constraints_assignment(int n, void*& /*ctx*/) final { + return constraints_.get_packer_constraint_by_idx((std::size_t)n); + } + /** Generated for complex type "vpr_constraints": * - * + * * - * + * + * * * */ @@ -312,30 +421,82 @@ class VprConstraintsSerializer final : public uxsd::VprConstraintsBase::max_digits10); + void* context; + uxsd::write_vpr_constraints_xml(writer, context, fp); + } else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Unknown extension on output %s", + file_name.c_str()); + } + } + return; +} + void write_vpr_floorplan_constraints(const char* file_name, int expand, bool subtile, int horizontal_partitions, int vertical_partitions) { VprConstraints constraints; diff --git a/vpr/src/base/vpr_constraints_writer.h b/vpr/src/base/vpr_constraints_writer.h index 756f8c17c29..158a87aa040 100644 --- a/vpr/src/base/vpr_constraints_writer.h +++ b/vpr/src/base/vpr_constraints_writer.h @@ -25,6 +25,14 @@ #ifndef VPR_SRC_BASE_VPR_CONSTRAINTS_WRITER_H_ #define VPR_SRC_BASE_VPR_CONSTRAINTS_WRITER_H_ +/** + * @brief Write out vpr constratins to an XML file based on current placement and + * route constraint settings + * + * @param vpr_setup VPR setup information + */ +void write_vpr_constraints(t_vpr_setup& vpr_setup); + /** * @brief Write out floorplan constraints to an XML file based on current placement * diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index a7b6d6bdf79..eecacb8696b 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -196,7 +196,13 @@ struct DeviceContext : public Context { /* A read-only view of routing resource graph to be the ONLY database * for client functions: GUI, placer, router, timing analyzer etc. */ - RRGraphView rr_graph{rr_graph_builder.rr_nodes(), rr_graph_builder.node_lookup(), rr_graph_builder.rr_node_metadata(), rr_graph_builder.rr_edge_metadata(), rr_indexed_data, rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch()}; + RRGraphView rr_graph{rr_graph_builder.rr_nodes(), rr_graph_builder.node_lookup(), rr_graph_builder.rr_node_metadata(), rr_graph_builder.rr_edge_metadata(), rr_indexed_data, rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch(), rr_graph_builder.node_in_edge_storage(), rr_graph_builder.node_ptc_storage()}; + + /* Track ids for each rr_node in the rr_graph. + * This is used by drawer for tileable routing resource graph + */ + std::map> rr_node_track_ids; + std::vector arch_switch_inf; // [0..(num_arch_switches-1)] std::map all_sw_inf; @@ -446,6 +452,11 @@ struct RoutingContext : public Context { vtr::Cache>, RouterLookahead> cached_router_lookahead_; + + /** + * @brief Routing constraints, read only + */ + VprConstraints constraints; }; /** diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 905f14cd442..e30c7908761 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1260,6 +1260,8 @@ struct t_router_opts { enum e_route_type route_type; int fixed_channel_width; int min_channel_width_hint; /// switchblocks; + /* Xifan Tang: subtype of switch blocks. + * Sub type and Fs are applied to pass tracks + */ + int subFs; + enum e_switch_block_type switch_block_subtype; + + /* Xifan Tang: tileable routing */ + bool tileable; + bool through_channel; + short global_route_switch; short delayless_switch; int wire_to_arch_ipin_switch; @@ -1425,17 +1438,17 @@ struct t_det_routing_arch { * Note that this index will store the index of the segment * relative to its **parallel** segment types, not all segments * as stored in device_ctx. Look in rr_graph.cpp: build_rr_graph - * for details but here is an example: say our segment_inf_vec in + * for details but here is an example: say our segment_inf_vec in * device_ctx is as follows: [seg_a_x, seg_b_x, seg_a_y, seg_b_y] - * when building the rr_graph, static segment_inf_vectors will be - * created for each direction, thus you will have the following - * 2 vectors: X_vec =[seg_a_x,seg_b_x] and Y_vec = [seg_a_y,seg_b_y]. - * As a result, e.g. seg_b_y::index == 1 (index in Y_vec) + * when building the rr_graph, static segment_inf_vectors will be + * created for each direction, thus you will have the following + * 2 vectors: X_vec =[seg_a_x,seg_b_x] and Y_vec = [seg_a_y,seg_b_y]. + * As a result, e.g. seg_b_y::index == 1 (index in Y_vec) * and != 3 (index in device_ctx segment_inf_vec). - * @param abs_index index is relative to the segment_inf vec as stored in device_ctx. - * Note that the above vector is **unifies** both x-parallel and - * y-parallel segments and is loaded up originally in read_xml_arch_file.cpp - * + * @param abs_index index is relative to the segment_inf vec as stored in device_ctx. + * Note that the above vector is **unifies** both x-parallel and + * y-parallel segments and is loaded up originally in read_xml_arch_file.cpp + * * @param type_name_ptr pointer to name of the segment type this track belongs * to. points to the appropriate name in s_segment_inf */ @@ -1519,7 +1532,7 @@ class t_chan_seg_details { const t_seg_details* seg_detail_ = nullptr; }; -/* Defines a 3-D array of t_chan_seg_details data structures (one per-each horizontal and vertical channel) +/* Defines a 3-D array of t_chan_seg_details data structures (one per-each horizontal and vertical channel) * once allocated in rr_graph2.cpp, is can be accessed like: [0..grid.width()][0..grid.height()][0..num_tracks-1] */ typedef vtr::NdMatrix t_chan_details; @@ -1542,14 +1555,14 @@ constexpr bool is_src_sink(e_rr_type type) { return (type == SOURCE || type == S * @brief Basic element used to store the traceback (routing) of each net. * * @param index Array index (ID) of this routing resource node. - * @param net_pin_index: Net pin index associated with the node. This value - * ranges from 1 to fanout [1..num_pins-1]. For cases when - * different speed paths are taken to the same SINK for - * different pins, node index cannot uniquely identify - * each SINK, so the net pin index guarantees an unique - * identification for each SINK node. For non-SINK nodes - * and for SINK nodes with no associated net pin index - * (i.e. special SINKs like the source of a clock tree + * @param net_pin_index: Net pin index associated with the node. This value + * ranges from 1 to fanout [1..num_pins-1]. For cases when + * different speed paths are taken to the same SINK for + * different pins, node index cannot uniquely identify + * each SINK, so the net pin index guarantees an unique + * identification for each SINK node. For non-SINK nodes + * and for SINK nodes with no associated net pin index + * (i.e. special SINKs like the source of a clock tree * which do not correspond to an actual netlist connection), * the value for this member should be set to OPEN (-1). * @param iswitch Index of the switch type used to go from this rr_node to @@ -1708,12 +1721,12 @@ struct t_power_opts { }; /** @brief Channel width data - * @param max= Maximum channel width between x_max and y_max. - * @param x_min= Minimum channel width of horizontal channels. Initialized when init_chan() is invoked in rr_graph2.cpp - * @param y_min= Same as above but for vertical channels. - * @param x_max= Maximum channel width of horiozntal channels. Initialized when init_chan() is invoked in rr_graph2.cpp - * @param y_max= Same as above but for vertical channels. - * @param x_list= Stores the channel width of all horizontal channels and thus goes from [0..grid.height()] + * @param max= Maximum channel width between x_max and y_max. + * @param x_min= Minimum channel width of horizontal channels. Initialized when init_chan() is invoked in rr_graph2.cpp + * @param y_min= Same as above but for vertical channels. + * @param x_max= Maximum channel width of horiozntal channels. Initialized when init_chan() is invoked in rr_graph2.cpp + * @param y_max= Same as above but for vertical channels. + * @param x_list= Stores the channel width of all horizontal channels and thus goes from [0..grid.height()] * (imagine a 2D Cartesian grid with horizontal lines starting at every grid point on a line parallel to the y-axis) * @param y_list= Stores the channel width of all verical channels and thus goes from [0..grid.width()] * (imagine a 2D Cartesian grid with vertical lines starting at every grid point on a line parallel to the x-axis) diff --git a/vpr/src/route/route_common.cpp b/vpr/src/route/route_common.cpp index e880daa8776..79d3819c4fa 100644 --- a/vpr/src/route/route_common.cpp +++ b/vpr/src/route/route_common.cpp @@ -235,6 +235,10 @@ void try_graph(int width_fac, graph_directionality = GRAPH_BIDIR; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch->directionality == UNI_DIRECTIONAL && det_routing_arch->tileable) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } @@ -289,6 +293,10 @@ bool try_route(const Netlist<>& net_list, graph_directionality = GRAPH_BIDIR; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch->directionality == UNI_DIRECTIONAL && det_routing_arch->tileable) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } diff --git a/vpr/src/route/router_delay_profiling.cpp b/vpr/src/route/router_delay_profiling.cpp index 67684b303d6..0e15502622d 100644 --- a/vpr/src/route/router_delay_profiling.cpp +++ b/vpr/src/route/router_delay_profiling.cpp @@ -234,6 +234,9 @@ void alloc_routing_structs(t_chan_width chan_width, graph_type = GRAPH_GLOBAL; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + if ((UNI_DIRECTIONAL == det_routing_arch->directionality) && (true == det_routing_arch->tileable)) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } } create_rr_graph(graph_type, diff --git a/vpr/src/route/router_lookahead_map.cpp b/vpr/src/route/router_lookahead_map.cpp index 9836e7cfb91..7f86e6352a0 100644 --- a/vpr/src/route/router_lookahead_map.cpp +++ b/vpr/src/route/router_lookahead_map.cpp @@ -1350,7 +1350,9 @@ static void compute_tile_lookahead(std::unordered_map>& switch_fanin_remap, - const std::map arch_sw_inf, - const float R_minW_nmos, - const float R_minW_pmos, - const int wire_to_arch_ipin_switch, - int* wire_to_rr_ipin_switch); - static void remap_rr_node_switch_indices(RRGraphBuilder& rr_graph_builder, const t_arch_switch_fanin& switch_fanin); @@ -457,27 +440,9 @@ static void alloc_rr_switch_inf(RRGraphBuilder& rr_graph_builder, t_arch_switch_fanin& arch_switch_fanins, const std::map& arch_sw_map); -static void rr_graph_externals(const std::vector& segment_inf, - const std::vector& segment_inf_x, - const std::vector& segment_inf_y, - int wire_to_rr_ipin_switch, - enum e_base_cost_type base_cost_type); - -static t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, const int delayless_switch); - static t_seg_details* alloc_and_load_global_route_seg_details(const int global_route_switch, int* num_seg_details = nullptr); -static std::vector> alloc_and_load_actual_fc(const std::vector& types, - const int max_pins, - const std::vector& segment_inf, - const int* sets_per_seg_type, - const t_chan_width* nodes_per_chan, - const e_fc_type fc_type, - const enum e_directionality directionality, - bool* Fc_clipped, - bool is_flat); - static RRNodeId pick_best_direct_connect_target_rr_node(const RRGraphView& rr_graph, RRNodeId from_rr, const std::vector& candidate_rr_nodes); @@ -655,25 +620,48 @@ void create_rr_graph(const t_graph_type graph_type, } } else { free_rr_graph(); - build_rr_graph(graph_type, - block_types, - grid, - nodes_per_chan, - det_routing_arch->switch_block_type, - det_routing_arch->Fs, - det_routing_arch->switchblocks, - segment_inf, - det_routing_arch->global_route_switch, - det_routing_arch->wire_to_arch_ipin_switch, - det_routing_arch->delayless_switch, - det_routing_arch->R_minW_nmos, - det_routing_arch->R_minW_pmos, - router_opts.base_cost_type, - router_opts.clock_modeling, - directs, num_directs, - &det_routing_arch->wire_to_rr_ipin_switch, - is_flat, - Warnings); + if (GRAPH_UNIDIR_TILEABLE != graph_type) { + build_rr_graph(graph_type, + block_types, + grid, + nodes_per_chan, + det_routing_arch->switch_block_type, + det_routing_arch->Fs, + det_routing_arch->switchblocks, + segment_inf, + det_routing_arch->global_route_switch, + det_routing_arch->wire_to_arch_ipin_switch, + det_routing_arch->delayless_switch, + det_routing_arch->R_minW_nmos, + det_routing_arch->R_minW_pmos, + router_opts.base_cost_type, + router_opts.clock_modeling, + directs, num_directs, + &det_routing_arch->wire_to_rr_ipin_switch, + is_flat, + Warnings); + } else { + /* We do not support dedicated network for clocks in tileable rr_graph generation */ + VTR_LOG_WARN("Tileable routing resource graph does not support clock modeling yet! Related options are ignored...\n"); + build_tileable_unidir_rr_graph(block_types, + grid, + nodes_per_chan, + det_routing_arch->switch_block_type, + det_routing_arch->Fs, + det_routing_arch->switch_block_subtype, + det_routing_arch->subFs, + segment_inf, + det_routing_arch->delayless_switch, + det_routing_arch->wire_to_arch_ipin_switch, + det_routing_arch->R_minW_nmos, + det_routing_arch->R_minW_pmos, + router_opts.base_cost_type, + directs, num_directs, + &det_routing_arch->wire_to_rr_ipin_switch, + router_opts.trim_obs_channels || det_routing_arch->through_channel, /* Allow/Prohibit through tracks across multi-height and multi-width grids */ + false, /* Do not allow passing tracks to be wired to the same routing channels */ + Warnings); + } } } @@ -688,6 +676,7 @@ void create_rr_graph(const t_graph_type graph_type, mutable_device_ctx.rr_graph_builder, is_flat); + /* Reorder nodes upon needs in algorithms and router options */ if (router_opts.reorder_rr_graph_nodes_algorithm != DONT_REORDER) { mutable_device_ctx.rr_graph_builder.reorder_nodes(router_opts.reorder_rr_graph_nodes_algorithm, router_opts.reorder_rr_graph_nodes_threshold, @@ -1410,13 +1399,13 @@ void build_tile_rr_graph(RRGraphBuilder& rr_graph_builder, * and count how many different fan-ins exist for each arch switch. * Then we create these rr switches and update the switch indices * of rr_nodes to index into the rr_switch_inf array. */ -static void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, - std::vector>& switch_fanin_remap, - const std::map arch_sw_inf, - const float R_minW_nmos, - const float R_minW_pmos, - const int wire_to_arch_ipin_switch, - int* wire_to_rr_ipin_switch) { +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch) { /* we will potentially be creating a couple of versions of each arch switch where * each version corresponds to a different fan-in. We will need to fill device_ctx.rr_switch_inf * with this expanded list of switches. @@ -1596,11 +1585,11 @@ static void remap_rr_node_switch_indices(RRGraphBuilder& rr_graph_builder, rr_graph_builder.remap_rr_node_switch_indices(switch_fanin); } -static void rr_graph_externals(const std::vector& segment_inf, - const std::vector& segment_inf_x, - const std::vector& segment_inf_y, - int wire_to_rr_ipin_switch, - enum e_base_cost_type base_cost_type) { +void rr_graph_externals(const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + int wire_to_rr_ipin_switch, + enum e_base_cost_type base_cost_type) { auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; const auto& grid = device_ctx.grid; @@ -1698,15 +1687,15 @@ static t_seg_details* alloc_and_load_global_route_seg_details(const int global_r } /* Calculates the number of track connections from each block pin to each segment type */ -static std::vector> alloc_and_load_actual_fc(const std::vector& types, - const int max_pins, - const std::vector& segment_inf, - const int* sets_per_seg_type, - const t_chan_width* nodes_per_chan, - const e_fc_type fc_type, - const enum e_directionality directionality, - bool* Fc_clipped, - bool is_flat) { +std::vector> alloc_and_load_actual_fc(const std::vector& types, + const int max_pins, + const std::vector& segment_inf, + const int* sets_per_seg_type, + const t_chan_width* nodes_per_chan, + const e_fc_type fc_type, + const enum e_directionality directionality, + bool* Fc_clipped, + bool is_flat) { //Initialize Fc of all blocks to zero auto zeros = vtr::Matrix({size_t(max_pins), segment_inf.size()}, 0); std::vector> Fc(types.size(), zeros); @@ -2418,6 +2407,8 @@ void free_rr_graph() { device_ctx.rr_graph_builder.clear(); + device_ctx.rr_node_track_ids.clear(); + device_ctx.rr_indexed_data.clear(); device_ctx.switch_fanin_remap.clear(); @@ -3848,7 +3839,7 @@ static void build_unidir_rr_opins(RRGraphBuilder& rr_graph_builder, * This data structure supplements the the info in the "directs" data structure * TODO: The function that does this parsing in placement is poorly done because it lacks generality on heterogeniety, should replace with this one */ -static t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, int delayless_switch) { +t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, int delayless_switch) { int i; t_clb_to_clb_directs* clb_to_clb_directs; char *tile_name, *port_name; diff --git a/vpr/src/route/rr_graph.h b/vpr/src/route/rr_graph.h index 821e0a0de01..0b49e71b218 100644 --- a/vpr/src/route/rr_graph.h +++ b/vpr/src/route/rr_graph.h @@ -10,6 +10,7 @@ #include "vpr_types.h" #include "rr_graph_type.h" #include "describe_rr_node.h" +#include "clb2clb_directs.h" /* Warnings about the routing graph that can be returned. * This is to avoid output messages during a value sweep */ @@ -44,6 +45,33 @@ void free_rr_graph(); t_rr_switch_inf create_rr_switch_from_arch_switch(const t_arch_switch_inf& arch_sw_inf, const float R_minW_nmos, const float R_minW_pmos); + +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch); + +void rr_graph_externals(const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + int wire_to_rr_ipin_switch, + enum e_base_cost_type base_cost_type); + +t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, int delayless_switch); + +std::vector> alloc_and_load_actual_fc(const std::vector& types, + const int max_pins, + const std::vector& segment_inf, + const int* sets_per_seg_type, + const t_chan_width* nodes_per_chan, + const e_fc_type fc_type, + const enum e_directionality directionality, + bool* Fc_clipped, + bool is_flat); + // Sets the spec for the rr_switch based on the arch switch void load_rr_switch_from_arch_switch(RRGraphBuilder& rr_graph_builder, const std::map& arch_sw_inf, diff --git a/vpr/src/tileable_rr_graph/chan_node_details.cpp b/vpr/src/tileable_rr_graph/chan_node_details.cpp new file mode 100644 index 00000000000..ca8c6915734 --- /dev/null +++ b/vpr/src/tileable_rr_graph/chan_node_details.cpp @@ -0,0 +1,291 @@ +/************************************************************************ + * This file contains member functions for class ChanNodeDetails + ***********************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +#include "chan_node_details.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +ChanNodeDetails::ChanNodeDetails(const ChanNodeDetails& src) { + /* duplicate */ + size_t chan_width = src.get_chan_width(); + this->reserve(chan_width); + for (size_t itrack = 0; itrack < chan_width; ++itrack) { + track_node_ids_.push_back(src.get_track_node_id(itrack)); + track_direction_.push_back(src.get_track_direction(itrack)); + seg_ids_.push_back(src.get_track_segment_id(itrack)); + seg_length_.push_back(src.get_track_segment_length(itrack)); + track_start_.push_back(src.is_track_start(itrack)); + track_end_.push_back(src.is_track_end(itrack)); + } +} + +ChanNodeDetails::ChanNodeDetails() { + this->clear(); +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +size_t ChanNodeDetails::get_chan_width() const { + VTR_ASSERT(validate_chan_width()); + return track_node_ids_.size(); +} + +size_t ChanNodeDetails::get_track_node_id(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_node_ids_[track_id]; +} + +/* Return a copy of vector */ +std::vector ChanNodeDetails::get_track_node_ids() const { + std::vector copy; + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + copy.push_back(track_node_ids_[inode]); + } + return copy; +} + +Direction ChanNodeDetails::get_track_direction(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_direction_[track_id]; +} + +size_t ChanNodeDetails::get_track_segment_length(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return seg_length_[track_id]; +} + +size_t ChanNodeDetails::get_track_segment_id(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return seg_ids_[track_id]; +} + +bool ChanNodeDetails::is_track_start(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_start_[track_id]; +} + +bool ChanNodeDetails::is_track_end(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_end_[track_id]; +} + +/* Track_id is the starting point of group (whose is_start should be true) + * This function will try to find the track_ids with the same directionality as track_id and seg_length + * A group size is the number of such nodes between the starting points (include the 1st starting point) + */ +std::vector ChanNodeDetails::get_seg_group(const size_t& track_id) const { + VTR_ASSERT(validate_chan_width()); + VTR_ASSERT(validate_track_id(track_id)); + VTR_ASSERT(is_track_start(track_id)); + + std::vector group; + /* Make sure a clean start */ + group.clear(); + + for (size_t itrack = track_id; itrack < get_chan_width(); ++itrack) { + if ((get_track_direction(itrack) != get_track_direction(track_id)) + || (get_track_segment_id(itrack) != get_track_segment_id(track_id))) { + /* Bypass any nodes in different direction and segment information*/ + continue; + } + if ((false == is_track_start(itrack)) + || ((true == is_track_start(itrack)) && (itrack == track_id))) { + group.push_back(itrack); + continue; + } + /* Stop if this another starting point */ + if (true == is_track_start(itrack)) { + break; + } + } + return group; +} + +/* Get a list of track_ids with the given list of track indices */ +std::vector ChanNodeDetails::get_seg_group_node_id(const std::vector& seg_group) const { + std::vector group; + /* Make sure a clean start */ + group.clear(); + + for (size_t id = 0; id < seg_group.size(); ++id) { + VTR_ASSERT(validate_track_id(seg_group[id])); + group.push_back(get_track_node_id(seg_group[id])); + } + + return group; +} + +/* Get the number of tracks that starts in this routing channel */ +size_t ChanNodeDetails::get_num_starting_tracks(const Direction& track_direction) const { + size_t counter = 0; + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass unmatched track_direction */ + if (track_direction != get_track_direction(itrack)) { + continue; + } + if (false == is_track_start(itrack)) { + continue; + } + counter++; + } + return counter; +} + +/* Get the number of tracks that ends in this routing channel */ +size_t ChanNodeDetails::get_num_ending_tracks(const Direction& track_direction) const { + size_t counter = 0; + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass unmatched track_direction */ + if (track_direction != get_track_direction(itrack)) { + continue; + } + if (false == is_track_end(itrack)) { + continue; + } + counter++; + } + return counter; +} + +/************************************************************************ + * Mutators + ***********************************************************************/ +/* Reserve the capacitcy of vectors */ +void ChanNodeDetails::reserve(const size_t& chan_width) { + track_node_ids_.reserve(chan_width); + track_direction_.reserve(chan_width); + seg_length_.reserve(chan_width); + seg_ids_.reserve(chan_width); + track_start_.reserve(chan_width); + track_end_.reserve(chan_width); +} + +/* Add a track to the channel */ +void ChanNodeDetails::add_track(const size_t& track_node_id, const Direction& track_direction, const size_t& seg_id, const size_t& seg_length, const size_t& is_start, const size_t& is_end) { + track_node_ids_.push_back(track_node_id); + track_direction_.push_back(track_direction); + seg_ids_.push_back(seg_id); + seg_length_.push_back(seg_length); + track_start_.push_back(is_start); + track_end_.push_back(is_end); +} + +/* Update the node_id of a given track */ +void ChanNodeDetails::set_track_node_id(const size_t& track_index, const size_t& track_node_id) { + VTR_ASSERT(validate_track_id(track_index)); + track_node_ids_[track_index] = track_node_id; +} + +/* Update the node_ids from a vector */ +void ChanNodeDetails::set_track_node_ids(const std::vector& track_node_ids) { + /* the size of vector should match chan_width */ + VTR_ASSERT(get_chan_width() == track_node_ids.size()); + for (size_t inode = 0; inode < track_node_ids.size(); ++inode) { + track_node_ids_[inode] = track_node_ids[inode]; + } +} + +/* Set tracks with a given direction to start */ +void ChanNodeDetails::set_tracks_start(const Direction& track_direction) { + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Bypass non-match tracks */ + if (track_direction != get_track_direction(inode)) { + continue; /* Pass condition*/ + } + track_start_[inode] = true; + } +} + +/* Set tracks with a given direction to end */ +void ChanNodeDetails::set_tracks_end(const Direction& track_direction) { + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Bypass non-match tracks */ + if (track_direction != get_track_direction(inode)) { + continue; /* Pass condition*/ + } + track_end_[inode] = true; + } +} + +/* rotate the track_node_id by an offset */ +void ChanNodeDetails::rotate_track_node_id(const size_t& offset, const Direction& track_direction, const bool& counter_rotate) { + /* Direct return if offset = 0*/ + if (0 == offset) { + return; + } + + /* Rotate the node_ids by groups + * A group begins from a track_start and ends before another track_start + */ + VTR_ASSERT(validate_chan_width()); + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass non-start segment */ + if (false == is_track_start(itrack)) { + continue; + } + /* Bypass segments do not match track_direction */ + if (track_direction != get_track_direction(itrack)) { + continue; + } + /* Find the group nodes */ + std::vector track_group = get_seg_group(itrack); + /* Build a vector of the node ids of the tracks */ + std::vector track_group_node_id = get_seg_group_node_id(track_group); + /* adapt offset to the range of track_group_node_id */ + size_t actual_offset = offset % track_group_node_id.size(); + /* Rotate or Counter rotate */ + if (true == counter_rotate) { + std::rotate(track_group_node_id.rbegin(), track_group_node_id.rbegin() + actual_offset, track_group_node_id.rend()); + } else { + std::rotate(track_group_node_id.begin(), track_group_node_id.begin() + actual_offset, track_group_node_id.end()); + } + /* Update the node_ids */ + for (size_t inode = 0; inode < track_group.size(); ++inode) { + track_node_ids_[track_group[inode]] = track_group_node_id[inode]; + } + } + return; +} + +void ChanNodeDetails::clear() { + track_node_ids_.clear(); + track_direction_.clear(); + seg_ids_.clear(); + seg_length_.clear(); + track_start_.clear(); + track_end_.clear(); +} + +/************************************************************************ + * Validators + ***********************************************************************/ +bool ChanNodeDetails::validate_chan_width() const { + size_t chan_width = track_node_ids_.size(); + if ((chan_width == track_direction_.size()) + && (chan_width == seg_ids_.size()) + && (chan_width == seg_length_.size()) + && (chan_width == track_start_.size()) + && (chan_width == track_end_.size())) { + return true; + } + return false; +} + +bool ChanNodeDetails::validate_track_id(const size_t& track_id) const { + if ((track_id < track_node_ids_.size()) + && (track_id < track_direction_.size()) + && (track_id < seg_ids_.size()) + && (track_id < seg_length_.size()) + && (track_id < track_start_.size()) + && (track_id < track_end_.size())) { + return true; + } + return false; +} diff --git a/vpr/src/tileable_rr_graph/chan_node_details.h b/vpr/src/tileable_rr_graph/chan_node_details.h new file mode 100644 index 00000000000..e322b5c6d73 --- /dev/null +++ b/vpr/src/tileable_rr_graph/chan_node_details.h @@ -0,0 +1,73 @@ +#ifndef CHAN_NODE_DETAILS_H +#define CHAN_NODE_DETAILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "vpr_types.h" +#include "rr_node_types.h" +#include "rr_graph_types.h" + +/************************************************************************ + * This file contains a class to model the details of routing node + * in a channel: + * 1. segment information: length, frequency etc. + * 2. starting point of segment + * 3. ending point of segment + * 4. potentail track_id(ptc_num) of each segment + ***********************************************************************/ + +/************************************************************************ + * ChanNodeDetails records segment length, directionality and starting of routing tracks + * +---------------------------------+ + * | Index | Direction | Start Point | + * +---------------------------------+ + * | 0 | --------> | Yes | + * +---------------------------------+ + ***********************************************************************/ + +class ChanNodeDetails { + public: /* Constructor */ + ChanNodeDetails(const ChanNodeDetails&); /* Duplication */ + ChanNodeDetails(); /* Initilization */ + public: /* Accessors */ + size_t get_chan_width() const; + size_t get_track_node_id(const size_t& track_id) const; + std::vector get_track_node_ids() const; + Direction get_track_direction(const size_t& track_id) const; + size_t get_track_segment_length(const size_t& track_id) const; + size_t get_track_segment_id(const size_t& track_id) const; + bool is_track_start(const size_t& track_id) const; + bool is_track_end(const size_t& track_id) const; + std::vector get_seg_group(const size_t& track_id) const; + std::vector get_seg_group_node_id(const std::vector& seg_group) const; + size_t get_num_starting_tracks(const Direction& track_direction) const; + size_t get_num_ending_tracks(const Direction& track_direction) const; + + public: /* Mutators */ + void reserve(const size_t& chan_width); /* Reserve the capacitcy of vectors */ + void add_track(const size_t& track_node_id, const Direction& track_direction, const size_t& seg_id, const size_t& seg_length, const size_t& is_start, const size_t& is_end); + void set_track_node_id(const size_t& track_index, const size_t& track_node_id); + void set_track_node_ids(const std::vector& track_node_ids); + void set_tracks_start(const Direction& track_direction); + void set_tracks_end(const Direction& track_direction); + void rotate_track_node_id(const size_t& offset, + const Direction& track_direction, + const bool& counter_rotate); /* rotate the track_node_id by an offset */ + void clear(); + + private: /* validators */ + bool validate_chan_width() const; + bool validate_track_id(const size_t& track_id) const; + + private: /* Internal data */ + std::vector track_node_ids_; /* indices of each track */ + std::vector track_direction_; /* direction of each track */ + std::vector seg_ids_; /* id of segment of each track */ + std::vector seg_length_; /* Length of each segment */ + std::vector track_start_; /* flag to identify if this is the starting point of the track */ + std::vector track_end_; /* flag to identify if this is the ending point of the track */ +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/clb2clb_directs.h b/vpr/src/tileable_rr_graph/clb2clb_directs.h new file mode 100644 index 00000000000..7cc54bb8813 --- /dev/null +++ b/vpr/src/tileable_rr_graph/clb2clb_directs.h @@ -0,0 +1,16 @@ +#ifndef CLB2CLB_DIRECTS_H +#define CLB2CLB_DIRECTS_H + +#include "physical_types.h" + +struct t_clb_to_clb_directs { + t_physical_tile_type_ptr from_clb_type; + int from_clb_pin_start_index; + int from_clb_pin_end_index; + t_physical_tile_type_ptr to_clb_type; + int to_clb_pin_start_index; + int to_clb_pin_end_index; + int switch_index; //The switch type used by this direct connection +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.cpp b/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.cpp new file mode 100644 index 00000000000..ea3b52a5440 --- /dev/null +++ b/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.cpp @@ -0,0 +1,201 @@ +/******************************************************************** + * This file includes most utilized functions for the rr_graph + * data structure in the OpenFPGA context + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "openfpga_rr_graph_utils.h" +#include "rr_graph_types.h" +#include "rr_graph_view.h" +#include "vtr_geometry.h" + +/************************************************************************ + * Get the coordinator of a starting point of a routing track + * For routing tracks in INC_DIRECTION + * (xlow, ylow) should be the starting point + * + * For routing tracks in DEC_DIRECTION + * (xhigh, yhigh) should be the starting point + ***********************************************************************/ +vtr::Point get_track_rr_node_start_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node) { + /* Make sure we have CHANX or CHANY */ + VTR_ASSERT((CHANX == rr_graph.node_type(track_rr_node)) + || (CHANY == rr_graph.node_type(track_rr_node))); + + vtr::Point start_coordinator; + + if (Direction::INC == rr_graph.node_direction(track_rr_node)) { + start_coordinator.set(rr_graph.node_xlow(track_rr_node), rr_graph.node_ylow(track_rr_node)); + } else { + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(track_rr_node)); + start_coordinator.set(rr_graph.node_xhigh(track_rr_node), rr_graph.node_yhigh(track_rr_node)); + } + + return start_coordinator; +} + +/************************************************************************ + * Get the coordinator of a end point of a routing track + * For routing tracks in INC_DIRECTION + * (xhigh, yhigh) should be the starting point + * + * For routing tracks in DEC_DIRECTION + * (xlow, ylow) should be the starting point + ***********************************************************************/ +vtr::Point get_track_rr_node_end_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node) { + /* Make sure we have CHANX or CHANY */ + VTR_ASSERT((CHANX == rr_graph.node_type(track_rr_node)) + || (CHANY == rr_graph.node_type(track_rr_node))); + + vtr::Point end_coordinator; + + if (Direction::INC == rr_graph.node_direction(track_rr_node)) { + end_coordinator.set(rr_graph.node_xhigh(track_rr_node), rr_graph.node_yhigh(track_rr_node)); + } else { + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(track_rr_node)); + end_coordinator.set(rr_graph.node_xlow(track_rr_node), rr_graph.node_ylow(track_rr_node)); + } + + return end_coordinator; +} + +/************************************************************************ + * Find the driver switches for a node in the rr_graph + * This function only return unique driver switches + ***********************************************************************/ +std::vector get_rr_graph_driver_switches(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_switches; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + if (driver_switches.end() == std::find(driver_switches.begin(), driver_switches.end(), rr_graph.edge_switch(edge))) { + driver_switches.push_back(rr_graph.edge_switch(edge)); + } + } + + return driver_switches; +} + +/************************************************************************ + * Find the driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Find the configurable driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + /* Bypass non-configurable edges */ + if (false == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Find the configurable driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + /* Bypass configurable edges */ + if (true == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Check if an OPIN of a rr_graph is directly driving an IPIN + * To meet this requirement, the OPIN must: + * - Have only 1 fan-out + * - The only fan-out is an IPIN + ***********************************************************************/ +bool is_opin_direct_connected_ipin(const RRGraphView& rr_graph, + const RRNodeId& node) { + /* We only accept OPIN */ + VTR_ASSERT(OPIN == rr_graph.node_type(node)); + + if (1 != rr_graph.node_out_edges(node).size()) { + return false; + } + + VTR_ASSERT(1 == rr_graph.node_out_edges(node).size()); + for (auto edge : rr_graph.node_out_edges(node)) { + const RRNodeId& sink_node = rr_graph.edge_sink_node(node, edge); + if (IPIN != rr_graph.node_type(sink_node)) { + return false; + } + } + + return true; +} + +/************************************************************************ + * Check if an IPIN of a rr_graph is directly connected to an OPIN + * To meet this requirement, the IPIN must: + * - Have only 1 fan-in + * - The only fan-in is an OPIN + ***********************************************************************/ +bool is_ipin_direct_connected_opin(const RRGraphView& rr_graph, + const RRNodeId& node) { + /* We only accept IPIN */ + VTR_ASSERT(IPIN == rr_graph.node_type(node)); + + if (1 != rr_graph.node_in_edges(node).size()) { + return false; + } + + VTR_ASSERT(1 == rr_graph.node_in_edges(node).size()); + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + if (OPIN != rr_graph.node_type(src_node)) { + return false; + } + } + + return true; +} + +/** @brief Get a side of a given node in a routing resource graph. + * Note that this function expect one valid side to be got. Otherwise, it will fail! */ +e_side get_rr_graph_single_node_side(const RRGraphView& rr_graph, + const RRNodeId& node) { + e_side node_side = NUM_SIDES; + int num_sides = 0; + for (e_side candidate_side : SIDES) { + if (rr_graph.is_node_on_specific_side(node, candidate_side)) { + node_side = candidate_side; + num_sides++; + } + } + VTR_ASSERT(1 == num_sides && node_side != NUM_SIDES); + return node_side; +} diff --git a/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.h b/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.h new file mode 100644 index 00000000000..cba64125615 --- /dev/null +++ b/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.h @@ -0,0 +1,45 @@ +#ifndef OPENFPGA_RR_GRAPH_UTILS_H +#define OPENFPGA_RR_GRAPH_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +/* Headers from vpr library */ +#include "rr_graph_obj.h" +#include "rr_graph_view.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +vtr::Point get_track_rr_node_start_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node); + +vtr::Point get_track_rr_node_end_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node); + +std::vector get_rr_graph_driver_switches(const RRGraphView& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node); + +bool is_opin_direct_connected_ipin(const RRGraphView& rr_graph, + const RRNodeId& node); + +bool is_ipin_direct_connected_opin(const RRGraphView& rr_graph, + const RRNodeId& node); + +e_side get_rr_graph_single_node_side(const RRGraphView& rr_graph, + const RRNodeId& node); + +#endif diff --git a/vpr/src/tileable_rr_graph/openfpga_side_manager.cpp b/vpr/src/tileable_rr_graph/openfpga_side_manager.cpp new file mode 100644 index 00000000000..3b1ee1ec404 --- /dev/null +++ b/vpr/src/tileable_rr_graph/openfpga_side_manager.cpp @@ -0,0 +1,170 @@ +/******************************************************************** + * Memeber function for class SideManagerManager + *******************************************************************/ +#include "openfpga_side_manager.h" + +/* Constructors */ +SideManager::SideManager(enum e_side side) { + side_ = side; +} + +SideManager::SideManager() { + side_ = NUM_SIDES; +} + +SideManager::SideManager(size_t side) { + set_side(side); +} + +/* Public Accessors */ +enum e_side SideManager::get_side() const { + return side_; +} + +enum e_side SideManager::get_opposite() const { + switch (side_) { + case TOP: + return BOTTOM; + case RIGHT: + return LEFT; + case BOTTOM: + return TOP; + case LEFT: + return RIGHT; + default: + return NUM_SIDES; + } +} + +enum e_side SideManager::get_rotate_clockwise() const { + switch (side_) { + case TOP: + return RIGHT; + case RIGHT: + return BOTTOM; + case BOTTOM: + return LEFT; + case LEFT: + return TOP; + default: + return NUM_SIDES; + } +} + +enum e_side SideManager::get_rotate_counterclockwise() const { + switch (side_) { + case TOP: + return LEFT; + case RIGHT: + return TOP; + case BOTTOM: + return RIGHT; + case LEFT: + return BOTTOM; + default: + return NUM_SIDES; + } +} + +bool SideManager::validate() const { + if (NUM_SIDES == side_) { + return false; + } + return true; +} + +size_t SideManager::to_size_t() const { + switch (side_) { + case TOP: + return 0; + case RIGHT: + return 1; + case BOTTOM: + return 2; + case LEFT: + return 3; + default: + return 4; + } +} + +/* Convert to char* */ +const char* SideManager::c_str() const { + switch (side_) { + case TOP: + return "top"; + case RIGHT: + return "right"; + case BOTTOM: + return "bottom"; + case LEFT: + return "left"; + default: + return "invalid_side"; + } +} + +/* Convert to char* */ +std::string SideManager::to_string() const { + std::string ret; + switch (side_) { + case TOP: + ret.assign("top"); + break; + case RIGHT: + ret.assign("right"); + break; + case BOTTOM: + ret.assign("bottom"); + break; + case LEFT: + ret.assign("left"); + break; + default: + ret.assign("invalid_side"); + break; + } + + return ret; +} + +/* Public Mutators */ +void SideManager::set_side(size_t side) { + switch (side) { + case 0: + side_ = TOP; + return; + case 1: + side_ = RIGHT; + return; + case 2: + side_ = BOTTOM; + return; + case 3: + side_ = LEFT; + return; + default: + side_ = NUM_SIDES; + return; + } +} + +void SideManager::set_side(enum e_side side) { + side_ = side; + return; +} + +void SideManager::set_opposite() { + side_ = get_opposite(); + return; +} + +void SideManager::rotate_clockwise() { + side_ = get_rotate_clockwise(); + return; +} + +void SideManager::rotate_counterclockwise() { + side_ = get_rotate_counterclockwise(); + return; +} diff --git a/vpr/src/tileable_rr_graph/openfpga_side_manager.h b/vpr/src/tileable_rr_graph/openfpga_side_manager.h new file mode 100644 index 00000000000..a6eb265cd64 --- /dev/null +++ b/vpr/src/tileable_rr_graph/openfpga_side_manager.h @@ -0,0 +1,47 @@ +#ifndef OPENFPGA_SIDE_MANAGER_H +#define OPENFPGA_SIDE_MANAGER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +/* Header files form archfpga library */ +#include "physical_types.h" + +/******************************************************************** + * Define a class for the sides of a physical block in FPGA architecture + * Basically, each block has four sides : + * TOP, RIGHT, BOTTOM, LEFT + * This class aims to provide a easy proctol for manipulating a side + ********************************************************************/ + +class SideManager { + public: /* Constructor */ + SideManager(enum e_side side); + SideManager(); + SideManager(size_t side); + + public: /* Accessors */ + enum e_side get_side() const; + enum e_side get_opposite() const; + enum e_side get_rotate_clockwise() const; + enum e_side get_rotate_counterclockwise() const; + bool validate() const; + size_t to_size_t() const; + const char* c_str() const; + std::string to_string() const; + + public: /* Mutators */ + void set_side(size_t side); + void set_side(enum e_side side); + void set_opposite(); + void rotate_clockwise(); + void rotate_counterclockwise(); + + private: /* internal data */ + enum e_side side_; +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_chan.cpp b/vpr/src/tileable_rr_graph/rr_chan.cpp new file mode 100644 index 00000000000..c6a991ea524 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_chan.cpp @@ -0,0 +1,190 @@ +/************************************************************************ + * Member functions for class RRChan + ***********************************************************************/ +#include "vtr_log.h" +#include "vtr_assert.h" +#include "rr_chan.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +/* default constructor */ +RRChan::RRChan() { + type_ = NUM_RR_TYPES; + nodes_.resize(0); + node_segments_.resize(0); +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +t_rr_type RRChan::get_type() const { + return type_; +} + +/* get the number of tracks in this channel */ +size_t RRChan::get_chan_width() const { + return nodes_.size(); +} + +/* get the track_id of a node */ +int RRChan::get_node_track_id(const RRNodeId& node) const { + /* if the given node is NULL, we return an invalid id */ + if (RRNodeId::INVALID() == node) { + return -1; + } + /* check each member and return if we find a match in content */ + std::vector::const_iterator it = std::find(nodes_.begin(), nodes_.end(), node); + if (nodes_.end() == it) { + return -1; + } + return it - nodes_.begin(); +} + +/* get the rr_node with the track_id */ +RRNodeId RRChan::get_node(const size_t& track_num) const { + if (false == valid_node_id(track_num)) { + return RRNodeId::INVALID(); + } + return nodes_[track_num]; +} + +/* get the segment id of a node */ +RRSegmentId RRChan::get_node_segment(const RRNodeId& node) const { + int node_id = get_node_track_id(node); + if (false == valid_node_id(node_id)) { + return RRSegmentId::INVALID(); + } + return get_node_segment(node_id); +} + +/* get the segment id of a node */ +RRSegmentId RRChan::get_node_segment(const size_t& track_num) const { + if (false == valid_node_id(track_num)) { + return RRSegmentId::INVALID(); + } + return node_segments_[track_num]; +} + +/* Get a list of segments used in this routing channel */ +std::vector RRChan::get_segment_ids() const { + std::vector seg_list; + + /* make sure a clean start */ + seg_list.clear(); + + /* Traverse node_segments */ + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + std::vector::iterator it; + /* Try to find the node_segment id in the list */ + it = find(seg_list.begin(), seg_list.end(), node_segments_[inode]); + if (it == seg_list.end()) { + /* Not found, add it to the list */ + seg_list.push_back(node_segments_[inode]); + } + } + + return seg_list; +} + +/* Get a list of nodes whose segment_id is specified */ +std::vector RRChan::get_node_ids_by_segment_ids(const RRSegmentId& seg_id) const { + std::vector node_list; + + /* make sure a clean start */ + node_list.clear(); + + /* Traverse node_segments */ + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Try to find the node_segment id in the list */ + if (seg_id == node_segments_[inode]) { + node_list.push_back(inode); + } + } + + return node_list; +} + +/************************************************************************ + * Mutators + ***********************************************************************/ +void RRChan::set(const RRChan& rr_chan) { + /* Ensure a clean start */ + this->clear(); + /* Assign type of this routing channel */ + this->type_ = rr_chan.get_type(); + /* Copy node and node_segments */ + this->nodes_.resize(rr_chan.get_chan_width()); + this->node_segments_.resize(rr_chan.get_chan_width()); + for (size_t inode = 0; inode < rr_chan.get_chan_width(); ++inode) { + this->nodes_[inode] = rr_chan.get_node(inode); + this->node_segments_[inode] = rr_chan.get_node_segment(inode); + } + return; +} + +/* modify type */ +void RRChan::set_type(const t_rr_type& type) { + VTR_ASSERT(valid_type(type)); + type_ = type; +} + +/* Reserve node list */ +void RRChan::reserve_node(const size_t& node_size) { + nodes_.reserve(node_size); /* reserve to the maximum */ + node_segments_.reserve(node_size); /* reserve to the maximum */ +} + +/* add a node to the array */ +void RRChan::add_node(const RRGraphView& rr_graph, const RRNodeId& node, const RRSegmentId& node_segment) { + /* fill the dedicated element in the vector */ + nodes_.push_back(node); + node_segments_.push_back(node_segment); + + if (NUM_RR_TYPES == type_) { + type_ = rr_graph.node_type(node); + } else { + VTR_ASSERT(type_ == rr_graph.node_type(node)); + } + + VTR_ASSERT(valid_node_type(rr_graph, node)); +} + +/* Clear content */ +void RRChan::clear() { + nodes_.clear(); + node_segments_.clear(); +} + +/************************************************************************ + * Internal validators + ***********************************************************************/ +/* for type, only valid type is CHANX and CHANY */ +bool RRChan::valid_type(const t_rr_type& type) const { + if ((CHANX == type) || (CHANY == type)) { + return true; + } + return false; +} + +/* Check each node, see if the node type is consistent with the type */ +bool RRChan::valid_node_type(const RRGraphView& rr_graph, const RRNodeId& node) const { + valid_type(rr_graph.node_type(node)); + if (NUM_RR_TYPES == type_) { + return true; + } + valid_type(type_); + if (type_ != rr_graph.node_type(node)) { + return false; + } + return true; +} + +/* check if the node id is valid */ +bool RRChan::valid_node_id(const size_t& node_id) const { + if (node_id < nodes_.size()) { + return true; + } + + return false; +} diff --git a/vpr/src/tileable_rr_graph/rr_chan.h b/vpr/src/tileable_rr_graph/rr_chan.h new file mode 100644 index 00000000000..5ba9bc09af5 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_chan.h @@ -0,0 +1,83 @@ +#ifndef RR_CHAN_H +#define RR_CHAN_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +/* Headers from vpr library */ +#include "rr_graph_view.h" + +/******************************************************************** + * RRChan object aim to describe a routing channel in a routing resource graph + * - What are the nodes in the RRGraph object, for each routing track + * - What are routing segments used by each node in the channel + * - What are the directions of each routing channel + * being either X-direction or Y-direction + * + * Note : + * - This is a collection of rr_nodes from the RRGraph Object + * It does not rebuild or contruct any connects between rr_nodes + * It is just an annotation on an existing RRGraph + * ------------- ------ + * | | | | + * | | | Y | + * | CLB | | Chan | + * | | | | + * | | | | + * ------------- ------ + * ------------- + * | X | + * | Channel | + * ------------- + *******************************************************************/ +class RRChan { + public: /* Constructors */ + RRChan(); + + public: /* Accessors */ + t_rr_type get_type() const; + size_t get_chan_width() const; /* get the number of tracks in this channel */ + int get_node_track_id(const RRNodeId& node) const; /* get the track_id of a node */ + RRNodeId get_node(const size_t& track_num) const; /* get the rr_node with the track_id */ + RRSegmentId get_node_segment(const RRNodeId& node) const; + RRSegmentId get_node_segment(const size_t& track_num) const; + std::vector get_segment_ids() const; /* Get a list of segments used in this routing channel */ + std::vector get_node_ids_by_segment_ids(const RRSegmentId& seg_id) const; /* Get a list of segments used in this routing channel */ + public: /* Mutators */ + /* copy */ + void set(const RRChan&); + + /* modify the type of routing channel */ + void set_type(const t_rr_type& type); + + /* reseve a number of nodes to the array */ + void reserve_node(const size_t& node_size); + + /* add a node to the routing channel */ + void add_node(const RRGraphView& rr_graph, const RRNodeId& node, const RRSegmentId& node_segment); + + /* clear the content */ + void clear(); + + private: /* internal functions */ + /* For the type of a routing channel, only valid type is CHANX and CHANY */ + bool valid_type(const t_rr_type& type) const; + + /* Check each node, see if the node type is consistent with the type of routing channel */ + bool valid_node_type(const RRGraphView& rr_graph, const RRNodeId& node) const; + + /* Validate if the track number in the range */ + bool valid_node_id(const size_t& node_id) const; + + private: /* Internal Data */ + t_rr_type type_; /* channel type: CHANX or CHANY */ + std::vector nodes_; /* rr nodes of each track in the channel */ + std::vector node_segments_; /* segment of each track */ +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp new file mode 100644 index 00000000000..a97b37e2d88 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp @@ -0,0 +1,546 @@ +/************************************************************************ + * This file contains most utilized functions for rr_graph builders + ***********************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "vpr_utils.h" + +#include "rr_graph_builder_utils.h" + +/************************************************************************ + * Correct number of routing channel width to be compatible to + * uni-directional routing architecture + ***********************************************************************/ +size_t find_unidir_routing_channel_width(const size_t& chan_width) { + size_t actual_chan_width = chan_width; + /* Correct the chan_width: it should be an even number */ + if (0 != actual_chan_width % 2) { + actual_chan_width++; /* increment it to be even */ + } + VTR_ASSERT(0 == actual_chan_width % 2); + + return actual_chan_width; +} + +/************************************************************************ + * Get the class index of a grid pin + ***********************************************************************/ +int get_grid_pin_class_index(const t_grid_tile& cur_grid, + const int pin_index) { + /* check */ + VTR_ASSERT(pin_index < cur_grid.type->num_pins); + return cur_grid.type->pin_class[pin_index]; +} + +/* Deteremine the side of a io grid */ +e_side determine_io_grid_pin_side(const vtr::Point& device_size, + const vtr::Point& grid_coordinate) { + /* TOP side IO of FPGA */ + if (device_size.y() == grid_coordinate.y()) { + return BOTTOM; /* Such I/O has only Bottom side pins */ + } else if (device_size.x() == grid_coordinate.x()) { /* RIGHT side IO of FPGA */ + return LEFT; /* Such I/O has only Left side pins */ + } else if (0 == grid_coordinate.y()) { /* BOTTOM side IO of FPGA */ + return TOP; /* Such I/O has only Top side pins */ + } else if (0 == grid_coordinate.x()) { /* LEFT side IO of FPGA */ + return RIGHT; /* Such I/O has only Right side pins */ + } else if ((grid_coordinate.x() < device_size.x()) && (grid_coordinate.y() < device_size.y())) { + /* I/O grid in the center grid */ + return NUM_SIDES; + } + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid coordinate (%lu, %lu) for I/O Grid whose size is (%lu, %lu)!\n", + grid_coordinate.x(), grid_coordinate.y(), + device_size.x(), device_size.y()); + exit(1); +} + +/* Deteremine the side of a pin of a grid */ +std::vector find_grid_pin_sides(const t_grid_tile& grid, + const size_t& pin_id) { + std::vector pin_sides; + + for (const e_side& side : {TOP, RIGHT, BOTTOM, LEFT}) { + if (true == grid.type->pinloc[grid.width_offset][grid.height_offset][size_t(side)][pin_id]) { + pin_sides.push_back(side); + } + } + + return pin_sides; +} + +/************************************************************************ + * Get a list of pin_index for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +std::vector get_grid_side_pins(const t_grid_tile& cur_grid, + const e_pin_type& pin_type, + const e_side& pin_side, + const int& pin_width, + const int& pin_height) { + std::vector pin_list; + /* Make sure a clear start */ + pin_list.clear(); + + for (int ipin = 0; ipin < cur_grid.type->num_pins; ++ipin) { + int class_id = cur_grid.type->pin_class[ipin]; + if ((1 == cur_grid.type->pinloc[pin_width][pin_height][pin_side][ipin]) + && (pin_type == cur_grid.type->class_inf[class_id].type)) { + pin_list.push_back(ipin); + } + } + return pin_list; +} + +/************************************************************************ + * Get the number of pins for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +size_t get_grid_num_pins(const t_grid_tile& cur_grid, + const e_pin_type& pin_type, + const e_side& io_side) { + size_t num_pins = 0; + + /* For IO_TYPE sides */ + for (const e_side& side : {TOP, RIGHT, BOTTOM, LEFT}) { + /* skip unwanted sides */ + if ((true == is_io_type(cur_grid.type)) + && (side != io_side) && (NUM_SIDES != io_side)) { + continue; + } + /* Get pin list */ + for (int width = 0; width < cur_grid.type->width; ++width) { + for (int height = 0; height < cur_grid.type->height; ++height) { + std::vector pin_list = get_grid_side_pins(cur_grid, pin_type, side, width, height); + num_pins += pin_list.size(); + } + } + } + + return num_pins; +} + +/************************************************************************ + * Get the number of pins for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +size_t get_grid_num_classes(const t_grid_tile& cur_grid, + const e_pin_type& pin_type) { + size_t num_classes = 0; + + for (size_t iclass = 0; iclass < cur_grid.type->class_inf.size(); ++iclass) { + /* Bypass unmatched pin_type */ + if (pin_type != cur_grid.type->class_inf[iclass].type) { + continue; + } + num_classes++; + } + + return num_classes; +} + +/************************************************************************ + * Idenfity if a X-direction routing channel exist in the fabric + * This could be entirely possible that a routig channel + * is in the middle of a multi-width and multi-height grid + * + * As the chanx always locates on top of a grid with the same coord + * + * +----------+ + * | CHANX | + * | [x][y] | + * +----------+ + * + * +----------+ + * | Grid | height_offset = height - 1 + * | [x][y] | + * +----------+ + * + * +----------+ + * | Grid | height_offset = height - 2 + * | [x][y-1] | + * +----------+ + * If the CHANX is in the middle of a multi-width and multi-height grid + * it should locate at a grid whose height_offset is lower than the its height defined in physical_tile + * When height_offset == height - 1, it means that the grid is at the top side of this multi-width and multi-height block + ***********************************************************************/ +bool is_chanx_exist(const DeviceGrid& grids, + const vtr::Point& chanx_coord, + const bool& through_channel) { + if ((1 > chanx_coord.x()) || (chanx_coord.x() > grids.width() - 2)) { + return false; + } + + if (chanx_coord.y() > grids.height() - 2) { + return false; + } + + if (true == through_channel) { + return true; + } + + return (grids[chanx_coord.x()][chanx_coord.y()].height_offset == grids[chanx_coord.x()][chanx_coord.y()].type->height - 1); +} + +/************************************************************************ + * Idenfity if a Y-direction routing channel exist in the fabric + * This could be entirely possible that a routig channel + * is in the middle of a multi-width and multi-height grid + * + * As the chany always locates on right of a grid with the same coord + * + * +-----------+ +---------+ +--------+ + * | Grid | | Grid | | CHANY | + * | [x-1][y] | | [x][y] | | [x][y] | + * +-----------+ +---------+ +--------+ + * width_offset width_offset + * = width - 2 = width -1 + * If the CHANY is in the middle of a multi-width and multi-height grid + * it should locate at a grid whose width_offset is lower than the its width defined in physical_tile + * When height_offset == height - 1, it means that the grid is at the top side of this multi-width and multi-height block + * + * If through channel is allowed, the chany will always exists + * unless it falls out of the grid array + ***********************************************************************/ +bool is_chany_exist(const DeviceGrid& grids, + const vtr::Point& chany_coord, + const bool& through_channel) { + if (chany_coord.x() > grids.width() - 2) { + return false; + } + + if ((1 > chany_coord.y()) || (chany_coord.y() > grids.height() - 2)) { + return false; + } + + if (true == through_channel) { + return true; + } + + return (grids[chany_coord.x()][chany_coord.y()].width_offset == grids[chany_coord.x()][chany_coord.y()].type->width - 1); +} + +/************************************************************************ + * Identify if a X-direction routing channel is at the right side of a + * multi-height grid + * + * +-----------------+ + * | | + * | | +-------------+ + * | Grid | | CHANX | + * | [x-1][y] | | [x][y] | + * | | +-------------+ + * | | + * +-----------------+ + ***********************************************************************/ +bool is_chanx_right_to_multi_height_grid(const DeviceGrid& grids, + const vtr::Point& chanx_coord, + const bool& through_channel) { + VTR_ASSERT(0 < chanx_coord.x()); + if (1 == chanx_coord.x()) { + /* This is already the LEFT side of FPGA fabric, + * it is the same results as chanx is right to a multi-height grid + */ + return true; + } + + if (false == through_channel) { + /* We check the left neighbor of chanx, if it does not exist, the chanx is left to a multi-height grid */ + vtr::Point left_chanx_coord(chanx_coord.x() - 1, chanx_coord.y()); + if (false == is_chanx_exist(grids, left_chanx_coord)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Identify if a X-direction routing channel is at the left side of a + * multi-height grid + * + * +-----------------+ + * | | + * +---------------+ | | + * | CHANX | | Grid | + * | [x][y] | | [x+1][y] | + * +---------------+ | | + * | | + * +-----------------+ + ***********************************************************************/ +bool is_chanx_left_to_multi_height_grid(const DeviceGrid& grids, + const vtr::Point& chanx_coord, + const bool& through_channel) { + VTR_ASSERT(chanx_coord.x() < grids.width() - 1); + if (grids.width() - 2 == chanx_coord.x()) { + /* This is already the RIGHT side of FPGA fabric, + * it is the same results as chanx is right to a multi-height grid + */ + return true; + } + + if (false == through_channel) { + /* We check the right neighbor of chanx, if it does not exist, the chanx is left to a multi-height grid */ + vtr::Point right_chanx_coord(chanx_coord.x() + 1, chanx_coord.y()); + if (false == is_chanx_exist(grids, right_chanx_coord)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Identify if a Y-direction routing channel is at the top side of a + * multi-width grid + * + * +--------+ + * | CHANY | + * | [x][y] | + * +--------+ + * + * +-----------------+ + * | | + * | | + * | Grid | + * | [x-1][y] | + * | | + * | | + * +-----------------+ + ***********************************************************************/ +bool is_chany_top_to_multi_width_grid(const DeviceGrid& grids, + const vtr::Point& chany_coord, + const bool& through_channel) { + VTR_ASSERT(0 < chany_coord.y()); + if (1 == chany_coord.y()) { + /* This is already the BOTTOM side of FPGA fabric, + * it is the same results as chany is at the top of a multi-width grid + */ + return true; + } + + if (false == through_channel) { + /* We check the bottom neighbor of chany, if it does not exist, the chany is top to a multi-height grid */ + vtr::Point bottom_chany_coord(chany_coord.x(), chany_coord.y() - 1); + if (false == is_chany_exist(grids, bottom_chany_coord)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Identify if a Y-direction routing channel is at the bottom side of a + * multi-width grid + * + * +-----------------+ + * | | + * | | + * | Grid | + * | [x][y+1] | + * | | + * | | + * +-----------------+ + * +--------+ + * | CHANY | + * | [x][y] | + * +--------+ + * + ***********************************************************************/ +bool is_chany_bottom_to_multi_width_grid(const DeviceGrid& grids, + const vtr::Point& chany_coord, + const bool& through_channel) { + VTR_ASSERT(chany_coord.y() < grids.height() - 1); + if (grids.height() - 2 == chany_coord.y()) { + /* This is already the TOP side of FPGA fabric, + * it is the same results as chany is at the bottom of a multi-width grid + */ + return true; + } + + if (false == through_channel) { + /* We check the top neighbor of chany, if it does not exist, the chany is left to a multi-height grid */ + vtr::Point top_chany_coord(chany_coord.x(), chany_coord.y() + 1); + if (false == is_chany_exist(grids, top_chany_coord)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Get the track_id of a routing track w.r.t its coordinator + * In tileable routing architecture, the track_id changes SB by SB. + * Therefore the track_ids are stored in a vector, indexed by the relative coordinator + * based on the starting point of the track + * For routing tracks in INC_DIRECTION + * (xlow, ylow) should be the starting point + * + * (xlow, ylow) (xhigh, yhigh) + * track_id[0] -------------------------------> track_id[xhigh - xlow + yhigh - ylow] + * + * For routing tracks in DEC_DIRECTION + * (xhigh, yhigh) should be the starting point + * + * (xlow, ylow) (xhigh, yhigh) + * track_id[0] <------------------------------- track_id[xhigh - xlow + yhigh - ylow] + * + * + ***********************************************************************/ +short get_rr_node_actual_track_id(const RRGraph& rr_graph, + const RRNodeId& track_rr_node, + const vtr::Point& coord, + const vtr::vector>& tileable_rr_graph_node_track_ids) { + vtr::Point low_coord(rr_graph.node_xlow(track_rr_node), rr_graph.node_ylow(track_rr_node)); + size_t offset = (int)abs((int)coord.x() - (int)low_coord.x() + (int)coord.y() - (int)low_coord.y()); + return tileable_rr_graph_node_track_ids[track_rr_node][offset]; +} + +/************************************************************************ + * Get the ptc of a routing track in the channel where it ends + * For routing tracks in INC_DIRECTION + * the ptc is the last of track_ids + * + * For routing tracks in DEC_DIRECTION + * the ptc is the first of track_ids + ***********************************************************************/ +short get_track_rr_node_end_track_id(const RRGraph& rr_graph, + const RRNodeId& track_rr_node, + const vtr::vector>& tileable_rr_graph_node_track_ids) { + /* Make sure we have CHANX or CHANY */ + VTR_ASSERT((CHANX == rr_graph.node_type(track_rr_node)) + || (CHANY == rr_graph.node_type(track_rr_node))); + + if (Direction::INC == rr_graph.node_direction(track_rr_node)) { + return tileable_rr_graph_node_track_ids[track_rr_node].back(); + } + + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(track_rr_node)); + return tileable_rr_graph_node_track_ids[track_rr_node].front(); +} + +/************************************************************************ + * Find the number of nodes in the same class + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_num_nodes(const RRGraph& rr_graph, + const std::vector& node_types) { + short counter = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + counter++; + } + + return counter; +} + +/************************************************************************ + * Find the maximum fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_max_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + short max_fan_in = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + max_fan_in = std::max(rr_graph.node_fan_in(node), max_fan_in); + } + + return max_fan_in; +} + +/************************************************************************ + * Find the minimum fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_min_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + short min_fan_in = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + min_fan_in = std::min(rr_graph.node_fan_in(node), min_fan_in); + } + + return min_fan_in; +} + +/************************************************************************ + * Find the average fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_average_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + /* Get the maximum SB mux size */ + size_t sum = 0; + size_t counter = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + + sum += rr_graph.node_fan_in(node); + counter++; + } + + return sum / counter; +} + +/************************************************************************ + * Print statistics of multiplexers in a routing resource graph + ************************************************************************/ +void print_rr_graph_mux_stats(const RRGraph& rr_graph) { + /* Print MUX size distribution */ + std::vector sb_node_types; + sb_node_types.push_back(CHANX); + sb_node_types.push_back(CHANY); + + /* Print statistics */ + VTR_LOG("------------------------------------------------\n"); + VTR_LOG("Total No. of Switch Block multiplexer size: %d\n", + find_rr_graph_num_nodes(rr_graph, sb_node_types)); + VTR_LOG("Maximum Switch Block multiplexer size: %d\n", + find_rr_graph_max_fan_in(rr_graph, sb_node_types)); + VTR_LOG("Minimum Switch Block multiplexer size: %d\n", + find_rr_graph_min_fan_in(rr_graph, sb_node_types)); + VTR_LOG("Average Switch Block multiplexer size: %lu\n", + find_rr_graph_average_fan_in(rr_graph, sb_node_types)); + VTR_LOG("------------------------------------------------\n"); + + /* Get the maximum CB mux size */ + std::vector cb_node_types(1, IPIN); + + VTR_LOG("------------------------------------------------\n"); + VTR_LOG("Total No. of Connection Block Multiplexer size: %d\n", + find_rr_graph_num_nodes(rr_graph, cb_node_types)); + VTR_LOG("Maximum Connection Block Multiplexer size: %d\n", + find_rr_graph_max_fan_in(rr_graph, cb_node_types)); + VTR_LOG("Minimum Connection Block Multiplexer size: %d\n", + find_rr_graph_min_fan_in(rr_graph, cb_node_types)); + VTR_LOG("Average Connection Block Multiplexer size: %lu\n", + find_rr_graph_average_fan_in(rr_graph, cb_node_types)); + VTR_LOG("------------------------------------------------\n"); +} diff --git a/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h new file mode 100644 index 00000000000..ff0cc9ab782 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h @@ -0,0 +1,92 @@ +#ifndef RR_GRAPH_BUILDER_UTILS_H +#define RR_GRAPH_BUILDER_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "device_grid.h" +#include "rr_graph_obj.h" +#include "vtr_geometry.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +size_t find_unidir_routing_channel_width(const size_t& chan_width); + +int get_grid_pin_class_index(const t_grid_tile& cur_grid, + const int pin_index); + +std::vector find_grid_pin_sides(const t_grid_tile& grid, + const size_t& pin_id); + +e_side determine_io_grid_pin_side(const vtr::Point& device_size, + const vtr::Point& grid_coordinate); + +std::vector get_grid_side_pins(const t_grid_tile& cur_grid, + const e_pin_type& pin_type, + const e_side& pin_side, + const int& pin_width, + const int& pin_height); + +size_t get_grid_num_pins(const t_grid_tile& cur_grid, + const e_pin_type& pin_type, + const e_side& io_side); + +size_t get_grid_num_classes(const t_grid_tile& cur_grid, + const e_pin_type& pin_type); + +bool is_chanx_exist(const DeviceGrid& grids, + const vtr::Point& chanx_coord, + const bool& through_channel = false); + +bool is_chany_exist(const DeviceGrid& grids, + const vtr::Point& chany_coord, + const bool& through_channel = false); + +bool is_chanx_right_to_multi_height_grid(const DeviceGrid& grids, + const vtr::Point& chanx_coord, + const bool& through_channel); + +bool is_chanx_left_to_multi_height_grid(const DeviceGrid& grids, + const vtr::Point& chanx_coord, + const bool& through_channel); + +bool is_chany_top_to_multi_width_grid(const DeviceGrid& grids, + const vtr::Point& chany_coord, + const bool& through_channel); + +bool is_chany_bottom_to_multi_width_grid(const DeviceGrid& grids, + const vtr::Point& chany_coord, + const bool& through_channel); + +short get_rr_node_actual_track_id(const RRGraph& rr_graph, + const RRNodeId& track_rr_node, + const vtr::Point& coord, + const vtr::vector>& tileable_rr_graph_node_track_ids); + +vtr::Point get_track_rr_node_start_coordinator(const RRGraph& rr_graph, + const RRNodeId& track_rr_node); + +vtr::Point get_track_rr_node_end_coordinator(const RRGraph& rr_graph, + const RRNodeId& track_rr_node); + +short get_track_rr_node_end_track_id(const RRGraph& rr_graph, + const RRNodeId& track_rr_node, + const vtr::vector>& tileable_rr_graph_node_track_ids); + +short find_rr_graph_num_nodes(const RRGraph& rr_graph, + const std::vector& node_types); + +short find_rr_graph_max_fan_in(const RRGraph& rr_graph, + const std::vector& node_types); + +short find_rr_graph_min_fan_in(const RRGraph& rr_graph, + const std::vector& node_types); + +short find_rr_graph_average_fan_in(const RRGraph& rr_graph, + const std::vector& node_types); + +void print_rr_graph_mux_stats(const RRGraph& rr_graph); + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_graph_types.h b/vpr/src/tileable_rr_graph/rr_graph_types.h new file mode 100644 index 00000000000..ca97d7d38a9 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_types.h @@ -0,0 +1,45 @@ +#ifndef RR_GRAPH_TYPES_H +#define RR_GRAPH_TYPES_H + +/******************************************************************** + * Data types required by routing resource graph (RRGraph) definition + *******************************************************************/ + +/******************************************************************** + * Directionality of a routing track (node type CHANX and CHANY) in + * a routing resource graph + *******************************************************************/ +enum e_direction : unsigned char { + INC_DIRECTION = 0, + DEC_DIRECTION = 1, + BI_DIRECTION = 2, + NO_DIRECTION = 3, + NUM_DIRECTIONS +}; + +/* Xifan Tang - string used in describe_rr_node() and write_xml_rr_graph_obj() */ +constexpr std::array DIRECTION_STRING_WRITE_XML = {{"INC_DIR", "DEC_DIR", "BI_DIR", "NO_DIR"}}; + +#if 0 +/* Type of a routing resource node. x-directed channel segment, * + * y-directed channel segment, input pin to a clb to pad, output * + * from a clb or pad (i.e. output pin of a net) and: * + * SOURCE: A dummy node that is a logical output within a block * + * -- i.e., the gate that generates a signal. * + * SINK: A dummy node that is a logical input within a block * + * -- i.e. the gate that needs a signal. */ +typedef enum e_rr_type : unsigned char { + SOURCE = 0, + SINK, + IPIN, + OPIN, + CHANX, + CHANY, + NUM_RR_TYPES +} t_rr_type; +#endif + +// constexpr std::array RR_TYPES = {{SOURCE, SINK, IPIN, OPIN, CHANX, CHANY}}; +// constexpr std::array rr_node_typename{{"SOURCE", "SINK", "IPIN", "OPIN", "CHANX", "CHANY"}}; + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_graph_view_util.cpp b/vpr/src/tileable_rr_graph/rr_graph_view_util.cpp new file mode 100644 index 00000000000..66baf26a1f6 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_view_util.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** + * This file include most-utilized functions that manipulate on the + * RRGraph object + ***************************************************************************/ +#include "rr_graph_view_util.h" + +/**************************************************************************** + * Find the switches interconnecting two nodes + * Return a vector of switch ids + ***************************************************************************/ +std::vector find_rr_graph_switches(const RRGraphView& rr_graph, + const RRNodeId& from_node, + const RRNodeId& to_node) { + std::vector switches; + + std::vector edges = rr_graph.find_edges(from_node, to_node); + if (true == edges.empty()) { + /* edge is open, we return an empty vector of switches */ + return switches; + } + + /* Reach here, edge list is not empty, find switch id one by one + * and update the switch list + */ + for (auto edge : edges) { + switches.push_back(rr_graph.edge_switch(edge)); + } + + return switches; +} + +/********************************************************************* + * Like the RRGraph.find_node() but returns all matching nodes, + * rather than just the first. This is particularly useful for getting all instances + * of a specific IPIN/OPIN at a specific grid tile (x,y) location. + **********************************************************************/ +std::vector find_rr_graph_nodes(const RRGraphView& rr_graph, + const int& x, + const int& y, + const t_rr_type& rr_type, + const int& ptc) { + std::vector indices; + + if (rr_type == IPIN || rr_type == OPIN) { + //For pins we need to look at all the sides of the current grid tile + + for (e_side side : SIDES) { + RRNodeId rr_node_index = rr_graph.node_lookup().find_node(x, y, rr_type, ptc, side); + + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + } else { + //Sides do not effect non-pins so there should only be one per ptc + RRNodeId rr_node_index = rr_graph.node_lookup().find_node(x, y, rr_type, ptc); + + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + + return indices; +} + +/********************************************************************* + * Find a list of rr nodes in a routing channel at (x,y) + **********************************************************************/ +std::vector find_rr_graph_chan_nodes(const RRGraphView& rr_graph, + const int& x, + const int& y, + const t_rr_type& rr_type) { + std::vector indices; + + VTR_ASSERT(rr_type == CHANX || rr_type == CHANY); + + for (const RRNodeId& rr_node_index : rr_graph.node_lookup().find_channel_nodes(x, y, rr_type)) { + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + + return indices; +} + +/********************************************************************* + * Find a list of rr_nodes that locate at a side of a grid + **********************************************************************/ +std::vector find_rr_graph_grid_nodes(const RRGraphView& rr_graph, + const DeviceGrid& device_grid, + const int& x, + const int& y, + const t_rr_type& rr_type, + const e_side& side, + bool include_clock) { + std::vector indices; + + VTR_ASSERT(rr_type == IPIN || rr_type == OPIN); + + /* Ensure that (x, y) is a valid location in grids */ + VTR_ASSERT(size_t(x) <= device_grid.width() && size_t(y) <= device_grid.height()); + + /* Ensure we have a valid side */ + VTR_ASSERT(side != NUM_SIDES); + + /* Find all the pins on the side of the grid */ + int width_offset = device_grid[x][y].width_offset; + int height_offset = device_grid[x][y].height_offset; + for (int pin = 0; pin < device_grid[x][y].type->num_pins; ++pin) { + /* Skip those pins have been ignored during rr_graph build-up */ + if (true == device_grid[x][y].type->is_ignored_pin[pin]) { + /* If specified, force to include all the clock pins */ + if (!include_clock || std::find(device_grid[x][y].type->get_clock_pins_indices().begin(), device_grid[x][y].type->get_clock_pins_indices().end(), pin) == device_grid[x][y].type->get_clock_pins_indices().end()) { + continue; + } + } + if (false == device_grid[x][y].type->pinloc[width_offset][height_offset][side][pin]) { + /* Not the pin on this side, we skip */ + continue; + } + + /* Try to find the rr node */ + RRNodeId rr_node_index = rr_graph.node_lookup().find_node(x, y, rr_type, pin, side); + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + + return indices; +} diff --git a/vpr/src/tileable_rr_graph/rr_graph_view_util.h b/vpr/src/tileable_rr_graph/rr_graph_view_util.h new file mode 100644 index 00000000000..ec7d352aa49 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_view_util.h @@ -0,0 +1,35 @@ +#ifndef RR_GRAPH_VIEW_UTIL_H +#define RR_GRAPH_VIEW_UTIL_H + +/* Include header files which include data structures used by + * the function declaration + */ +#include +#include "device_grid.h" +#include "rr_graph_view.h" + +/* Get node-to-node switches in a RRGraph */ +std::vector find_rr_graph_switches(const RRGraphView& rr_graph, + const RRNodeId& from_node, + const RRNodeId& to_node); + +std::vector find_rr_graph_nodes(const RRGraphView& rr_graph, + const int& x, + const int& y, + const t_rr_type& rr_type, + const int& ptc); + +std::vector find_rr_graph_chan_nodes(const RRGraphView& rr_graph, + const int& x, + const int& y, + const t_rr_type& rr_type); + +std::vector find_rr_graph_grid_nodes(const RRGraphView& rr_graph, + const DeviceGrid& device_grid, + const int& x, + const int& y, + const t_rr_type& rr_type, + const e_side& side, + bool include_clock = false); + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_gsb.cpp b/vpr/src/tileable_rr_graph/rr_gsb.cpp new file mode 100644 index 00000000000..901a9c0bef7 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_gsb.cpp @@ -0,0 +1,1024 @@ +/************************************************************************ + * Member functions for class RRGSB + ***********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_log.h" +#include "vtr_assert.h" + +#include "openfpga_rr_graph_utils.h" +#include "openfpga_side_manager.h" + +#include "rr_gsb.h" +#include "vtr_geometry.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +/* Constructor for an empty object */ +RRGSB::RRGSB() { + /* Set a clean start! */ + coordinate_.set(0, 0); + + chan_node_.clear(); + chan_node_direction_.clear(); + chan_node_in_edges_.clear(); + ipin_node_in_edges_.clear(); + + ipin_node_.clear(); + + opin_node_.clear(); +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +/* Get the number of sides of this SB */ +size_t RRGSB::get_num_sides() const { + VTR_ASSERT(validate_num_sides()); + return chan_node_direction_.size(); +} + +/* Get the number of routing tracks on a side */ +size_t RRGSB::get_chan_width(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return chan_node_[side_manager.to_size_t()].get_chan_width(); +} + +/* Get the number of routing tracks on a side */ +t_rr_type RRGSB::get_chan_type(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return chan_node_[side_manager.to_size_t()].get_type(); +} + +/* Get the maximum number of routing tracks on all sides */ +size_t RRGSB::get_max_chan_width() const { + size_t max_chan_width = 0; + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + max_chan_width = std::max(max_chan_width, get_chan_width(side_manager.get_side())); + } + return max_chan_width; +} + +const RRChan& RRGSB::chan(const e_side& chan_side) const { + return chan_node_[size_t(chan_side)]; +} + +/* Get the number of routing tracks of a X/Y-direction CB */ +size_t RRGSB::get_cb_chan_width(const t_rr_type& cb_type) const { + return get_chan_width(get_cb_chan_side(cb_type)); +} + +/* Get the sides of ipin_nodes belong to the cb */ +std::vector RRGSB::get_cb_ipin_sides(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + + std::vector ipin_sides; + + /* Make sure a clean start */ + ipin_sides.clear(); + + switch (cb_type) { + case CHANX: + ipin_sides.push_back(TOP); + ipin_sides.push_back(BOTTOM); + break; + case CHANY: + ipin_sides.push_back(RIGHT); + ipin_sides.push_back(LEFT); + break; + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } + + return ipin_sides; +} + +/* Get the direction of a rr_node at a given side and track_id */ +enum PORTS RRGSB::get_chan_node_direction(const e_side& side, const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + return chan_node_direction_[side_manager.to_size_t()][track_id]; +} + +/* Get a list of segments used in this routing channel */ +std::vector RRGSB::get_chan_segment_ids(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + return chan_node_[side_manager.to_size_t()].get_segment_ids(); +} + +/* Get a list of rr_nodes whose sed_id is specified */ +std::vector RRGSB::get_chan_node_ids_by_segment_ids(const e_side& side, + const RRSegmentId& seg_id) const { + return chan_node_[size_t(side)].get_node_ids_by_segment_ids(seg_id); +} + +/* get a rr_node at a given side and track_id */ +RRNodeId RRGSB::get_chan_node(const e_side& side, const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + return chan_node_[side_manager.to_size_t()].get_node(track_id); +} + +std::vector RRGSB::get_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + /* The chan node must be an output port for the GSB, we allow users to access input edges*/ + VTR_ASSERT(OUT_PORT == get_chan_node_direction(side, track_id)); + + /* if sorted, we give sorted edges + * if not sorted, we give the empty vector + */ + if (0 == chan_node_in_edges_.size()) { + std::vector unsorted_edges; + + for (const RREdgeId& edge : rr_graph.node_in_edges(get_chan_node(side, track_id))) { + unsorted_edges.push_back(edge); + } + + return unsorted_edges; + } + + return chan_node_in_edges_[side_manager.to_size_t()][track_id]; +} + +std::vector RRGSB::get_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& ipin_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_ipin_node_id(side, ipin_id)); + + /* if sorted, we give sorted edges + * if not sorted, we give the empty vector + */ + if (0 == ipin_node_in_edges_.size()) { + std::vector unsorted_edges; + + for (const RREdgeId& edge : rr_graph.node_in_edges(get_ipin_node(side, ipin_id))) { + unsorted_edges.push_back(edge); + } + + return unsorted_edges; + } + + return ipin_node_in_edges_[side_manager.to_size_t()][ipin_id]; +} + +/* get the segment id of a channel rr_node */ +RRSegmentId RRGSB::get_chan_node_segment(const e_side& side, const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + return chan_node_[side_manager.to_size_t()].get_node_segment(track_id); +} + +/* Get the number of IPIN rr_nodes on a side */ +size_t RRGSB::get_num_ipin_nodes(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return ipin_node_[side_manager.to_size_t()].size(); +} + +/* get a opin_node at a given side and track_id */ +RRNodeId RRGSB::get_ipin_node(const e_side& side, const size_t& node_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_ipin_node_id(side, node_id)); + + return ipin_node_[side_manager.to_size_t()][node_id]; +} + +/* Get the number of OPIN rr_nodes on a side */ +size_t RRGSB::get_num_opin_nodes(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return opin_node_[side_manager.to_size_t()].size(); +} + +/* get a opin_node at a given side and track_id */ +RRNodeId RRGSB::get_opin_node(const e_side& side, const size_t& node_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_opin_node_id(side, node_id)); + + return opin_node_[side_manager.to_size_t()][node_id]; +} + +/* Get the node index of a routing track of a connection block, return -1 if not found */ +int RRGSB::get_cb_chan_node_index(const t_rr_type& cb_type, const RRNodeId& node) const { + enum e_side chan_side = get_cb_chan_side(cb_type); + return get_chan_node_index(chan_side, node); +} + +/* Get the node index in the array, return -1 if not found */ +int RRGSB::get_chan_node_index(const e_side& node_side, const RRNodeId& node) const { + VTR_ASSERT(validate_side(node_side)); + return chan_node_[size_t(node_side)].get_node_track_id(node); +} + +/* Get the node index in the array, return -1 if not found */ +int RRGSB::get_node_index(const RRGraphView& rr_graph, + const RRNodeId& node, + const e_side& node_side, + const PORTS& node_direction) const { + size_t cnt; + int ret; + + cnt = 0; + ret = -1; + + /* Depending on the type of rr_node, we search different arrays */ + switch (rr_graph.node_type(node)) { + case CHANX: + case CHANY: + for (size_t inode = 0; inode < get_chan_width(node_side); ++inode) { + if ((node == chan_node_[size_t(node_side)].get_node(inode)) + /* Check if direction meets specification */ + && (node_direction == chan_node_direction_[size_t(node_side)][inode])) { + cnt++; + ret = inode; + break; + } + } + break; + case IPIN: + for (size_t inode = 0; inode < get_num_ipin_nodes(node_side); ++inode) { + if (node == ipin_node_[size_t(node_side)][inode]) { + cnt++; + ret = inode; + break; + } + } + break; + case OPIN: + for (size_t inode = 0; inode < get_num_opin_nodes(node_side); ++inode) { + if (node == opin_node_[size_t(node_side)][inode]) { + cnt++; + ret = inode; + break; + } + } + break; + default: + VTR_LOG("Invalid cur_rr_node type! Should be [CHANX|CHANY|IPIN|OPIN]\n"); + exit(1); + } + + VTR_ASSERT((0 == cnt) || (1 == cnt)); + + return ret; /* Return an invalid value: nonthing is found*/ +} + +/* Get the side of a node in this SB */ +void RRGSB::get_node_side_and_index(const RRGraphView& rr_graph, + const RRNodeId& node, + const PORTS& node_direction, + e_side& node_side, + int& node_index) const { + size_t side; + SideManager side_manager; + + /* Count the number of existence of cur_rr_node in cur_sb_info + * It could happen that same cur_rr_node appears on different sides of a SB + * For example, a routing track go vertically across the SB. + * Then its corresponding rr_node appears on both TOP and BOTTOM sides of this SB. + * We need to ensure that the found rr_node has the same direction as user want. + * By specifying the direction of rr_node, There should be only one rr_node can satisfy! + */ + for (side = 0; side < get_num_sides(); ++side) { + side_manager.set_side(side); + node_index = get_node_index(rr_graph, node, side_manager.get_side(), node_direction); + if (-1 != node_index) { + break; + } + } + + if (side == get_num_sides()) { + /* we find nothing, return NUM_SIDES, and a OPEN node (-1) */ + node_side = NUM_SIDES; + VTR_ASSERT(-1 == node_index); + return; + } + + node_side = side_manager.get_side(); + VTR_ASSERT(-1 != node_index); + + return; +} + +/* Check if the node exist in the opposite side of this Switch Block */ +bool RRGSB::is_sb_node_exist_opposite_side(const RRGraphView& rr_graph, + const RRNodeId& node, + const e_side& node_side) const { + SideManager side_manager(node_side); + int index; + + VTR_ASSERT((CHANX == rr_graph.node_type(node)) || (CHANY == rr_graph.node_type(node))); + + /* See if we can find the same src_rr_node in the opposite chan_side + * if there is one, it means a shorted wire across the SB + */ + index = get_node_index(rr_graph, node, side_manager.get_opposite(), IN_PORT); + + return (-1 != index); +} + +/* check if the CB exist in this GSB */ +bool RRGSB::is_cb_exist(const t_rr_type& cb_type) const { + /* if channel width is zero, there is no CB */ + return (0 != get_cb_chan_width(cb_type)); +} + +/* check if the SB exist in this GSB */ +bool RRGSB::is_sb_exist() const { + /* if all the channel width is zero and number of OPINs are zero, there is no SB */ + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + if (0 != get_chan_width(side_manager.get_side())) { + return true; + } + if (0 != get_num_opin_nodes(side_manager.get_side())) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Check if the node indicates a passing wire across the Switch Block part of the GSB + * Therefore, we actually do the following check + * Check if a track starts from this GSB or not + * For INC_DIRECTION + * (xlow, ylow) should be same as the GSB side coordinate + * For DEC_DIRECTION + * (xhigh, yhigh) should be same as the GSB side coordinate + ***********************************************************************/ +bool RRGSB::is_sb_node_passing_wire(const RRGraphView& rr_graph, + const e_side& node_side, + const size_t& track_id) const { + /* Get the rr_node */ + RRNodeId track_node = get_chan_node(node_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = get_side_block_coordinate(node_side); + + /* Get the coordinate of where the track starts */ + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* INC_DIRECTION start_track: (xlow, ylow) should be same as the GSB side coordinate */ + /* DEC_DIRECTION start_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + if ((track_start.x() == side_coordinate.x()) + && (track_start.y() == side_coordinate.y()) + && (OUT_PORT == get_chan_node_direction(node_side, track_id))) { + /* Double check: start track should be an OUTPUT PORT of the GSB */ + return false; /* This is a starting point */ + } + + /* Get the coordinate of where the track ends */ + vtr::Point track_end = get_track_rr_node_end_coordinate(rr_graph, track_node); + + /* INC_DIRECTION end_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + /* DEC_DIRECTION end_track: (xlow, ylow) should be same as the GSB side coordinate */ + if ((track_end.x() == side_coordinate.x()) + && (track_end.y() == side_coordinate.y()) + && (IN_PORT == get_chan_node_direction(node_side, track_id))) { + /* Double check: end track should be an INPUT PORT of the GSB */ + return false; /* This is an ending point */ + } + + /* Reach here it means that this will be a passing wire, + * we should be able to find the node on the opposite side of the GSB! + */ + if (true != is_sb_node_exist_opposite_side(rr_graph, track_node, node_side)) { + VTR_LOG("Cannot find a node on the opposite side to GSB[%lu][%lu] track node[%lu] at %s!\nDetailed node information:\n", + get_x(), get_y(), track_id, SIDE_STRING[node_side]); + VTR_LOG("Node type: %s\n", rr_graph.node_type_string(track_node)); + VTR_LOG("Node coordinate: %d\n", rr_graph.node_coordinate_to_string(track_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(track_node)); + } + VTR_ASSERT(true == is_sb_node_exist_opposite_side(rr_graph, track_node, node_side)); + + return true; +} + +/* check if the candidate SB satisfy the basic requirements on being a mirror of the current one */ +/* Idenify mirror Switch blocks + * Check each two switch blocks: + * Number of channel/opin/ipin rr_nodes are same + * If all above are satisfied, the two switch blocks may be mirrors ! + */ +bool RRGSB::is_sb_mirrorable(const RRGraphView& rr_graph, const RRGSB& cand) const { + /* check the numbers of sides */ + if (get_num_sides() != cand.get_num_sides()) { + return false; + } + + /* check the numbers/directionality of channel rr_nodes */ + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + + /* Ensure we have the same channel width on this side */ + if (get_chan_width(side_manager.get_side()) != cand.get_chan_width(side_manager.get_side())) { + return false; + } + + if (((size_t(-1) == get_track_id_first_short_connection(rr_graph, side_manager.get_side())) + && (size_t(-1) != cand.get_track_id_first_short_connection(rr_graph, side_manager.get_side()))) + || ((size_t(-1) != get_track_id_first_short_connection(rr_graph, side_manager.get_side())) + && (size_t(-1) == cand.get_track_id_first_short_connection(rr_graph, side_manager.get_side())))) { + return false; + } + } + + /* check the numbers of opin_rr_nodes */ + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + + if (get_num_opin_nodes(side_manager.get_side()) != cand.get_num_opin_nodes(side_manager.get_side())) { + return false; + } + } + + return true; +} + +/* Public Accessors: Cooridinator conversion */ + +/* get the x coordinate of this GSB */ +size_t RRGSB::get_x() const { + return coordinate_.x(); +} + +/* get the y coordinate of this GSB */ +size_t RRGSB::get_y() const { + return coordinate_.y(); +} + +/* get the x coordinate of this switch block */ +size_t RRGSB::get_sb_x() const { + return coordinate_.x(); +} + +/* get the y coordinate of this switch block */ +size_t RRGSB::get_sb_y() const { + return coordinate_.y(); +} + +/* Get the number of sides of this SB */ +vtr::Point RRGSB::get_sb_coordinate() const { + return coordinate_; +} + +/* get the x coordinate of this X/Y-direction block */ +size_t RRGSB::get_cb_x(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case CHANX: + return get_side_block_coordinate(LEFT).x(); + case CHANY: + return get_side_block_coordinate(TOP).x(); + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +/* get the y coordinate of this X/Y-direction block */ +size_t RRGSB::get_cb_y(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case CHANX: + return get_side_block_coordinate(LEFT).y(); + case CHANY: + return get_side_block_coordinate(TOP).y(); + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +/* Get the coordinate of the X/Y-direction CB */ +vtr::Point RRGSB::get_cb_coordinate(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case CHANX: + return get_side_block_coordinate(LEFT); + case CHANY: + return get_side_block_coordinate(TOP); + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +e_side RRGSB::get_cb_chan_side(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case CHANX: + return LEFT; + case CHANY: + return TOP; + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +/* Get the side of routing channel in the GSB according to the side of IPIN */ +e_side RRGSB::get_cb_chan_side(const e_side& ipin_side) const { + switch (ipin_side) { + case TOP: + return LEFT; + case RIGHT: + return TOP; + case BOTTOM: + return LEFT; + case LEFT: + return TOP; + default: + VTR_LOG("Invalid type of ipin_side!\n"); + exit(1); + } +} + +vtr::Point RRGSB::get_side_block_coordinate(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + vtr::Point ret(get_sb_x(), get_sb_y()); + + switch (side_manager.get_side()) { + case TOP: + /* (0 == side) */ + /* 1. Channel Y [x][y+1] inputs */ + ret.set_y(ret.y() + 1); + break; + case RIGHT: + /* 1 == side */ + /* 2. Channel X [x+1][y] inputs */ + ret.set_x(ret.x() + 1); + break; + case BOTTOM: + /* 2 == side */ + /* 3. Channel Y [x][y] inputs */ + break; + case LEFT: + /* 3 == side */ + /* 4. Channel X [x][y] inputs */ + break; + default: + VTR_LOG(" Invalid side!\n"); + exit(1); + } + + return ret; +} + +vtr::Point RRGSB::get_grid_coordinate() const { + vtr::Point ret(get_sb_x(), get_sb_y()); + ret.set_y(ret.y() + 1); + + return ret; +} + +/************************************************************************ + * Public Mutators + ***********************************************************************/ +/* get a copy from a source */ +void RRGSB::set(const RRGSB& src) { + /* Copy coordinate */ + this->set_coordinate(src.get_sb_coordinate().x(), src.get_sb_coordinate().y()); + + /* Initialize sides */ + this->init_num_sides(src.get_num_sides()); + + /* Copy vectors */ + for (size_t side = 0; side < src.get_num_sides(); ++side) { + SideManager side_manager(side); + /* Copy chan_nodes */ + /* skip if there is no channel width */ + if (0 < src.get_chan_width(side_manager.get_side())) { + this->chan_node_[side_manager.get_side()].set(src.chan_node_[side_manager.get_side()]); + /* Copy chan_node_direction_*/ + this->chan_node_direction_[side_manager.get_side()].clear(); + for (size_t inode = 0; inode < src.get_chan_width(side_manager.get_side()); ++inode) { + this->chan_node_direction_[side_manager.get_side()].push_back(src.get_chan_node_direction(side_manager.get_side(), inode)); + } + } + + /* Copy opin_node and opin_node_grid_side_ */ + this->opin_node_[side_manager.get_side()].clear(); + for (size_t inode = 0; inode < src.get_num_opin_nodes(side_manager.get_side()); ++inode) { + this->opin_node_[side_manager.get_side()].push_back(src.get_opin_node(side_manager.get_side(), inode)); + } + + /* Copy ipin_node and ipin_node_grid_side_ */ + this->ipin_node_[side_manager.get_side()].clear(); + for (size_t inode = 0; inode < src.get_num_ipin_nodes(side_manager.get_side()); ++inode) { + this->ipin_node_[side_manager.get_side()].push_back(src.get_ipin_node(side_manager.get_side(), inode)); + } + } +} + +/* Set the coordinate (x,y) for the switch block */ +void RRGSB::set_coordinate(const size_t& x, const size_t& y) { + coordinate_.set(x, y); +} + +/* Allocate the vectors with the given number of sides */ +void RRGSB::init_num_sides(const size_t& num_sides) { + /* Initialize the vectors */ + chan_node_.resize(num_sides); + chan_node_direction_.resize(num_sides); + ipin_node_.resize(num_sides); + opin_node_.resize(num_sides); +} + +/* Add a node to the chan_node_ list and also assign its direction in chan_node_direction_ */ +void RRGSB::add_chan_node(const e_side& node_side, + const RRChan& rr_chan, + const std::vector& rr_chan_dir) { + /* Validate: 1. side is valid, the type of node is valid */ + VTR_ASSERT(validate_side(node_side)); + + /* fill the dedicated element in the vector */ + chan_node_[size_t(node_side)].set(rr_chan); + chan_node_direction_[size_t(node_side)].resize(rr_chan_dir.size()); + for (size_t inode = 0; inode < rr_chan_dir.size(); ++inode) { + chan_node_direction_[size_t(node_side)][inode] = rr_chan_dir[inode]; + } +} + +/* Add a node to the chan_node_ list and also assign its direction in chan_node_direction_ */ +void RRGSB::add_ipin_node(const RRNodeId& node, const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + /* push pack the dedicated element in the vector */ + ipin_node_[size_t(node_side)].push_back(node); +} + +/* Add a node to the chan_node_ list and also assign its direction in chan_node_direction_ */ +void RRGSB::add_opin_node(const RRNodeId& node, const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + /* push pack the dedicated element in the vector */ + opin_node_[size_t(node_side)].push_back(node); +} + +void RRGSB::sort_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& chan_side, + const size_t& track_id) { + std::map> from_grid_edge_map; + std::map> from_track_edge_map; + + const RRNodeId& chan_node = chan_node_[size_t(chan_side)].get_node(track_id); + + /* Count the edges and ensure every of them has been sorted */ + size_t edge_counter = 0; + + /* For each incoming edge, find the node side and index in this GSB. + * and cache these. Then we will use the data to sort the edge in the + * following sequence: + * 0----------------------------------------------------------------> num_in_edges() + * |<--TOP side-->|<--RIGHT side-->|<--BOTTOM side-->|<--LEFT side-->| + * For each side, the edge will be sorted by the node index starting from 0 + * For each side, the edge from grid pins will be the 1st part + * while the edge from routing tracks will be the 2nd part + */ + for (const RREdgeId& edge : rr_graph.node_in_edges(chan_node)) { + /* We care the source node of this edge, and it should be an input of the GSB!!! */ + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + e_side side = NUM_SIDES; + int index = 0; + get_node_side_and_index(rr_graph, src_node, IN_PORT, side, index); + + /* Must have valid side and index */ + if (NUM_SIDES == side) { + VTR_LOG("GSB[%lu][%lu]:\n", get_x(), get_y()); + VTR_LOG("----------------------------------\n"); + VTR_LOG("SRC node:\n"); + VTR_LOG("Node info: %s\n", rr_graph.node_coordinate_to_string(src_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(src_node)); + VTR_LOG("Fan-out nodes:\n"); + for (const auto& temp_edge : rr_graph.edge_range(src_node)) { + VTR_LOG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_sink_node(temp_edge)).c_str()); + } + VTR_LOG("\n----------------------------------\n"); + VTR_LOG("Channel node:\n"); + VTR_LOG("Node info: %s\n", rr_graph.node_coordinate_to_string(chan_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(chan_node)); + VTR_LOG("Fan-in nodes:\n"); + for (const auto& temp_edge : rr_graph.node_in_edges(chan_node)) { + VTR_LOG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_src_node(temp_edge)).c_str()); + } + } + + VTR_ASSERT(NUM_SIDES != side); + VTR_ASSERT(OPEN != index); + + if (OPIN == rr_graph.node_type(src_node)) { + from_grid_edge_map[side][index] = edge; + } else { + VTR_ASSERT((CHANX == rr_graph.node_type(src_node)) + || (CHANY == rr_graph.node_type(src_node))); + from_track_edge_map[side][index] = edge; + } + + edge_counter++; + } + + /* Store the sorted edge */ + for (size_t side = 0; side < get_num_sides(); ++side) { + /* Edges from grid outputs are the 1st part */ + for (size_t opin_id = 0; opin_id < opin_node_[side].size(); ++opin_id) { + if ((0 < from_grid_edge_map.count(side)) + && (0 < from_grid_edge_map.at(side).count(opin_id))) { + chan_node_in_edges_[size_t(chan_side)][track_id].push_back(from_grid_edge_map[side][opin_id]); + } + } + + /* Edges from routing tracks are the 2nd part */ + for (size_t itrack = 0; itrack < chan_node_[side].get_chan_width(); ++itrack) { + if ((0 < from_track_edge_map.count(side)) + && (0 < from_track_edge_map.at(side).count(itrack))) { + chan_node_in_edges_[size_t(chan_side)][track_id].push_back(from_track_edge_map[side][itrack]); + } + } + } + + VTR_ASSERT(edge_counter == chan_node_in_edges_[size_t(chan_side)][track_id].size()); +} + +void RRGSB::sort_chan_node_in_edges(const RRGraphView& rr_graph) { + /* Allocate here, as sort edge is optional, we do not allocate when adding nodes */ + chan_node_in_edges_.resize(get_num_sides()); + + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + chan_node_in_edges_[side].resize(chan_node_[side].get_chan_width()); + for (size_t track_id = 0; track_id < chan_node_[side].get_chan_width(); ++track_id) { + /* Only sort the output nodes and bypass passing wires */ + if ((OUT_PORT == chan_node_direction_[side][track_id]) + && (false == is_sb_node_passing_wire(rr_graph, side_manager.get_side(), track_id))) { + sort_chan_node_in_edges(rr_graph, side_manager.get_side(), track_id); + } + } + } +} + +void RRGSB::sort_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& ipin_side, + const size_t& ipin_id) { + std::map from_track_edge_map; + + e_side chan_side = get_cb_chan_side(ipin_side); + + const RRNodeId& ipin_node = ipin_node_[size_t(ipin_side)][ipin_id]; + + /* Count the edges and ensure every of them has been sorted */ + size_t edge_counter = 0; + + /* For each incoming edge, find the node side and index in this GSB. + * and cache these. Then we will use the data to sort the edge in the + * following sequence: + * 0----------------------------------------------------------------> num_in_edges() + * |<--TOP side-->|<--RIGHT side-->|<--BOTTOM side-->|<--LEFT side-->| + * For each side, the edge will be sorted by the node index starting from 0 + * For each side, the edge from grid pins will be the 1st part + * while the edge from routing tracks will be the 2nd part + */ + for (const RREdgeId& edge : rr_graph.node_in_edges(ipin_node)) { + /* We care the source node of this edge, and it should be an input of the GSB!!! */ + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + /* The driver routing channel node can be either an input or an output to the GSB. + * Just try to find a qualified one. */ + int index = OPEN; + index = get_node_index(rr_graph, src_node, chan_side, IN_PORT); + if (OPEN == index) { + index = get_node_index(rr_graph, src_node, chan_side, OUT_PORT); + } + + /* Must have valid side and index */ + if (OPEN == index) { + VTR_LOG("GSB[%lu][%lu]:\n", get_x(), get_y()); + VTR_LOG("----------------------------------\n"); + VTR_LOG("SRC node:\n"); + VTR_LOG("Node info: %s\n", rr_graph.node_coordinate_to_string(src_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(src_node)); + VTR_LOG("Fan-out nodes:\n"); + for (const auto& temp_edge : rr_graph.edge_range(src_node)) { + VTR_LOG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_sink_node(temp_edge)).c_str()); + } + VTR_LOG("\n----------------------------------\n"); + VTR_LOG("IPIN node:\n"); + VTR_LOG("Node info: %s\n", rr_graph.node_coordinate_to_string(ipin_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(ipin_node)); + VTR_LOG("Fan-in nodes:\n"); + for (const auto& temp_edge : rr_graph.node_in_edges(ipin_node)) { + VTR_LOG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_src_node(temp_edge)).c_str()); + } + } + + VTR_ASSERT(OPEN != index); + + VTR_ASSERT(CHANX == rr_graph.node_type(src_node) || CHANY == rr_graph.node_type(src_node)); + from_track_edge_map[index] = edge; + edge_counter++; + } + + /* Store the sorted edge */ + for (size_t itrack = 0; itrack < chan_node_[size_t(chan_side)].get_chan_width(); ++itrack) { + if (0 < from_track_edge_map.count(itrack)) { + ipin_node_in_edges_[size_t(ipin_side)][ipin_id].push_back(from_track_edge_map[itrack]); + } + } + + VTR_ASSERT(edge_counter == ipin_node_in_edges_[size_t(ipin_side)][ipin_id].size()); +} + +void RRGSB::sort_ipin_node_in_edges(const RRGraphView& rr_graph) { + /* Allocate here, as sort edge is optional, we do not allocate when adding nodes */ + ipin_node_in_edges_.resize(get_num_sides()); + + for (t_rr_type cb_type : {CHANX, CHANY}) { + for (e_side ipin_side : get_cb_ipin_sides(cb_type)) { + SideManager side_manager(ipin_side); + ipin_node_in_edges_[size_t(ipin_side)].resize(ipin_node_[size_t(ipin_side)].size()); + for (size_t ipin_id = 0; ipin_id < ipin_node_[size_t(ipin_side)].size(); ++ipin_id) { + sort_ipin_node_in_edges(rr_graph, side_manager.get_side(), ipin_id); + } + } + } +} + +/************************************************************************ + * Public Mutators: clean-up functions + ***********************************************************************/ +/* Reset the RRGSB to pristine state */ +void RRGSB::clear() { + /* Clean all the vectors */ + VTR_ASSERT(validate_num_sides()); + /* Clear the inner vector of each matrix */ + for (size_t side = 0; side < get_num_sides(); ++side) { + chan_node_direction_[side].clear(); + chan_node_[side].clear(); + ipin_node_[side].clear(); + opin_node_[side].clear(); + } + chan_node_direction_.clear(); + chan_node_.clear(); + ipin_node_.clear(); + opin_node_.clear(); +} + +/* Clean the chan_width of a side */ +void RRGSB::clear_chan_nodes(const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + + chan_node_[size_t(node_side)].clear(); + chan_node_direction_[size_t(node_side)].clear(); +} + +/* Clean the number of IPINs of a side */ +void RRGSB::clear_ipin_nodes(const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + + ipin_node_[size_t(node_side)].clear(); +} + +/* Clean the number of OPINs of a side */ +void RRGSB::clear_opin_nodes(const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + + opin_node_[size_t(node_side)].clear(); +} + +/* Clean chan/opin/ipin nodes at one side */ +void RRGSB::clear_one_side(const e_side& node_side) { + clear_chan_nodes(node_side); + clear_ipin_nodes(node_side); + clear_opin_nodes(node_side); +} + +/************************************************************************ + * Internal Accessors: identify mirrors + ***********************************************************************/ +size_t RRGSB::get_track_id_first_short_connection(const RRGraphView& rr_graph, const e_side& node_side) const { + VTR_ASSERT(validate_side(node_side)); + + /* Walk through chan_nodes and find the first short connection */ + for (size_t inode = 0; inode < get_chan_width(node_side); ++inode) { + if (true == is_sb_node_passing_wire(rr_graph, node_side, inode)) { + return inode; + } + } + + return size_t(-1); +} + +/************************************************************************ + * Internal validators + ***********************************************************************/ +/* Validate if the number of sides are consistent among internal data arrays ! */ +bool RRGSB::validate_num_sides() const { + size_t num_sides = chan_node_direction_.size(); + + if (num_sides != chan_node_.size()) { + return false; + } + + if (num_sides != ipin_node_.size()) { + return false; + } + + if (num_sides != opin_node_.size()) { + return false; + } + + return true; +} + +/* Check if the side valid in the context: does the switch block have the side? */ +bool RRGSB::validate_side(const e_side& side) const { + return (size_t(side) < get_num_sides()); +} + +/* Check the track_id is valid for chan_node_ and chan_node_direction_ */ +bool RRGSB::validate_track_id(const e_side& side, const size_t& track_id) const { + if (false == validate_side(side)) { + return false; + } + + return ((track_id < chan_node_[size_t(side)].get_chan_width()) + && (track_id < chan_node_direction_[size_t(side)].size())); +} + +/* Check the opin_node_id is valid for opin_node_ and opin_node_grid_side_ */ +bool RRGSB::validate_opin_node_id(const e_side& side, const size_t& node_id) const { + if (false == validate_side(side)) { + return false; + } + return (node_id < opin_node_[size_t(side)].size()); +} + +/* Check the ipin_node_id is valid for opin_node_ and opin_node_grid_side_ */ +bool RRGSB::validate_ipin_node_id(const e_side& side, const size_t& node_id) const { + if (false == validate_side(side)) { + return false; + } + return (node_id < ipin_node_[size_t(side)].size()); +} + +bool RRGSB::validate_cb_type(const t_rr_type& cb_type) const { + return ((CHANX == cb_type) || (CHANY == cb_type)); +} diff --git a/vpr/src/tileable_rr_graph/rr_gsb.h b/vpr/src/tileable_rr_graph/rr_gsb.h new file mode 100644 index 00000000000..77f7b192cd0 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_gsb.h @@ -0,0 +1,260 @@ +#ifndef RR_GSB_H +#define RR_GSB_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +#include "rr_chan.h" +#include "rr_graph_view.h" + +/******************************************************************** + * Object Generic Switch Block + * This block contains + * 1. A switch block + * 2. A X-direction Connection block locates at the left side of the switch block + * 2. A Y-direction Connection block locates at the top side of the switch block + * + * +-------------+ +---------------------------------+ + * | | | Y-direction CB | + * | Grid | | [x][y + 1] | + * | [x][y+1] | +---------------------------------+ + * +-------------+ + * TOP SIDE + * +-------------+ +---------------------------------+ + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | X-direction | | | + * | CB | LEFT SIDE | Switch Block | RIGHT SIDE + * | [x][y] | | [x][y] | + * | | | | + * | | | CHAN_NODES CHAN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * +-------------+ +---------------------------------+ + * BOTTOM SIDE + * num_sides: number of sides of this switch block + * chan_rr_node: a collection of rr_nodes as routing tracks locating at each side of the Switch block <0..num_sides-1><0..chan_width-1> + * chan_rr_node_direction: Indicate if this rr_node is an input or an output of the Switch block <0..num_sides-1><0..chan_width-1> + * ipin_rr_node: a collection of rr_nodes as IPIN of a GRID locating at each side of the Switch block <0..num_sides-1><0..num_ipin_rr_nodes-1> + * ipin_rr_node_grid_side: specify the side of the input pins on which side of a GRID <0..num_sides-1><0..num_ipin_rr_nodes-1> + * opin_rr_node: a collection of rr_nodes as OPIN of a GRID locating at each side of the Switch block <0..num_sides-1><0..num_opin_rr_nodes-1> + * opin_rr_node_grid_side: specify the side of the output pins on which side of a GRID <0..num_sides-1><0..num_opin_rr_nodes-1> + * num_reserved_conf_bits: number of reserved configuration bits this switch block requires (mainly due to RRAM-based multiplexers) + * num_conf_bits: number of configuration bits this switch block requires + *******************************************************************/ +class RRGSB { + public: /* Contructors */ + RRGSB(); /* Default constructor */ + public: /* Accessors */ + /* Get the number of sides of this SB */ + size_t get_num_sides() const; + + /* Get the number of routing tracks on a side */ + size_t get_chan_width(const e_side& side) const; + + /* Get the type of routing tracks on a side */ + t_rr_type get_chan_type(const e_side& side) const; + + /* Get the maximum number of routing tracks on all sides */ + size_t get_max_chan_width() const; + + /* Get the number of routing tracks of a X/Y-direction CB */ + size_t get_cb_chan_width(const t_rr_type& cb_type) const; + + /* Return read-only object of the routing channels with a given side */ + const RRChan& chan(const e_side& chan_side) const; + + /* Get the sides of CB ipins in the array */ + std::vector get_cb_ipin_sides(const t_rr_type& cb_type) const; + + /* Get the direction of a rr_node at a given side and track_id */ + enum PORTS get_chan_node_direction(const e_side& side, const size_t& track_id) const; + + /* Get a list of segments used in this routing channel */ + std::vector get_chan_segment_ids(const e_side& side) const; + + /* Get a list of segments used in this routing channel */ + std::vector get_chan_node_ids_by_segment_ids(const e_side& side, + const RRSegmentId& seg_id) const; + + /* get a rr_node at a given side and track_id */ + RRNodeId get_chan_node(const e_side& side, const size_t& track_id) const; + + /* get all the sorted incoming edges for a rr_node at a given side and track_id */ + std::vector get_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& track_id) const; + + /* get all the sorted incoming edges for a IPIN rr_node at a given side and ipin_id */ + std::vector get_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& ipin_id) const; + + /* get the segment id of a channel rr_node */ + RRSegmentId get_chan_node_segment(const e_side& side, const size_t& track_id) const; + + /* Get the number of IPIN rr_nodes on a side */ + size_t get_num_ipin_nodes(const e_side& side) const; + + /* get a rr_node at a given side and track_id */ + RRNodeId get_ipin_node(const e_side& side, const size_t& node_id) const; + + /* Get the number of OPIN rr_nodes on a side */ + size_t get_num_opin_nodes(const e_side& side) const; + + /* get a rr_node at a given side and track_id */ + RRNodeId get_opin_node(const e_side& side, const size_t& node_id) const; + + int get_cb_chan_node_index(const t_rr_type& cb_type, const RRNodeId& node) const; + + int get_chan_node_index(const e_side& node_side, const RRNodeId& node) const; + + /* Get the node index in the array, return -1 if not found */ + int get_node_index(const RRGraphView& rr_graph, const RRNodeId& node, const e_side& node_side, const PORTS& node_direction) const; + + /* Given a rr_node, try to find its side and index in the Switch block */ + void get_node_side_and_index(const RRGraphView& rr_graph, const RRNodeId& node, const PORTS& node_direction, e_side& node_side, int& node_index) const; + + /* Check if the node exist in the opposite side of this Switch Block */ + bool is_sb_node_exist_opposite_side(const RRGraphView& rr_graph, const RRNodeId& node, const e_side& node_side) const; + + public: /* Accessors: to identify mirrors */ + /* check if the connect block exists in the GSB */ + bool is_cb_exist(const t_rr_type& cb_type) const; + + /* check if the switch block exists in the GSB */ + bool is_sb_exist() const; + + /* Check if the node imply a short connection inside the SB, which happens to long wires across a FPGA fabric */ + bool is_sb_node_passing_wire(const RRGraphView& rr_graph, const e_side& node_side, const size_t& track_id) const; + + /* check if the candidate SB satisfy the basic requirements + * on being a mirror of the current one + */ + bool is_sb_mirrorable(const RRGraphView& rr_graph, const RRGSB& cand) const; + + public: /* Cooridinator conversion and output */ + size_t get_x() const; /* get the x coordinate of this switch block */ + size_t get_y() const; /* get the y coordinate of this switch block */ + size_t get_sb_x() const; /* get the x coordinate of this switch block */ + size_t get_sb_y() const; /* get the y coordinate of this switch block */ + vtr::Point get_sb_coordinate() const; /* Get the coordinate of the SB */ + size_t get_cb_x(const t_rr_type& cb_type) const; /* get the x coordinate of this X/Y-direction block */ + size_t get_cb_y(const t_rr_type& cb_type) const; /* get the y coordinate of this X/Y-direction block */ + vtr::Point get_cb_coordinate(const t_rr_type& cb_type) const; /* Get the coordinate of the X/Y-direction CB */ + e_side get_cb_chan_side(const t_rr_type& cb_type) const; /* get the side of a Connection block */ + e_side get_cb_chan_side(const e_side& ipin_side) const; /* get the side of a Connection block */ + vtr::Point get_side_block_coordinate(const e_side& side) const; + vtr::Point get_grid_coordinate() const; + + public: /* Mutators */ + /* get a copy from a source */ + void set(const RRGSB& src); + void set_coordinate(const size_t& x, const size_t& y); + + /* Allocate the vectors with the given number of sides */ + void init_num_sides(const size_t& num_sides); + + /* Add a node to the chan_rr_node_ list and also + * assign its direction in chan_rr_node_direction_ + */ + void add_chan_node(const e_side& node_side, + const RRChan& rr_chan, + const std::vector& rr_chan_dir); + + /* Add a node to the chan_rr_node_ list and also + * assign its direction in chan_rr_node_direction_ + */ + void add_ipin_node(const RRNodeId& node, + const e_side& node_side); + + /* Add a node to the chan_rr_node_ list and also + * assign its direction in chan_rr_node_direction_ + */ + void add_opin_node(const RRNodeId& node, + const e_side& node_side); + + /* Sort all the incoming edges for routing channel rr_node */ + void sort_chan_node_in_edges(const RRGraphView& rr_graph); + /* Sort all the incoming edges for input pin rr_node */ + void sort_ipin_node_in_edges(const RRGraphView& rr_graph); + + public: /* Mutators: cleaners */ + void clear(); + + /* Clean the chan_width of a side */ + void clear_chan_nodes(const e_side& node_side); + + /* Clean the number of IPINs of a side */ + void clear_ipin_nodes(const e_side& node_side); + + /* Clean the number of OPINs of a side */ + void clear_opin_nodes(const e_side& node_side); + + /* Clean chan/opin/ipin nodes at one side */ + void clear_one_side(const e_side& node_side); + + private: /* Private Mutators: edge sorting */ + /* Sort all the incoming edges for one channel rr_node */ + void sort_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& chan_side, + const size_t& track_id); + + /* Sort all the incoming edges for one input pin rr_node */ + void sort_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& chan_side, + const size_t& ipin_id); + + private: /* internal functions */ + size_t get_track_id_first_short_connection(const RRGraphView& rr_graph, const e_side& node_side) const; + + private: /* internal validators */ + bool validate_num_sides() const; + bool validate_side(const e_side& side) const; + bool validate_track_id(const e_side& side, const size_t& track_id) const; + bool validate_opin_node_id(const e_side& side, const size_t& node_id) const; + bool validate_ipin_node_id(const e_side& side, const size_t& node_id) const; + bool validate_cb_type(const t_rr_type& cb_type) const; + + private: /* Internal Data */ + /* Coordinator */ + vtr::Point coordinate_; + + /* Routing channel data + * Each GSB may have four sides of routing track nodes + */ + /* Node id in rr_graph denoting each routing track */ + std::vector chan_node_; + + /* Direction of a port when the channel node appear in the GSB module */ + std::vector> chan_node_direction_; + + /* Sequence of edge ids for each routing channel node, + * this is sorted by the location of edge source nodes in the context of GSB + * The edge sorting is critical to uniquify the routing modules in OpenFPGA + * This is due to that VPR allocate and sort edges randomly when building the rr_graph + * As a result, previous nodes of a chan node may be the same in different GSBs + * but their sequence is not. This will cause graph comparison to fail when uniquifying + * the routing modules. Therefore, edge sorting can be done inside the GSB + * + * Storage organization: + * [chan_side][chan_node][edge_id_in_gsb_context] + */ + std::vector>> chan_node_in_edges_; + /* Sequence of edge ids for each input pin node. Same rules applied as the channel nodes */ + std::vector>> ipin_node_in_edges_; + + /* Logic Block Inputs data */ + std::vector> ipin_node_; + + /* Logic Block Outputs data */ + std::vector> opin_node_; +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_chan_details_builder.cpp b/vpr/src/tileable_rr_graph/tileable_chan_details_builder.cpp new file mode 100644 index 00000000000..1dbdb7a3917 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_chan_details_builder.cpp @@ -0,0 +1,229 @@ +/************************************************************************ + * This file contains a builder for the ChanNodeDetails data structure + * Different from VPR rr_graph builders, this builder aims to create a + * highly regular routing channel. Thus, it is called tileable, + * which brings significant advantage in producing large FPGA fabrics. + ***********************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "rr_graph_builder_utils.h" +#include "tileable_chan_details_builder.h" + +/************************************************************************ + * Generate the number of tracks for each types of routing segments + * w.r.t. the frequency of each of segments and channel width + * Note that if we dertermine the number of tracks per type using + * chan_width * segment_frequency / total_freq may cause + * The total track num may not match the chan_width, + * therefore, we assign tracks one by one until we meet the frequency requirement + * In this way, we can assign the number of tracks with repect to frequency + ***********************************************************************/ +std::vector get_num_tracks_per_seg_type(const size_t& chan_width, + const std::vector& segment_inf, + const bool& use_full_seg_groups) { + std::vector result; + std::vector demand; + /* Make sure a clean start */ + result.resize(segment_inf.size()); + demand.resize(segment_inf.size()); + + /* Scale factor so we can divide by any length + * and still use integers */ + /* Get the sum of frequency */ + size_t scale = 1; + size_t freq_sum = 0; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + scale *= segment_inf[iseg].length; + freq_sum += segment_inf[iseg].frequency; + } + size_t reduce = scale * freq_sum; + + /* Init assignments to 0 and set the demand values */ + /* Get the fraction of each segment type considering the frequency: + * num_track_per_seg = chan_width * (freq_of_seg / sum_freq) + */ + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + result[iseg] = 0; + demand[iseg] = scale * chan_width * segment_inf[iseg].frequency; + if (true == use_full_seg_groups) { + demand[iseg] /= segment_inf[iseg].length; + } + } + + /* check if the sum of num_tracks, matches the chan_width */ + /* Keep assigning tracks until we use them up */ + size_t assigned = 0; + size_t size = 0; + size_t imax = 0; + while (assigned < chan_width) { + /* Find current maximum demand */ + double max = 0; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + if (demand[iseg] > max) { + imax = iseg; + } + max = std::max(demand[iseg], max); + } + + /* Assign tracks to the type and reduce the types demand */ + size = (use_full_seg_groups ? segment_inf[imax].length : 1); + demand[imax] -= reduce; + result[imax] += size; + assigned += size; + } + + /* Undo last assignment if we were closer to goal without it */ + if ((assigned - chan_width) > (size / 2)) { + result[imax] -= size; + } + + return result; +} + +/************************************************************************ + * Adapt the number of channel width to a tileable routing architecture + ***********************************************************************/ +int adapt_to_tileable_route_chan_width(const int& chan_width, + const std::vector& segment_infs) { + int tileable_chan_width = 0; + + /* Estimate the number of segments per type by the given ChanW*/ + std::vector num_tracks_per_seg_type = get_num_tracks_per_seg_type(chan_width, + segment_infs, + true); /* Force to use the full segment group */ + /* Sum-up the number of tracks */ + for (size_t iseg = 0; iseg < num_tracks_per_seg_type.size(); ++iseg) { + tileable_chan_width += num_tracks_per_seg_type[iseg]; + } + + return tileable_chan_width; +} + +/************************************************************************ + * Build details of routing tracks in a channel + * The function will + * 1. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * + * 2. The starting point of each segment in the channel will be assigned + * For each segment group with same directionality (tracks have the same length), + * every L track will be a starting point (where L denotes the length of segments) + * In this case, if the number of tracks is not a multiple of L, + * indeed we may have some | Yes | No | + * +---------------------------------------+--------------+ + * | 1 | <--------MUX | Yes | No | + * +---------------------------------------+--------------+ + * | 2 | --------> | No | No | + * +---------------------------------------+--------------+ + * | 3 | <-------- | No | No | + * +---------------------------------------+--------------+ + * | 4 | --------> | No | No | + * +---------------------------------------+--------------+ + * | 5 | <-------- | No | No | + * +---------------------------------------+--------------+ + * | 7 | -------->MUX | No | Yes | + * +---------------------------------------+--------------+ + * | 8 | MUX<-------- | No | Yes | + * +---------------------------------------+--------------+ + * | 9 | MUX--------> | Yes | No | + * +---------------------------------------+--------------+ + * | 10 | <--------MUX | Yes | No | + * +---------------------------------------+--------------+ + * | 11 | -------->MUX | No | Yes | + * +------------------------------------------------------+ + * | 12 | <-------- | No | No | + * +------------------------------------------------------+ + * + * 3. SPECIAL for fringes: TOP|RIGHT|BOTTOM|RIGHT + * if device_side is NUM_SIDES, we assume this channel does not locate on borders + * All segments will start and ends with no exception + * + * 4. IMPORTANT: we should be aware that channel width maybe different + * in X-direction and Y-direction channels!!! + * So we will load segment details for different channels + ***********************************************************************/ +ChanNodeDetails build_unidir_chan_node_details(const size_t& chan_width, + const size_t& max_seg_length, + const bool& force_start, + const bool& force_end, + const std::vector& segment_inf) { + ChanNodeDetails chan_node_details; + size_t actual_chan_width = find_unidir_routing_channel_width(chan_width); + VTR_ASSERT(0 == actual_chan_width % 2); + + /* Reserve channel width */ + chan_node_details.reserve(chan_width); + /* Return if zero width is forced */ + if (0 == actual_chan_width) { + return chan_node_details; + } + + /* Find the number of segments required by each group */ + std::vector num_tracks = get_num_tracks_per_seg_type(actual_chan_width / 2, segment_inf, false); + + /* Add node to ChanNodeDetails */ + size_t cur_track = 0; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + /* segment length will be set to maxium segment length if this is a longwire */ + size_t seg_len = segment_inf[iseg].length; + if (true == segment_inf[iseg].longline) { + seg_len = max_seg_length; + } + for (size_t itrack = 0; itrack < num_tracks[iseg]; ++itrack) { + bool seg_start = false; + bool seg_end = false; + /* Every first track of a group of Length-N wires, we set a starting point */ + if (0 == itrack % seg_len) { + seg_start = true; + } + /* Every last track of a group of Length-N wires or this is the last track in this group, we set an ending point */ + if ((seg_len - 1 == itrack % seg_len) + || (itrack == num_tracks[iseg] - 1)) { + seg_end = true; + } + /* Since this is a unidirectional routing architecture, + * Add a pair of tracks, 1 INC track and 1 DEC track + */ + chan_node_details.add_track(cur_track, Direction::INC, iseg, seg_len, seg_start, seg_end); + cur_track++; + chan_node_details.add_track(cur_track, Direction::DEC, iseg, seg_len, seg_start, seg_end); + cur_track++; + } + } + /* Check if all the tracks have been satisified */ + VTR_ASSERT(cur_track == actual_chan_width); + + /* If this is on the border of a device/heterogeneous blocks, segments should start/end */ + if (true == force_start) { + /* INC should all start */ + chan_node_details.set_tracks_start(Direction::INC); + /* DEC should all end */ + chan_node_details.set_tracks_end(Direction::DEC); + } + + /* If this is on the border of a device/heterogeneous blocks, segments should start/end */ + if (true == force_end) { + /* INC should all end */ + chan_node_details.set_tracks_end(Direction::INC); + /* DEC should all start */ + chan_node_details.set_tracks_start(Direction::DEC); + } + + return chan_node_details; +} diff --git a/vpr/src/tileable_rr_graph/tileable_chan_details_builder.h b/vpr/src/tileable_rr_graph/tileable_chan_details_builder.h new file mode 100644 index 00000000000..5ce79b61221 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_chan_details_builder.h @@ -0,0 +1,27 @@ +#ifndef TILEABLE_CHAN_DETAILS_BUILDER_H +#define TILEABLE_CHAN_DETAILS_BUILDER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "physical_types.h" +#include "chan_node_details.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +std::vector get_num_tracks_per_seg_type(const size_t& chan_width, + const std::vector& segment_inf, + const bool& use_full_seg_groups); + +int adapt_to_tileable_route_chan_width(const int& chan_width, const std::vector& segment_inf); + +ChanNodeDetails build_unidir_chan_node_details(const size_t& chan_width, + const size_t& max_seg_length, + const bool& force_start, + const bool& force_end, + const std::vector& segment_inf); + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.cpp new file mode 100644 index 00000000000..bcb8c7b0690 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.cpp @@ -0,0 +1,312 @@ +/************************************************************************ + * This file contains a builder for the complex rr_graph data structure + * Different from VPR rr_graph builders, this builder aims to create a + * highly regular rr_graph, where each Connection Block (CB), Switch + * Block (SB) is the same (except for those on the borders). Thus, the + * rr_graph is called tileable, which brings significant advantage in + * producing large FPGA fabrics. + ***********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_time.h" +#include "vtr_log.h" +#include "vtr_memory.h" + +#include "vpr_error.h" +#include "vpr_utils.h" + +#include "rr_graph.h" +#include "check_rr_graph.h" +#include "get_parallel_segs.h" + +#include "rr_graph_builder_utils.h" +#include "tileable_chan_details_builder.h" +#include "tileable_rr_graph_node_builder.h" +#include "tileable_rr_graph_edge_builder.h" +#include "tileable_rr_graph_builder.h" + +#include "globals.h" + +/************************************************************************ + * Main function of this file + * Builder for a detailed uni-directional tileable rr_graph + * Global graph is not supported here, the VPR rr_graph generator can be used + * It follows the procedures to complete the rr_graph generation + * 1. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * 2. Estimate the number of nodes in the rr_graph + * This will estimate the number of + * a. IPINs, input pins of each grid + * b. OPINs, output pins of each grid + * c. SOURCE, virtual node which drives OPINs + * d. SINK, virtual node which is connected to IPINs + * e. CHANX and CHANY, routing segments of each channel + * 3. Create the connectivity of OPINs + * a. Evenly assign connections to OPINs to routing tracks + * b. the connection pattern should be same across the fabric + * 4. Create the connectivity of IPINs + * a. Evenly assign connections from routing tracks to IPINs + * b. the connection pattern should be same across the fabric + * 5. Create the switch block patterns, + * It is based on the type of switch block, the supported patterns are + * a. Disjoint, which connects routing track (i)th from (i)th and (i)th routing segments + * b. Universal, which connects routing track (i)th from (i)th and (M-i)th routing segments + * c. Wilton, which rotates the connection of Disjoint by 1 track + * 6. Allocate rr_graph, fill the node information + * For each node, fill + * a. basic information: coordinate(xlow, xhigh, ylow, yhigh), ptc_num + * b. edges (both incoming and outcoming) + * c. handle direct-connections + * 7. Build fast look-up for the rr_graph + * 8. Allocate external data structures + * a. cost_index + * b. RC tree + ***********************************************************************/ + +void build_tileable_unidir_rr_graph(const std::vector& types, + const DeviceGrid& grids, + const t_chan_width& chan_width, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const std::vector& segment_inf, + const int& delayless_switch, + const int& wire_to_arch_ipin_switch, + const float R_minW_nmos, + const float R_minW_pmos, + const enum e_base_cost_type& base_cost_type, + const t_direct_inf* directs, + const int& num_directs, + int* wire_to_rr_ipin_switch, + const bool& through_channel, + const bool& wire_opposite_side, + int* Warnings) { + vtr::ScopedStartFinishTimer timer("Build tileable routing resource graph"); + + /* Reset warning flag */ + *Warnings = RR_GRAPH_NO_WARN; + + /* Create a matrix of grid */ + /* Create a vector of channel width, we support X-direction and Y-direction has different W */ + vtr::Point device_chan_width(chan_width.x_max, chan_width.y_max); + + VTR_LOG("X-direction routing channel width is %lu\n", device_chan_width.x()); + VTR_LOG("Y-direction routing channel width is %lu\n", device_chan_width.y()); + + /* Get a mutable device ctx so that we have a mutable rr_graph */ + DeviceContext& device_ctx = g_vpr_ctx.mutable_device(); + + /* The number of segments are in general small, reserve segments may not bring + * significant memory efficiency */ + device_ctx.rr_graph_builder.reserve_segments(segment_inf.size()); + /* Create the segments */ + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + device_ctx.rr_graph_builder.add_rr_segment(segment_inf[iseg]); + } + + /* TODO: Load architecture switch to rr_graph switches + * Draft the switches as internal data of RRGraph object + * These are temporary switches copied from arch switches + * We use them to build the edges + * We will reset all the switches in the function + * alloc_and_load_rr_switch_inf() + */ + /* TODO: Spot the switch id in the architecture switch list */ + RRSwitchId wire_to_ipin_rr_switch = RRSwitchId::INVALID(); + RRSwitchId delayless_rr_switch = RRSwitchId::INVALID(); + + device_ctx.rr_graph_builder.reserve_switches(device_ctx.arch_switch_inf.size()); + /* Create the switches */ + for (size_t iswitch = 0; iswitch < device_ctx.arch_switch_inf.size(); ++iswitch) { + const t_rr_switch_inf& temp_rr_switch = create_rr_switch_from_arch_switch(device_ctx.arch_switch_inf[iswitch], R_minW_nmos, R_minW_pmos); + RRSwitchId rr_switch = device_ctx.rr_graph_builder.add_rr_switch(temp_rr_switch); + if ((int)iswitch == wire_to_arch_ipin_switch) { + wire_to_ipin_rr_switch = rr_switch; + } + if ((int)iswitch == delayless_switch) { + delayless_rr_switch = rr_switch; + } + } + + /* Validate the special switches */ + VTR_ASSERT(true == device_ctx.rr_graph.valid_switch(wire_to_ipin_rr_switch)); + VTR_ASSERT(true == device_ctx.rr_graph.valid_switch(delayless_rr_switch)); + + /* A temp data about the driver switch ids for each rr_node */ + vtr::vector rr_node_driver_switches; + + /* A temp data about the track ids for each CHANX and CHANY rr_node */ + std::map> rr_node_track_ids; + + /************************ + * Allocate the rr_nodes + ************************/ + alloc_tileable_rr_graph_nodes(device_ctx.rr_graph_builder, + rr_node_driver_switches, + grids, + device_chan_width, + segment_inf, + through_channel); + + /************************ + * Create all the rr_nodes + ************************/ + create_tileable_rr_graph_nodes(device_ctx.rr_graph, + device_ctx.rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + device_ctx.rr_rc_data, + grids, + device_chan_width, + segment_inf, + wire_to_ipin_rr_switch, + delayless_rr_switch, + through_channel); + + /************************************************************************ + * Create the connectivity of OPINs + * a. Evenly assign connections to OPINs to routing tracks + * b. the connection pattern should be same across the fabric + * + * Create the connectivity of IPINs + * a. Evenly assign connections from routing tracks to IPINs + * b. the connection pattern should be same across the fabric + ***********************************************************************/ + /* Global routing uses a single longwire track */ + int max_chan_width = find_unidir_routing_channel_width(chan_width.max); + VTR_ASSERT(max_chan_width > 0); + + /* get maximum number of pins across all blocks */ + int max_pins = types[0].num_pins; + for (const auto& type : types) { + if (is_empty_type(&type)) { + continue; + } + + if (type.num_pins > max_pins) { + max_pins = type.num_pins; + } + } + + /* Fc assignment still uses the old function from VPR. + * Should use tileable version so that we have can have full control + */ + std::vector num_tracks = get_num_tracks_per_seg_type(max_chan_width / 2, segment_inf, false); + int* sets_per_seg_type = (int*)vtr::malloc(sizeof(int) * segment_inf.size()); + VTR_ASSERT(num_tracks.size() == segment_inf.size()); + for (size_t iseg = 0; iseg < num_tracks.size(); ++iseg) { + sets_per_seg_type[iseg] = num_tracks[iseg]; + } + + bool Fc_clipped = false; + /* [0..num_types-1][0..num_pins-1] */ + std::vector> Fc_in; + Fc_in = alloc_and_load_actual_fc(types, max_pins, segment_inf, sets_per_seg_type, (const t_chan_width*)&chan_width, + e_fc_type::IN, UNI_DIRECTIONAL, &Fc_clipped, false); + if (Fc_clipped) { + *Warnings |= RR_GRAPH_WARN_FC_CLIPPED; + } + + Fc_clipped = false; + /* [0..num_types-1][0..num_pins-1] */ + std::vector> Fc_out; + Fc_out = alloc_and_load_actual_fc(types, max_pins, segment_inf, sets_per_seg_type, (const t_chan_width*)&chan_width, + e_fc_type::OUT, UNI_DIRECTIONAL, &Fc_clipped, false); + + if (Fc_clipped) { + *Warnings |= RR_GRAPH_WARN_FC_CLIPPED; + } + + /************************************************************************ + * Build the connections tile by tile: + * We classify rr_nodes into a general switch block (GSB) data structure + * where we create edges to each rr_nodes in the GSB with respect to + * Fc_in and Fc_out, switch block patterns + * In addition, we will also handle direct-connections: + * Add edges that bridge OPINs and IPINs to the rr_graph + ***********************************************************************/ + /* Create edges for a tileable rr_graph */ + build_rr_graph_edges(device_ctx.rr_graph, + device_ctx.rr_graph_builder, + rr_node_driver_switches, + grids, + device_chan_width, + segment_inf, + Fc_in, Fc_out, + sb_type, Fs, sb_subtype, subFs, + wire_opposite_side); + + /************************************************************************ + * Build direction connection lists + * TODO: use tile direct builder + ***********************************************************************/ + /* Create data structure of direct-connections */ + t_clb_to_clb_directs* clb_to_clb_directs = NULL; + if (num_directs > 0) { + clb_to_clb_directs = alloc_and_load_clb_to_clb_directs(directs, num_directs, delayless_switch); + } + std::vector arch_directs; + std::vector clb2clb_directs; + for (int idirect = 0; idirect < num_directs; ++idirect) { + arch_directs.push_back(directs[idirect]); + clb2clb_directs.push_back(clb_to_clb_directs[idirect]); + } + + build_rr_graph_direct_connections(device_ctx.rr_graph, device_ctx.rr_graph_builder, device_ctx.grid, delayless_rr_switch, + arch_directs, clb2clb_directs); + + /* Allocate and load routing resource switches, which are derived from the switches from the architecture file, + * based on their fanin in the rr graph. This routine also adjusts the rr nodes to point to these new rr switches */ + device_ctx.rr_graph_builder.init_fan_in(); + alloc_and_load_rr_switch_inf(device_ctx.rr_graph_builder, device_ctx.switch_fanin_remap, device_ctx.all_sw_inf, R_minW_nmos, R_minW_pmos, wire_to_arch_ipin_switch, wire_to_rr_ipin_switch); + + /* Save the channel widths for the newly constructed graph */ + device_ctx.chan_width = chan_width; + + /* Save the track ids for tileable routing resource graph */ + device_ctx.rr_node_track_ids = rr_node_track_ids; + + /* Build incoming edges */ + device_ctx.rr_graph_builder.partition_edges(); + device_ctx.rr_graph_builder.build_in_edges(); + + /************************************************************************ + * Allocate external data structures + * a. cost_index + * b. RC tree + ***********************************************************************/ + t_unified_to_parallel_seg_index segment_index_map; + std::vector segment_inf_x = get_parallel_segs(segment_inf, segment_index_map, X_AXIS); + std::vector segment_inf_y = get_parallel_segs(segment_inf, segment_index_map, Y_AXIS); + rr_graph_externals(segment_inf, segment_inf_x, segment_inf_y, + *wire_to_rr_ipin_switch, base_cost_type); + + /************************************************************************ + * Sanitizer for the rr_graph, check connectivities of rr_nodes + ***********************************************************************/ + /* Essential check for rr_graph, build look-up and */ + if (false == device_ctx.rr_graph_builder.validate()) { + /* Error out if built-in validator of rr_graph fails */ + vpr_throw(VPR_ERROR_ROUTE, + __FILE__, + __LINE__, + "Fundamental errors occurred when validating rr_graph object!\n"); + } + + /* No clock network support yet; Does not support flatten rr_graph yet */ + check_rr_graph(device_ctx.rr_graph, types, device_ctx.rr_indexed_data, grids, device_ctx.chan_width, GRAPH_UNIDIR, OPEN, false); + + /************************************************************************ + * Free all temp stucts + ***********************************************************************/ + free(sets_per_seg_type); + + if (nullptr != clb_to_clb_directs) { + free(clb_to_clb_directs); + } +} diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.h b/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.h new file mode 100644 index 00000000000..c5275e1d0a7 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.h @@ -0,0 +1,36 @@ +#ifndef TILEABLE_RR_GRAPH_BUILDER_H +#define TILEABLE_RR_GRAPH_BUILDER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "physical_types.h" +#include "device_grid.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +void build_tileable_unidir_rr_graph(const std::vector& types, + const DeviceGrid& grids, + const t_chan_width& chan_width, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const std::vector& segment_inf, + const int& delayless_switch, + const int& wire_to_arch_ipin_switch, + const float R_minW_nmos, + const float R_minW_pmos, + const enum e_base_cost_type& base_cost_type, + const t_direct_inf* directs, + const int& num_directs, + int* wire_to_rr_ipin_switch, + const bool& through_channel, + const bool& wire_opposite_side, + int* Warnings); + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp new file mode 100644 index 00000000000..b009874badf --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp @@ -0,0 +1,182 @@ +/************************************************************************ + * This file contains functions that are used to build edges + * between nodes of a tileable routing resource graph + ***********************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +#include "vpr_utils.h" + +#include "rr_graph_builder_utils.h" +#include "tileable_rr_graph_gsb.h" +#include "tileable_rr_graph_edge_builder.h" + +/************************************************************************ + * Build the edges for all the SOURCE and SINKs nodes: + * 1. create edges between SOURCE and OPINs + ***********************************************************************/ +void build_rr_graph_edges_for_source_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + size_t& num_edges_to_create) { + size_t edge_count = 0; + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass all the non OPIN nodes */ + if (OPIN != rr_graph.node_type(node)) { + continue; + } + /* Now, we have an OPIN node, we get the source node index */ + short xlow = rr_graph.node_xlow(node); + short ylow = rr_graph.node_ylow(node); + short src_node_class_num = get_grid_pin_class_index(grids[xlow][ylow], + rr_graph.node_pin_num(node)); + /* Create edges between SOURCE and OPINs */ + RRNodeId src_node = rr_graph.node_lookup().find_node(xlow - grids[xlow][ylow].width_offset, + ylow - grids[xlow][ylow].height_offset, + SOURCE, src_node_class_num); + VTR_ASSERT(true == rr_graph.valid_node(src_node)); + + /* add edges to the src_node */ + rr_graph_builder.create_edge(src_node, node, rr_node_driver_switches[node]); + edge_count++; + } + /* Allocate edges for all the source nodes */ + rr_graph_builder.build_edges(true); + num_edges_to_create += edge_count; +} + +/************************************************************************ + * Build the edges for all the SINKs nodes: + * 1. create edges between IPINs and SINKs + ***********************************************************************/ +void build_rr_graph_edges_for_sink_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + size_t& num_edges_to_create) { + size_t edge_count = 0; + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass all the non IPIN nodes */ + if (IPIN != rr_graph.node_type(node)) { + continue; + } + /* Now, we have an OPIN node, we get the source node index */ + short xlow = rr_graph.node_xlow(node); + short ylow = rr_graph.node_ylow(node); + short sink_node_class_num = get_grid_pin_class_index(grids[xlow][ylow], + rr_graph.node_pin_num(node)); + /* 1. create edges between IPINs and SINKs */ + const RRNodeId& sink_node = rr_graph.node_lookup().find_node(xlow - grids[xlow][ylow].width_offset, + ylow - grids[xlow][ylow].height_offset, + SINK, sink_node_class_num, SIDES[0]); + VTR_ASSERT(true == rr_graph.valid_node(sink_node)); + + /* add edges to connect the IPIN node to SINK nodes */ + rr_graph_builder.create_edge(node, sink_node, rr_node_driver_switches[sink_node]); + edge_count++; + } + /* Allocate edges for all the source nodes */ + rr_graph_builder.build_edges(true); + num_edges_to_create += edge_count; +} + +/************************************************************************ + * Build the edges of each rr_node tile by tile: + * We classify rr_nodes into a general switch block (GSB) data structure + * where we create edges to each rr_nodes in the GSB with respect to + * Fc_in and Fc_out, switch block patterns + * For each GSB: + * 1. create edges between CHANX | CHANY and IPINs (connections inside connection blocks) + * 2. create edges between OPINs, CHANX and CHANY (connections inside switch blocks) + * 3. create edges between OPINs and IPINs (direct-connections) + ***********************************************************************/ +void build_rr_graph_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector>& Fc_in, + const std::vector>& Fc_out, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const bool& wire_opposite_side) { + size_t num_edges_to_create = 0; + /* Create edges for SOURCE and SINK nodes for a tileable rr_graph */ + build_rr_graph_edges_for_source_nodes(rr_graph, rr_graph_builder, rr_node_driver_switches, grids, num_edges_to_create); + build_rr_graph_edges_for_sink_nodes(rr_graph, rr_graph_builder, rr_node_driver_switches, grids, num_edges_to_create); + + vtr::Point gsb_range(grids.width() - 2, grids.height() - 2); + + /* Go Switch Block by Switch Block */ + for (size_t ix = 0; ix <= gsb_range.x(); ++ix) { + for (size_t iy = 0; iy <= gsb_range.y(); ++iy) { + //vpr_printf(TIO_MESSAGE_INFO, "Building edges for GSB[%lu][%lu]\n", ix, iy); + + vtr::Point gsb_coord(ix, iy); + /* Create a GSB object */ + const RRGSB& rr_gsb = build_one_tileable_rr_gsb(grids, rr_graph, + device_chan_width, segment_inf, + gsb_coord); + + /* adapt the track_to_ipin_lookup for the GSB nodes */ + t_track2pin_map track2ipin_map; /* [0..track_gsb_side][0..num_tracks][ipin_indices] */ + track2ipin_map = build_gsb_track_to_ipin_map(rr_graph, rr_gsb, grids, segment_inf, Fc_in); + + /* adapt the opin_to_track_map for the GSB nodes */ + t_pin2track_map opin2track_map; /* [0..gsb_side][0..num_opin_node][track_indices] */ + opin2track_map = build_gsb_opin_to_track_map(rr_graph, rr_gsb, grids, segment_inf, Fc_out); + + /* adapt the switch_block_conn for the GSB nodes */ + t_track2track_map sb_conn; /* [0..from_gsb_side][0..chan_width-1][track_indices] */ + sb_conn = build_gsb_track_to_track_map(rr_graph, rr_gsb, + sb_type, Fs, sb_subtype, subFs, wire_opposite_side, + segment_inf); + + /* Build edges for a GSB */ + build_edges_for_one_tileable_rr_gsb(rr_graph_builder, rr_gsb, + track2ipin_map, opin2track_map, + sb_conn, rr_node_driver_switches, num_edges_to_create); + /* Finish this GSB, go to the next*/ + rr_graph_builder.build_edges(true); + } + } +} + +/************************************************************************ + * Build direct edges for Grids * + ***********************************************************************/ +void build_rr_graph_direct_connections(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const RRSwitchId& delayless_switch, + const std::vector& directs, + const std::vector& clb_to_clb_directs) { + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + /* Skip EMPTY tiles */ + if (true == is_empty_type(grids[ix][iy].type)) { + continue; + } + /* Skip height > 1 or width > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids[ix][iy].width_offset) + || (0 < grids[ix][iy].height_offset)) { + continue; + } + vtr::Point from_grid_coordinate(ix, iy); + build_direct_connections_for_one_gsb(rr_graph, + rr_graph_builder, + grids, + from_grid_coordinate, + delayless_switch, + directs, clb_to_clb_directs); + } + } +} diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.h b/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.h new file mode 100644 index 00000000000..9e56e33ca19 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.h @@ -0,0 +1,56 @@ +#ifndef TILEABLE_RR_GRAPH_EDGE_BUILDER_H +#define TILEABLE_RR_GRAPH_EDGE_BUILDER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_ndmatrix.h" +#include "vtr_geometry.h" + +#include "physical_types.h" +#include "device_grid.h" +#include "rr_graph_obj.h" +#include "rr_graph_view.h" +#include "rr_graph.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +void build_rr_graph_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector>& Fc_in, + const std::vector>& Fc_out, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const bool& wire_opposite_side); + +void build_rr_graph_direct_connections(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const RRSwitchId& delayless_switch, + const std::vector& directs, + const std::vector& clb_to_clb_directs); + +void build_rr_graph_edges_for_source_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + size_t& num_edges_to_create); + +void build_rr_graph_edges_for_sink_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + size_t& num_edges_to_create); + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp new file mode 100644 index 00000000000..e039fbf7418 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp @@ -0,0 +1,1436 @@ +/************************************************************************ + * This file contains a builder for track-to-track connections inside a + * tileable General Switch Block (GSB). + ***********************************************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_side_manager.h" + +#include "vpr_utils.h" +#include "rr_graph_view_util.h" +#include "openfpga_rr_graph_utils.h" +#include "rr_graph_builder_utils.h" +#include "tileable_chan_details_builder.h" +#include "tileable_rr_graph_gsb.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" +#include "vtr_geometry.h" + +/************************************************************************ + * Internal data structures + ***********************************************************************/ +typedef std::vector> t_track_group; + +/************************************************************************ + * A enumeration to list the status of a track inside a GSB + * 1. start; 2. end; 3. passing + * This is used to group tracks which ease the building of + * track-to-track mapping matrix + ***********************************************************************/ +enum e_track_status { + TRACK_START, + TRACK_END, + TRACK_PASS, + NUM_TRACK_STATUS /* just a place holder to get the number of status */ +}; + +/************************************************************************ + * Check if a track starts from this GSB or not + * (xlow, ylow) should be same as the GSB side coordinate + * + * Check if a track ends at this GSB or not + * (xhigh, yhigh) should be same as the GSB side coordinate + ***********************************************************************/ +static enum e_track_status determine_track_status_of_gsb(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const enum e_side& gsb_side, + const size_t& track_id) { + enum e_track_status track_status = TRACK_PASS; + /* Get the rr_node */ + RRNodeId track_node = rr_gsb.get_chan_node(gsb_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); + + /* Get the coordinate of where the track starts */ + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* INC_DIRECTION start_track: (xlow, ylow) should be same as the GSB side coordinate */ + /* DEC_DIRECTION start_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + if ((track_start.x() == side_coordinate.x()) + && (track_start.y() == side_coordinate.y()) + && (OUT_PORT == rr_gsb.get_chan_node_direction(gsb_side, track_id))) { + /* Double check: start track should be an OUTPUT PORT of the GSB */ + track_status = TRACK_START; + } + + /* Get the coordinate of where the track ends */ + vtr::Point track_end = get_track_rr_node_end_coordinate(rr_graph, track_node); + + /* INC_DIRECTION end_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + /* DEC_DIRECTION end_track: (xlow, ylow) should be same as the GSB side coordinate */ + if ((track_end.x() == side_coordinate.x()) + && (track_end.y() == side_coordinate.y()) + && (IN_PORT == rr_gsb.get_chan_node_direction(gsb_side, track_id))) { + /* Double check: end track should be an INPUT PORT of the GSB */ + track_status = TRACK_END; + } + + return track_status; +} + +/************************************************************************ + * Check if the GSB is in the Connection Block (CB) population list of the segment + * SB population of a L4 wire: 1 0 0 1 + * + * +----+ +----+ +----+ +----+ + * | CB |--->| CB |--->| CB |--->| CB | + * +----+ +----+ +----+ +----+ + * Engage CB connection Yes No No Yes + * + * We will find the offset between gsb_side_coordinate and (xlow,ylow) of the track + * Use the offset to check if the tracks should engage in this GSB connection + ***********************************************************************/ +static bool is_gsb_in_track_cb_population(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_side& gsb_side, + const int& track_id, + const std::vector& segment_inf) { + /* Get the rr_node */ + RRNodeId track_node = rr_gsb.get_chan_node(gsb_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); + + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* Get the offset */ + size_t offset = std::abs((int)side_coordinate.x() - (int)track_start.x()) + + std::abs((int)side_coordinate.y() - (int)track_start.y()); + + /* Get segment id */ + RRSegmentId seg_id = rr_gsb.get_chan_node_segment(gsb_side, track_id); + /* validate offset */ + VTR_ASSERT(offset < segment_inf[size_t(seg_id)].cb.size()); + + /* Get the SB population */ + bool in_cb_population = false; + if (true == segment_inf[size_t(seg_id)].cb[offset]) { + in_cb_population = true; + } + + return in_cb_population; +} + +/************************************************************************ + * Check if the GSB is in the Switch Block (SB) population list of the segment + * SB population of a L3 wire: 1 0 0 1 + * + * +----+ +----+ +----+ +----+ + * | SB |--->| SB |--->| SB |--->| SB | + * +----+ +----+ +----+ +----+ + * Engage SB connection Yes No No Yes + * + * We will find the offset between gsb_side_coordinate and (xlow,ylow) of the track + * Use the offset to check if the tracks should engage in this GSB connection + ***********************************************************************/ +static bool is_gsb_in_track_sb_population(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_side& gsb_side, + const int& track_id, + const std::vector& segment_inf) { + /* Get the rr_node */ + const RRNodeId& track_node = rr_gsb.get_chan_node(gsb_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); + + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* Get the offset */ + size_t offset = std::abs((int)side_coordinate.x() - (int)track_start.x()) + + std::abs((int)side_coordinate.y() - (int)track_start.y()); + + /* Get segment id */ + RRSegmentId seg_id = rr_gsb.get_chan_node_segment(gsb_side, track_id); + /* validate offset */ + VTR_ASSERT(offset < segment_inf[size_t(seg_id)].sb.size()); + + /* Get the SB population */ + bool in_sb_population = false; + if (true == segment_inf[size_t(seg_id)].sb[offset]) { + in_sb_population = true; + } + + return in_sb_population; +} + +/************************************************************************ + * Create a list of track_id based on the to_track and num_to_tracks + * We consider the following list [to_track, to_track + Fs/3 - 1] + * if the [to_track + Fs/3 - 1] exceeds the num_to_tracks, we start over from 0! + ***********************************************************************/ +static std::vector get_to_track_list(const int& Fs, const int& to_track, const int& num_to_tracks) { + std::vector to_tracks; + + for (int i = 0; i < Fs; i = i + 3) { + /* TODO: currently, for Fs > 3, I always search the next from_track until Fs is satisfied + * The optimal track selection should be done in a more scientific way!!! + */ + int to_track_i = to_track + i; + /* make sure the track id is still in range */ + if (to_track_i > num_to_tracks - 1) { + to_track_i = to_track_i % num_to_tracks; + } + /* Ensure we are in the range */ + VTR_ASSERT(to_track_i < num_to_tracks); + /* from track must be connected */ + to_tracks.push_back(to_track_i); + } + return to_tracks; +} + +/************************************************************************ + * This function aims to return the track indices that drive the from_track + * in a Switch Block + * The track_ids to return will depend on different topologies of SB + * SUBSET, UNIVERSAL, and WILTON. + ***********************************************************************/ +static std::vector get_switch_block_to_track_id(const e_switch_block_type& switch_block_type, + const int& Fs, + const e_side& from_side, + const int& from_track, + const e_side& to_side, + const int& num_to_tracks) { + /* This routine returns the track number to which the from_track should + * connect. It supports any Fs % 3 == 0, switch blocks. + */ + std::vector to_tracks; + + /* TODO: currently, for Fs > 3, I always search the next from_track until Fs is satisfied + * The optimal track selection should be done in a more scientific way!!! + */ + VTR_ASSERT(0 == Fs % 3); + + /* Adapt from_track to fit in the range of num_to_tracks */ + size_t actual_from_track = from_track % num_to_tracks; + + switch (switch_block_type) { + case SUBSET: /* NB: Global routing uses SUBSET too */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + /* Finish, we return */ + return to_tracks; + case UNIVERSAL: + if ((from_side == LEFT) + || (from_side == RIGHT)) { + /* For the prev_side, to_track is from_track + * For the next_side, to_track is num_to_tracks - 1 - from_track + * For the opposite_side, to_track is always from_track + */ + SideManager side_manager(from_side); + if ((to_side == side_manager.get_opposite()) + || (to_side == side_manager.get_rotate_counterclockwise())) { + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == side_manager.get_rotate_clockwise()) { + to_tracks = get_to_track_list(Fs, num_to_tracks - 1 - actual_from_track, num_to_tracks); + } + } + + if ((from_side == TOP) + || (from_side == BOTTOM)) { + /* For the next_side, to_track is from_track + * For the prev_side, to_track is num_to_tracks - 1 - from_track + * For the opposite_side, to_track is always from_track + */ + SideManager side_manager(from_side); + if ((to_side == side_manager.get_opposite()) + || (to_side == side_manager.get_rotate_clockwise())) { + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == side_manager.get_rotate_counterclockwise()) { + to_tracks = get_to_track_list(Fs, num_to_tracks - 1 - actual_from_track, num_to_tracks); + } + } + /* Finish, we return */ + return to_tracks; + /* End switch_block_type == UNIVERSAL case. */ + case WILTON: + /* See S. Wilton Phd thesis, U of T, 1996 p. 103 for details on following. */ + if (from_side == LEFT) { + if (to_side == RIGHT) { /* CHANX to CHANX */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == TOP) { /* from CHANX to CHANY */ + to_tracks = get_to_track_list(Fs, (num_to_tracks - actual_from_track) % num_to_tracks, num_to_tracks); + } else if (to_side == BOTTOM) { + to_tracks = get_to_track_list(Fs, (num_to_tracks + actual_from_track - 1) % num_to_tracks, num_to_tracks); + } + } else if (from_side == RIGHT) { + if (to_side == LEFT) { /* CHANX to CHANX */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == TOP) { /* from CHANX to CHANY */ + to_tracks = get_to_track_list(Fs, (num_to_tracks + actual_from_track - 1) % num_to_tracks, num_to_tracks); + } else if (to_side == BOTTOM) { + to_tracks = get_to_track_list(Fs, (2 * num_to_tracks - 2 - actual_from_track) % num_to_tracks, num_to_tracks); + } + } else if (from_side == BOTTOM) { + if (to_side == TOP) { /* CHANY to CHANY */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == LEFT) { /* from CHANY to CHANX */ + to_tracks = get_to_track_list(Fs, (actual_from_track + 1) % num_to_tracks, num_to_tracks); + } else if (to_side == RIGHT) { + to_tracks = get_to_track_list(Fs, (2 * num_to_tracks - 2 - actual_from_track) % num_to_tracks, num_to_tracks); + } + } else if (from_side == TOP) { + if (to_side == BOTTOM) { /* CHANY to CHANY */ + to_tracks = get_to_track_list(Fs, from_track, num_to_tracks); + } else if (to_side == LEFT) { /* from CHANY to CHANX */ + to_tracks = get_to_track_list(Fs, (num_to_tracks - actual_from_track) % num_to_tracks, num_to_tracks); + } else if (to_side == RIGHT) { + to_tracks = get_to_track_list(Fs, (actual_from_track + 1) % num_to_tracks, num_to_tracks); + } + } + /* Finish, we return */ + return to_tracks; + /* End switch_block_type == WILTON case. */ + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid switch block pattern !\n"); + exit(1); + } + + return to_tracks; +} + +/************************************************************************ + * Build the track_to_track_map[from_side][0..chan_width-1][to_side][track_indices] + * For a group of from_track nodes and to_track nodes + * For each side of from_tracks, we call a routine to get the list of to_tracks + * Then, we fill the track2track_map + ***********************************************************************/ +static void build_gsb_one_group_track_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_switch_block_type& sb_type, + const int& Fs, + const bool& wire_opposite_side, + const t_track_group& from_tracks, /* [0..gsb_side][track_indices] */ + const t_track_group& to_tracks, /* [0..gsb_side][track_indices] */ + t_track2track_map& track2track_map) { + for (size_t side = 0; side < from_tracks.size(); ++side) { + SideManager side_manager(side); + e_side from_side = side_manager.get_side(); + /* Find the other sides where the start tracks will locate */ + std::vector to_track_sides; + /* 0. opposite side */ + to_track_sides.push_back(side_manager.get_opposite()); + /* 1. prev side */ + /* Previous side definition: TOP => LEFT; RIGHT=>TOP; BOTTOM=>RIGHT; LEFT=>BOTTOM */ + to_track_sides.push_back(side_manager.get_rotate_counterclockwise()); + /* 2. next side */ + /* Next side definition: TOP => RIGHT; RIGHT=>BOTTOM; BOTTOM=>LEFT; LEFT=>TOP */ + to_track_sides.push_back(side_manager.get_rotate_clockwise()); + + for (size_t inode = 0; inode < from_tracks[side].size(); ++inode) { + for (size_t to_side_id = 0; to_side_id < to_track_sides.size(); ++to_side_id) { + enum e_side to_side = to_track_sides[to_side_id]; + SideManager to_side_manager(to_side); + size_t to_side_index = to_side_manager.to_size_t(); + /* Bypass those to_sides have no nodes */ + if (0 == to_tracks[to_side_index].size()) { + continue; + } + /* Bypass those from_side is same as to_side */ + if (from_side == to_side) { + continue; + } + /* Bypass those from_side is opposite to to_side if required */ + if ((true == wire_opposite_side) + && (to_side_manager.get_opposite() == from_side)) { + continue; + } + /* Get other track_ids depending on the switch block pattern */ + /* Find the track ids that will start at the other sides */ + std::vector to_track_ids = get_switch_block_to_track_id(sb_type, Fs, from_side, inode, + to_side, + to_tracks[to_side_index].size()); + /* Update the track2track_map: */ + for (size_t to_track_id = 0; to_track_id < to_track_ids.size(); ++to_track_id) { + size_t from_side_index = side_manager.to_size_t(); + size_t from_track_index = from_tracks[side][inode]; + /* Check the id is still in the range !*/ + VTR_ASSERT(to_track_ids[to_track_id] < to_tracks[to_side_index].size()); + size_t to_track_index = to_tracks[to_side_index][to_track_ids[to_track_id]]; + //printf("from_track(size=%lu): %lu , to_track_ids[%lu]:%lu, to_track_index: %lu in a group of %lu tracks\n", + // from_tracks[side].size(), inode, to_track_id, to_track_ids[to_track_id], + // to_track_index, to_tracks[to_side_index].size()); + const RRNodeId& to_track_node = rr_gsb.get_chan_node(to_side, to_track_index); + VTR_ASSERT(true == rr_graph.valid_node(to_track_node)); + + /* from_track should be IN_PORT */ + VTR_ASSERT(IN_PORT == rr_gsb.get_chan_node_direction(from_side, from_track_index)); + /* to_track should be OUT_PORT */ + VTR_ASSERT(OUT_PORT == rr_gsb.get_chan_node_direction(to_side, to_track_index)); + + //VTR_LOG("Consider a connection from pass tracks %d on side %s to track node %ld on side %s\n", from_track_index, SIDE_STRING[from_side], size_t(to_track_node), SIDE_STRING[to_side]); + + /* Check if the to_track_node is already in the list ! */ + std::vector::iterator it = std::find(track2track_map[from_side_index][from_track_index].begin(), + track2track_map[from_side_index][from_track_index].end(), + to_track_node); + if (it != track2track_map[from_side_index][from_track_index].end()) { + continue; /* the node_id is already in the list, go for the next */ + } + /* Clear, we should add to the list */ + track2track_map[from_side_index][from_track_index].push_back(to_track_node); + //VTR_LOG("Built a connection from pass tracks %d on side %s to track node %ld on side %s\n", from_track_index, SIDE_STRING[from_side], size_t(to_track_node), SIDE_STRING[to_side]); + } + } + } + } +} + +/************************************************************************ + * Build the track_to_track_map[from_side][0..chan_width-1][to_side][track_indices] + * based on the existing routing resources in the General Switch Block (GSB) + * The track_indices is the indices of tracks that the node at from_side and [0..chan_width-1] will drive + * IMPORTANT: the track_indices are the indicies in the GSB context, but not the rr_graph!!! + * We separate the connections into two groups: + * Group 1: the routing tracks start from this GSB + * We will apply switch block patterns (SUBSET, UNIVERSAL, WILTON) + * Group 2: the routing tracks do not start from this GSB (bypassing wires) + * We will apply switch block patterns (SUBSET, UNIVERSAL, WILTON) + * but we will check the Switch Block (SB) population of these + * routing segments, and determine which requires connections + * + * CHANY CHANY CHANY CHANY + * [0] [1] [2] [3] + * start yes no yes no + * end +-------------------------+ start Group 1 Group 2 + * no CHANX[0] | TOP | CHANX[0] yes TOP/BOTTOM TOP/BOTTOM + * | | CHANY[0,2] CHANY[1,3] + * yes CHANX[1] | | CHANX[1] no + * | LEFT RIGHT | + * no CHANX[2] | | CHANX[2] yes + * | | + * yes CHANX[3] | BOTTOM | CHANX[3] no + * +-------------------------+ + * CHANY CHANY CHANY CHANY + * [0] [1] [2] [3] + * start yes no yes no + * + * The mapping is done in the following steps: (For each side of the GSB) + * 1. Build a list of tracks that will start from this side + * if a track starts, its xlow/ylow is the same as the x,y of this gsb + * 2. Build a list of tracks on the other sides belonging to Group 1. + * Take the example of RIGHT side, we will collect + * a. tracks that will end at the LEFT side + * b. tracks that will start at the TOP side + * c. tracks that will start at the BOTTOM side + * 3. Apply switch block patterns to Group 1 (SUBSET, UNIVERSAL, WILTON) + * 4. Build a list of tracks on the other sides belonging to Group 1. + * Take the example of RIGHT side, we will collect + * a. tracks that will bypass at the TOP side + * b. tracks that will bypass at the BOTTOM side + * 5. Apply switch block patterns to Group 2 (SUBSET, UNIVERSAL, WILTON) + ***********************************************************************/ +t_track2track_map build_gsb_track_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const bool& wire_opposite_side, + const std::vector& segment_inf) { + t_track2track_map track2track_map; /* [0..gsb_side][0..chan_width][track_indices] */ + + /* Categorize tracks into 3 groups: + * (1) tracks will start here + * (2) tracks will end here + * (2) tracks will just pass through the SB */ + t_track_group start_tracks; /* [0..gsb_side][track_indices] */ + t_track_group end_tracks; /* [0..gsb_side][track_indices] */ + t_track_group pass_tracks; /* [0..gsb_side][track_indices] */ + + /* resize to the number of sides */ + start_tracks.resize(rr_gsb.get_num_sides()); + end_tracks.resize(rr_gsb.get_num_sides()); + pass_tracks.resize(rr_gsb.get_num_sides()); + + /* Walk through each side */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + e_side gsb_side = side_manager.get_side(); + /* Build a list of tracks that will start from this side */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + /* We need to check Switch block population of this track + * The track node will not be considered if there supposed to be no SB at this position + */ + if (false == is_gsb_in_track_sb_population(rr_graph, rr_gsb, gsb_side, inode, segment_inf)) { + continue; /* skip this node and go to the next */ + } + /* check if this track will start from here */ + enum e_track_status track_status = determine_track_status_of_gsb(rr_graph, rr_gsb, gsb_side, inode); + + switch (track_status) { + case TRACK_START: + /* update starting track list */ + start_tracks[side].push_back(inode); + break; + case TRACK_END: + /* Update end track list */ + end_tracks[side].push_back(inode); + break; + case TRACK_PASS: + /* Update passing track list */ + /* Note that the pass_track should be IN_PORT only !!! */ + if (IN_PORT == rr_gsb.get_chan_node_direction(gsb_side, inode)) { + pass_tracks[side].push_back(inode); + } + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid track status!\n"); + exit(1); + } + } + } + + /* Allocate track2track map */ + track2track_map.resize(rr_gsb.get_num_sides()); + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side gsb_side = side_manager.get_side(); + /* allocate track2track_map[gsb_side] */ + track2track_map[side].resize(rr_gsb.get_chan_width(gsb_side)); + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + /* allocate track2track_map[gsb_side][inode] */ + track2track_map[side][inode].clear(); + } + } + + /* For Group 1: we build connections between end_tracks and start_tracks*/ + build_gsb_one_group_track_to_track_map(rr_graph, rr_gsb, + sb_type, Fs, + true, /* End tracks should always to wired to start tracks */ + end_tracks, start_tracks, + track2track_map); + + /* For Group 2: we build connections between end_tracks and start_tracks*/ + /* Currently, I use the same Switch Block pattern for the passing tracks and end tracks, + * TODO: This can be improved with different patterns! + */ + //for (e_side curr_side : SIDES) { + // VTR_LOG("Number of pass tracks %d on side %s\n", pass_tracks[size_t(curr_side)].size(), SIDE_STRING[curr_side]); + //} + build_gsb_one_group_track_to_track_map(rr_graph, rr_gsb, + sb_subtype, subFs, + wire_opposite_side, /* Pass tracks may not be wired to start tracks */ + pass_tracks, start_tracks, + track2track_map); + + return track2track_map; +} + +/* Build a RRChan Object with the given channel type and coorindators */ +static RRChan build_one_tileable_rr_chan(const vtr::Point& chan_coordinate, + const t_rr_type& chan_type, + const RRGraphView& rr_graph, + const ChanNodeDetails& chan_details) { + std::vector chan_rr_nodes; + + /* Create a rr_chan object and check if it is unique in the graph */ + RRChan rr_chan; + + /* Fill the information */ + rr_chan.set_type(chan_type); + + /* Collect rr_nodes for this channel */ + chan_rr_nodes = find_rr_graph_chan_nodes(rr_graph, + chan_coordinate.x(), chan_coordinate.y(), + chan_type); + + /* Reserve */ + /* rr_chan.reserve_node(size_t(chan_width)); */ + + /* Fill the rr_chan */ + for (size_t itrack = 0; itrack < chan_rr_nodes.size(); ++itrack) { + size_t iseg = chan_details.get_track_segment_id(itrack); + rr_chan.add_node(rr_graph, chan_rr_nodes[itrack], RRSegmentId(iseg)); + } + + return rr_chan; +} + +/*********************************************************************** + * Build a General Switch Block (GSB) + * which includes: + * [I] A Switch Box subckt consists of following ports: + * 1. Channel Y [x][y] inputs + * 2. Channel X [x+1][y] inputs + * 3. Channel Y [x][y-1] outputs + * 4. Channel X [x][y] outputs + * 5. Grid[x][y+1] Right side outputs pins + * 6. Grid[x+1][y+1] Left side output pins + * 7. Grid[x+1][y+1] Bottom side output pins + * 8. Grid[x+1][y] Top side output pins + * 9. Grid[x+1][y] Left side output pins + * 10. Grid[x][y] Right side output pins + * 11. Grid[x][y] Top side output pins + * 12. Grid[x][y+1] Bottom side output pins + * + * -------------- -------------- + * | | CBY | | + * | Grid | ChanY | Grid | + * | [x][y+1] | [x][y+1] | [x+1][y+1] | + * | | | | + * -------------- -------------- + * ---------- + * ChanX & CBX | Switch | ChanX + * [x][y] | Box | [x+1][y] + * | [x][y] | + * ---------- + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y] | [x][y] | [x+1][y] | + * | | | | + * -------------- -------------- + * For channels chanY with INC_DIRECTION on the top side, they should be marked as outputs + * For channels chanY with DEC_DIRECTION on the top side, they should be marked as inputs + * For channels chanY with INC_DIRECTION on the bottom side, they should be marked as inputs + * For channels chanY with DEC_DIRECTION on the bottom side, they should be marked as outputs + * For channels chanX with INC_DIRECTION on the left side, they should be marked as inputs + * For channels chanX with DEC_DIRECTION on the left side, they should be marked as outputs + * For channels chanX with INC_DIRECTION on the right side, they should be marked as outputs + * For channels chanX with DEC_DIRECTION on the right side, they should be marked as inputs + * + * [II] A X-direction Connection Block [x][y] + * The connection block shares the same routing channel[x][y] with the Switch Block + * We just need to fill the ipin nodes at TOP and BOTTOM sides + * as well as properly fill the ipin_grid_side information + * [III] A Y-direction Connection Block [x][y+1] + * The connection block shares the same routing channel[x][y+1] with the Switch Block + * We just need to fill the ipin nodes at LEFT and RIGHT sides + * as well as properly fill the ipin_grid_side information + ***********************************************************************/ +RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids, + const RRGraphView& rr_graph, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const vtr::Point& gsb_coordinate) { + /* Create an object to return */ + RRGSB rr_gsb; + + /* Check */ + VTR_ASSERT(gsb_coordinate.x() <= grids.width()); + VTR_ASSERT(gsb_coordinate.y() <= grids.height()); + + /* Coordinator initialization */ + rr_gsb.set_coordinate(gsb_coordinate.x(), gsb_coordinate.y()); + + /* Basic information*/ + rr_gsb.init_num_sides(4); /* Fixed number of sides */ + + /* Find all rr_nodes of channels */ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + /* Local variables inside this for loop */ + SideManager side_manager(side); + vtr::Point coordinate = rr_gsb.get_side_block_coordinate(side_manager.get_side()); + RRChan rr_chan; + std::vector> temp_opin_rr_nodes(2); + enum e_side opin_grid_side[2] = {NUM_SIDES, NUM_SIDES}; + enum PORTS chan_dir_to_port_dir_mapping[2] = {OUT_PORT, IN_PORT}; /* 0: INC_DIRECTION => ?; 1: DEC_DIRECTION => ? */ + + /* Build a segment details, where we need the segment ids for building rr_chan + * We do not care starting and ending points here, so set chan_side as NUM_SIDES + */ + ChanNodeDetails chanx_details = build_unidir_chan_node_details(device_chan_width.x(), grids.width() - 1, + false, false, segment_inf); + ChanNodeDetails chany_details = build_unidir_chan_node_details(device_chan_width.y(), grids.height() - 1, + false, false, segment_inf); + + switch (side) { + case TOP: /* TOP = 0 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.y() == grids.height() - 1) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(coordinate, CHANY, rr_graph, chany_details); + chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */ + chan_dir_to_port_dir_mapping[1] = IN_PORT; /* DEC_DIRECTION => IN_PORT */ + + /* Assign grid side of OPIN */ + /* Grid[x][y+1] RIGHT side outputs pins */ + opin_grid_side[0] = RIGHT; + /* Grid[x+1][y+1] left side outputs pins */ + opin_grid_side[1] = LEFT; + + /* Build the Switch block: opin and opin_grid_side */ + /* Include Grid[x][y+1] RIGHT side outputs pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + gsb_coordinate.x(), gsb_coordinate.y() + 1, + OPIN, opin_grid_side[0]); + /* Include Grid[x+1][y+1] Left side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + gsb_coordinate.x() + 1, gsb_coordinate.y() + 1, + OPIN, opin_grid_side[1]); + + break; + case RIGHT: /* RIGHT = 1 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.x() == grids.width() - 1) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Collect rr_nodes for Tracks for top: chany[x][y+1] */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(coordinate, CHANX, rr_graph, chanx_details); + chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */ + chan_dir_to_port_dir_mapping[1] = IN_PORT; /* DEC_DIRECTION => IN_PORT */ + + /* Assign grid side of OPIN */ + /* Grid[x+1][y+1] BOTTOM side outputs pins */ + opin_grid_side[0] = BOTTOM; + /* Grid[x+1][y] TOP side outputs pins */ + opin_grid_side[1] = TOP; + + /* Build the Switch block: opin and opin_grid_side */ + /* include Grid[x+1][y+1] Bottom side output pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + gsb_coordinate.x() + 1, gsb_coordinate.y() + 1, + OPIN, opin_grid_side[0]); + /* include Grid[x+1][y] Top side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + gsb_coordinate.x() + 1, gsb_coordinate.y(), + OPIN, opin_grid_side[1]); + break; + case BOTTOM: /* BOTTOM = 2*/ + /* For the bording, we should take special care */ + if (gsb_coordinate.y() == 0) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Collect rr_nodes for Tracks for bottom: chany[x][y] */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(coordinate, CHANY, rr_graph, chany_details); + chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */ + chan_dir_to_port_dir_mapping[1] = OUT_PORT; /* DEC_DIRECTION => OUT_PORT */ + + /* Assign grid side of OPIN */ + /* Grid[x+1][y] LEFT side outputs pins */ + opin_grid_side[0] = LEFT; + /* Grid[x][y] RIGHT side outputs pins */ + opin_grid_side[1] = RIGHT; + + /* Build the Switch block: opin and opin_grid_side */ + /* include Grid[x+1][y] Left side output pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + gsb_coordinate.x() + 1, gsb_coordinate.y(), + OPIN, opin_grid_side[0]); + /* include Grid[x][y] Right side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + gsb_coordinate.x(), gsb_coordinate.y(), + OPIN, opin_grid_side[1]); + break; + case LEFT: /* LEFT = 3 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.x() == 0) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Collect rr_nodes for Tracks for left: chanx[x][y] */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(coordinate, CHANX, rr_graph, chanx_details); + chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */ + chan_dir_to_port_dir_mapping[1] = OUT_PORT; /* DEC_DIRECTION => OUT_PORT */ + + /* Grid[x][y+1] BOTTOM side outputs pins */ + opin_grid_side[0] = BOTTOM; + /* Grid[x][y] TOP side outputs pins */ + opin_grid_side[1] = TOP; + + /* Build the Switch block: opin and opin_grid_side */ + /* include Grid[x][y+1] Bottom side outputs pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + gsb_coordinate.x(), gsb_coordinate.y() + 1, + OPIN, opin_grid_side[0]); + /* include Grid[x][y] Top side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + gsb_coordinate.x(), gsb_coordinate.y(), + OPIN, opin_grid_side[1]); + + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid side index!\n"); + exit(1); + } + + /* Organize a vector of port direction */ + if (0 < rr_chan.get_chan_width()) { + std::vector rr_chan_dir; + rr_chan_dir.resize(rr_chan.get_chan_width()); + for (size_t itrack = 0; itrack < rr_chan.get_chan_width(); ++itrack) { + /* Identify the directionality, record it in rr_node_direction */ + if (Direction::INC == rr_graph.node_direction(rr_chan.get_node(itrack))) { + rr_chan_dir[itrack] = chan_dir_to_port_dir_mapping[0]; + } else { + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(rr_chan.get_node(itrack))); + rr_chan_dir[itrack] = chan_dir_to_port_dir_mapping[1]; + } + } + /* Fill chan_rr_nodes */ + rr_gsb.add_chan_node(side_manager.get_side(), rr_chan, rr_chan_dir); + } + + /* Fill opin_rr_nodes */ + /* Copy from temp_opin_rr_node to opin_rr_node */ + for (const RRNodeId& inode : temp_opin_rr_nodes[0]) { + /* Grid[x+1][y+1] Bottom side outputs pins */ + rr_gsb.add_opin_node(inode, side_manager.get_side()); + } + for (const RRNodeId& inode : temp_opin_rr_nodes[1]) { + /* Grid[x+1][y] TOP side outputs pins */ + rr_gsb.add_opin_node(inode, side_manager.get_side()); + } + + /* Clean ipin_rr_nodes */ + /* We do not have any IPIN for a Switch Block */ + rr_gsb.clear_ipin_nodes(side_manager.get_side()); + + /* Clear the temp data */ + temp_opin_rr_nodes[0].clear(); + temp_opin_rr_nodes[1].clear(); + opin_grid_side[0] = NUM_SIDES; + opin_grid_side[1] = NUM_SIDES; + } + + /* Add IPIN nodes from adjacent grids: the 4 grids sitting on the 4 corners of the Switch Block + * + * - The concept of top/bottom side of connection block in GSB domain: + * + * | Grid[x][y+1] | + * | BOTTOM side | + * +-----------------------+ + * | + * v + * +-----------------------+ + * | TOP side | + * | X- Connection Block | + * | BOTTOM side | + * +-----------------------+ + * ^ + * | + * +-----------------------+ + * | TOP side | + * | Grid[x][y] | + * + * - The concept of top/bottom side of connection block in GSB domain: + * + * ---------------+ +---------------------- ... ---------------------+ +---------------- + * Grid[x][y+1] |->| Y- Connection Block Y- Connection Block |<-| Grid[x+1][y+1] + * RIGHT side | | LEFT side ... RIGHT side | | LEFT side + * --------------+ +---------------------- ... ---------------------+ +---------------- + * + */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + size_t ix; + size_t iy; + enum e_side chan_side; + std::vector temp_ipin_rr_nodes; + enum e_side ipin_rr_node_grid_side; + + switch (side) { + case TOP: + /* Consider the routing channel that is connected to the left side of the switch block */ + chan_side = LEFT; + /* The input pins of the routing channel come from the bottom side of Grid[x][y+1] */ + ix = rr_gsb.get_sb_x(); + iy = rr_gsb.get_sb_y() + 1; + ipin_rr_node_grid_side = BOTTOM; + break; + case RIGHT: + /* Consider the routing channel that is connected to the top side of the switch block */ + chan_side = TOP; + /* The input pins of the routing channel come from the left side of Grid[x+1][y+1] */ + ix = rr_gsb.get_sb_x() + 1; + iy = rr_gsb.get_sb_y() + 1; + ipin_rr_node_grid_side = LEFT; + break; + case BOTTOM: + /* Consider the routing channel that is connected to the left side of the switch block */ + chan_side = LEFT; + /* The input pins of the routing channel come from the top side of Grid[x][y] */ + ix = rr_gsb.get_sb_x(); + iy = rr_gsb.get_sb_y(); + ipin_rr_node_grid_side = TOP; + break; + case LEFT: + /* Consider the routing channel that is connected to the top side of the switch block */ + chan_side = TOP; + /* The input pins of the routing channel come from the right side of Grid[x][y+1] */ + ix = rr_gsb.get_sb_x(); + iy = rr_gsb.get_sb_y() + 1; + ipin_rr_node_grid_side = RIGHT; + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid side index!\n"); + exit(1); + } + + /* If there is no channel at this side, we skip ipin_node annotation */ + if (0 == rr_gsb.get_chan_width(chan_side)) { + continue; + } + /* Collect IPIN rr_nodes*/ + temp_ipin_rr_nodes = find_rr_graph_grid_nodes(rr_graph, grids, + ix, iy, IPIN, ipin_rr_node_grid_side); + /* Fill the ipin nodes of RRGSB */ + for (const RRNodeId& inode : temp_ipin_rr_nodes) { + rr_gsb.add_ipin_node(inode, side_manager.get_side()); + } + /* Clear the temp data */ + temp_ipin_rr_nodes.clear(); + } + + return rr_gsb; +} + +/************************************************************************ + * Create edges for each rr_node of a General Switch Blocks (GSB): + * 1. create edges between CHANX | CHANY and IPINs (connections inside connection blocks) + * 2. create edges between OPINs, CHANX and CHANY (connections inside switch blocks) + * 3. create edges between OPINs and IPINs (direct-connections) + ***********************************************************************/ +void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const t_track2pin_map& track2ipin_map, + const t_pin2track_map& opin2track_map, + const t_track2track_map& track2track_map, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges_to_create) { + size_t edge_count = 0; + /* Walk through each sides */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side gsb_side = side_manager.get_side(); + + /* Find OPINs */ + for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(gsb_side); ++inode) { + const RRNodeId& opin_node = rr_gsb.get_opin_node(gsb_side, inode); + + /* 1. create edges between OPINs and CHANX|CHANY, using opin2track_map */ + /* add edges to the opin_node */ + for (const RRNodeId& track_node : opin2track_map[gsb_side][inode]) { + rr_graph_builder.create_edge(opin_node, track_node, rr_node_driver_switches[track_node]); + edge_count++; + } + } + + /* Find CHANX or CHANY */ + /* For TRACKs to IPINs, we only care LEFT and TOP sides + * Skip RIGHT and BOTTOM for the ipin2track_map since they should be handled in other GSBs + */ + if ((side_manager.get_side() == rr_gsb.get_cb_chan_side(CHANX)) + || (side_manager.get_side() == rr_gsb.get_cb_chan_side(CHANY))) { + /* 2. create edges between CHANX|CHANY and IPINs, using ipin2track_map */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); + for (const RRNodeId& ipin_node : track2ipin_map[gsb_side][inode]) { + rr_graph_builder.create_edge(chan_node, ipin_node, rr_node_driver_switches[ipin_node]); + edge_count++; + } + } + } + + /* 3. create edges between CHANX|CHANY and CHANX|CHANY, using track2track_map */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); + for (const RRNodeId& track_node : track2track_map[gsb_side][inode]) { + rr_graph_builder.create_edge(chan_node, track_node, rr_node_driver_switches[track_node]); + edge_count++; + } + } + } + num_edges_to_create += edge_count; +} + +/************************************************************************ + * Build track2ipin_map for an IPIN + * 1. build a list of routing tracks which are allowed for connections + * We will check the Connection Block (CB) population of each routing track. + * By comparing current chan_y - ylow, we can determine if a CB connection + * is required for each routing track + * 2. Divide the routing tracks by segment types, so that we can balance + * the connections between IPINs and different types of routing tracks. + * 3. Scale the Fc of each pin to the actual number of routing tracks + * actual_Fc = (int) Fc * num_tracks / chan_width + ***********************************************************************/ +static void build_gsb_one_ipin_track2pin_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const enum e_side& ipin_side, + const size_t& ipin_node_id, + const std::vector& Fc, + const size_t& offset, + const std::vector& segment_inf, + t_track2pin_map& track2ipin_map) { + /* Get a list of segment_ids*/ + enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side); + SideManager chan_side_manager(chan_side); + std::vector seg_list = rr_gsb.get_chan_segment_ids(chan_side); + size_t chan_width = rr_gsb.get_chan_width(chan_side); + SideManager ipin_side_manager(ipin_side); + const RRNodeId& ipin_node = rr_gsb.get_ipin_node(ipin_side, ipin_node_id); + + for (size_t iseg = 0; iseg < seg_list.size(); ++iseg) { + /* Get a list of node that have the segment id */ + std::vector track_list = rr_gsb.get_chan_node_ids_by_segment_ids(chan_side, seg_list[iseg]); + /* Refine the track_list: keep those will have connection blocks in the GSB */ + std::vector actual_track_list; + for (size_t inode = 0; inode < track_list.size(); ++inode) { + /* Check if tracks allow connection blocks in the GSB*/ + if (false == is_gsb_in_track_cb_population(rr_graph, rr_gsb, chan_side, track_list[inode], segment_inf)) { + continue; /* Bypass condition */ + } + /* Push the node to actual_track_list */ + actual_track_list.push_back(track_list[inode]); + } + /* Check the actual track list */ + VTR_ASSERT(0 == actual_track_list.size() % 2); + + /* Scale Fc */ + int actual_Fc = std::ceil((float)Fc[iseg] * (float)actual_track_list.size() / (float)chan_width); + /* Minimum Fc should be 2 : ensure we will connect to a pair of routing tracks */ + actual_Fc = std::max(1, actual_Fc); + /* Compute the step between two connection from this IPIN to tracks: + * step = W' / Fc', W' and Fc' are the adapted W and Fc from actual_track_list and Fc_in + */ + size_t track_step = std::floor((float)actual_track_list.size() / (float)actual_Fc); + /* Make sure step should be at least 2 */ + track_step = std::max(1, (int)track_step); + /* Adapt offset to the range of actual_track_list */ + size_t actual_offset = offset % actual_track_list.size(); + /* rotate the track list by an offset */ + if (0 < actual_offset) { + std::rotate(actual_track_list.begin(), actual_track_list.begin() + actual_offset, actual_track_list.end()); + } + + /* Assign tracks: since we assign 2 track per round, we increment itrack by 2* step */ + int track_cnt = 0; + /* Keep assigning until we meet the Fc requirement */ + for (size_t itrack = 0; itrack < actual_track_list.size(); itrack = itrack + 2 * track_step) { + /* Update pin2track map */ + size_t chan_side_index = chan_side_manager.to_size_t(); + /* itrack may exceed the size of actual_track_list, adapt it */ + size_t actual_itrack = itrack % actual_track_list.size(); + /* track_index may exceed the chan_width(), adapt it */ + size_t track_index = actual_track_list[actual_itrack] % chan_width; + + track2ipin_map[chan_side_index][track_index].push_back(ipin_node); + + /* track_index may exceed the chan_width(), adapt it */ + track_index = (actual_track_list[actual_itrack] + 1) % chan_width; + + track2ipin_map[chan_side_index][track_index].push_back(ipin_node); + + track_cnt += 2; + } + + /* Ensure the number of tracks is similar to Fc */ + /* Give a warning if Fc is < track_cnt */ + /* + * if (actual_Fc != track_cnt) { + * vpr_printf(TIO_MESSAGE_INFO, + * "IPIN Node(%lu) will have a different Fc(=%lu) than specified(=%lu)!\n", + * ipin_node - rr_graph->rr_node, track_cnt, actual_Fc); + * } + */ + } +} + +/************************************************************************ + * Build opin2track_map for an OPIN + * 1. build a list of routing tracks which are allowed for connections + * We will check the Switch Block (SB) population of each routing track. + * By comparing current chan_y - ylow, we can determine if a SB connection + * is required for each routing track + * 2. Divide the routing tracks by segment types, so that we can balance + * the connections between OPINs and different types of routing tracks. + * 3. Scale the Fc of each pin to the actual number of routing tracks + * actual_Fc = (int) Fc * num_tracks / chan_width + ***********************************************************************/ +static void build_gsb_one_opin_pin2track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const enum e_side& opin_side, + const size_t& opin_node_id, + const std::vector& Fc, + const size_t& offset, + const std::vector& segment_inf, + t_pin2track_map& opin2track_map) { + /* Get a list of segment_ids*/ + std::vector seg_list = rr_gsb.get_chan_segment_ids(opin_side); + enum e_side chan_side = opin_side; + size_t chan_width = rr_gsb.get_chan_width(chan_side); + SideManager opin_side_manager(opin_side); + + for (size_t iseg = 0; iseg < seg_list.size(); ++iseg) { + /* Get a list of node that have the segment id */ + std::vector track_list = rr_gsb.get_chan_node_ids_by_segment_ids(chan_side, seg_list[iseg]); + /* Refine the track_list: keep those will have connection blocks in the GSB */ + std::vector actual_track_list; + for (size_t inode = 0; inode < track_list.size(); ++inode) { + /* Check if tracks allow connection blocks in the GSB*/ + if (false == is_gsb_in_track_sb_population(rr_graph, rr_gsb, chan_side, track_list[inode], segment_inf)) { + continue; /* Bypass condition */ + } + if (TRACK_START != determine_track_status_of_gsb(rr_graph, rr_gsb, chan_side, track_list[inode])) { + continue; /* Bypass condition */ + } + /* Push the node to actual_track_list */ + actual_track_list.push_back(track_list[inode]); + } + + /* Go the next segment if offset is zero or actual_track_list is empty */ + if (0 == actual_track_list.size()) { + continue; + } + + /* Scale Fc */ + int actual_Fc = std::ceil((float)Fc[iseg] * (float)actual_track_list.size() / (float)chan_width); + /* Minimum Fc should be 1 : ensure we will drive 1 routing track */ + actual_Fc = std::max(1, actual_Fc); + /* Compute the step between two connection from this IPIN to tracks: + * step = W' / Fc', W' and Fc' are the adapted W and Fc from actual_track_list and Fc_in + */ + size_t track_step = std::floor((float)actual_track_list.size() / (float)actual_Fc); + /* Track step mush be a multiple of 2!!!*/ + /* Make sure step should be at least 1 */ + track_step = std::max(1, (int)track_step); + /* Adapt offset to the range of actual_track_list */ + size_t actual_offset = offset % actual_track_list.size(); + + /* No need to rotate if offset is zero */ + if (0 < actual_offset) { + /* rotate the track list by an offset */ + std::rotate(actual_track_list.begin(), actual_track_list.begin() + actual_offset, actual_track_list.end()); + } + + /* Assign tracks */ + int track_cnt = 0; + /* Keep assigning until we meet the Fc requirement */ + for (size_t itrack = 0; itrack < actual_track_list.size(); itrack = itrack + track_step) { + /* Update pin2track map */ + size_t opin_side_index = opin_side_manager.to_size_t(); + /* itrack may exceed the size of actual_track_list, adapt it */ + size_t actual_itrack = itrack % actual_track_list.size(); + size_t track_index = actual_track_list[actual_itrack]; + const RRNodeId& track_rr_node_index = rr_gsb.get_chan_node(chan_side, track_index); + opin2track_map[opin_side_index][opin_node_id].push_back(track_rr_node_index); + /* update track counter */ + track_cnt++; + /* Stop when we have enough Fc: this may lead to some tracks have zero drivers. + * So I comment it. And we just make sure its track_cnt >= actual_Fc + * if (actual_Fc == track_cnt) { + * break; + * } + */ + } + + /* Ensure the number of tracks is similar to Fc */ + /* Give a warning if Fc is < track_cnt */ + /* + * if (actual_Fc != track_cnt) { + * vpr_printf(TIO_MESSAGE_INFO, + * "OPIN Node(%lu) will have a different Fc(=%lu) than specified(=%lu)!\n", + * opin_node_id, track_cnt, actual_Fc); + * } + */ + } +} + +/************************************************************************ + * Build the track_to_ipin_map[gsb_side][0..chan_width-1][ipin_indices] + * based on the existing routing resources in the General Switch Block (GSB) + * This function supports both X-directional and Y-directional tracks + * The mapping is done in the following steps: + * 1. Build ipin_to_track_map[gsb_side][0..num_ipin_nodes-1][track_indices] + * For each IPIN, we ensure at least one connection to the tracks. + * Then, we assign IPINs to tracks evenly while satisfying the actual_Fc + * 2. Convert the ipin_to_track_map to track_to_ipin_map + ***********************************************************************/ +t_track2pin_map build_gsb_track_to_ipin_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_in) { + t_track2pin_map track2ipin_map; + /* Resize the matrix */ + track2ipin_map.resize(rr_gsb.get_num_sides()); + + /* offset counter: it aims to balance the track-to-IPIN for each connection block */ + size_t offset_size = 0; + std::vector offset; + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side ipin_side = side_manager.get_side(); + /* Get the chan_side */ + enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side); + SideManager chan_side_manager(chan_side); + /* resize offset to the maximum chan_side*/ + offset_size = std::max(offset_size, chan_side_manager.to_size_t() + 1); + } + /* Initial offset */ + offset.resize(offset_size); + offset.assign(offset.size(), 0); + + /* Walk through each side */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side ipin_side = side_manager.get_side(); + /* Get the chan_side */ + enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side); + SideManager chan_side_manager(chan_side); + /* This track2pin mapping is for Connection Blocks, so we only care two sides! */ + /* Get channel width and resize the matrix */ + size_t chan_width = rr_gsb.get_chan_width(chan_side); + track2ipin_map[chan_side_manager.to_size_t()].resize(chan_width); + /* Find the ipin/opin nodes */ + for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(ipin_side); ++inode) { + const RRNodeId& ipin_node = rr_gsb.get_ipin_node(ipin_side, inode); + /* Skip EMPTY type */ + if (true == is_empty_type(grids[rr_graph.node_xlow(ipin_node)][rr_graph.node_ylow(ipin_node)].type)) { + continue; + } + + int grid_type_index = grids[rr_graph.node_xlow(ipin_node)][rr_graph.node_ylow(ipin_node)].type->index; + /* Get Fc of the ipin */ + /* skip Fc = 0 or unintialized, those pins are in the */ + bool skip_conn2track = true; + std::vector ipin_Fc_out; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + int ipin_Fc = Fc_in[grid_type_index][rr_graph.node_pin_num(ipin_node)][iseg]; + ipin_Fc_out.push_back(ipin_Fc); + if (0 != ipin_Fc) { + skip_conn2track = false; + continue; + } + } + + if (true == skip_conn2track) { + continue; + } + + VTR_ASSERT(ipin_Fc_out.size() == segment_inf.size()); + + /* Build track2ipin_map for this IPIN */ + build_gsb_one_ipin_track2pin_map(rr_graph, rr_gsb, ipin_side, inode, ipin_Fc_out, + /* Give an offset for the first track that this ipin will connect to */ + offset[chan_side_manager.to_size_t()], + segment_inf, track2ipin_map); + /* update offset */ + offset[chan_side_manager.to_size_t()] += 2; + //printf("offset[%lu]=%lu\n", chan_side_manager.to_size_t(), offset[chan_side_manager.to_size_t()]); + } + } + + return track2ipin_map; +} + +/************************************************************************ + * Build the opin_to_track_map[gsb_side][0..num_opin_nodes-1][track_indices] + * based on the existing routing resources in the General Switch Block (GSB) + * This function supports both X-directional and Y-directional tracks + * The mapping is done in the following steps: + * 1. Build a list of routing tracks whose starting points locate at this GSB + * (xlow - gsb_x == 0) + * 2. Divide the routing tracks by segment types, so that we can balance + * the connections between OPINs and different types of routing tracks. + * 3. Scale the Fc of each pin to the actual number of routing tracks + * actual_Fc = (int) Fc * num_tracks / chan_width + ***********************************************************************/ +t_pin2track_map build_gsb_opin_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_out) { + t_pin2track_map opin2track_map; + /* Resize the matrix */ + opin2track_map.resize(rr_gsb.get_num_sides()); + + /* offset counter: it aims to balance the OPIN-to-track for each switch block */ + std::vector offset; + /* Get the chan_side: which is the same as the opin side */ + offset.resize(rr_gsb.get_num_sides()); + /* Initial offset */ + offset.assign(offset.size(), 0); + + /* Walk through each side */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side opin_side = side_manager.get_side(); + /* Get the chan_side */ + /* This track2pin mapping is for Connection Blocks, so we only care two sides! */ + /* Get channel width and resize the matrix */ + size_t num_opin_nodes = rr_gsb.get_num_opin_nodes(opin_side); + opin2track_map[side].resize(num_opin_nodes); + /* Find the ipin/opin nodes */ + for (size_t inode = 0; inode < num_opin_nodes; ++inode) { + const RRNodeId& opin_node = rr_gsb.get_opin_node(opin_side, inode); + /* Skip EMPTY type */ + if (true == is_empty_type(grids[rr_graph.node_xlow(opin_node)][rr_graph.node_ylow(opin_node)].type)) { + continue; + } + int grid_type_index = grids[rr_graph.node_xlow(opin_node)][rr_graph.node_ylow(opin_node)].type->index; + + /* Get Fc of the ipin */ + /* skip Fc = 0 or unintialized, those pins are in the */ + bool skip_conn2track = true; + std::vector opin_Fc_out; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + int opin_Fc = Fc_out[grid_type_index][rr_graph.node_pin_num(opin_node)][iseg]; + opin_Fc_out.push_back(opin_Fc); + if (0 != opin_Fc) { + skip_conn2track = false; + continue; + } + } + + if (true == skip_conn2track) { + continue; + } + VTR_ASSERT(opin_Fc_out.size() == segment_inf.size()); + + /* Build track2ipin_map for this IPIN */ + build_gsb_one_opin_pin2track_map(rr_graph, rr_gsb, opin_side, inode, opin_Fc_out, + /* Give an offset for the first track that this ipin will connect to */ + offset[side_manager.to_size_t()], + segment_inf, opin2track_map); + /* update offset: aim to rotate starting tracks by 1*/ + offset[side_manager.to_size_t()] += 1; + } + + /* Check: + * 1. We want to ensure that each OPIN will drive at least one track + * 2. We want to ensure that each track will be driven by at least 1 OPIN */ + } + + return opin2track_map; +} + +/************************************************************************ + * Add all direct clb-pin-to-clb-pin edges to given opin + ***********************************************************************/ +void build_direct_connections_for_one_gsb(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const vtr::Point& from_grid_coordinate, + const RRSwitchId& delayless_switch, + const std::vector& directs, + const std::vector& clb_to_clb_directs) { + VTR_ASSERT(directs.size() == clb_to_clb_directs.size()); + + const t_grid_tile& from_grid = grids[from_grid_coordinate.x()][from_grid_coordinate.y()]; + t_physical_tile_type_ptr grid_type = from_grid.type; + + /* Iterate through all direct connections */ + for (size_t i = 0; i < directs.size(); ++i) { + /* Bypass unmatched direct clb-to-clb connections */ + if (grid_type != clb_to_clb_directs[i].from_clb_type) { + continue; + } + + /* This opin is specified to connect directly to an ipin, + * now compute which ipin to connect to + */ + vtr::Point to_grid_coordinate(from_grid_coordinate.x() + directs[i].x_offset, + from_grid_coordinate.y() + directs[i].y_offset); + + /* Bypass unmatched direct clb-to-clb connections */ + t_physical_tile_type_ptr to_grid_type = grids[to_grid_coordinate.x()][to_grid_coordinate.y()].type; + /* Check if to_grid if the same grid */ + if (to_grid_type != clb_to_clb_directs[i].to_clb_type) { + continue; + } + + bool swap; + int max_index, min_index; + /* Compute index of opin with regards to given pins */ + if (clb_to_clb_directs[i].from_clb_pin_start_index + > clb_to_clb_directs[i].from_clb_pin_end_index) { + swap = true; + max_index = clb_to_clb_directs[i].from_clb_pin_start_index; + min_index = clb_to_clb_directs[i].from_clb_pin_end_index; + } else { + swap = false; + min_index = clb_to_clb_directs[i].from_clb_pin_start_index; + max_index = clb_to_clb_directs[i].from_clb_pin_end_index; + } + + /* get every opin in the range */ + for (int opin = min_index; opin <= max_index; ++opin) { + int offset = opin - min_index; + + if ((to_grid_coordinate.x() < grids.width() - 1) + && (to_grid_coordinate.y() < grids.height() - 1)) { + int ipin = OPEN; + if (clb_to_clb_directs[i].to_clb_pin_start_index + > clb_to_clb_directs[i].to_clb_pin_end_index) { + if (true == swap) { + ipin = clb_to_clb_directs[i].to_clb_pin_end_index + offset; + } else { + ipin = clb_to_clb_directs[i].to_clb_pin_start_index - offset; + } + } else { + if (true == swap) { + ipin = clb_to_clb_directs[i].to_clb_pin_end_index - offset; + } else { + ipin = clb_to_clb_directs[i].to_clb_pin_start_index + offset; + } + } + + /* Get the pin index in the rr_graph */ + int from_grid_width_ofs = from_grid.width_offset; + int from_grid_height_ofs = from_grid.height_offset; + int to_grid_width_ofs = grids[to_grid_coordinate.x()][to_grid_coordinate.y()].width_offset; + int to_grid_height_ofs = grids[to_grid_coordinate.x()][to_grid_coordinate.y()].height_offset; + + /* Find the side of grid pins, the pin location should be unique! + * Pin location is required by searching a node in rr_graph + */ + std::vector opin_grid_side = find_grid_pin_sides(from_grid, opin); + VTR_ASSERT(1 == opin_grid_side.size()); + + std::vector ipin_grid_side = find_grid_pin_sides(grids[to_grid_coordinate.x()][to_grid_coordinate.y()], ipin); + VTR_ASSERT(1 == ipin_grid_side.size()); + + RRNodeId opin_node_id = rr_graph.node_lookup().find_node(from_grid_coordinate.x() - from_grid_width_ofs, + from_grid_coordinate.y() - from_grid_height_ofs, + OPIN, opin, opin_grid_side[0]); + RRNodeId ipin_node_id = rr_graph.node_lookup().find_node(to_grid_coordinate.x() - to_grid_width_ofs, + to_grid_coordinate.y() - to_grid_height_ofs, + IPIN, ipin, ipin_grid_side[0]); + + /* add edges to the opin_node */ + VTR_ASSERT(opin_node_id && ipin_node_id); + rr_graph_builder.create_edge(opin_node_id, ipin_node_id, delayless_switch); + } + } + } + /* Build actual edges */ + rr_graph_builder.build_edges(true); +} diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.h b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.h new file mode 100644 index 00000000000..9e1d63963ed --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.h @@ -0,0 +1,77 @@ +#ifndef TILEABLE_RR_GRAPH_GSB_H +#define TILEABLE_RR_GRAPH_GSB_H +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "vtr_vector.h" +#include "vtr_geometry.h" + +#include "physical_types.h" +#include "device_grid.h" + +#include "rr_gsb.h" +#include "rr_graph_obj.h" +#include "rr_graph.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/************************************************************************ + * Data stuctures related to the functions + ***********************************************************************/ +typedef std::vector>> t_track2track_map; +typedef std::vector>> t_track2pin_map; +typedef std::vector>> t_pin2track_map; + +/************************************************************************ + * Functions + ***********************************************************************/ +t_track2track_map build_gsb_track_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const bool& wire_opposite_side, + const std::vector& segment_inf); + +RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids, + const RRGraphView& rr_graph, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const vtr::Point& gsb_coordinate); + +void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const t_track2pin_map& track2ipin_map, + const t_pin2track_map& opin2track_map, + const t_track2track_map& track2track_map, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges); + +t_track2pin_map build_gsb_track_to_ipin_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_in); + +t_pin2track_map build_gsb_opin_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_out); + +void build_direct_connections_for_one_gsb(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const vtr::Point& from_grid_coordinate, + const RRSwitchId& delayless_switch, + const std::vector& directs, + const std::vector& clb_to_clb_directs); + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp new file mode 100644 index 00000000000..e3b063a33ff --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp @@ -0,0 +1,1091 @@ +/************************************************************************ + * This file contains functions that are used to allocate nodes + * for the tileable routing resource graph builder + ***********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_geometry.h" + +/* Headers from openfpgautil library */ +#include "openfpga_side_manager.h" + +#include "vpr_types.h" +#include "vpr_utils.h" + +#include "rr_node.h" + +#include "rr_graph_builder_utils.h" +#include "rr_graph_builder.h" +#include "tileable_chan_details_builder.h" +#include "tileable_rr_graph_node_builder.h" +#include "rr_rc_data.h" + +/************************************************************************ + * Find the number output pins by considering all the grid + ***********************************************************************/ +static size_t estimate_num_grid_rr_nodes_by_type(const DeviceGrid& grids, + const t_rr_type& node_type) { + size_t num_grid_rr_nodes = 0; + + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + /* Skip EMPTY tiles */ + if (true == is_empty_type(grids[ix][iy].type)) { + continue; + } + + /* Skip height > 1 or width > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids[ix][iy].width_offset) + || (0 < grids[ix][iy].height_offset)) { + continue; + } + + enum e_side io_side = NUM_SIDES; + + /* If this is the block on borders, we consider IO side */ + if (true == is_io_type(grids[ix][iy].type)) { + vtr::Point io_device_size(grids.width() - 1, grids.height() - 1); + vtr::Point grid_coordinate(ix, iy); + io_side = determine_io_grid_pin_side(io_device_size, grid_coordinate); + } + + switch (node_type) { + case OPIN: + /* get the number of OPINs */ + num_grid_rr_nodes += get_grid_num_pins(grids[ix][iy], DRIVER, io_side); + break; + case IPIN: + /* get the number of IPINs */ + num_grid_rr_nodes += get_grid_num_pins(grids[ix][iy], RECEIVER, io_side); + break; + case SOURCE: + /* SOURCE: number of classes whose type is DRIVER */ + num_grid_rr_nodes += get_grid_num_classes(grids[ix][iy], DRIVER); + break; + case SINK: + /* SINK: number of classes whose type is RECEIVER */ + num_grid_rr_nodes += get_grid_num_classes(grids[ix][iy], RECEIVER); + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid routing resource node!\n"); + exit(1); + } + } + } + + return num_grid_rr_nodes; +} + +/************************************************************************ + * For X-direction Channel: CHANX + * We pair each x-direction routing channel to the grid below it + * as they share the same coordinate + * + * As such, the range of CHANX coordinate starts from x = 1, y = 0 + * which is the grid (I/O) at the left bottom of the fabric + * + * As such, the range of CHANX coordinate ends to x = width - 2, y = height - 2 + * which is the grid at the top right of the core fabric + * Note that the I/O ring is + * + * TOP SIDE OF FPGA + * + * +-------------+ +-------------+ +---------------------+ + * | Grid | | Grid | ... | Grid | + * | [1][0] | | [2][0] | | [width-2][height-1] | + * +-------------+ +-------------+ +---------------------+ + * + * +-------------+ +-------------+ +---------------------+ + * | X-Channel | | X-Channel | ... | X-Channel | + * | [1][0] | | [2][0] | | [width-2][height-2] | + * +-------------+ +-------------+ +---------------------+ + * + * +-------------+ +-------------+ +---------------------+ + * | Grid | | Grid | ... | Grid | + * | [1][0] | | [2][0] | | [width-2][height-2] | + * +-------------+ +-------------+ +---------------------+ + * + * ... ... ... + * + * +-------------+ +-------------+ +--------------+ + * | X-Channel | | X-Channel | ... | X-Channel | + * | [1][1] | | [2][1] | | [width-2][1] | + * +-------------+ +-------------+ +--------------+ + * + * LEFT +-------------+ +-------------+ +--------------+ RIGHT + * SIDE | Grid | | Grid | ... | Grid | SIDE + * GRID | [1][1] | | [2][1] | | [width-2][1] | GRID + * +-------------+ +-------------+ +--------------+ + * + * +-------------+ +-------------+ +--------------+ + * | X-Channel | | X-Channel | ... | X-Channel | + * | [1][0] | | [2][0] | | [width-2][0] | + * +-------------+ +-------------+ +--------------+ + * + * +-------------+ +-------------+ +--------------+ + * | Grid | | Grid | ... | Grid | + * | [1][0] | | [2][0] | | [width-2][0] | + * +-------------+ +-------------+ +--------------+ + * + * BOTTOM SIDE OF FPGA + * + * The figure above describe how the X-direction routing channels are + * organized in a homogeneous FPGA fabric + * Note that we talk about general-purpose uni-directional routing architecture here + * It means that a routing track may span across multiple grids + * However, the hard limits are as follows + * All the routing tracks will start at the most LEFT routing channel + * All the routing tracks will end at the most RIGHT routing channel + * + * Things will become more complicated in terms of track starting and end + * in the context of heterogeneous FPGAs + * We may have a grid which span multiple column and rows, as exemplified in the figure below + * In such case, + * all the routing tracks [x-1][y] at the left side of the grid [x][y] are forced to end + * all the routing tracks [x+2][y] at the right side of the grid [x][y] are forced to start + * And there are no routing tracks inside the grid[x][y] + * It means that X-channel [x][y] & [x+1][y] will no exist + * + * +------------+ +-------------+ +-------------+ +--------------+ + * | X-Channel | | X-Channel | | X-Channel | | X-Channel | + * | [x-1][y+2] | | [x][y+2] | | [x+1][y+2] | | [x+2][y+2] | + * +------------+ +-------------+ +-------------+ +--------------+ + * + * +------------+ +-----------------------------------+ +--------------+ + * | Grid | | | | Grid | + * | [x-1][y+1] | | | | [x+2][y+1] | + * +------------+ | | +--------------+ + * | | + * +------------+ | | +--------------+ + * | X-channel | | Grid | | X-Channel | + * | [x-1][y] | | [x][y] - [x+1][y+1] | | [x+2][y] | + * +------------+ | | +--------------+ + * | | + * +------------+ | | +--------------+ + * | Grid | | | | Grid | + * | [x-1][y] | | | | [x+2][y] | + * +------------+ +-----------------------------------+ +--------------+ + * + * + * + ***********************************************************************/ +static size_t estimate_num_chanx_rr_nodes(const DeviceGrid& grids, + const size_t& chan_width, + const std::vector& segment_infs, + const bool& through_channel) { + size_t num_chanx_rr_nodes = 0; + + for (size_t iy = 0; iy < grids.height() - 1; ++iy) { + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + vtr::Point chanx_coord(ix, iy); + + /* Bypass if the routing channel does not exist when through channels are not allowed */ + if ((false == through_channel) + && (false == is_chanx_exist(grids, chanx_coord))) { + continue; + } + + bool force_start = false; + bool force_end = false; + + /* All the tracks have to start when + * - the routing channel touch the RIGHT side a heterogeneous block + * - the routing channel touch the LEFT side of FPGA + */ + if (true == is_chanx_right_to_multi_height_grid(grids, chanx_coord, through_channel)) { + force_start = true; + } + + /* All the tracks have to end when + * - the routing channel touch the LEFT side a heterogeneous block + * - the routing channel touch the RIGHT side of FPGA + */ + if (true == is_chanx_left_to_multi_height_grid(grids, chanx_coord, through_channel)) { + force_end = true; + } + + /* Evaluate if the routing channel locates in the middle of a grid */ + ChanNodeDetails chanx_details = build_unidir_chan_node_details(chan_width, grids.width() - 2, force_start, force_end, segment_infs); + /* When an INC_DIRECTION CHANX starts, we need a new rr_node */ + num_chanx_rr_nodes += chanx_details.get_num_starting_tracks(Direction::INC); + /* When an DEC_DIRECTION CHANX ends, we need a new rr_node */ + num_chanx_rr_nodes += chanx_details.get_num_ending_tracks(Direction::DEC); + } + } + + return num_chanx_rr_nodes; +} + +/************************************************************************ + * Estimate the number of CHANY rr_nodes for Y-direction routing channels + * The technical rationale is very similar to the X-direction routing channel + * Refer to the detailed explanation there + ***********************************************************************/ +static size_t estimate_num_chany_rr_nodes(const DeviceGrid& grids, + const size_t& chan_width, + const std::vector& segment_infs, + const bool& through_channel) { + size_t num_chany_rr_nodes = 0; + + for (size_t ix = 0; ix < grids.width() - 1; ++ix) { + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + vtr::Point chany_coord(ix, iy); + + /* Bypass if the routing channel does not exist when through channel are not allowed */ + if ((false == through_channel) + && (false == is_chany_exist(grids, chany_coord))) { + continue; + } + + bool force_start = false; + bool force_end = false; + + /* All the tracks have to start when + * - the routing channel touch the TOP side a heterogeneous block + * - the routing channel touch the BOTTOM side of FPGA + */ + if (true == is_chany_top_to_multi_width_grid(grids, chany_coord, through_channel)) { + force_start = true; + } + + /* All the tracks have to end when + * - the routing channel touch the BOTTOM side a heterogeneous block + * - the routing channel touch the TOP side of FPGA + */ + if (true == is_chany_bottom_to_multi_width_grid(grids, chany_coord, through_channel)) { + force_end = true; + } + + ChanNodeDetails chany_details = build_unidir_chan_node_details(chan_width, grids.height() - 2, force_start, force_end, segment_infs); + /* When an INC_DIRECTION CHANX starts, we need a new rr_node */ + num_chany_rr_nodes += chany_details.get_num_starting_tracks(Direction::INC); + /* When an DEC_DIRECTION CHANX ends, we need a new rr_node */ + num_chany_rr_nodes += chany_details.get_num_ending_tracks(Direction::DEC); + } + } + + return num_chany_rr_nodes; +} + +/************************************************************************ + * Estimate the number of nodes by each type in a routing resource graph + ***********************************************************************/ +static std::vector estimate_num_rr_nodes(const DeviceGrid& grids, + const vtr::Point& chan_width, + const std::vector& segment_infs, + const bool& through_channel) { + /* Reset the OPIN, IPIN, SOURCE, SINK counter to be zero */ + std::vector num_rr_nodes_per_type(NUM_RR_TYPES, 0); + + /** + * 1 Find number of rr nodes related to grids + */ + num_rr_nodes_per_type[OPIN] = estimate_num_grid_rr_nodes_by_type(grids, OPIN); + num_rr_nodes_per_type[IPIN] = estimate_num_grid_rr_nodes_by_type(grids, IPIN); + num_rr_nodes_per_type[SOURCE] = estimate_num_grid_rr_nodes_by_type(grids, SOURCE); + num_rr_nodes_per_type[SINK] = estimate_num_grid_rr_nodes_by_type(grids, SINK); + + /** + * 2. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * + * SPECIAL for fringes: + * All segments will start and ends with no exception + * + * IMPORTANT: we should be aware that channel width maybe different + * in X-direction and Y-direction channels!!! + * So we will load segment details for different channels + */ + num_rr_nodes_per_type[CHANX] = estimate_num_chanx_rr_nodes(grids, + chan_width.x(), + segment_infs, + through_channel); + num_rr_nodes_per_type[CHANY] = estimate_num_chany_rr_nodes(grids, + chan_width.y(), + segment_infs, + through_channel); + + return num_rr_nodes_per_type; +} + +/************************************************************************ + * Allocate rr_nodes to a rr_graph object + * This function just allocate the memory and ensure its efficiency + * It will NOT fill detailed information for each node!!! + * + * Note: ensure that there are NO nodes in the rr_graph + ***********************************************************************/ +void alloc_tileable_rr_graph_nodes(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const vtr::Point& chan_width, + const std::vector& segment_infs, + const bool& through_channel) { + VTR_ASSERT(0 == rr_graph_builder.rr_nodes().size()); + + std::vector num_rr_nodes_per_type = estimate_num_rr_nodes(grids, + chan_width, + segment_infs, + through_channel); + + /* Reserve the number of node to be memory efficient */ + size_t num_nodes = 0; + for (const size_t& num_node_per_type : num_rr_nodes_per_type) { + num_nodes += num_node_per_type; + } + + rr_graph_builder.reserve_nodes(num_nodes); + + rr_node_driver_switches.resize(num_nodes); +} + +/************************************************************************ + * Configure OPIN rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_opin_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const vtr::Point& grid_coordinate, + const t_grid_tile& cur_grid, + const e_side& io_side, + const RRSwitchId& delayless_switch) { + SideManager io_side_manager(io_side); + + /* Walk through the width height of each grid, + * get pins and configure the rr_nodes + */ + for (int width = 0; width < cur_grid.type->width; ++width) { + for (int height = 0; height < cur_grid.type->height; ++height) { + /* Walk through sides */ + for (e_side side : SIDES) { + SideManager side_manager(side); + /* skip unwanted sides */ + if ((true == is_io_type(cur_grid.type)) + && (side != io_side_manager.to_size_t()) && (NUM_SIDES != io_side)) { + continue; + } + /* Find OPINs */ + /* Configure pins by pins */ + std::vector opin_list = get_grid_side_pins(cur_grid, DRIVER, side_manager.get_side(), + width, height); + for (const int& pin_num : opin_list) { + /* Create a new node and fill information */ + RRNodeId node = rr_graph_builder.create_node(grid_coordinate.x() + width, grid_coordinate.y() + height, OPIN, pin_num, side); + + /* node bounding box */ + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x() + width, + grid_coordinate.y() + height, + grid_coordinate.x() + width, + grid_coordinate.y() + height); + rr_graph_builder.add_node_side(node, side_manager.get_side()); + rr_graph_builder.set_node_pin_num(node, pin_num); + + rr_graph_builder.set_node_capacity(node, 1); + + /* cost index is a FIXED value for OPIN */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(OPIN_COST_INDEX)); + + /* Switch info */ + rr_node_driver_switches[node] = delayless_switch; + + /* RC data */ + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } /* End of loading OPIN rr_nodes */ + } /* End of side enumeration */ + } /* End of height enumeration */ + } /* End of width enumeration */ +} + +/************************************************************************ + * Configure IPIN rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_ipin_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const vtr::Point& grid_coordinate, + const t_grid_tile& cur_grid, + const e_side& io_side, + const RRSwitchId& wire_to_ipin_switch) { + SideManager io_side_manager(io_side); + + /* Walk through the width and height of each grid, + * get pins and configure the rr_nodes + */ + for (int width = 0; width < cur_grid.type->width; ++width) { + for (int height = 0; height < cur_grid.type->height; ++height) { + /* Walk through sides */ + for (e_side side : SIDES) { + SideManager side_manager(side); + /* skip unwanted sides */ + if ((true == is_io_type(cur_grid.type)) + && (side != io_side_manager.to_size_t()) && (NUM_SIDES != io_side)) { + continue; + } + + /* Find IPINs */ + /* Configure pins by pins */ + std::vector ipin_list = get_grid_side_pins(cur_grid, RECEIVER, side_manager.get_side(), width, height); + for (const int& pin_num : ipin_list) { + /* Create a new node and fill information */ + RRNodeId node = rr_graph_builder.create_node(grid_coordinate.x() + width, grid_coordinate.y() + height, IPIN, pin_num, side); + + /* node bounding box */ + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x() + width, + grid_coordinate.y() + height, + grid_coordinate.x() + width, + grid_coordinate.y() + height); + rr_graph_builder.add_node_side(node, side_manager.get_side()); + rr_graph_builder.set_node_pin_num(node, pin_num); + + rr_graph_builder.set_node_capacity(node, 1); + + /* cost index is a FIXED value for OPIN */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(IPIN_COST_INDEX)); + + /* Switch info */ + rr_node_driver_switches[node] = wire_to_ipin_switch; + + /* RC data */ + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } /* End of loading IPIN rr_nodes */ + } /* End of side enumeration */ + } /* End of height enumeration */ + } /* End of width enumeration */ +} + +/************************************************************************ + * Configure SOURCE rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_source_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const vtr::Point& grid_coordinate, + const t_grid_tile& cur_grid, + const e_side& io_side, + const RRSwitchId& delayless_switch) { + SideManager io_side_manager(io_side); + + /* Set a SOURCE rr_node for each DRIVER class */ + for (size_t iclass = 0; iclass < cur_grid.type->class_inf.size(); ++iclass) { + /* Set a SINK rr_node for the OPIN */ + if (DRIVER != cur_grid.type->class_inf[iclass].type) { + continue; + } + + /* Create a new node and fill information */ + RRNodeId node = rr_graph_builder.create_node(grid_coordinate.x(), grid_coordinate.y(), SOURCE, iclass); + + /* node bounding box */ + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x(), + grid_coordinate.y(), + grid_coordinate.x() + cur_grid.type->width - 1, + grid_coordinate.y() + cur_grid.type->height - 1); + rr_graph_builder.set_node_class_num(node, iclass); + + /* The capacity should be the number of pins in this class*/ + rr_graph_builder.set_node_capacity(node, cur_grid.type->class_inf[iclass].num_pins); + + /* cost index is a FIXED value for SOURCE */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(SOURCE_COST_INDEX)); + + /* Switch info */ + rr_node_driver_switches[node] = delayless_switch; + + /* RC data */ + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } /* End of class enumeration */ +} + +/************************************************************************ + * Configure SINK rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_sink_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const vtr::Point& grid_coordinate, + const t_grid_tile& cur_grid, + const e_side& io_side, + const RRSwitchId& delayless_switch) { + SideManager io_side_manager(io_side); + + /* Set a SINK rr_node for each RECEIVER class */ + for (size_t iclass = 0; iclass < cur_grid.type->class_inf.size(); ++iclass) { + /* Set a SINK rr_node for the OPIN */ + if (RECEIVER != cur_grid.type->class_inf[iclass].type) { + continue; + } + + /* Create a new node and fill information */ + RRNodeId node = rr_graph_builder.create_node(grid_coordinate.x(), grid_coordinate.y(), SINK, iclass); + + /* node bounding box */ + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x(), + grid_coordinate.y(), + grid_coordinate.x() + cur_grid.type->width - 1, + grid_coordinate.y() + cur_grid.type->height - 1); + rr_graph_builder.set_node_class_num(node, iclass); + + rr_graph_builder.set_node_capacity(node, 1); + + /* The capacity should be the number of pins in this class*/ + rr_graph_builder.set_node_capacity(node, cur_grid.type->class_inf[iclass].num_pins); + + /* cost index is a FIXED value for SINK */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(SINK_COST_INDEX)); + + /* Switch info */ + rr_node_driver_switches[node] = delayless_switch; + + /* RC data */ + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } /* End of class enumeration */ +} + +/************************************************************************ + * Create all the rr_nodes for grids + ***********************************************************************/ +static void load_grid_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const RRSwitchId& wire_to_ipin_switch, + const RRSwitchId& delayless_switch) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + for (size_t ix = 0; ix < grids.width(); ++ix) { + /* Skip EMPTY tiles */ + if (true == is_empty_type(grids[ix][iy].type)) { + continue; + } + + /* We only build rr_nodes for grids with width_offset = 0 and height_offset = 0 */ + if ((0 < grids[ix][iy].width_offset) + || (0 < grids[ix][iy].height_offset)) { + continue; + } + + vtr::Point grid_coordinate(ix, iy); + enum e_side io_side = NUM_SIDES; + + std::vector wanted_sides{TOP, RIGHT, BOTTOM, LEFT}; + + /* If this is the block on borders, we consider IO side */ + if (true == is_io_type(grids[ix][iy].type)) { + vtr::Point io_device_size(grids.width() - 1, grids.height() - 1); + io_side = determine_io_grid_pin_side(io_device_size, grid_coordinate); + wanted_sides.clear(); + wanted_sides.push_back(io_side); + } + + for (e_side side : wanted_sides) { + for (int width_offset = 0; width_offset < grids[ix][iy].type->width; ++width_offset) { + int x_tile = ix + width_offset; + for (int height_offset = 0; height_offset < grids[ix][iy].type->height; ++height_offset) { + int y_tile = iy + height_offset; + rr_graph_builder.node_lookup().reserve_nodes(x_tile, y_tile, OPIN, grids[ix][iy].type->num_pins, side); + rr_graph_builder.node_lookup().reserve_nodes(x_tile, y_tile, IPIN, grids[ix][iy].type->num_pins, side); + } + } + } + + /* Configure source rr_nodes for this grid */ + load_one_grid_source_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + grid_coordinate, + grids[ix][iy], + io_side, + delayless_switch); + + /* Configure sink rr_nodes for this grid */ + load_one_grid_sink_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + grid_coordinate, + grids[ix][iy], + io_side, + delayless_switch); + + /* Configure opin rr_nodes for this grid */ + load_one_grid_opin_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + grid_coordinate, + grids[ix][iy], + io_side, + delayless_switch); + + /* Configure ipin rr_nodes for this grid */ + load_one_grid_ipin_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + grid_coordinate, + grids[ix][iy], + io_side, + wire_to_ipin_switch); + } + } + //Copy the SOURCE/SINK nodes to all offset positions for blocks with width > 1 and/or height > 1 + // This ensures that look-ups on non-root locations will still find the correct SOURCE/SINK + for (size_t x = 0; x < grids.width(); x++) { + for (size_t y = 0; y < grids.height(); y++) { + int width_offset = grids[x][y].width_offset; + int height_offset = grids[x][y].height_offset; + if (width_offset != 0 || height_offset != 0) { + int root_x = x - width_offset; + int root_y = y - height_offset; + + rr_graph_builder.node_lookup().mirror_nodes(vtr::Point(root_x, root_y), + vtr::Point(x, y), + SOURCE, + SIDES[0]); + rr_graph_builder.node_lookup().mirror_nodes(vtr::Point(root_x, root_y), + vtr::Point(x, y), + SINK, + SIDES[0]); + } + } + } +} + +/************************************************************************ + * Initialize the basic information of routing track rr_nodes + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + ***********************************************************************/ +static void load_one_chan_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + const vtr::Point& chan_coordinate, + const t_rr_type& chan_type, + ChanNodeDetails& chan_details, + const std::vector& segment_infs, + const int& cost_index_offset) { + /* Check each node_id(potential ptc_num) in the channel : + * If this is a starting point, we set a new rr_node with xlow/ylow, ptc_num + * If this is a ending point, we set xhigh/yhigh and track_ids + * For other nodes, we set changes in track_ids + */ + for (size_t itrack = 0; itrack < chan_details.get_chan_width(); ++itrack) { + /* For INC direction, a starting point requires a new chan rr_node */ + if (((true == chan_details.is_track_start(itrack)) + && (Direction::INC == chan_details.get_track_direction(itrack))) + /* For DEC direction, an ending point requires a new chan rr_node */ + || ((true == chan_details.is_track_end(itrack)) + && (Direction::DEC == chan_details.get_track_direction(itrack)))) { + /* Create a new chan rr_node */ + RRNodeId node = rr_graph_builder.create_node(chan_coordinate.x(), chan_coordinate.y(), chan_type, itrack); + + rr_graph_builder.set_node_direction(node, chan_details.get_track_direction(itrack)); + rr_graph_builder.add_node_track_num(node, chan_coordinate, itrack); + rr_node_track_ids[node].push_back(itrack); + + rr_graph_builder.set_node_capacity(node, 1); + + /* assign switch id */ + size_t seg_id = chan_details.get_track_segment_id(itrack); + rr_node_driver_switches[node] = RRSwitchId(segment_infs[seg_id].arch_opin_switch); + + /* Update chan_details with node_id */ + chan_details.set_track_node_id(itrack, size_t(node)); + + /* cost index depends on the segment index */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(cost_index_offset + seg_id)); + /* Finish here, go to next */ + } + + /* For INC direction, an ending point requires an update on xhigh and yhigh */ + if (((true == chan_details.is_track_end(itrack)) + && (Direction::INC == chan_details.get_track_direction(itrack))) + || + /* For DEC direction, an starting point requires an update on xlow and ylow */ + ((true == chan_details.is_track_start(itrack)) + && (Direction::DEC == chan_details.get_track_direction(itrack)))) { + /* Get the node_id */ + const RRNodeId& rr_node_id = RRNodeId(chan_details.get_track_node_id(itrack)); + + /* Do a quick check, make sure we do not mistakenly modify other nodes */ + VTR_ASSERT(chan_type == rr_graph.node_type(rr_node_id)); + VTR_ASSERT(chan_details.get_track_direction(itrack) == rr_graph.node_direction(rr_node_id)); + + /* set xhigh/yhigh and push changes to track_ids */ + rr_graph_builder.set_node_coordinates(rr_node_id, rr_graph.node_xlow(rr_node_id), + rr_graph.node_ylow(rr_node_id), + chan_coordinate.x(), + chan_coordinate.y()); + + /* Do not update track_ids for length-1 wires, they should have only 1 track_id */ + if ((rr_graph.node_xhigh(rr_node_id) > rr_graph.node_xlow(rr_node_id)) + || (rr_graph.node_yhigh(rr_node_id) > rr_graph.node_ylow(rr_node_id))) { + rr_node_track_ids[rr_node_id].push_back(itrack); + rr_graph_builder.add_node_track_num(rr_node_id, chan_coordinate, itrack); + } + /* Finish here, go to next */ + } + + /* Finish processing starting and ending tracks */ + if ((true == chan_details.is_track_start(itrack)) + || (true == chan_details.is_track_end(itrack))) { + /* Finish here, go to next */ + continue; + } + + /* For other nodes, we get the node_id and just update track_ids */ + /* Ensure those nodes are neither starting nor ending points */ + VTR_ASSERT((false == chan_details.is_track_start(itrack)) + && (false == chan_details.is_track_end(itrack))); + + /* Get the node_id */ + const RRNodeId& rr_node_id = RRNodeId(chan_details.get_track_node_id(itrack)); + + /* Do a quick check, make sure we do not mistakenly modify other nodes */ + VTR_ASSERT(chan_type == rr_graph.node_type(rr_node_id)); + VTR_ASSERT(chan_details.get_track_direction(itrack) == rr_graph.node_direction(rr_node_id)); + + /* Deposit xhigh and yhigh using the current chan_coordinate + * We will update when this track ends + */ + rr_graph_builder.set_node_coordinates(rr_node_id, rr_graph.node_xlow(rr_node_id), + rr_graph.node_ylow(rr_node_id), + chan_coordinate.x(), + chan_coordinate.y()); + + /* Update track_ids */ + rr_node_track_ids[rr_node_id].push_back(itrack); + rr_graph_builder.add_node_track_num(rr_node_id, chan_coordinate, itrack); + /* Finish here, go to next */ + } +} + +/************************************************************************ + * Initialize the basic information of X-channel rr_nodes: + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + * grid_info : pb_graph_pin + ***********************************************************************/ +static void load_chanx_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + const DeviceGrid& grids, + const size_t& chan_width, + const std::vector& segment_infs, + const bool& through_channel) { + /* For X-direction Channel: CHANX */ + for (size_t iy = 0; iy < grids.height() - 1; ++iy) { + /* Keep a vector of node_ids for the channels, because we will rotate them when walking through ix */ + std::vector track_node_ids; + + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + vtr::Point chanx_coord(ix, iy); + + /* Bypass if the routing channel does not exist when through channels are not allowed */ + if ((false == through_channel) + && (false == is_chanx_exist(grids, chanx_coord))) { + continue; + } + + bool force_start = false; + bool force_end = false; + + /* All the tracks have to start when + * - the routing channel touch the RIGHT side a heterogeneous block + * - the routing channel touch the LEFT side of FPGA + */ + if (true == is_chanx_right_to_multi_height_grid(grids, chanx_coord, through_channel)) { + force_start = true; + } + + /* All the tracks have to end when + * - the routing channel touch the LEFT side a heterogeneous block + * - the routing channel touch the RIGHT side of FPGA + */ + if (true == is_chanx_left_to_multi_height_grid(grids, chanx_coord, through_channel)) { + force_end = true; + } + + ChanNodeDetails chanx_details = build_unidir_chan_node_details(chan_width, grids.width() - 2, + force_start, force_end, segment_infs); + /* Force node_ids from the previous chanx */ + if (0 < track_node_ids.size()) { + /* Rotate should be done based on a typical case of routing tracks. + * Tracks on the borders are not regularly started and ended, + * which causes the node_rotation malfunction + */ + ChanNodeDetails chanx_details_tt = build_unidir_chan_node_details(chan_width, grids.width() - 2, + false, false, segment_infs); + chanx_details_tt.set_track_node_ids(track_node_ids); + + /* TODO: + * Do NOT rotate the tracks when the routing channel + * locates inside a multi-height and multi-width grid + * Let the routing channel passing through the grid (if through channel is allowed!) + * An example: + * + * +------------------------------ + * | | + * | Grid | + * track0 ----->+-----------------------------+----> track0 + * | | + */ + if (true == is_chanx_exist(grids, chanx_coord, through_channel)) { + /* Rotate the chanx_details by an offset of ix - 1, the distance to the most left channel */ + /* For INC_DIRECTION, we use clockwise rotation + * node_id A ----> -----> node_id D + * node_id B ----> / ----> node_id A + * node_id C ----> / ----> node_id B + * node_id D ----> ----> node_id C + */ + chanx_details_tt.rotate_track_node_id(1, Direction::INC, true); + /* For DEC_DIRECTION, we use clockwise rotation + * node_id A <----- <----- node_id B + * node_id B <----- \ <----- node_id C + * node_id C <----- \ <----- node_id D + * node_id D <----- <----- node_id A + */ + chanx_details_tt.rotate_track_node_id(1, Direction::DEC, false); + } + + track_node_ids = chanx_details_tt.get_track_node_ids(); + chanx_details.set_track_node_ids(track_node_ids); + } + + /* Configure CHANX in this channel */ + load_one_chan_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + chanx_coord, CHANX, + chanx_details, + segment_infs, + CHANX_COST_INDEX_START); + /* Get a copy of node_ids */ + track_node_ids = chanx_details.get_track_node_ids(); + } + } +} + +/************************************************************************ + * Initialize the basic information of Y-channel rr_nodes: + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + ***********************************************************************/ +static void load_chany_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + const DeviceGrid& grids, + const size_t& chan_width, + const std::vector& segment_infs, + const bool& through_channel) { + /* For Y-direction Channel: CHANY */ + for (size_t ix = 0; ix < grids.width() - 1; ++ix) { + /* Keep a vector of node_ids for the channels, because we will rotate them when walking through ix */ + std::vector track_node_ids; + + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + vtr::Point chany_coord(ix, iy); + + /* Bypass if the routing channel does not exist when through channel are not allowed */ + if ((false == through_channel) + && (false == is_chany_exist(grids, chany_coord))) { + continue; + } + + bool force_start = false; + bool force_end = false; + + /* All the tracks have to start when + * - the routing channel touch the TOP side a heterogeneous block + * - the routing channel touch the BOTTOM side of FPGA + */ + if (true == is_chany_top_to_multi_width_grid(grids, chany_coord, through_channel)) { + force_start = true; + } + + /* All the tracks have to end when + * - the routing channel touch the BOTTOM side a heterogeneous block + * - the routing channel touch the TOP side of FPGA + */ + if (true == is_chany_bottom_to_multi_width_grid(grids, chany_coord, through_channel)) { + force_end = true; + } + + ChanNodeDetails chany_details = build_unidir_chan_node_details(chan_width, grids.height() - 2, + force_start, force_end, segment_infs); + /* Force node_ids from the previous chany + * This will not be applied when the routing channel is cut off (force to start) + */ + if (0 < track_node_ids.size()) { + /* Rotate should be done based on a typical case of routing tracks. + * Tracks on the borders are not regularly started and ended, + * which causes the node_rotation malfunction + */ + ChanNodeDetails chany_details_tt = build_unidir_chan_node_details(chan_width, grids.height() - 2, + false, false, segment_infs); + + chany_details_tt.set_track_node_ids(track_node_ids); + + /* TODO: + * Do NOT rotate the tracks when the routing channel + * locates inside a multi-height and multi-width grid + * Let the routing channel passing through the grid (if through channel is allowed!) + * An example: + * + * +------------------------------ + * | | + * | Grid | + * track0 ----->+-----------------------------+----> track0 + * | | + * we should rotate only once at the bottom side of a grid + */ + if (true == is_chany_exist(grids, chany_coord, through_channel)) { + /* Rotate the chany_details by an offset of 1*/ + /* For INC_DIRECTION, we use clockwise rotation + * node_id A ----> -----> node_id D + * node_id B ----> / ----> node_id A + * node_id C ----> / ----> node_id B + * node_id D ----> ----> node_id C + */ + chany_details_tt.rotate_track_node_id(1, Direction::INC, true); + /* For DEC_DIRECTION, we use clockwise rotation + * node_id A <----- <----- node_id B + * node_id B <----- \ <----- node_id C + * node_id C <----- \ <----- node_id D + * node_id D <----- <----- node_id A + */ + chany_details_tt.rotate_track_node_id(1, Direction::DEC, false); + } + + track_node_ids = chany_details_tt.get_track_node_ids(); + chany_details.set_track_node_ids(track_node_ids); + } + /* Configure CHANX in this channel */ + load_one_chan_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + chany_coord, CHANY, + chany_details, + segment_infs, + CHANX_COST_INDEX_START + segment_infs.size()); + /* Get a copy of node_ids */ + track_node_ids = chany_details.get_track_node_ids(); + } + } +} + +/************************************************************************ + * Reverse the track_ids of CHANX and CHANY nodes in DEC_DIRECTION + * This is required as the track ids are allocated in the sequence + * of incrementing x and y + * However, DEC direction routing tracks should have a reversed sequence in + * track ids + ***********************************************************************/ +static void reverse_dec_chan_rr_node_track_ids(const RRGraphView& rr_graph, + std::map>& rr_node_track_ids) { + // this should call rr_graph_builder to do the job + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass condition: only focus on CHANX and CHANY in DEC_DIRECTION */ + if (CHANX != rr_graph.node_type(node) && CHANY != rr_graph.node_type(node)) { + continue; + } + /* Reach here, we must have a node of CHANX or CHANY */ + if (Direction::DEC != rr_graph.node_direction(node)) { + continue; + } + std::reverse(rr_node_track_ids[node].begin(), + rr_node_track_ids[node].end()); + } +} + +/************************************************************************ + * Create all the rr_nodes covering both grids and routing channels + ***********************************************************************/ +void create_tileable_rr_graph_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const vtr::Point& chan_width, + const std::vector& segment_infs, + const RRSwitchId& wire_to_ipin_switch, + const RRSwitchId& delayless_switch, + const bool& through_channel) { + /* Allocates and loads all the structures needed for fast lookups of the * + * index of an rr_node. rr_node_indices is a matrix containing the index * + * of the *first* rr_node at a given (i,j) location. */ + + /* Alloc the lookup table + * .. warning: It is mandatory. There are bugs in resize() when called incrementally in RRSpatialLookup. + * When comment the following block out, you will see errors */ + for (t_rr_type rr_type : RR_TYPES) { + if (rr_type == CHANX) { + rr_graph_builder.node_lookup().resize_nodes(grids.height(), grids.width(), rr_type, NUM_SIDES); + } else { + rr_graph_builder.node_lookup().resize_nodes(grids.width(), grids.height(), rr_type, NUM_SIDES); + } + } + + load_grid_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + grids, + wire_to_ipin_switch, + delayless_switch); + + load_chanx_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + grids, + chan_width.x(), + segment_infs, + through_channel); + + load_chany_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + grids, + chan_width.y(), + segment_infs, + through_channel); + + reverse_dec_chan_rr_node_track_ids(rr_graph, + rr_node_track_ids); + + /* Update node look-up for CHANX and CHANY nodes */ + for (const RRNodeId& rr_node_id : rr_graph.nodes()) { + if (CHANX == rr_graph.node_type(rr_node_id) || CHANY == rr_graph.node_type(rr_node_id)) { + rr_graph_builder.add_track_node_to_lookup(rr_node_id); + } + } +} diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.h b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.h new file mode 100644 index 00000000000..1372513200a --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.h @@ -0,0 +1,42 @@ +#ifndef TILEABLE_RR_GRAPH_NODE_BUILDER_H +#define TILEABLE_RR_GRAPH_NODE_BUILDER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +/* Headers from readarch library */ +#include "physical_types.h" + +/* Headers from vpr library */ +#include "device_grid.h" +#include "rr_node_types.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +void alloc_tileable_rr_graph_nodes(RRGraphBuilder& rr_graph_builder, + vtr::vector& driver_switches, + const DeviceGrid& grids, + const vtr::Point& chan_width, + const std::vector& segment_infs, + const bool& through_channel); + +void create_tileable_rr_graph_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const vtr::Point& chan_width, + const std::vector& segment_infs, + const RRSwitchId& wire_to_ipin_switch, + const RRSwitchId& delayless_switch, + const bool& through_channel); + +#endif diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 53f17a0b82d..02bcb55ba18 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -20,6 +20,7 @@ #include "pack_types.h" #include "device_grid.h" #include "timing_fail_error.h" +#include "route_constraint.h" #include "re_cluster_util.h" /* This module contains subroutines that are used in several unrelated parts * @@ -74,8 +75,6 @@ static AtomPinId find_atom_pin_for_pb_route_id(ClusterBlockId clb, int pb_route_ static bool block_type_contains_blif_model(t_logical_block_type_ptr type, const std::regex& blif_model_regex); static bool pb_type_contains_blif_model(const t_pb_type* pb_type, const std::regex& blif_model_regex); -static t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_block_type_ptr type); -static void free_pb_graph_pin_lookup_from_index(t_pb_graph_pin** pb_graph_pin_lookup_from_type); /******************** Subroutine definitions *********************************/ @@ -1228,7 +1227,7 @@ static void load_pb_graph_pin_lookup_from_index_rec(t_pb_graph_pin** pb_graph_pi } /* Create a lookup that returns a pb_graph_pin pointer given the pb_graph_pin index */ -static t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_block_type_ptr type) { +t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_block_type_ptr type) { t_pb_graph_pin** pb_graph_pin_lookup_from_type = nullptr; t_pb_graph_node* pb_graph_head = type->pb_graph_head; @@ -1256,7 +1255,7 @@ static t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_ } /* Free pb_graph_pin lookup array */ -static void free_pb_graph_pin_lookup_from_index(t_pb_graph_pin** pb_graph_pin_lookup_from_type) { +void free_pb_graph_pin_lookup_from_index(t_pb_graph_pin** pb_graph_pin_lookup_from_type) { if (pb_graph_pin_lookup_from_type == nullptr) { return; } @@ -2274,6 +2273,22 @@ bool is_inter_cluster_node(t_physical_tile_type_ptr physical_tile, } } +void apply_route_constraints(VprConstraints& vpr_constraint) { + ClusteringContext& mutable_cluster_ctx = g_vpr_ctx.mutable_clustering(); + for (auto net_id : mutable_cluster_ctx.clb_nlist.nets()) { + std::string net_name = mutable_cluster_ctx.clb_nlist.net_name(net_id); + const RouteConstraint rc = vpr_constraint.get_route_constraint_by_net_name(net_name); + if (rc.is_valid()) { + mutable_cluster_ctx.clb_nlist.set_net_is_global(net_id, true); + if (rc.route_model() == "route") { + mutable_cluster_ctx.clb_nlist.set_net_is_ignored(net_id, false); + } else { + mutable_cluster_ctx.clb_nlist.set_net_is_ignored(net_id, true); + } + } + } +} + int get_rr_node_max_ptc(const RRGraphView& rr_graph_view, RRNodeId node_id, bool is_flat) { diff --git a/vpr/src/util/vpr_utils.h b/vpr/src/util/vpr_utils.h index 67214a69a60..3291e22e46f 100644 --- a/vpr/src/util/vpr_utils.h +++ b/vpr/src/util/vpr_utils.h @@ -180,7 +180,9 @@ bool primitive_type_feasible(AtomBlockId blk_id, const t_pb_type* cur_pb_type); t_pb_graph_pin* get_pb_graph_node_pin_from_model_port_pin(const t_model_ports* model_port, const int model_pin, const t_pb_graph_node* pb_graph_node); const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLookup& netlist_lookup, const AtomPinId pin_id); t_pb_graph_pin* get_pb_graph_node_pin_from_block_pin(ClusterBlockId iblock, int ipin); +t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_block_type_ptr type); vtr::vector alloc_and_load_pin_id_to_pb_mapping(); +void free_pb_graph_pin_lookup_from_index(t_pb_graph_pin** pb_graph_pin_lookup_from_type); void free_pin_id_to_pb_mapping(vtr::vector& pin_id_to_pb_mapping); std::tuple get_cluster_blk_physical_spec(ClusterBlockId cluster_blk_id); @@ -306,4 +308,8 @@ t_arch_switch_inf create_internal_arch_sw(float delay); void add_pb_child_to_list(std::list& pb_list, const t_pb* parent_pb); +// apply route constraints for route flow +class VprConstraints; +void apply_route_constraints(VprConstraints& constraint); + #endif diff --git a/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml b/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml new file mode 100644 index 00000000000..5193264205e --- /dev/null +++ b/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml b/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml new file mode 100644 index 00000000000..6b027fe6a21 --- /dev/null +++ b/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml @@ -0,0 +1,635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/benchmarks/microbenchmarks/and_clk.blif b/vtr_flow/benchmarks/microbenchmarks/and_clk.blif new file mode 100644 index 00000000000..3f31b82b8d7 --- /dev/null +++ b/vtr_flow/benchmarks/microbenchmarks/and_clk.blif @@ -0,0 +1,12 @@ + +.model discrete_dffn +.inputs clk_ni d_i +.outputs d_o + +.names clk_ni int_clk_reg_exp_0 +0 1 + +.subckt dff D=d_i Q=d_o C=int_clk_reg_exp_0 + +.end + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt new file mode 100755 index 00000000000..fbf3358dd11 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt @@ -0,0 +1,29 @@ +############################################ +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/blif/4 + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=diffeq.blif +circuit_list_add=ex5p.blif +circuit_list_add=s298.blif + +# Add architectures to list to sweep +arch_list_add=k4_N4_tileable_90nm.xml + +# 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_params=-starting_stage vpr -track_memory_usage + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt new file mode 100644 index 00000000000..8576e7f7dc7 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt @@ -0,0 +1,4 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem yosys_synth_time max_yosys_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 placed_wirelength_est place_mem place_time place_quench_time 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 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_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_total_timing_analysis_time crit_path_total_sta_time +k4_N4_tileable_90nm.xml diffeq.blif common 9.82 vpr 61.45 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 417 64 -1 -1 success v8.0.0-6725-gff83963de-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.3.0 on Linux-4.4.0-19041-Microsoft x86_64 2022-11-01T15:38:22 LAPTOP-CVNHOGSN /home/tangxifan/vtr-verilog-to-routing/vtr_flow/tasks 62920 64 39 1935 1974 1 1104 520 23 23 529 clb auto 18.8 MiB 0.14 10227 61.2 MiB 0.57 0.01 6.71028 -1587.65 -6.71028 6.71028 0.25 0.0007736 0.0005813 0.0896507 0.0699632 34 17207 50 983127 929624 921133. 1741.27 7.25 0.536827 0.433199 14874 22 8645 29434 2727679 902790 6.8984 6.8984 -1779.84 -6.8984 0 0 1.17586e+06 2222.80 0.07 0.39 0.058014 0.0505961 +k4_N4_tileable_90nm.xml ex5p.blif common 15.22 vpr 52.07 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 346 8 -1 -1 success v8.0.0-6725-gff83963de-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.3.0 on Linux-4.4.0-19041-Microsoft x86_64 2022-11-01T15:38:22 LAPTOP-CVNHOGSN /home/tangxifan/vtr-verilog-to-routing/vtr_flow/tasks 53324 8 63 1072 1135 0 909 417 21 21 441 clb auto 14.2 MiB 0.10 11660 52.1 MiB 0.46 0.00 7.1886 -311.061 -7.1886 nan 0.20 0.000496 0.0003719 0.0573232 0.0458367 56 22886 46 804782 771343 1.13430e+06 2572.11 12.88 0.310125 0.253318 18533 21 8880 28500 4332796 1534540 7.97924 nan -339.269 -7.97924 0 0 1.45200e+06 3292.52 0.08 0.50 0.0368923 0.0322975 +k4_N4_tileable_90nm.xml s298.blif common 21.05 vpr 75.85 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 571 4 -1 -1 success v8.0.0-6725-gff83963de-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.3.0 on Linux-4.4.0-19041-Microsoft x86_64 2022-11-01T15:38:22 LAPTOP-CVNHOGSN /home/tangxifan/vtr-verilog-to-routing/vtr_flow/tasks 77672 4 6 1942 1948 1 1193 581 26 26 676 clb auto 21.3 MiB 0.14 14254 75.9 MiB 0.69 0.01 13.2884 -103 -13.2884 13.2884 0.33 0.0008181 0.0006197 0.0966404 0.0764946 42 26119 42 1.28409e+06 1.27294e+06 1.41510e+06 2093.35 17.58 0.615507 0.493511 22147 19 9442 45483 4771423 1297512 13.6006 13.6006 -107.558 -13.6006 0 0 1.86822e+06 2763.64 0.19 0.67 0.0640435 0.0560669 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/config.txt new file mode 100644 index 00000000000..a3ceed7c14d --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/config.txt @@ -0,0 +1,27 @@ +############################################## +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/microbenchmarks + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=and_clk.blif + +# Add architectures to list to sweep +arch_list_add=k4_frac_N4_fracff_localClkGen_40nm.xml + +# 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 =-starting_stage vpr --route_chan_width 300 --max_router_iterations 400 --router_lookahead map --initial_pres_fac 1.0 --router_profiler_astar_fac 1.5 --seed 3 --read_vpr_constraints ../../../../floor_plan.xml:../../../../route_constraint.xml --write_vpr_constraints vpr_constraints.xml --clock_modeling ideal diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/golden_results.txt new file mode 100644 index 00000000000..1de222ff286 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/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 placed_wirelength_est place_mem place_time place_quench_time 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 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 crit_path_total_internal_heap_pushes crit_path_total_internal_heap_pops crit_path_total_external_heap_pushes crit_path_total_external_heap_pops crit_path_total_external_SOURCE_pushes crit_path_total_external_SOURCE_pops crit_path_total_internal_SOURCE_pushes crit_path_total_internal_SOURCE_pops crit_path_total_external_SINK_pushes crit_path_total_external_SINK_pops crit_path_total_internal_SINK_pushes crit_path_total_internal_SINK_pops crit_path_total_external_IPIN_pushes crit_path_total_external_IPIN_pops crit_path_total_internal_IPIN_pushes crit_path_total_internal_IPIN_pops crit_path_total_external_OPIN_pushes crit_path_total_external_OPIN_pops crit_path_total_internal_OPIN_pushes crit_path_total_internal_OPIN_pops crit_path_total_external_CHANX_pushes crit_path_total_external_CHANX_pops crit_path_total_internal_CHANX_pushes crit_path_total_internal_CHANX_pops crit_path_total_external_CHANY_pushes crit_path_total_external_CHANY_pops crit_path_total_internal_CHANY_pushes crit_path_total_internal_CHANY_pops crit_path_rt_node_SOURCE_pushes crit_path_rt_node_SINK_pushes crit_path_rt_node_IPIN_pushes crit_path_rt_node_OPIN_pushes crit_path_rt_node_CHANX_pushes crit_path_rt_node_CHANY_pushes crit_path_adding_all_rt crit_path_adding_high_fanout_rt crit_path_total_number_of_adding_all_rt_from_calling_high_fanout_rt 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 +k4_frac_N4_fracff_localClkGen_40nm.xml and_clk.blif common 0.07 vpr 46.89 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 2 2 -1 -1 success bd5999406-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.15.0-67-generic x86_64 2023-03-17T17:48:18 workstation /home/tao/works/dev/route/vtr-verilog-to-routing/vtr_flow/tasks 48016 2 1 4 5 1 4 5 4 4 16 clb auto 7.9 MiB 0.00 11 46.9 MiB 0.00 0.00 1.13498 -1.13498 -1.13498 1.13498 0.00 3.031e-06 1.541e-06 3.5814e-05 2.5748e-05 -1 8 1 215576 107788 120301. 7518.81 0.00 0.00010342 7.9026e-05 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/floor_plan.xml b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/floor_plan.xml new file mode 100644 index 00000000000..861adf40966 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/floor_plan.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/route_constraint.xml b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/route_constraint.xml new file mode 100644 index 00000000000..ca3d794706b --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/route_constraint.xml @@ -0,0 +1,7 @@ + + + + + + + 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 73335b2f919..266ca15595f 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 @@ -1,3 +1,4 @@ +regression_tests/vtr_reg_strong/koios regression_tests/vtr_reg_strong/strong_absorb_buffers regression_tests/vtr_reg_strong/strong_analysis_only regression_tests/vtr_reg_strong/strong_analytic_placer @@ -82,4 +83,6 @@ regression_tests/vtr_reg_strong/koios regression_tests/vtr_reg_strong/koios_no_complex_dsp regression_tests/vtr_reg_strong/strong_timing_fail regression_tests/vtr_reg_strong/strong_timing_no_fail +regression_tests/vtr_reg_strong/strong_vpr_constraint +regression_tests/vtr_reg_strong/strong_tileable_rr_graph regression_tests/vtr_reg_strong/strong_noc