From 3dd4079de244071f3c401eee5a2dce1810d7168e Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 22 Nov 2021 09:33:33 +0100 Subject: [PATCH 1/7] Initial FPGA Interchange rr_graph builder Signed-off-by: Maciej Dudek --- vpr/src/base/SetupVPR.cpp | 1 + vpr/src/route/rr_graph.cpp | 18 +++++++++++++++++- vpr/src/route/rr_graph_fpga_interchange.cpp | 21 +++++++++++++++++++++ vpr/src/route/rr_graph_fpga_interchange.h | 18 ++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 vpr/src/route/rr_graph_fpga_interchange.cpp create mode 100644 vpr/src/route/rr_graph_fpga_interchange.h diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 8d3d415340d..051f1d169c0 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -421,6 +421,7 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->max_logged_overused_rr_nodes = Options.max_logged_overused_rr_nodes; RouterOpts->generate_rr_node_overuse_report = Options.generate_rr_node_overuse_report; + RouterOpts->FPGAInterchange = Options.FPGAInterchangeDevice; } static void SetupAnnealSched(const t_options& Options, diff --git a/vpr/src/route/rr_graph.cpp b/vpr/src/route/rr_graph.cpp index d63c447207a..bfa852b78bd 100644 --- a/vpr/src/route/rr_graph.cpp +++ b/vpr/src/route/rr_graph.cpp @@ -31,6 +31,7 @@ #include "build_switchblocks.h" #include "rr_graph_writer.h" #include "rr_graph_reader.h" +#include "rr_graph_fpga_interchange.h" #include "router_lookahead_map.h" #include "rr_graph_clock.h" #include "edge_groups.h" @@ -314,7 +315,7 @@ void create_rr_graph(const t_graph_type graph_type, int* Warnings) { const auto& device_ctx = g_vpr_ctx.device(); - if (!det_routing_arch->read_rr_graph_filename.empty()) { + if (!det_routing_arch->read_rr_graph_filename.empty() || router_opts.FPGAInterchange) { if (device_ctx.read_rr_graph_filename != det_routing_arch->read_rr_graph_filename) { free_rr_graph(); @@ -328,6 +329,21 @@ void create_rr_graph(const t_graph_type graph_type, router_opts.do_check_rr_graph); reorder_rr_graph_nodes(router_opts); + } else if (!device_ctx.read_rr_graph_filename.size() && router_opts.FPGAInterchange) { + free_rr_graph(); + + VTR_LOG("Custom RR_graph generator\n"); + build_rr_graph_fpga_interchange(graph_type, + grid, + segment_inf, + router_opts.base_cost_type, + &det_routing_arch->wire_to_rr_ipin_switch, + det_routing_arch->read_rr_graph_filename.c_str(), + router_opts.read_rr_edge_metadata, + router_opts.do_check_rr_graph); + + exit(0); + reorder_rr_graph_nodes(router_opts); } } else { if (channel_widths_unchanged(device_ctx.chan_width, nodes_per_chan) && !device_ctx.rr_nodes.empty()) { diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp new file mode 100644 index 00000000000..4d601e2a53f --- /dev/null +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -0,0 +1,21 @@ +#include "rr_graph_fpga_interchange.h" + +#include "arch_util.h" +#include "vtr_time.h" +#include "globals.h" + +void build_rr_graph_fpga_interchange(const t_graph_type graph_type, + const DeviceGrid& grid, + const std::vector& segment_inf, + const enum e_base_cost_type base_cost_type, + int* wire_to_rr_ipin_switch, + const char* read_rr_graph_name, + bool read_edge_metadata, + bool do_check_rr_graph) { + vtr::ScopedStartFinishTimer timer("Building RR Graph from device database"); + + auto& device_ctx = g_vpr_ctx.mutable_device(); + device_ctx.rr_segments = segment_inf; + + VTR_LOG("%s\n", get_arch_file_name()); +}; diff --git a/vpr/src/route/rr_graph_fpga_interchange.h b/vpr/src/route/rr_graph_fpga_interchange.h new file mode 100644 index 00000000000..8241fd613e9 --- /dev/null +++ b/vpr/src/route/rr_graph_fpga_interchange.h @@ -0,0 +1,18 @@ +/* Defines the function used to load an rr graph written in xml format into vpr*/ + +#ifndef RR_GRAPH_FPGA_INTERCHANGE_H +#define RR_GRAPH_FPGA_INTERCHANGE_H + +#include "rr_graph.h" +#include "device_grid.h" + +void build_rr_graph_fpga_interchange(const t_graph_type graph_type, + const DeviceGrid& grid, + const std::vector& segment_inf, + const enum e_base_cost_type base_cost_type, + int* wire_to_rr_ipin_switch, + const char* read_rr_graph_name, + bool read_edge_metadata, + bool do_check_rr_graph); + +#endif /* RR_GRAPH_FPGA_INTERCHANGE_H */ From 89cb9bc2c7bdc32f20563f6481cb0b89288279d0 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Thu, 25 Nov 2021 12:47:45 +0100 Subject: [PATCH 2/7] RR_graph building for fpga interchange Signed-off-by: Maciej Dudek --- .../interchange/DeviceResources.capnp | 16 +- .../src/fpga_interchange_arch_utils.cpp | 78 ++ .../src/fpga_interchange_arch_utils.h | 41 + .../src/read_fpga_interchange_arch.cpp | 85 +- vpr/src/base/SetupVPR.cpp | 2 +- vpr/src/base/vpr_types.h | 3 + vpr/src/route/rr_graph.cpp | 6 +- vpr/src/route/rr_graph_fpga_interchange.cpp | 1186 ++++++++++++++++- vpr/src/route/rr_graph_fpga_interchange.h | 6 +- 9 files changed, 1317 insertions(+), 106 deletions(-) create mode 100644 libs/libarchfpga/src/fpga_interchange_arch_utils.cpp create mode 100644 libs/libarchfpga/src/fpga_interchange_arch_utils.h diff --git a/libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp b/libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp index 09c8d438555..49a0c8c8384 100644 --- a/libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp +++ b/libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp @@ -59,9 +59,8 @@ annotation wireRef(*) :WireRef; using WireIdx = UInt32; struct WireTypeRef { - type @0 :Ref.ReferenceType = parent; + type @0 :Ref.ReferenceType = rootValue; field @1 :Text = "wireTypes"; - depth @2 :Int32 = 1; } annotation wireTypeRef(*) :WireTypeRef; using WireTypeIdx = UInt32; @@ -81,19 +80,16 @@ using TileTypeSiteTypeIdx = UInt32; using TileTypeSubTileIdx = UInt16; struct PIPTimingRef { - type @0 :Ref.ReferenceType = parent; - field @1 :Text = "pipTimingList"; - depth @2 :Int32 = 1; + type @0 :Ref.ReferenceType = rootValue; + field @1 :Text = "pipTimings"; } annotation pipTimingRef(*) :PIPTimingRef; using PipTimingIdx = UInt32; struct NodeTimingRef { - type @0 :Ref.ReferenceType = parent; - field @1 :Text = "nodeTimingList"; - depth @2 :Int32 = 1; - + type @0 :Ref.ReferenceType = rootValue; + field @1 :Text = "nodeTimings"; } annotation nodeTimingRef(*) :NodeTimingRef; using NodeTimingIdx = UInt32; @@ -632,7 +628,7 @@ struct Device { } struct PinDelay { - pin @0 : BELPinIdx $belPinRef(); + pin @0 : BELPinIdx; union { noClock @1 : Void; clockEdge @2 : ClockEdge; diff --git a/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp b/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp new file mode 100644 index 00000000000..8184deb1ade --- /dev/null +++ b/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp @@ -0,0 +1,78 @@ +#include "fpga_interchange_arch_utils.h" + +/****************** Utility functions ******************/ + +/** + * @brief The FPGA interchange timing model includes three different corners (min, typ and max) for each of the two + * speed_models (slow and fast). + * + * Timing data can be found on PIPs, nodes, site pins and bel pins. + * This function retrieves the timing value based on the wanted speed model and the wanted corner. + * + * Corner model is considered valid if at least one configuration is set. + * In that case this value shall be returned. + * + * More information on the FPGA Interchange timing model can be found here: + * - https://github.com/chipsalliance/fpga-interchange-schema/blob/main/interchange/DeviceResources.capnp + */ + +float get_corner_value(DeviceResources::Device::CornerModel::Reader model, const char* speed_model, const char* value) { + bool slow_model = std::string(speed_model) == std::string("slow"); + bool fast_model = std::string(speed_model) == std::string("fast"); + + bool min_corner = std::string(value) == std::string("min"); + bool typ_corner = std::string(value) == std::string("typ"); + bool max_corner = std::string(value) == std::string("max"); + + if (!slow_model && !fast_model) { + archfpga_throw("", __LINE__, "Wrong speed model `%s`. Expected `slow` or `fast`\n", speed_model); + } + + if (!min_corner && !typ_corner && !max_corner) { + archfpga_throw("", __LINE__, "Wrong corner model `%s`. Expected `min`, `typ` or `max`\n", value); + } + + bool has_fast = model.getFast().hasFast(); + bool has_slow = model.getSlow().hasSlow(); + + if ((slow_model || (fast_model && !has_fast)) && has_slow) { + auto half = model.getSlow().getSlow(); + if (min_corner && half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (typ_corner && half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (max_corner && half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + if (half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + archfpga_throw("", __LINE__, "Invalid speed model %s. No value found!\n", speed_model); + } + } + } else if ((fast_model || slow_model) && has_fast) { + auto half = model.getFast().getFast(); + if (min_corner && half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (typ_corner && half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (max_corner && half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + if (half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + archfpga_throw("", __LINE__, "Invalid speed model %s. No value found!\n", speed_model); + } + } + } + return 0.; +} diff --git a/libs/libarchfpga/src/fpga_interchange_arch_utils.h b/libs/libarchfpga/src/fpga_interchange_arch_utils.h new file mode 100644 index 00000000000..6de305bec40 --- /dev/null +++ b/libs/libarchfpga/src/fpga_interchange_arch_utils.h @@ -0,0 +1,41 @@ +#ifndef FPGAINTERCHANGE_ARCH_UTILS_FILE_H +#define FPGAINTERCHANGE_ARCH_UTILS_FILE_H + +#include "arch_types.h" +#include "arch_error.h" +#include "vtr_error.h" + +#include "DeviceResources.capnp.h" +#include "LogicalNetlist.capnp.h" +#include "capnp/serialize.h" +#include "capnp/serialize-packed.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/****************** Utility functions ******************/ + +/** + * @brief The FPGA interchange timing model includes three different corners (min, typ and max) for each of the two + * speed_models (slow and fast). + * + * Timing data can be found on PIPs, nodes, site pins and bel pins. + * This function retrieves the timing value based on the wanted speed model and the wanted corner. + * + * Corner model is considered valid if at least one configuration is set. + * In that case this value shall be returned. + * + * More information on the FPGA Interchange timing model can be found here: + * - https://github.com/chipsalliance/fpga-interchange-schema/blob/main/interchange/DeviceResources.capnp + */ + +float get_corner_value(DeviceResources::Device::CornerModel::Reader model, const char* speed_model, const char* value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index b32455c6af6..8728f52c79d 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -21,6 +21,7 @@ #include "arch_types.h" #include "read_fpga_interchange_arch.h" +#include "fpga_interchange_arch_utils.h" /* * FPGA Interchange Device frontend @@ -78,82 +79,6 @@ struct t_ic_data { /****************** Utility functions ******************/ -/** - * @brief The FPGA interchange timing model includes three different corners (min, typ and max) for each of the two - * speed_models (slow and fast). - * - * Timing data can be found on PIPs, nodes, site pins and bel pins. - * This function retrieves the timing value based on the wanted speed model and the wanted corner. - * - * More information on the FPGA Interchange timing model can be found here: - * - https://github.com/chipsalliance/fpga-interchange-schema/blob/main/interchange/DeviceResources.capnp - */ -static float get_corner_value(Device::CornerModel::Reader model, const char* speed_model, const char* value) { - bool slow_model = std::string(speed_model) == std::string("slow"); - bool fast_model = std::string(speed_model) == std::string("fast"); - - bool min_corner = std::string(value) == std::string("min"); - bool typ_corner = std::string(value) == std::string("typ"); - bool max_corner = std::string(value) == std::string("max"); - - if (!slow_model && !fast_model) { - archfpga_throw("", __LINE__, - "Wrong speed model `%s`. Expected `slow` or `fast`\n", speed_model); - } - - if (!min_corner && !typ_corner && !max_corner) { - archfpga_throw("", __LINE__, - "Wrong corner model `%s`. Expected `min`, `typ` or `max`\n", value); - } - - bool has_fast = model.getFast().hasFast(); - bool has_slow = model.getSlow().hasSlow(); - - if (slow_model && has_slow) { - auto half = model.getSlow().getSlow(); - if (min_corner && half.getMin().isMin()) { - return half.getMin().getMin(); - } else if (typ_corner && half.getTyp().isTyp()) { - return half.getTyp().getTyp(); - } else if (max_corner && half.getMax().isMax()) { - return half.getMax().getMax(); - } else { - if (half.getMin().isMin()) { - return half.getMin().getMin(); - } else if (half.getTyp().isTyp()) { - return half.getTyp().getTyp(); - } else if (half.getMax().isMax()) { - return half.getMax().getMax(); - } else { - archfpga_throw("", __LINE__, - "Invalid speed model %s. No value found!\n", speed_model); - } - } - } else if (fast_model && has_fast) { - auto half = model.getFast().getFast(); - if (min_corner && half.getMin().isMin()) { - return half.getMin().getMin(); - } else if (typ_corner && half.getTyp().isTyp()) { - return half.getTyp().getTyp(); - } else if (max_corner && half.getMax().isMax()) { - return half.getMax().getMax(); - } else { - if (half.getMin().isMin()) { - return half.getMin().getMin(); - } else if (half.getTyp().isTyp()) { - return half.getTyp().getTyp(); - } else if (half.getMax().isMax()) { - return half.getMax().getMax(); - } else { - archfpga_throw("", __LINE__, - "Invalid speed model %s. No value found!\n", speed_model); - } - } - } - - return 0.; -} - /** @brief Returns the port corresponding to the given model in the architecture */ static t_model_ports* get_model_port(t_arch* arch, std::string model, std::string port, bool fail = true) { for (t_model* m : {arch->models, arch->model_library}) { @@ -2238,17 +2163,11 @@ struct ArchReader { } } - // FIXME: have only one segment type for the time being, so that - // the RR graph generation is correct. - // This can be removed once the RR graph reader from the interchange - // device is ready and functional. - size_t num_seg = 1; //wire_names.size(); + int num_seg = wire_names.size(); arch_->Segments.resize(num_seg); size_t index = 0; for (auto i : wire_names) { - if (index >= num_seg) break; - // Use default values as we will populate rr_graph with correct values // This segments are just declaration of future use arch_->Segments[index].name = str(i); diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 051f1d169c0..cf3e9cfec96 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -421,7 +421,7 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->max_logged_overused_rr_nodes = Options.max_logged_overused_rr_nodes; RouterOpts->generate_rr_node_overuse_report = Options.generate_rr_node_overuse_report; - RouterOpts->FPGAInterchange = Options.FPGAInterchangeDevice; + RouterOpts->FPGAInterchange = (Options.arch_format == e_arch_format::FPGAInterchange); } static void SetupAnnealSched(const t_options& Options, diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 322b1b711a0..d3cad1228a6 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1278,6 +1278,9 @@ struct t_router_opts { e_rr_node_reorder_algorithm reorder_rr_graph_nodes_algorithm = DONT_REORDER; int reorder_rr_graph_nodes_threshold = 0; int reorder_rr_graph_nodes_seed = 1; + + // Options related to FPGA Interchange + bool FPGAInterchange; }; struct t_analysis_opts { diff --git a/vpr/src/route/rr_graph.cpp b/vpr/src/route/rr_graph.cpp index bfa852b78bd..4d75d318128 100644 --- a/vpr/src/route/rr_graph.cpp +++ b/vpr/src/route/rr_graph.cpp @@ -316,7 +316,7 @@ void create_rr_graph(const t_graph_type graph_type, const auto& device_ctx = g_vpr_ctx.device(); if (!det_routing_arch->read_rr_graph_filename.empty() || router_opts.FPGAInterchange) { - if (device_ctx.read_rr_graph_filename != det_routing_arch->read_rr_graph_filename) { + if (device_ctx.read_rr_graph_filename != det_routing_arch->read_rr_graph_filename && !router_opts.FPGAInterchange) { free_rr_graph(); load_rr_file(graph_type, @@ -338,11 +338,7 @@ void create_rr_graph(const t_graph_type graph_type, segment_inf, router_opts.base_cost_type, &det_routing_arch->wire_to_rr_ipin_switch, - det_routing_arch->read_rr_graph_filename.c_str(), - router_opts.read_rr_edge_metadata, router_opts.do_check_rr_graph); - - exit(0); reorder_rr_graph_nodes(router_opts); } } else { diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 4d601e2a53f..db1367a3090 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -1,21 +1,1197 @@ +#include +#include +#include +#include +#include + #include "rr_graph_fpga_interchange.h" #include "arch_util.h" #include "vtr_time.h" +#include "vtr_error.h" #include "globals.h" +#include "check_rr_graph.h" +#include "rr_metadata.h" +#include "rr_graph_indexed_data.h" +#include "fpga_interchange_arch_utils.h" + +using namespace DeviceResources; +using namespace LogicalNetlist; +using namespace capnp; + +typedef std::tuple location; + +/* + * Intermediate data type. + * It is used to build and explore FPGA Interchange + * nodes graph and later convert them to rr_nodes. + */ +struct node_ { + public: + location loc; + int segment_id; + node_* links[4]; //left, up, right, down + node_* visited; + bool on_route; + bool has_pins; + node_() = delete; + node_(int x, int y) + : loc{x, y} { + links[0] = links[1] = links[2] = links[3] = nullptr; + visited = nullptr; + on_route = false; + has_pins = false; + } +}; + +/* + * Main class for FPGA Interchange device to rr_graph flow + */ +struct RR_Graph_Builder { + public: + RR_Graph_Builder(Device::Reader& arch_reader, + const DeviceGrid& grid, + DeviceContext& device_ctx, + const std::vector& physical_tile_types, + const vtr::vector& segment_inf, + const enum e_base_cost_type base_cost_type) + : ar_(arch_reader) + , device_ctx_(device_ctx) + , segment_inf_(segment_inf) + , grid_(grid) + , base_cost_type_(base_cost_type) + , empty_(device_ctx.arch->strings.intern_string(vtr::string_view(""))) { + for (auto& type : physical_tile_types) { + tile_type_to_pb_type_[std::string(type.name)] = &type; + } + + for (size_t i = 0; i < segment_inf.size(); ++i) { + wire_name_to_seg_idx_[segment_inf_[RRSegmentId(i)].name] = i; + } + + for (size_t i = 1; i < grid.width() - 1; ++i) { + for (size_t j = 1; j < grid.height() - 1; ++j) { + location loc{i, j}; + if (grid[i][j].type->index == 0) + empty_tiles.insert(loc); + } + } + } + + void build_rr_graph() { + create_switch_inf(); + create_tile_id_to_loc(); + create_uniq_node_ids(); + create_sink_source_nodes(); + create_pips_(); + process_nodes(); + pack_to_rr_graph(); +#ifdef DEBUG + print_(); +#endif + finish_rr_generation(); + } + + private: + Device::Reader& ar_; + DeviceContext& device_ctx_; + const vtr::vector& segment_inf_; + const DeviceGrid& grid_; + const enum e_base_cost_type base_cost_type_; + + vtr::interned_string empty_; + + std::unordered_map tile_type_to_pb_type_; + + std::unordered_map wire_name_to_seg_idx_; + + std::set empty_tiles; + + std::unordered_map tile_to_loc_; + std::map loc_to_tile_; + + std::map, int /*idx*/> pips_models_; + std::map, std::tuple>> pips_; + + std::map, int> wire_to_node_; + std::unordered_map> node_to_locs_; + std::map /**/, int /*segment type*/> node_tile_to_segment_; + + /* + * Offsets for FPGA Interchange node processing + */ + location offsets[4] = {location(-1, 0), location(0, 1), location(1, 0), location(0, -1)}; + + /* + * Intermediate data storage. + * - Shorts represent connections between rr_nodes in a single FPGA Interchange node. + * - Redirect is map from node_id at location to location channel and index of track in that channel. + * It's useful for ends of the nodes (FPGA Interchange) that don't have representation in CHANS. + * - CHAN_loc_map maps from location and CHAN to vector containing tracks description. + */ + std::vector> shorts_; + std::map, std::tuple> redirect_; + std::map, std::vector> /*idx = ptc,*/> chan_loc_map_; + + /* + * Sets contain tuples of node ids and location. + * Each value correspondence to node id n being used by either pip or pin at location l. + */ + std::set> used_by_pip_; + std::set> used_by_pin_; + + /* Sink_source_loc_map is used to create ink/source and ipin/opin rr_nodes, + * rr_edges from sink/source to ipin/opin and from ipin/opin to their coresponding segments + * uniq_id is used to create edge from segment to ipin/opin, edges from ipin/opin to sink/source are created simply as one to one + */ + std::unordered_map> /*idx = ptc,*/> sink_source_loc_map_; + + /* + * RR generation stuff + */ + vtr::vector seg_index_; + std::map, int> loc_type_idx_to_rr_idx_; + int rr_idx = 0; // Do not decrement! + + std::string str(int idx) { + return std::string(ar_.getStrList()[idx].cStr()); + } + + /* + * Debug print function + */ + void print_() { + for (const auto& entry : sink_source_loc_map_) { + const auto& key = entry.first; + const auto& value = entry.second; + int x, y; + std::tie(x, y) = tile_to_loc_[key]; + VTR_LOG("Location x:%d y:%d Name:%s\n", x, y, str(key).c_str()); + int it = 0; + for (const auto& pin : value) { + bool dir; + float RC; + int node_id; + std::tie(dir, RC, node_id) = pin; + VTR_LOG("\tpin:%d input:%d R/C:%e\n", it, dir, RC); + VTR_LOG("\ttile_name:%s node_id:%d\n", str(key).c_str(), node_id); + it++; + } + } + + int it = 0; + + for (auto const& switch_id : device_ctx_.rr_switch_inf) { + VTR_LOG("Switch: %d Name:%s\n", it++, switch_id.name); + } + + for (auto& entry : chan_loc_map_) { + location loc; + e_rr_type type; + std::tie(loc, type) = entry.first; + VTR_LOG("CHAN%c X:%d Y:%d\n", type == e_rr_type::CHANX ? 'X' : 'Y', std::get<0>(loc), std::get<1>(loc)); + for (auto& seg : entry.second) { + VTR_LOG("\tSegment id:%d name:%s\n", std::get<0>(seg), segment_inf_[RRSegmentId(std::get<0>(seg))].name.c_str()); + } + } + + VTR_LOG("Redirects:\n"); + for (auto& entry : redirect_) { + int node; + location loc; + std::tie(node, loc) = entry.first; + + location new_loc; + e_rr_type new_type; + int new_idx; + std::tie(new_loc, new_type, new_idx) = entry.second; + VTR_LOG("\t Node:%d X:%d Y:%d -> X:%d Y:%d CHAN%c idx:%d\n", node, std::get<0>(loc), std::get<1>(loc), std::get<0>(new_loc), + std::get<1>(new_loc), new_type == e_rr_type::CHANX ? 'X' : 'Y', + new_idx); + } + + VTR_LOG("Shorts:\n"); + for (auto& entry : shorts_) { + location loc1, loc2; + e_rr_type type1, type2; + int id1, id2; + std::tie(loc1, type1, id1, loc2, type2, id2) = entry; + VTR_LOG("\tCHAN%c X:%d Y%d", type1 == e_rr_type::CHANX ? 'X' : 'Y', std::get<0>(loc1), std::get<1>(loc1)); + VTR_LOG(" Segment id:%d name:%s ->", id1, + segment_inf_[RRSegmentId(std::get<0>(chan_loc_map_[std::make_tuple(loc1, type1)][id1]))].name.c_str()); + VTR_LOG(" CHAN%c X:%d Y%d", type2 == e_rr_type::CHANX ? 'X' : 'Y', std::get<0>(loc2), std::get<1>(loc2)); + VTR_LOG(" Segment id:%d name:%s\n", id2, + segment_inf_[RRSegmentId(std::get<0>(chan_loc_map_[std::make_tuple(loc2, type2)][id2]))].name.c_str()); + } + + for (auto& pip : pips_) { + auto& key = pip.first; + auto& value = pip.second; + std::tuple meta; + int switch_id, tile, wire0, wire1; + bool forward; + std::tie(switch_id, meta) = value; + std::tie(tile, wire0, wire1, forward) = meta; + auto& r1 = redirect_[std::make_tuple(std::get<0>(key), tile_to_loc_[tile])]; + auto& r2 = redirect_[std::make_tuple(std::get<1>(key), tile_to_loc_[tile])]; + VTR_LOG("Switch_type: %d, %s.%s %s.%s: forward:%s\n", + switch_id, str(tile).c_str(), str(wire0).c_str(), str(tile).c_str(), str(wire1).c_str(), forward ? "yes" : "no"); + VTR_LOG("Edge: CHAN%c X:%d Y:%d idx:%d -> CHAN%c X:%d Y:%d idx%d\n", + std::get<1>(r1) == e_rr_type::CHANX ? 'X' : 'Y', + std::get<0>(std::get<0>(r1)), + std::get<1>(std::get<0>(r1)), + std::get<2>(r1), + std::get<1>(r2) == e_rr_type::CHANX ? 'X' : 'Y', + std::get<0>(std::get<0>(r2)), + std::get<1>(std::get<0>(r2)), + std::get<2>(r2)); + } + VTR_LOG("RR_Nodes\n"); + for (auto i : loc_type_idx_to_rr_idx_) { + location loc; + e_rr_type type; + int idx; + std::tie(loc, type, idx) = i.first; + VTR_LOG("\t X:%d Y:%d type:%d idx:%d->rr_node:%d\n", std::get<0>(loc), std::get<1>(loc), type, idx, i.second); + } + } + + /* + * Fill device_ctx rr_switch_inf structure and store id of each PIP type for future use + */ + void create_switch_inf() { + std::set> seen; + for (const auto& tile : ar_.getTileTypeList()) { + for (const auto& pip : tile.getPips()) { + if (pip.isPseudoCells()) + continue; + seen.emplace(pip.getTiming(), pip.getBuffered21()); + if (!pip.getDirectional()) { + seen.emplace(pip.getTiming(), pip.getBuffered20()); + } + } + } + device_ctx_.rr_switch_inf.reserve(seen.size() + 1); + int id = 2; + make_room_in_vector(&device_ctx_.rr_switch_inf, 0); + device_ctx_.rr_switch_inf[0].R = 0; + device_ctx_.rr_switch_inf[0].Cin = 0; + device_ctx_.rr_switch_inf[0].Cout = 0; + device_ctx_.rr_switch_inf[0].Cinternal = 0; + device_ctx_.rr_switch_inf[0].Tdel = 0; + device_ctx_.rr_switch_inf[0].buf_size = 0; + device_ctx_.rr_switch_inf[0].mux_trans_size = 0; + device_ctx_.rr_switch_inf[0].name = "short"; + device_ctx_.rr_switch_inf[0].set_type(SwitchType::SHORT); + make_room_in_vector(&device_ctx_.rr_switch_inf, 1); + device_ctx_.rr_switch_inf[1].R = 0; + device_ctx_.rr_switch_inf[1].Cin = 0; + device_ctx_.rr_switch_inf[1].Cout = 0; + device_ctx_.rr_switch_inf[1].Cinternal = 0; + device_ctx_.rr_switch_inf[1].Tdel = 0; + device_ctx_.rr_switch_inf[1].buf_size = 0; + device_ctx_.rr_switch_inf[1].mux_trans_size = 0; + device_ctx_.rr_switch_inf[1].name = "generic"; + device_ctx_.rr_switch_inf[1].set_type(SwitchType::MUX); + const auto& pip_models = ar_.getPipTimings(); + float R, Cin, Cout, Cint, Tdel; + std::string switch_name; + SwitchType type; + for (const auto& value : seen) { + make_room_in_vector(&device_ctx_.rr_switch_inf, id); + int timing_model_id; + bool buffered; + std::tie(timing_model_id, buffered) = value; + const auto& model = pip_models[timing_model_id]; + pips_models_[std::make_tuple(timing_model_id, buffered)] = id; + + R = Cin = Cint = Cout = Tdel = 0.0; + std::stringstream name; + std::string mux_type_string = buffered ? "mux_" : "passGate_"; + name << mux_type_string; + + R = get_corner_value(model.getOutputResistance(), "slow", "min"); + name << "R" << std::scientific << R; + + Cin = get_corner_value(model.getInputCapacitance(), "slow", "min"); + name << "Cin" << std::scientific << Cin; + + Cout = get_corner_value(model.getOutputCapacitance(), "slow", "min"); + name << "Cout" << std::scientific << Cout; + + if (buffered) { + Cint = get_corner_value(model.getInternalCapacitance(), "slow", "min"); + name << "Cinternal" << std::scientific << Cint; + } + + Tdel = get_corner_value(model.getInternalDelay(), "slow", "min"); + name << "Tdel" << std::scientific << Tdel; + + switch_name = name.str(); + type = buffered ? SwitchType::MUX : SwitchType::PASS_GATE; + + auto& as = device_ctx_.rr_switch_inf[id]; + as.name = vtr::strdup(switch_name.c_str()); + as.set_type(type); + as.mux_trans_size = type == SwitchType::MUX ? 1 : 0; + + as.R = R; + as.Cin = Cin; + as.Cout = Cout; + as.Cinternal = Cint; + as.Tdel = Tdel; + as.buf_size = 0.; + + id++; + } + } + + /* + * Create mapping form tile_id to its location + */ + void create_tile_id_to_loc() { + for (const auto& tile : ar_.getTileList()) { + tile_to_loc_[tile.getName()] = location{tile.getCol() + 1, tile.getRow() + 1}; + loc_to_tile_[location(tile.getCol() + 1, tile.getRow() + 1)] = tile.getName(); + } + } + + /* + * Create uniq id for each FPGA Interchange node. + * Create mapping from wire to node id and from node id to its locations + * These ids are used later for site pins and pip conections (rr_edges) + */ + void create_uniq_node_ids() { + int id = 0; + for (const auto& node : ar_.getNodes()) { + for (const auto& wire_id_ : node.getWires()) { + const auto& wire = ar_.getWires()[wire_id_]; + int tile_id = wire.getTile(); + int wire_id = wire.getWire(); + wire_to_node_[std::make_tuple(tile_id, wire_id)] = id; + node_to_locs_[id].insert(tile_to_loc_[tile_id]); + if (wire_name_to_seg_idx_.find(str(wire_id)) == wire_name_to_seg_idx_.end()) + wire_name_to_seg_idx_[str(wire_id)] = -1; + node_tile_to_segment_[std::make_tuple(id, tile_id)] = wire_name_to_seg_idx_[str(wire_id)]; + } + id++; + } + } + + /* + * Create entry for each pip in device architecture, + * also store meta data related to that pip such as: tile_name, wire names, and its direction. + */ + void create_pips_() { + for (const auto& tile : ar_.getTileList()) { + int name = tile.getName(); + location loc = tile_to_loc_[name]; + const auto& tile_type = ar_.getTileTypeList()[tile.getType()]; + + for (const auto& pip : tile_type.getPips()) { + if (pip.isPseudoCells()) + continue; + if (tile.hasSubTilesPrefices()) + name = tile.getSubTilesPrefices()[pip.getSubTile()]; + int wire0_name, wire1_name; + int node0, node1; + int switch_id; + + wire0_name = tile_type.getWires()[pip.getWire0()]; + wire1_name = tile_type.getWires()[pip.getWire1()]; + if (wire_to_node_.find(std::make_tuple(name, wire0_name)) == wire_to_node_.end()) + continue; + if (wire_to_node_.find(std::make_tuple(name, wire1_name)) == wire_to_node_.end()) + continue; + node0 = wire_to_node_[std::make_tuple(name, wire0_name)]; + node1 = wire_to_node_[std::make_tuple(name, wire1_name)]; + + used_by_pip_.emplace(node0, loc); + used_by_pip_.emplace(node1, loc); + switch_id = pips_models_[std::make_tuple(pip.getTiming(), pip.getBuffered21())]; + std::tuple source_sink; + source_sink = std::make_tuple(node0, node1); + pips_.emplace(source_sink, std::make_tuple(switch_id, std::make_tuple(name, wire0_name, wire1_name, true))); + if (!pip.getBuffered21() && (pip.getDirectional() || pip.getBuffered20())) { + source_sink = std::make_tuple(node1, node0); + pips_.emplace(source_sink, std::make_tuple(switch_id, std::make_tuple(name, wire0_name, wire1_name, true))); + } + if (!pip.getDirectional()) { + switch_id = pips_models_[std::make_tuple(pip.getTiming(), pip.getBuffered20())]; + source_sink = std::make_tuple(node1, node0); + pips_.emplace(source_sink, std::make_tuple(switch_id, std::make_tuple(name, wire0_name, wire1_name, true))); + if (!pip.getBuffered20() && pip.getBuffered21()) { + source_sink = std::make_tuple(node0, node1); + pips_.emplace(source_sink, std::make_tuple(switch_id, std::make_tuple(name, wire0_name, wire1_name, true))); + } + } + } + } + } + + /* + * Build graph of FPGA Interchange node for further computations + */ + node_* build_node_graph(int node_id, + std::set nodes, + std::set empties, + int root_x, + int root_y) { + int visited_tiles = 0; + node_* root_node = new node_(root_x, root_y); + location key{root_x, root_y}; + + root_node->segment_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[key])]; + nodes.erase(key); + + std::map existing_nodes; + existing_nodes.emplace(key, root_node); + + std::priority_queue> builder; + builder.emplace(1, 0, root_node); + while (!builder.empty() && (!nodes.empty() || std::get<0>(builder.top()) != 0)) { + node_* vertex; + std::tie(std::ignore, std::ignore, vertex) = builder.top(); + builder.pop(); + location loc = vertex->loc; + for (int i = 0; i < 4; ++i) { + location offset = offsets[i]; + location other_loc{std::get<0>(loc) + std::get<0>(offset), std::get<1>(loc) + std::get<1>(offset)}; + node_* temp = nullptr; + if (existing_nodes.find(other_loc) != existing_nodes.end()) { + temp = existing_nodes[other_loc]; + } else if (nodes.find(other_loc) != nodes.end()) { + temp = new node_(std::get<0>(other_loc), std::get<1>(other_loc)); + temp->segment_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[other_loc])]; + nodes.erase(other_loc); + existing_nodes.emplace(other_loc, temp); + builder.emplace(1, -1 * visited_tiles, temp); + visited_tiles++; + } else if (empties.find(other_loc) != empties.end()) { + temp = new node_(std::get<0>(other_loc), std::get<1>(other_loc)); + temp->segment_id = -1; + empties.erase(other_loc); + existing_nodes.emplace(other_loc, temp); + builder.emplace(0, -1 * visited_tiles, temp); + visited_tiles++; + } + vertex->links[i] = temp; + } + } + + if (!nodes.empty()) + vpr_throw(VPR_ERROR_ARCH, get_arch_file_name(), __LINE__, "Node graph could not be created\n"); + + while (!builder.empty()) { + node_* vertex; + std::tie(std::ignore, std::ignore, vertex) = builder.top(); + builder.pop(); + location loc = vertex->loc; + for (int i = 0; i < 4; ++i) { + location offset = offsets[i]; + location other_loc{std::get<0>(loc) + std::get<0>(offset), std::get<1>(loc) + std::get<1>(offset)}; + node_* temp = nullptr; + if (existing_nodes.find(other_loc) != existing_nodes.end()) { + temp = existing_nodes[other_loc]; + temp->links[(i + 2) % 4] = nullptr; + } + } + existing_nodes.erase(vertex->loc); + delete (vertex); + } + + for (auto& node : existing_nodes) { + if (used_by_pip_.find(std::make_tuple(node_id, node.second->loc)) != used_by_pip_.end()) { + node.second->on_route = true; + root_node = node.second; + } + if (used_by_pin_.find(std::make_tuple(node_id, node.second->loc)) != used_by_pin_.end()) { + node.second->has_pins = true; + node.second->on_route = true; + root_node = node.second; + } + } + return root_node; + } + + /* + * Removes dangling nodes from a graph represented by the root node. + * Dangling nodes are nodes that do not connect to a pin, a pip or other non-dangling node. + */ + int reduce_graph_and_count_nodes(node_* root) { + int cnt = 0; + std::queue walker; + std::stack back_walker; + walker.push(root); + root->visited = root; + bool all_nulls; + while (!walker.empty()) { + all_nulls = true; + node_* vertex = walker.front(); + walker.pop(); + back_walker.emplace(vertex); + for (int i = 0; i < 4; ++i) { + if (vertex->links[i] != nullptr) { + all_nulls = false; + if (i == 0 || i == 1) + cnt++; + if (vertex->links[i]->visited == nullptr) { + vertex->links[i]->visited = vertex; + walker.emplace(vertex->links[i]); + } + } + } + cnt += all_nulls; + } + while (!back_walker.empty()) { + node_* vertex = back_walker.top(); + back_walker.pop(); + if (!vertex->has_pins && !vertex->on_route) { + if (vertex->visited->segment_id == -1) { + vertex->visited->segment_id = vertex->segment_id; + } + for (int i = 0; i < 4; ++i) { + node_* temp = nullptr; + if (vertex->links[i] != nullptr) { + if (i == 2 || i == 3) + cnt--; + temp = vertex->links[i]; + temp->links[(i + 2) % 4] = nullptr; + } + } + delete (vertex); + } else if (vertex->on_route) { + vertex->visited->on_route = true; + if (vertex->visited->segment_id == -1) + vertex->visited->segment_id = vertex->segment_id; + } + } + return cnt; + } + + void populate_chan_loc_map(int node_id, node_* root, float R, float C) { + /* + * Create future rr_nodes that corespond to given FPGA Interchange node + */ + std::map, int /*index*/> loc_chan_idx; + + std::queue walker; + std::stack back_walker; + walker.emplace(root); + bool all_nulls, is_chanx, is_chany; + while (!walker.empty()) { + node_* vertex = walker.front(); + walker.pop(); + back_walker.emplace(vertex); + is_chany = false; + is_chanx = vertex->has_pins; + all_nulls = true; + for (int i = 0; i < 4; ++i) { + if (vertex->links[i] != nullptr) { + all_nulls = false; + if (vertex->links[i]->visited == vertex || vertex->links[i] == vertex->visited) { + is_chanx = i == 0 ? true : is_chanx; + is_chany = i == 1 ? true : is_chany; + } + if (vertex->links[i]->visited == vertex) { + walker.emplace(vertex->links[i]); + } + } + } + is_chanx = all_nulls ? all_nulls : is_chanx; + std::tuple key1(vertex->loc, e_rr_type::CHANY); + std::tuple key2(location(std::get<0>(vertex->loc), std::get<1>(vertex->loc) - 1), e_rr_type::CHANX); + + if (is_chany) { + loc_chan_idx[key1] = chan_loc_map_[key1].size(); + chan_loc_map_[key1].emplace_back(vertex->segment_id, R, C); + } + if (is_chanx) { + loc_chan_idx[key2] = chan_loc_map_[key2].size(); + chan_loc_map_[key2].emplace_back(vertex->segment_id, R, C); + } + if (is_chanx && is_chany) { + shorts_.emplace_back(std::get<0>(key1), std::get<1>(key1), loc_chan_idx[key1], + std::get<0>(key2), std::get<1>(key2), loc_chan_idx[key2]); + } + } + + /* + * Create edges between future rr_nodes that correspond to the given FPGA Interchange node + */ + while (!back_walker.empty()) { + node_* vertex = back_walker.top(); + back_walker.pop(); + location loc = vertex->loc; + location down(std::get<0>(vertex->loc), std::get<1>(vertex->loc) - 1); // lower tile CHANY, our CHANX + location right(std::get<0>(down) + 1, std::get<1>(down)); // CHANX of tile to the right + + if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) != loc_chan_idx.end()) { + redirect_.emplace(std::make_tuple(node_id, loc), std::make_tuple( + down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)])); + + } else if (loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) != loc_chan_idx.end()) { + redirect_.emplace(std::make_tuple(node_id, loc), std::make_tuple( + loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)])); + + } else if (vertex->links[2] != nullptr && (vertex->links[2]->visited == vertex || vertex->links[2] == vertex->visited) && loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) { + redirect_.emplace(std::make_tuple(node_id, loc), std::make_tuple( + right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)])); + + } else if (vertex->links[3] != nullptr && (vertex->links[3]->visited == vertex || vertex->links[3] == vertex->visited) && loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) { + redirect_.emplace(std::make_tuple(node_id, loc), std::make_tuple( + down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)])); + } + + /* Check if node x is up-right corner: + * +-+-+ + * |x|b| + * +-+-+ + * |a| + * +-+ + * If so it does not use CHANX or CHANY, and it must connect CHANY from a with CHANX from b + */ + + if (vertex->links[2] != nullptr && vertex->links[3] != nullptr && (vertex->links[3]->visited == vertex || vertex->links[3] == vertex->visited) && (vertex->links[2]->visited == vertex || vertex->links[2] == vertex->visited) && loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) == loc_chan_idx.end() && loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) == loc_chan_idx.end()) { + shorts_.emplace_back(down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)], + right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)]); + continue; + } + + /* If tile has CHANX, try to cannect it to CHANY of tile below, + * CHANY and CHANX of tile to the left and CHANX of tile to the right + */ + if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) != loc_chan_idx.end()) { + if (vertex->links[0] != nullptr && vertex->links[0]->visited == vertex) { + location left(std::get<0>(down) - 1, std::get<1>(down)); // CHANX + location left_up(std::get<0>(loc) - 1, std::get<1>(loc)); // CHANY + if (loc_chan_idx.find(std::make_tuple(left, e_rr_type::CHANX)) != loc_chan_idx.end()) + shorts_.emplace_back(left, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(left, e_rr_type::CHANX)], + down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)]); + if (loc_chan_idx.find(std::make_tuple(left_up, e_rr_type::CHANY)) != loc_chan_idx.end()) + shorts_.emplace_back(left_up, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(left_up, e_rr_type::CHANY)], + down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)]); + } + if (vertex->links[2] != nullptr && vertex->links[2]->visited == vertex) { + if (loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) + shorts_.emplace_back(right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)], + down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)]); + } + if (vertex->links[3] != nullptr && vertex->links[3]->visited == vertex) { + if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) + shorts_.emplace_back(down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)], + down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)]); + } + } + + /* If tile has CHANY, try to cannect it to CHANY of tile below, + * CHANY and CHANX of tile above and CHANX of tile to the right + */ + if (loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) != loc_chan_idx.end()) { + if (vertex->links[1] != nullptr && vertex->links[1]->visited == vertex) { + location up(std::get<0>(down), std::get<1>(down) + 1); // CHANX + location up_up(std::get<0>(loc), std::get<1>(loc) + 1); // CHANY + if (loc_chan_idx.find(std::make_tuple(up, e_rr_type::CHANX)) != loc_chan_idx.end()) + shorts_.emplace_back(up, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(up, e_rr_type::CHANX)], + loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); + if (loc_chan_idx.find(std::make_tuple(up_up, e_rr_type::CHANY)) != loc_chan_idx.end()) + shorts_.emplace_back(up_up, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(up_up, e_rr_type::CHANY)], + loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); + } + if (vertex->links[2] != nullptr && vertex->links[2]->visited == vertex) { + if (loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) + shorts_.emplace_back(right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)], + loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); + } + if (vertex->links[3] != nullptr && vertex->links[3]->visited == vertex) { + if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) + shorts_.emplace_back(down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)], + loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); + } + } + } + } + + /* + * Remove a graph represented by a root node. + * Clean up of earlier stages + */ + void delete_nodes(node_* root) { + std::queue walker; + std::stack back_walker; + walker.emplace(root); + while (!walker.empty()) { + node_* vertex = walker.front(); + walker.pop(); + back_walker.emplace(vertex); + for (int i = 0; i < 4; ++i) { + if (vertex->links[i] != nullptr) { + if (vertex->links[i]->visited == vertex) { + walker.emplace(vertex->links[i]); + } + } + } + } + while (!back_walker.empty()) { + node_* vertex = back_walker.top(); + back_walker.pop(); + for (int i = 0; i < 4; ++i) { + node_* temp = nullptr; + if (vertex->links[i] != nullptr) { + temp = vertex->links[i]; + temp->links[(i + 2) % 4] = nullptr; + } + } + delete (vertex); + } + } + + /* + * Process FPGA Interchange nodes + */ + void process_nodes() { + auto wires = ar_.getWires(); + for (auto const& node : ar_.getNodes()) { + std::tuple base_wire_{wires[node.getWires()[0]].getTile(), wires[node.getWires()[0]].getWire()}; + int node_id = wire_to_node_[base_wire_]; + std::set all_possible_tiles = node_to_locs_[node_id]; + int root_x, root_y; + std::tie(root_x, root_y) = *all_possible_tiles.begin(); + std::set empties; + if (all_possible_tiles.size() != 1) { + for (auto const& empty_tile : empty_tiles) { + empties.insert(empty_tile); + } + } + + node_* root = build_node_graph(node_id, all_possible_tiles, empties, root_x, root_y); + int div = reduce_graph_and_count_nodes(root); + float capacitance, resistance; + auto model = ar_.getNodeTimings()[node.getNodeTiming()]; + capacitance = get_corner_value(model.getCapacitance(), "slow", "typ") / div; + resistance = get_corner_value(model.getResistance(), "slow", "typ") / div; + populate_chan_loc_map(node_id, root, resistance, capacitance); + delete_nodes(root); + } + } + + /* + * Create site sink/source ipin/opin mappins. + */ + void create_sink_source_nodes() { + const auto& tile_types = ar_.getTileTypeList(); + std::map, std::tuple> tile_type_site_num_pin_name_model; + for (const auto& tileType : tile_types) { + int it = 0; + if (tile_type_to_pb_type_.find(str(tileType.getName())) == tile_type_to_pb_type_.end()) + continue; + for (const auto& siteType : tileType.getSiteTypes()) { + int pin_count = 0; + for (const auto& pin_ : ar_.getSiteTypeList()[siteType.getPrimaryType()].getPins()) { + std::tuple pin(tileType.getName(), it, str(pin_.getName())); + const auto& model = pin_.getModel(); + float value = 0.0; + if (pin_.getDir() == LogicalNetlist::Netlist::Direction::OUTPUT && model.hasResistance()) { + value = get_corner_value(model.getResistance(), "slow", "max"); + } else if (pin_.getDir() == LogicalNetlist::Netlist::Direction::INPUT && model.hasCapacitance()) { + value = get_corner_value(model.getCapacitance(), "slow", "max"); + } + int wire_id = siteType.getPrimaryPinsToTileWires()[pin_count]; + tile_type_site_num_pin_name_model[pin] = std::tuple(value, wire_id); + pin_count++; + } + it++; + } + } + for (const auto& tile : ar_.getTileList()) { + const auto& tile_type_id = tile_types[tile.getType()].getName(); + const auto& tile_type_name = str(tile_type_id); + const auto& tile_id = tile.getName(); + if (tile_type_to_pb_type_.find(tile_type_name) == tile_type_to_pb_type_.end()) + continue; + sink_source_loc_map_[tile_id].resize(tile_type_to_pb_type_[tile_type_name]->num_pins); + int it = 0; + for (const auto& sub_tile : tile_type_to_pb_type_[tile_type_name]->sub_tiles) { + for (const auto& port : sub_tile.ports) { + float value; + int wire_id; + int node_id; + int pin_id = sub_tile.sub_tile_to_tile_pin_indices[port.index]; + std::tuple key{tile_type_id, it, std::string(port.name)}; + std::tie(value, wire_id) = tile_type_site_num_pin_name_model[key]; + node_id = wire_to_node_[std::make_tuple(tile_id, wire_id)]; + + location loc = tile_to_loc_[tile_id]; + + used_by_pin_.insert(std::make_tuple(node_id, loc)); + sink_source_loc_map_[tile_id][pin_id] = std::make_tuple(port.type == IN_PORT, value, node_id); + } + it++; + } + } + } + + /* + * This function populate device chan_width structures, they are needed in later stages of VPR flow + */ + void pack_chan_width() { + device_ctx_.chan_width.x_list.resize(grid_.height(), 0); + device_ctx_.chan_width.y_list.resize(grid_.width(), 0); + int min_x, min_y, max_x, max_y; + min_x = min_y = 0x7fffffff; + max_x = max_y = 0; + for (auto i : chan_loc_map_) { + auto key = i.first; + auto vec = i.second; + location loc; + e_rr_type type; + std::tie(loc, type) = key; + int x, y; + std::tie(x, y) = loc; + if (type == e_rr_type::CHANX) { + min_x = std::min(min_x, (int)vec.size()); + max_x = std::max(max_x, (int)vec.size()); + device_ctx_.chan_width.x_list[y] = std::max((int)device_ctx_.chan_width.x_list[y], (int)vec.size()); + } else { + min_y = std::min(min_x, (int)vec.size()); + max_y = std::max(max_x, (int)vec.size()); + device_ctx_.chan_width.y_list[x] = std::max((int)device_ctx_.chan_width.y_list[x], (int)vec.size()); + } + } + device_ctx_.chan_width.max = std::max(max_y, max_x); + device_ctx_.chan_width.x_min = min_x; + device_ctx_.chan_width.x_max = max_x; + device_ctx_.chan_width.y_min = min_y; + device_ctx_.chan_width.y_max = max_y; + } + + void pack_tiles() { + for (auto& i : sink_source_loc_map_) { + int tile_id = i.first; + int x, y; + location loc = tile_to_loc_[tile_id]; + std::tie(x, y) = loc; + auto pin_vec = i.second; + for (size_t j = 0; j < pin_vec.size(); ++j) { + bool input; + float RC; + int FPGA_Interchange_node_id; + std::tie(input, RC, FPGA_Interchange_node_id) = pin_vec[j]; + e_rr_type pin = input ? e_rr_type::SINK : e_rr_type::SOURCE; + + float R, C; + std::tie(R, C) = input ? std::tuple(0, RC) : std::tuple(RC, 0); + + location track_loc; + e_rr_type track_type; + int track_idx; + std::tie(track_loc, track_type, track_idx) = redirect_[std::make_tuple(FPGA_Interchange_node_id, loc)]; + auto val = chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx]; + int track_seg; + float track_R, track_C; + std::tie(track_seg, track_R, track_C) = val; + track_R += R; + track_C += C; + chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx] = std::make_tuple(track_seg, track_R, track_C); + + device_ctx_.rr_nodes.make_room_for_node(RRNodeId(rr_idx)); + loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, j)] = rr_idx; + auto node = device_ctx_.rr_nodes[rr_idx]; + RRNodeId node_id = node.id(); + device_ctx_.rr_graph_builder.set_node_type(node_id, pin); + device_ctx_.rr_graph_builder.set_node_capacity(node_id, 1); + if (pin == e_rr_type::SOURCE) { + device_ctx_.rr_graph_builder.set_node_cost_index(node_id, RRIndexedDataId(SOURCE_COST_INDEX)); + } else { + device_ctx_.rr_graph_builder.set_node_cost_index(node_id, RRIndexedDataId(SINK_COST_INDEX)); + } + device_ctx_.rr_graph_builder.set_node_rc_index(node_id, NodeRCIndex(find_create_rr_rc_data(0, 0))); + device_ctx_.rr_graph_builder.set_node_coordinates(node_id, x, y, x, y); + device_ctx_.rr_graph_builder.set_node_ptc_num(node_id, j); + rr_idx++; + } + for (size_t j = 0; j < pin_vec.size(); ++j) { + bool input; + std::tie(input, std::ignore, std::ignore) = pin_vec[j]; + e_rr_type pin = input ? e_rr_type::IPIN : e_rr_type::OPIN; + + device_ctx_.rr_nodes.make_room_for_node(RRNodeId(rr_idx)); + loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, j)] = rr_idx; + auto node = device_ctx_.rr_nodes[rr_idx]; + RRNodeId node_id = node.id(); + device_ctx_.rr_graph_builder.set_node_type(node_id, pin); + device_ctx_.rr_graph_builder.set_node_capacity(node_id, 1); + if (pin == e_rr_type::IPIN) { + device_ctx_.rr_graph_builder.set_node_cost_index(node_id, RRIndexedDataId(IPIN_COST_INDEX)); + } else { + device_ctx_.rr_graph_builder.set_node_cost_index(node_id, RRIndexedDataId(OPIN_COST_INDEX)); + } + device_ctx_.rr_graph_builder.set_node_rc_index(node_id, NodeRCIndex(find_create_rr_rc_data(0, 0))); + device_ctx_.rr_graph_builder.set_node_coordinates(node_id, x, y, x, y); + device_ctx_.rr_graph_builder.set_node_ptc_num(node_id, j); + device_ctx_.rr_graph_builder.add_node_side(node_id, e_side::BOTTOM); + rr_idx++; + } + } + } + + void pack_chans() { + for (auto i : chan_loc_map_) { + location loc; + e_rr_type type; + std::tie(loc, type) = i.first; + int x, y; + std::tie(x, y) = loc; + auto tracks = i.second; + for (size_t j = 0; j < tracks.size(); ++j) { + int seg; + float R, C; + std::tie(seg, R, C) = tracks[j]; + + device_ctx_.rr_nodes.make_room_for_node(RRNodeId(rr_idx)); + loc_type_idx_to_rr_idx_[std::make_tuple(loc, type, j)] = rr_idx; + auto node = device_ctx_.rr_nodes[rr_idx]; + RRNodeId node_id = node.id(); + + device_ctx_.rr_graph_builder.set_node_type(node_id, type); + device_ctx_.rr_graph_builder.set_node_capacity(node_id, 1); + device_ctx_.rr_graph_builder.set_node_rc_index(node_id, NodeRCIndex(find_create_rr_rc_data(R, C))); + device_ctx_.rr_graph_builder.set_node_direction(node_id, Direction::BIDIR); + + device_ctx_.rr_graph_builder.set_node_coordinates(node_id, x, y, x, y); + device_ctx_.rr_graph_builder.set_node_ptc_num(node_id, j); + + if (type == e_rr_type::CHANX) { + device_ctx_.rr_graph_builder.set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + seg)); + } else { + device_ctx_.rr_graph_builder.set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + segment_inf_.size() + seg)); + } + seg_index_[device_ctx_.rr_graph.node_cost_index(node_id)] = seg; + + rr_idx++; + } + } + } + + void pack_rr_nodes() { + device_ctx_.rr_nodes.clear(); + seg_index_.resize(CHANX_COST_INDEX_START + 2 * segment_inf_.size(), -1); + pack_tiles(); + pack_chans(); + device_ctx_.rr_nodes.shrink_to_fit(); + } + + void pack_tiles_edges() { + for (auto& i : sink_source_loc_map_) { + int tile_id = i.first; + int x, y; + location loc = tile_to_loc_[tile_id]; + std::tie(x, y) = loc; + auto pin_vec = i.second; + for (size_t j = 0; j < pin_vec.size(); ++j) { + bool input; + int node_id; + std::tie(input, std::ignore, node_id) = pin_vec[j]; + auto chan_key = redirect_[std::make_tuple(node_id, loc)]; + e_rr_type pin = input ? e_rr_type::SINK : e_rr_type::SOURCE; + e_rr_type mux = input ? e_rr_type::IPIN : e_rr_type::OPIN; + + int pin_id, mux_id, track_id; + VTR_ASSERT(loc_type_idx_to_rr_idx_.find(std::make_tuple(loc, pin, j)) != loc_type_idx_to_rr_idx_.end()); + VTR_ASSERT(loc_type_idx_to_rr_idx_.find(std::make_tuple(loc, mux, j)) != loc_type_idx_to_rr_idx_.end()); + pin_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, j)]; + mux_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, mux, j)]; + track_id = loc_type_idx_to_rr_idx_[chan_key]; + + int sink, sink_src, src; + sink = input ? pin_id : track_id; + sink_src = mux_id; + src = input ? track_id : pin_id; + + device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(src), RRNodeId(sink_src), 0); + device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(sink_src), RRNodeId(sink), sink == track_id ? 1 : 0); + } + } + } + + void pack_chans_edges() { + for (auto& i : shorts_) { + location l1, l2; + e_rr_type t1, t2; + int idx1, idx2; + std::tie(l1, t1, idx1, l2, t2, idx2) = i; + std::tuple key1(l1, t1, idx1), key2(l2, t2, idx2); + + VTR_ASSERT(loc_type_idx_to_rr_idx_.find(key1) != loc_type_idx_to_rr_idx_.end()); + VTR_ASSERT(loc_type_idx_to_rr_idx_.find(key2) != loc_type_idx_to_rr_idx_.end()); + + int src, sink; + src = loc_type_idx_to_rr_idx_[key1]; + sink = loc_type_idx_to_rr_idx_[key2]; + + device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(src), RRNodeId(sink), 0); + device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(sink), RRNodeId(src), 0); + } + } + + void pack_pips() { + for (auto& i : pips_) { + int node1, node2; + std::tie(node1, node2) = i.first; + int sw_id; + std::tuple metadata; + std::tie(sw_id, metadata) = i.second; + VTR_ASSERT(sw_id > 1); + int tile_id, wire0, wire1; + bool forward; + std::tie(tile_id, wire0, wire1, forward) = metadata; + location loc = tile_to_loc_[tile_id]; + auto key1 = redirect_[std::make_tuple(node1, loc)]; + auto key2 = redirect_[std::make_tuple(node2, loc)]; + + VTR_ASSERT(loc_type_idx_to_rr_idx_.find(key1) != loc_type_idx_to_rr_idx_.end()); + VTR_ASSERT(loc_type_idx_to_rr_idx_.find(key2) != loc_type_idx_to_rr_idx_.end()); + + int src, sink; + src = loc_type_idx_to_rr_idx_[key1]; + sink = loc_type_idx_to_rr_idx_[key2]; + device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(src), RRNodeId(sink), sw_id); + + vtr::interned_string name_(empty_); + vtr::interned_string value_(empty_); + + char metadata_[100]; + sprintf(metadata_, "%d,%d,%d,%d", tile_id, wire0, wire1, forward ? 1 : 0); + + name_ = device_ctx_.arch->strings.intern_string(vtr::string_view("FPGAInterchange")); + value_ = device_ctx_.arch->strings.intern_string(vtr::string_view(metadata_)); + vpr::add_rr_edge_metadata(src, sink, sw_id, name_, value_); + } + } + + void pack_rr_edges() { + pack_tiles_edges(); + pack_chans_edges(); + pack_pips(); + device_ctx_.rr_graph_builder.mark_edges_as_rr_switch_ids(); + device_ctx_.rr_graph_builder.partition_edges(); + } + + /* + * Fill device rr_graph informations. + */ + void pack_to_rr_graph() { + pack_chan_width(); + // switches are already packed + // segments are already packed before rr_generation + // physical_tile_types are already packed before rr_generation + // grid is already packed before rr_generation + pack_rr_nodes(); + pack_rr_edges(); + } + + /* + * Finish rr_graph generation. + * Build node_lookup, inito fan_ins, load indexed data + */ + void finish_rr_generation() { + for (t_rr_type rr_type : RR_TYPES) { + if (rr_type == CHANX) { + device_ctx_.rr_graph_builder.node_lookup().resize_nodes(grid_.height(), grid_.width(), rr_type, NUM_SIDES); + } else { + device_ctx_.rr_graph_builder.node_lookup().resize_nodes(grid_.width(), grid_.height(), rr_type, NUM_SIDES); + } + } + for (size_t node = 0; node < device_ctx_.rr_nodes.size(); node++) { + auto rr_node = device_ctx_.rr_nodes[node]; + device_ctx_.rr_graph_builder.add_node_to_all_locs(rr_node.id()); + } + + device_ctx_.rr_graph_builder.init_fan_in(); + + /* Create a temp copy to convert from vtr::vector to std::vector + * This is required because the ``alloc_and_load_rr_indexed_data()`` function supports only std::vector data + * type for ``rr_segments`` + * Note that this is a dirty fix (to avoid massive code changes) + * TODO: The ``alloc_and_load_rr_indexed_data()`` function should embrace ``vtr::vector`` for ``rr_segments`` + */ + std::vector temp_rr_segs; + temp_rr_segs.reserve(segment_inf_.size()); + for (auto& rr_seg : segment_inf_) { + temp_rr_segs.push_back(rr_seg); + } + + + alloc_and_load_rr_indexed_data( + temp_rr_segs, + 0, //we connect ipins to tracks with shorts + base_cost_type_); + + VTR_ASSERT(device_ctx_.rr_indexed_data.size() == seg_index_.size()); + for (size_t i = 0; i < seg_index_.size(); ++i) { + device_ctx_.rr_indexed_data[RRIndexedDataId(i)].seg_index = seg_index_[RRIndexedDataId(i)]; + } + } +}; void build_rr_graph_fpga_interchange(const t_graph_type graph_type, const DeviceGrid& grid, const std::vector& segment_inf, const enum e_base_cost_type base_cost_type, int* wire_to_rr_ipin_switch, - const char* read_rr_graph_name, - bool read_edge_metadata, bool do_check_rr_graph) { vtr::ScopedStartFinishTimer timer("Building RR Graph from device database"); auto& device_ctx = g_vpr_ctx.mutable_device(); - device_ctx.rr_segments = segment_inf; + size_t num_segments = segment_inf.size(); + device_ctx.rr_segments.reserve(num_segments); + for (long unsigned int iseg = 0; iseg < num_segments; ++iseg) { + device_ctx.rr_segments.push_back(segment_inf[(iseg)]); + } - VTR_LOG("%s\n", get_arch_file_name()); -}; + + // Decompress GZipped capnproto device file + gzFile file = gzopen(get_arch_file_name(), "r"); + VTR_ASSERT(file != Z_NULL); + + std::vector output_data; + output_data.resize(4096); + std::stringstream sstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary); + while (true) { + int ret = gzread(file, output_data.data(), output_data.size()); + VTR_ASSERT(ret >= 0); + if (ret > 0) { + sstream.write((const char*)output_data.data(), ret); + VTR_ASSERT(sstream); + } else { + VTR_ASSERT(ret == 0); + int error; + gzerror(file, &error); + VTR_ASSERT(error == Z_OK); + break; + } + } + + VTR_ASSERT(gzclose(file) == Z_OK); + + sstream.seekg(0); + kj::std::StdInputStream istream(sstream); + + // Reader options + capnp::ReaderOptions reader_options; + reader_options.nestingLimit = std::numeric_limits::max(); + reader_options.traversalLimitInWords = std::numeric_limits::max(); + + capnp::InputStreamMessageReader message_reader(istream, reader_options); + + auto device_reader = message_reader.getRoot(); + + RR_Graph_Builder builder(device_reader, grid, device_ctx, device_ctx.physical_tile_types, device_ctx.rr_segments, base_cost_type); + builder.build_rr_graph(); + *wire_to_rr_ipin_switch = 0; + device_ctx.read_rr_graph_filename.assign(get_arch_file_name()); + + if (do_check_rr_graph) { + check_rr_graph(graph_type, grid, device_ctx.physical_tile_types); + } +} diff --git a/vpr/src/route/rr_graph_fpga_interchange.h b/vpr/src/route/rr_graph_fpga_interchange.h index 8241fd613e9..ab38b5b881c 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.h +++ b/vpr/src/route/rr_graph_fpga_interchange.h @@ -5,14 +5,16 @@ #include "rr_graph.h" #include "device_grid.h" +#include "DeviceResources.capnp.h" +#include "LogicalNetlist.capnp.h" +#include "capnp/serialize.h" +#include "capnp/serialize-packed.h" void build_rr_graph_fpga_interchange(const t_graph_type graph_type, const DeviceGrid& grid, const std::vector& segment_inf, const enum e_base_cost_type base_cost_type, int* wire_to_rr_ipin_switch, - const char* read_rr_graph_name, - bool read_edge_metadata, bool do_check_rr_graph); #endif /* RR_GRAPH_FPGA_INTERCHANGE_H */ From 8653b3abd5c80d306bffcec6353d9c726ab048b5 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 20 Dec 2021 13:26:11 +0100 Subject: [PATCH 3/7] Small bug fixes Signed-off-by: Maciej Dudek --- vpr/src/route/rr_graph_fpga_interchange.cpp | 34 ++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index db1367a3090..f8e6bc6f6f1 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -497,7 +497,8 @@ struct RR_Graph_Builder { } } existing_nodes.erase(vertex->loc); - delete (vertex); + VTR_ASSERT(vertex != root_node); + delete vertex; } for (auto& node : existing_nodes) { @@ -518,11 +519,11 @@ struct RR_Graph_Builder { * Removes dangling nodes from a graph represented by the root node. * Dangling nodes are nodes that do not connect to a pin, a pip or other non-dangling node. */ - int reduce_graph_and_count_nodes(node_* root) { + int reduce_graph_and_count_nodes(node_*& root) { int cnt = 0; std::queue walker; std::stack back_walker; - walker.push(root); + walker.emplace(root); root->visited = root; bool all_nulls; while (!walker.empty()) { @@ -559,7 +560,9 @@ struct RR_Graph_Builder { temp->links[(i + 2) % 4] = nullptr; } } - delete (vertex); + if (vertex == root) + root = nullptr; + delete vertex; } else if (vertex->on_route) { vertex->visited->on_route = true; if (vertex->visited->segment_id == -1) @@ -577,12 +580,12 @@ struct RR_Graph_Builder { std::queue walker; std::stack back_walker; - walker.emplace(root); + walker.push(root); bool all_nulls, is_chanx, is_chany; while (!walker.empty()) { node_* vertex = walker.front(); walker.pop(); - back_walker.emplace(vertex); + back_walker.push(vertex); is_chany = false; is_chanx = vertex->has_pins; all_nulls = true; @@ -594,7 +597,7 @@ struct RR_Graph_Builder { is_chany = i == 1 ? true : is_chany; } if (vertex->links[i]->visited == vertex) { - walker.emplace(vertex->links[i]); + walker.push(vertex->links[i]); } } } @@ -717,17 +720,18 @@ struct RR_Graph_Builder { * Clean up of earlier stages */ void delete_nodes(node_* root) { - std::queue walker; - std::stack back_walker; - walker.emplace(root); + std::queue> walker; + std::stack> back_walker; + walker.push(root); while (!walker.empty()) { node_* vertex = walker.front(); walker.pop(); - back_walker.emplace(vertex); + back_walker.push(vertex); + location loc = vertex->loc; for (int i = 0; i < 4; ++i) { if (vertex->links[i] != nullptr) { if (vertex->links[i]->visited == vertex) { - walker.emplace(vertex->links[i]); + walker.push(vertex->links[i]); } } } @@ -742,7 +746,7 @@ struct RR_Graph_Builder { temp->links[(i + 2) % 4] = nullptr; } } - delete (vertex); + delete vertex; } } @@ -752,7 +756,7 @@ struct RR_Graph_Builder { void process_nodes() { auto wires = ar_.getWires(); for (auto const& node : ar_.getNodes()) { - std::tuple base_wire_{wires[node.getWires()[0]].getTile(), wires[node.getWires()[0]].getWire()}; + std::tuple base_wire_(wires[node.getWires()[0]].getTile(), wires[node.getWires()[0]].getWire()); int node_id = wire_to_node_[base_wire_]; std::set all_possible_tiles = node_to_locs_[node_id]; int root_x, root_y; @@ -766,6 +770,8 @@ struct RR_Graph_Builder { node_* root = build_node_graph(node_id, all_possible_tiles, empties, root_x, root_y); int div = reduce_graph_and_count_nodes(root); + if (root == nullptr) + continue; float capacitance, resistance; auto model = ar_.getNodeTimings()[node.getNodeTiming()]; capacitance = get_corner_value(model.getCapacitance(), "slow", "typ") / div; From 08d8c3051c76561f9bf4d09fc8a300aca1868856 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 20 Dec 2021 15:40:13 +0100 Subject: [PATCH 4/7] Fixes in rr graph generation Code refactor Signed-off-by: Maciej Dudek --- vpr/src/route/rr_graph_fpga_interchange.cpp | 482 +++++++++++--------- 1 file changed, 278 insertions(+), 204 deletions(-) diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index f8e6bc6f6f1..2f5d415c529 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -15,29 +15,43 @@ #include "rr_graph_indexed_data.h" #include "fpga_interchange_arch_utils.h" +//#define DEBUG + using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; -typedef std::tuple location; +static const auto INOUT = LogicalNetlist::Netlist::Direction::INOUT; + +typedef std::pair location; + +enum node_sides { + LEFT_EDGE = 0, + TOP_EDGE, + RIGHT_EDGE, + BOTTOM_EDGE, +}; + +auto NODESIDES = {LEFT_EDGE, TOP_EDGE, RIGHT_EDGE, BOTTOM_EDGE}; /* * Intermediate data type. * It is used to build and explore FPGA Interchange * nodes graph and later convert them to rr_nodes. */ -struct node_ { +struct intermediate_node { public: location loc; int segment_id; - node_* links[4]; //left, up, right, down - node_* visited; + intermediate_node* links[4]; //left, up, right, down + intermediate_node* visited; bool on_route; bool has_pins; - node_() = delete; - node_(int x, int y) + intermediate_node() = delete; + intermediate_node(int x, int y) : loc{x, y} { - links[0] = links[1] = links[2] = links[3] = nullptr; + for (auto i : NODESIDES) + links[i] = nullptr; visited = nullptr; on_route = false; has_pins = false; @@ -71,11 +85,50 @@ struct RR_Graph_Builder { for (size_t i = 1; i < grid.width() - 1; ++i) { for (size_t j = 1; j < grid.height() - 1; ++j) { - location loc{i, j}; + location loc(i, j); if (grid[i][j].type->index == 0) empty_tiles.insert(loc); } } + + auto primLib = ar_.getPrimLibs(); + auto portList = primLib.getPortList(); + + for (auto cell_mapping : ar_.getCellBelMap()) { + size_t cell_name = cell_mapping.getCell(); + + int found_valid_prim = false; + for (auto primitive : primLib.getCellDecls()) { + bool is_prim = str(primitive.getLib()) == std::string("primitives"); + bool is_cell = cell_name == primitive.getName(); + + bool has_inout = false; + for (auto port_idx : primitive.getPorts()) { + auto port = portList[port_idx]; + + if (port.getDir() == INOUT) { + has_inout = true; + break; + } + } + + if (is_prim && is_cell && !has_inout) { + found_valid_prim = true; + break; + } + } + + if (!found_valid_prim) + continue; + + for (auto common_pins : cell_mapping.getCommonPins()) { + for (auto site_type_entry : common_pins.getSiteTypes()) { + for (auto bel : site_type_entry.getBels()) { + bel_cell_mappings_.emplace(bel); + } + } + } + } } void build_rr_graph() { @@ -87,7 +140,7 @@ struct RR_Graph_Builder { process_nodes(); pack_to_rr_graph(); #ifdef DEBUG - print_(); + print(); #endif finish_rr_generation(); } @@ -107,11 +160,13 @@ struct RR_Graph_Builder { std::set empty_tiles; + std::set bel_cell_mappings_; + std::unordered_map tile_to_loc_; std::map loc_to_tile_; std::map, int /*idx*/> pips_models_; - std::map, std::tuple>> pips_; + std::map, std::tuple>> pips_; std::map, int> wire_to_node_; std::unordered_map> node_to_locs_; @@ -139,6 +194,7 @@ struct RR_Graph_Builder { */ std::set> used_by_pip_; std::set> used_by_pin_; + std::set usefull_node_; /* Sink_source_loc_map is used to create ink/source and ipin/opin rr_nodes, * rr_edges from sink/source to ipin/opin and from ipin/opin to their coresponding segments @@ -160,7 +216,7 @@ struct RR_Graph_Builder { /* * Debug print function */ - void print_() { + void print() { for (const auto& entry : sink_source_loc_map_) { const auto& key = entry.first; const auto& value = entry.second; @@ -189,7 +245,7 @@ struct RR_Graph_Builder { location loc; e_rr_type type; std::tie(loc, type) = entry.first; - VTR_LOG("CHAN%c X:%d Y:%d\n", type == e_rr_type::CHANX ? 'X' : 'Y', std::get<0>(loc), std::get<1>(loc)); + VTR_LOG("CHAN%c X:%d Y:%d\n", type == e_rr_type::CHANX ? 'X' : 'Y', loc.first, loc.second); for (auto& seg : entry.second) { VTR_LOG("\tSegment id:%d name:%s\n", std::get<0>(seg), segment_inf_[RRSegmentId(std::get<0>(seg))].name.c_str()); } @@ -205,9 +261,8 @@ struct RR_Graph_Builder { e_rr_type new_type; int new_idx; std::tie(new_loc, new_type, new_idx) = entry.second; - VTR_LOG("\t Node:%d X:%d Y:%d -> X:%d Y:%d CHAN%c idx:%d\n", node, std::get<0>(loc), std::get<1>(loc), std::get<0>(new_loc), - std::get<1>(new_loc), new_type == e_rr_type::CHANX ? 'X' : 'Y', - new_idx); + VTR_LOG("\t Node:%d X:%d Y:%d -> X:%d Y:%d CHAN%c idx:%d\n", node, loc.first, loc.second, new_loc.first, + new_loc.second, new_type == e_rr_type::CHANX ? 'X' : 'Y', new_idx); } VTR_LOG("Shorts:\n"); @@ -216,10 +271,10 @@ struct RR_Graph_Builder { e_rr_type type1, type2; int id1, id2; std::tie(loc1, type1, id1, loc2, type2, id2) = entry; - VTR_LOG("\tCHAN%c X:%d Y%d", type1 == e_rr_type::CHANX ? 'X' : 'Y', std::get<0>(loc1), std::get<1>(loc1)); + VTR_LOG("\tCHAN%c X:%d Y%d", type1 == e_rr_type::CHANX ? 'X' : 'Y', loc1.first, loc1.second); VTR_LOG(" Segment id:%d name:%s ->", id1, segment_inf_[RRSegmentId(std::get<0>(chan_loc_map_[std::make_tuple(loc1, type1)][id1]))].name.c_str()); - VTR_LOG(" CHAN%c X:%d Y%d", type2 == e_rr_type::CHANX ? 'X' : 'Y', std::get<0>(loc2), std::get<1>(loc2)); + VTR_LOG(" CHAN%c X:%d Y:%d", type2 == e_rr_type::CHANX ? 'X' : 'Y', loc2.first, loc2.second); VTR_LOG(" Segment id:%d name:%s\n", id2, segment_inf_[RRSegmentId(std::get<0>(chan_loc_map_[std::make_tuple(loc2, type2)][id2]))].name.c_str()); } @@ -228,22 +283,22 @@ struct RR_Graph_Builder { auto& key = pip.first; auto& value = pip.second; std::tuple meta; - int switch_id, tile, wire0, wire1; + int switch_id, tile_id, name, wire0, wire1; bool forward; - std::tie(switch_id, meta) = value; - std::tie(tile, wire0, wire1, forward) = meta; - auto& r1 = redirect_[std::make_tuple(std::get<0>(key), tile_to_loc_[tile])]; - auto& r2 = redirect_[std::make_tuple(std::get<1>(key), tile_to_loc_[tile])]; + std::tie(switch_id, tile_id, meta) = value; + std::tie(name, wire0, wire1, forward) = meta; + auto& r1 = redirect_[std::make_tuple(std::get<0>(key), tile_to_loc_[tile_id])]; + auto& r2 = redirect_[std::make_tuple(std::get<1>(key), tile_to_loc_[tile_id])]; VTR_LOG("Switch_type: %d, %s.%s %s.%s: forward:%s\n", - switch_id, str(tile).c_str(), str(wire0).c_str(), str(tile).c_str(), str(wire1).c_str(), forward ? "yes" : "no"); + switch_id, str(tile_id).c_str(), str(wire0).c_str(), str(name).c_str(), str(wire1).c_str(), forward ? "yes" : "no"); VTR_LOG("Edge: CHAN%c X:%d Y:%d idx:%d -> CHAN%c X:%d Y:%d idx%d\n", std::get<1>(r1) == e_rr_type::CHANX ? 'X' : 'Y', - std::get<0>(std::get<0>(r1)), - std::get<1>(std::get<0>(r1)), + std::get<0>(r1).first, + std::get<0>(r1).second, std::get<2>(r1), std::get<1>(r2) == e_rr_type::CHANX ? 'X' : 'Y', - std::get<0>(std::get<0>(r2)), - std::get<1>(std::get<0>(r2)), + std::get<0>(r2).first, + std::get<0>(r2).second, std::get<2>(r2)); } VTR_LOG("RR_Nodes\n"); @@ -252,7 +307,7 @@ struct RR_Graph_Builder { e_rr_type type; int idx; std::tie(loc, type, idx) = i.first; - VTR_LOG("\t X:%d Y:%d type:%d idx:%d->rr_node:%d\n", std::get<0>(loc), std::get<1>(loc), type, idx, i.second); + VTR_LOG("\t X:%d Y:%d type:%d idx:%d->rr_node:%d\n", loc.first, loc.second, type, idx, i.second); } } @@ -351,7 +406,7 @@ struct RR_Graph_Builder { */ void create_tile_id_to_loc() { for (const auto& tile : ar_.getTileList()) { - tile_to_loc_[tile.getName()] = location{tile.getCol() + 1, tile.getRow() + 1}; + tile_to_loc_[tile.getName()] = location(tile.getCol() + 1, tile.getRow() + 1); loc_to_tile_[location(tile.getCol() + 1, tile.getRow() + 1)] = tile.getName(); } } @@ -384,45 +439,51 @@ struct RR_Graph_Builder { */ void create_pips_() { for (const auto& tile : ar_.getTileList()) { - int name = tile.getName(); - location loc = tile_to_loc_[name]; + int tile_id = tile.getName(); + location loc = tile_to_loc_[tile_id]; const auto& tile_type = ar_.getTileTypeList()[tile.getType()]; for (const auto& pip : tile_type.getPips()) { if (pip.isPseudoCells()) continue; - if (tile.hasSubTilesPrefices()) - name = tile.getSubTilesPrefices()[pip.getSubTile()]; int wire0_name, wire1_name; int node0, node1; int switch_id; wire0_name = tile_type.getWires()[pip.getWire0()]; wire1_name = tile_type.getWires()[pip.getWire1()]; - if (wire_to_node_.find(std::make_tuple(name, wire0_name)) == wire_to_node_.end()) + if (wire_to_node_.find(std::make_tuple(tile_id, wire0_name)) == wire_to_node_.end()) continue; - if (wire_to_node_.find(std::make_tuple(name, wire1_name)) == wire_to_node_.end()) + if (wire_to_node_.find(std::make_tuple(tile_id, wire1_name)) == wire_to_node_.end()) continue; - node0 = wire_to_node_[std::make_tuple(name, wire0_name)]; - node1 = wire_to_node_[std::make_tuple(name, wire1_name)]; + node0 = wire_to_node_[std::make_tuple(tile_id, wire0_name)]; + node1 = wire_to_node_[std::make_tuple(tile_id, wire1_name)]; used_by_pip_.emplace(node0, loc); used_by_pip_.emplace(node1, loc); + + usefull_node_.insert(node0); + usefull_node_.insert(node1); + + int name = tile_id; + if (tile.hasSubTilesPrefices()) + name = tile.getSubTilesPrefices()[pip.getSubTile()]; + switch_id = pips_models_[std::make_tuple(pip.getTiming(), pip.getBuffered21())]; std::tuple source_sink; source_sink = std::make_tuple(node0, node1); - pips_.emplace(source_sink, std::make_tuple(switch_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, std::make_tuple(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); if (!pip.getBuffered21() && (pip.getDirectional() || pip.getBuffered20())) { source_sink = std::make_tuple(node1, node0); - pips_.emplace(source_sink, std::make_tuple(switch_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, std::make_tuple(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); } if (!pip.getDirectional()) { switch_id = pips_models_[std::make_tuple(pip.getTiming(), pip.getBuffered20())]; source_sink = std::make_tuple(node1, node0); - pips_.emplace(source_sink, std::make_tuple(switch_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, std::make_tuple(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); if (!pip.getBuffered20() && pip.getBuffered21()) { source_sink = std::make_tuple(node0, node1); - pips_.emplace(source_sink, std::make_tuple(switch_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, std::make_tuple(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); } } } @@ -432,136 +493,108 @@ struct RR_Graph_Builder { /* * Build graph of FPGA Interchange node for further computations */ - node_* build_node_graph(int node_id, - std::set nodes, - std::set empties, - int root_x, - int root_y) { - int visited_tiles = 0; - node_* root_node = new node_(root_x, root_y); - location key{root_x, root_y}; - - root_node->segment_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[key])]; - nodes.erase(key); - - std::map existing_nodes; - existing_nodes.emplace(key, root_node); - - std::priority_queue> builder; - builder.emplace(1, 0, root_node); - while (!builder.empty() && (!nodes.empty() || std::get<0>(builder.top()) != 0)) { - node_* vertex; - std::tie(std::ignore, std::ignore, vertex) = builder.top(); - builder.pop(); - location loc = vertex->loc; - for (int i = 0; i < 4; ++i) { - location offset = offsets[i]; - location other_loc{std::get<0>(loc) + std::get<0>(offset), std::get<1>(loc) + std::get<1>(offset)}; - node_* temp = nullptr; - if (existing_nodes.find(other_loc) != existing_nodes.end()) { - temp = existing_nodes[other_loc]; - } else if (nodes.find(other_loc) != nodes.end()) { - temp = new node_(std::get<0>(other_loc), std::get<1>(other_loc)); - temp->segment_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[other_loc])]; - nodes.erase(other_loc); - existing_nodes.emplace(other_loc, temp); - builder.emplace(1, -1 * visited_tiles, temp); - visited_tiles++; - } else if (empties.find(other_loc) != empties.end()) { - temp = new node_(std::get<0>(other_loc), std::get<1>(other_loc)); - temp->segment_id = -1; - empties.erase(other_loc); - existing_nodes.emplace(other_loc, temp); - builder.emplace(0, -1 * visited_tiles, temp); - visited_tiles++; - } - vertex->links[i] = temp; - } - } - - if (!nodes.empty()) - vpr_throw(VPR_ERROR_ARCH, get_arch_file_name(), __LINE__, "Node graph could not be created\n"); - - while (!builder.empty()) { - node_* vertex; - std::tie(std::ignore, std::ignore, vertex) = builder.top(); - builder.pop(); - location loc = vertex->loc; - for (int i = 0; i < 4; ++i) { - location offset = offsets[i]; - location other_loc{std::get<0>(loc) + std::get<0>(offset), std::get<1>(loc) + std::get<1>(offset)}; - node_* temp = nullptr; - if (existing_nodes.find(other_loc) != existing_nodes.end()) { - temp = existing_nodes[other_loc]; - temp->links[(i + 2) % 4] = nullptr; + std::set build_node_graph(int node_id, + std::set nodes) { + std::set roots; + std::map existing_nodes; + do { + intermediate_node* root_node = new intermediate_node((*nodes.begin()).first, + (*nodes.begin()).second); + location key = *nodes.begin(); + + root_node->segment_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[key])]; + nodes.erase(key); + + existing_nodes.emplace(key, root_node); + + std::queue builder; + builder.push(root_node); + + while (!builder.empty()) { + intermediate_node* vertex; + vertex = builder.front(); + builder.pop(); + location loc = vertex->loc; + for (auto i : NODESIDES) { + location offset = offsets[i]; + location other_loc(loc.first + offset.first, loc.second + offset.second); + intermediate_node* temp = nullptr; + if (existing_nodes.find(other_loc) != existing_nodes.end()) { + temp = existing_nodes[other_loc]; + } else if (nodes.find(other_loc) != nodes.end()) { + temp = new intermediate_node(other_loc.first, other_loc.second); + temp->segment_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[other_loc])]; + nodes.erase(other_loc); + existing_nodes.emplace(other_loc, temp); + builder.push(temp); + } + vertex->links[i] = temp; } } - existing_nodes.erase(vertex->loc); - VTR_ASSERT(vertex != root_node); - delete vertex; - } + roots.emplace(root_node); + } while (!nodes.empty()); for (auto& node : existing_nodes) { if (used_by_pip_.find(std::make_tuple(node_id, node.second->loc)) != used_by_pip_.end()) { node.second->on_route = true; - root_node = node.second; } if (used_by_pin_.find(std::make_tuple(node_id, node.second->loc)) != used_by_pin_.end()) { node.second->has_pins = true; node.second->on_route = true; - root_node = node.second; } } - return root_node; + return roots; } /* * Removes dangling nodes from a graph represented by the root node. * Dangling nodes are nodes that do not connect to a pin, a pip or other non-dangling node. */ - int reduce_graph_and_count_nodes(node_*& root) { + int reduce_graph_and_count_nodes_left(std::set& roots) { int cnt = 0; - std::queue walker; - std::stack back_walker; - walker.emplace(root); - root->visited = root; + std::queue walker; + std::stack back_walker; + for (intermediate_node* root : roots) { + walker.push(root); + root->visited = root; + } bool all_nulls; while (!walker.empty()) { all_nulls = true; - node_* vertex = walker.front(); + intermediate_node* vertex = walker.front(); walker.pop(); - back_walker.emplace(vertex); - for (int i = 0; i < 4; ++i) { + back_walker.push(vertex); + for (auto i : NODESIDES) { if (vertex->links[i] != nullptr) { all_nulls = false; - if (i == 0 || i == 1) + if (i == node_sides::LEFT_EDGE || i == node_sides::TOP_EDGE) cnt++; if (vertex->links[i]->visited == nullptr) { vertex->links[i]->visited = vertex; - walker.emplace(vertex->links[i]); + walker.push(vertex->links[i]); } } } cnt += all_nulls; } while (!back_walker.empty()) { - node_* vertex = back_walker.top(); + intermediate_node* vertex = back_walker.top(); back_walker.pop(); if (!vertex->has_pins && !vertex->on_route) { if (vertex->visited->segment_id == -1) { vertex->visited->segment_id = vertex->segment_id; } - for (int i = 0; i < 4; ++i) { - node_* temp = nullptr; + for (auto i : NODESIDES) { + intermediate_node* temp = nullptr; if (vertex->links[i] != nullptr) { - if (i == 2 || i == 3) + if (i == node_sides::RIGHT_EDGE || i == node_sides::BOTTOM_EDGE) cnt--; temp = vertex->links[i]; temp->links[(i + 2) % 4] = nullptr; } } - if (vertex == root) - root = nullptr; + if (roots.find(vertex) != roots.end()) + roots.erase(vertex); delete vertex; } else if (vertex->on_route) { vertex->visited->on_route = true; @@ -572,29 +605,33 @@ struct RR_Graph_Builder { return cnt; } - void populate_chan_loc_map(int node_id, node_* root, float R, float C) { + void populate_chan_loc_map(int node_id, std::set roots, float R, float C) { /* * Create future rr_nodes that corespond to given FPGA Interchange node */ std::map, int /*index*/> loc_chan_idx; - std::queue walker; - std::stack back_walker; - walker.push(root); + std::queue walker; + std::stack back_walker; + + for (intermediate_node* root : roots) { + walker.push(root); + } + bool all_nulls, is_chanx, is_chany; while (!walker.empty()) { - node_* vertex = walker.front(); + intermediate_node* vertex = walker.front(); walker.pop(); back_walker.push(vertex); is_chany = false; is_chanx = vertex->has_pins; all_nulls = true; - for (int i = 0; i < 4; ++i) { + for (auto i : NODESIDES) { if (vertex->links[i] != nullptr) { all_nulls = false; if (vertex->links[i]->visited == vertex || vertex->links[i] == vertex->visited) { - is_chanx = i == 0 ? true : is_chanx; - is_chany = i == 1 ? true : is_chany; + is_chanx = i == node_sides::LEFT_EDGE ? true : is_chanx; + is_chany = i == node_sides::TOP_EDGE ? true : is_chany; } if (vertex->links[i]->visited == vertex) { walker.push(vertex->links[i]); @@ -603,7 +640,9 @@ struct RR_Graph_Builder { } is_chanx = all_nulls ? all_nulls : is_chanx; std::tuple key1(vertex->loc, e_rr_type::CHANY); - std::tuple key2(location(std::get<0>(vertex->loc), std::get<1>(vertex->loc) - 1), e_rr_type::CHANX); + std::tuple key2(location(vertex->loc.first, vertex->loc.second - 1), e_rr_type::CHANX); + + VTR_ASSERT(vertex->segment_id != -1); if (is_chany) { loc_chan_idx[key1] = chan_loc_map_[key1].size(); @@ -623,27 +662,40 @@ struct RR_Graph_Builder { * Create edges between future rr_nodes that correspond to the given FPGA Interchange node */ while (!back_walker.empty()) { - node_* vertex = back_walker.top(); + intermediate_node* vertex = back_walker.top(); back_walker.pop(); location loc = vertex->loc; - location down(std::get<0>(vertex->loc), std::get<1>(vertex->loc) - 1); // lower tile CHANY, our CHANX - location right(std::get<0>(down) + 1, std::get<1>(down)); // CHANX of tile to the right + location down(vertex->loc.first, vertex->loc.second - 1); // lower tile CHANY, our CHANX + location right(down.first + 1, down.second); // CHANX of tile to the right + + bool left_node_exist = vertex->links[LEFT_EDGE] != nullptr, + top_node_exist = vertex->links[TOP_EDGE] != nullptr, + right_node_exist = vertex->links[RIGHT_EDGE] != nullptr, + bottom_node_exist = vertex->links[BOTTOM_EDGE] != nullptr; + + bool right_node_valid = false; + if (right_node_exist) + right_node_valid = vertex->links[RIGHT_EDGE]->visited == vertex || vertex->links[RIGHT_EDGE] == vertex->visited; + + bool bottom_node_valid = false; + if (bottom_node_exist) + bottom_node_valid = vertex->links[BOTTOM_EDGE]->visited == vertex || vertex->links[BOTTOM_EDGE] == vertex->visited; if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) != loc_chan_idx.end()) { - redirect_.emplace(std::make_tuple(node_id, loc), std::make_tuple( - down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)])); + redirect_.emplace(std::make_tuple(node_id, loc), + std::make_tuple(down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)])); } else if (loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) != loc_chan_idx.end()) { - redirect_.emplace(std::make_tuple(node_id, loc), std::make_tuple( - loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)])); + redirect_.emplace(std::make_tuple(node_id, loc), + std::make_tuple(loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)])); - } else if (vertex->links[2] != nullptr && (vertex->links[2]->visited == vertex || vertex->links[2] == vertex->visited) && loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) { - redirect_.emplace(std::make_tuple(node_id, loc), std::make_tuple( - right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)])); + } else if (right_node_exist && right_node_valid && loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) { + redirect_.emplace(std::make_tuple(node_id, loc), + std::make_tuple(right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)])); - } else if (vertex->links[3] != nullptr && (vertex->links[3]->visited == vertex || vertex->links[3] == vertex->visited) && loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) { - redirect_.emplace(std::make_tuple(node_id, loc), std::make_tuple( - down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)])); + } else if (bottom_node_exist && bottom_node_valid && loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) { + redirect_.emplace(std::make_tuple(node_id, loc), + std::make_tuple(down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)])); } /* Check if node x is up-right corner: @@ -655,7 +707,7 @@ struct RR_Graph_Builder { * If so it does not use CHANX or CHANY, and it must connect CHANY from a with CHANX from b */ - if (vertex->links[2] != nullptr && vertex->links[3] != nullptr && (vertex->links[3]->visited == vertex || vertex->links[3] == vertex->visited) && (vertex->links[2]->visited == vertex || vertex->links[2] == vertex->visited) && loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) == loc_chan_idx.end() && loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) == loc_chan_idx.end()) { + if (right_node_exist && right_node_valid && bottom_node_exist && bottom_node_valid && loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) == loc_chan_idx.end() && loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) == loc_chan_idx.end()) { shorts_.emplace_back(down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)], right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)]); continue; @@ -665,9 +717,9 @@ struct RR_Graph_Builder { * CHANY and CHANX of tile to the left and CHANX of tile to the right */ if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) != loc_chan_idx.end()) { - if (vertex->links[0] != nullptr && vertex->links[0]->visited == vertex) { - location left(std::get<0>(down) - 1, std::get<1>(down)); // CHANX - location left_up(std::get<0>(loc) - 1, std::get<1>(loc)); // CHANY + if (left_node_exist && vertex->links[0]->visited == vertex) { + location left(down.first - 1, down.second); // CHANX + location left_up(loc.first - 1, loc.second); // CHANY if (loc_chan_idx.find(std::make_tuple(left, e_rr_type::CHANX)) != loc_chan_idx.end()) shorts_.emplace_back(left, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(left, e_rr_type::CHANX)], down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)]); @@ -675,12 +727,12 @@ struct RR_Graph_Builder { shorts_.emplace_back(left_up, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(left_up, e_rr_type::CHANY)], down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)]); } - if (vertex->links[2] != nullptr && vertex->links[2]->visited == vertex) { + if (right_node_exist && vertex->links[2]->visited == vertex) { if (loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) shorts_.emplace_back(right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)], down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)]); } - if (vertex->links[3] != nullptr && vertex->links[3]->visited == vertex) { + if (bottom_node_exist && vertex->links[3]->visited == vertex) { if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) shorts_.emplace_back(down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)], down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)]); @@ -691,9 +743,9 @@ struct RR_Graph_Builder { * CHANY and CHANX of tile above and CHANX of tile to the right */ if (loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) != loc_chan_idx.end()) { - if (vertex->links[1] != nullptr && vertex->links[1]->visited == vertex) { - location up(std::get<0>(down), std::get<1>(down) + 1); // CHANX - location up_up(std::get<0>(loc), std::get<1>(loc) + 1); // CHANY + if (top_node_exist && vertex->links[1]->visited == vertex) { + location up(down.first, down.second + 1); // CHANX + location up_up(loc.first, loc.second + 1); // CHANY if (loc_chan_idx.find(std::make_tuple(up, e_rr_type::CHANX)) != loc_chan_idx.end()) shorts_.emplace_back(up, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(up, e_rr_type::CHANX)], loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); @@ -701,47 +753,53 @@ struct RR_Graph_Builder { shorts_.emplace_back(up_up, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(up_up, e_rr_type::CHANY)], loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); } - if (vertex->links[2] != nullptr && vertex->links[2]->visited == vertex) { + if (right_node_exist && vertex->links[2]->visited == vertex) { if (loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) shorts_.emplace_back(right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)], loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); } - if (vertex->links[3] != nullptr && vertex->links[3]->visited == vertex) { + if (bottom_node_exist && vertex->links[3]->visited == vertex) { if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) shorts_.emplace_back(down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)], loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); } } } + + intermediate_node* last = *roots.begin(); + for (intermediate_node* root : roots) { + if (root == last) + continue; + auto key1 = redirect_[std::make_tuple(node_id, last->loc)]; + auto key2 = redirect_[std::make_tuple(node_id, root->loc)]; + + shorts_.emplace_back(std::get<0>(key1), std::get<1>(key1), std::get<2>(key1), + std::get<0>(key2), std::get<1>(key2), std::get<2>(key2)); + + last = root; + } } /* * Remove a graph represented by a root node. * Clean up of earlier stages */ - void delete_nodes(node_* root) { - std::queue> walker; - std::stack> back_walker; - walker.push(root); + void delete_nodes(std::set roots) { + std::queue walker; + + for (intermediate_node* root : roots) { + walker.push(root); + } + while (!walker.empty()) { - node_* vertex = walker.front(); + intermediate_node* vertex = walker.front(); walker.pop(); - back_walker.push(vertex); - location loc = vertex->loc; - for (int i = 0; i < 4; ++i) { + for (auto i : NODESIDES) { + intermediate_node* temp = nullptr; if (vertex->links[i] != nullptr) { if (vertex->links[i]->visited == vertex) { walker.push(vertex->links[i]); } - } - } - } - while (!back_walker.empty()) { - node_* vertex = back_walker.top(); - back_walker.pop(); - for (int i = 0; i < 4; ++i) { - node_* temp = nullptr; - if (vertex->links[i] != nullptr) { temp = vertex->links[i]; temp->links[(i + 2) % 4] = nullptr; } @@ -758,27 +816,42 @@ struct RR_Graph_Builder { for (auto const& node : ar_.getNodes()) { std::tuple base_wire_(wires[node.getWires()[0]].getTile(), wires[node.getWires()[0]].getWire()); int node_id = wire_to_node_[base_wire_]; - std::set all_possible_tiles = node_to_locs_[node_id]; - int root_x, root_y; - std::tie(root_x, root_y) = *all_possible_tiles.begin(); - std::set empties; - if (all_possible_tiles.size() != 1) { - for (auto const& empty_tile : empty_tiles) { - empties.insert(empty_tile); - } + if (usefull_node_.find(node_id) == usefull_node_.end()) { + continue; } + std::set all_possible_tiles = node_to_locs_[node_id]; - node_* root = build_node_graph(node_id, all_possible_tiles, empties, root_x, root_y); - int div = reduce_graph_and_count_nodes(root); - if (root == nullptr) + std::set roots = build_node_graph(node_id, all_possible_tiles); + int div = reduce_graph_and_count_nodes_left(roots); + if (roots.empty()) { continue; - float capacitance, resistance; - auto model = ar_.getNodeTimings()[node.getNodeTiming()]; - capacitance = get_corner_value(model.getCapacitance(), "slow", "typ") / div; - resistance = get_corner_value(model.getResistance(), "slow", "typ") / div; - populate_chan_loc_map(node_id, root, resistance, capacitance); - delete_nodes(root); + } + float capacitance = 0.000000001, resistance = 5.7; // Some random data + if (ar_.hasNodeTimings()) { + auto model = ar_.getNodeTimings()[node.getNodeTiming()]; + capacitance = get_corner_value(model.getCapacitance(), "slow", "typ") / div; + resistance = get_corner_value(model.getResistance(), "slow", "typ") / div; + } + populate_chan_loc_map(node_id, roots, resistance, capacitance); + delete_nodes(roots); + } + } + + int next_good_site(int first_idx, const Device::Tile::Reader tile) { + auto tile_type = ar_.getTileTypeList()[tile.getType()]; + size_t ans = first_idx; + for (; ans < tile.getSites().size(); ans++) { + auto site = tile.getSites()[ans]; + auto site_type = ar_.getSiteTypeList()[tile_type.getSiteTypes()[site.getType()].getPrimaryType()]; + bool found = false; + for (auto bel : site_type.getBels()) { + bool res = bel_cell_mappings_.find(bel.getName()) != bel_cell_mappings_.end(); + found |= res; + } + if (found) + break; } + return ans; } /* @@ -816,7 +889,7 @@ struct RR_Graph_Builder { if (tile_type_to_pb_type_.find(tile_type_name) == tile_type_to_pb_type_.end()) continue; sink_source_loc_map_[tile_id].resize(tile_type_to_pb_type_[tile_type_name]->num_pins); - int it = 0; + int it = next_good_site(0, tile); for (const auto& sub_tile : tile_type_to_pb_type_[tile_type_name]->sub_tiles) { for (const auto& port : sub_tile.ports) { float value; @@ -825,14 +898,16 @@ struct RR_Graph_Builder { int pin_id = sub_tile.sub_tile_to_tile_pin_indices[port.index]; std::tuple key{tile_type_id, it, std::string(port.name)}; std::tie(value, wire_id) = tile_type_site_num_pin_name_model[key]; + VTR_ASSERT(wire_to_node_.find(std::make_tuple(tile_id, wire_id)) != wire_to_node_.end()); node_id = wire_to_node_[std::make_tuple(tile_id, wire_id)]; location loc = tile_to_loc_[tile_id]; used_by_pin_.insert(std::make_tuple(node_id, loc)); + usefull_node_.insert(node_id); sink_source_loc_map_[tile_id][pin_id] = std::make_tuple(port.type == IN_PORT, value, node_id); } - it++; + it = next_good_site(it + 1, tile); } } } @@ -859,8 +934,8 @@ struct RR_Graph_Builder { max_x = std::max(max_x, (int)vec.size()); device_ctx_.chan_width.x_list[y] = std::max((int)device_ctx_.chan_width.x_list[y], (int)vec.size()); } else { - min_y = std::min(min_x, (int)vec.size()); - max_y = std::max(max_x, (int)vec.size()); + min_y = std::min(min_y, (int)vec.size()); + max_y = std::max(max_y, (int)vec.size()); device_ctx_.chan_width.y_list[x] = std::max((int)device_ctx_.chan_width.y_list[x], (int)vec.size()); } } @@ -891,6 +966,7 @@ struct RR_Graph_Builder { location track_loc; e_rr_type track_type; int track_idx; + VTR_ASSERT(redirect_.find(std::make_tuple(FPGA_Interchange_node_id, loc)) != redirect_.end()); std::tie(track_loc, track_type, track_idx) = redirect_[std::make_tuple(FPGA_Interchange_node_id, loc)]; auto val = chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx]; int track_seg; @@ -1044,13 +1120,13 @@ struct RR_Graph_Builder { for (auto& i : pips_) { int node1, node2; std::tie(node1, node2) = i.first; - int sw_id; + int sw_id, tile_id; std::tuple metadata; - std::tie(sw_id, metadata) = i.second; + std::tie(sw_id, tile_id, metadata) = i.second; VTR_ASSERT(sw_id > 1); - int tile_id, wire0, wire1; + int name, wire0, wire1; bool forward; - std::tie(tile_id, wire0, wire1, forward) = metadata; + std::tie(name, wire0, wire1, forward) = metadata; location loc = tile_to_loc_[tile_id]; auto key1 = redirect_[std::make_tuple(node1, loc)]; auto key2 = redirect_[std::make_tuple(node2, loc)]; @@ -1067,7 +1143,7 @@ struct RR_Graph_Builder { vtr::interned_string value_(empty_); char metadata_[100]; - sprintf(metadata_, "%d,%d,%d,%d", tile_id, wire0, wire1, forward ? 1 : 0); + sprintf(metadata_, "%d,%d,%d,%d", name, wire0, wire1, forward ? 1 : 0); name_ = device_ctx_.arch->strings.intern_string(vtr::string_view("FPGAInterchange")); value_ = device_ctx_.arch->strings.intern_string(vtr::string_view(metadata_)); @@ -1127,7 +1203,6 @@ struct RR_Graph_Builder { temp_rr_segs.push_back(rr_seg); } - alloc_and_load_rr_indexed_data( temp_rr_segs, 0, //we connect ipins to tracks with shorts @@ -1155,7 +1230,6 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, device_ctx.rr_segments.push_back(segment_inf[(iseg)]); } - // Decompress GZipped capnproto device file gzFile file = gzopen(get_arch_file_name(), "r"); VTR_ASSERT(file != Z_NULL); From 57aac7656dc53a78cfd27feb734579dc9d4dd2d2 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Thu, 23 Dec 2021 16:11:59 +0100 Subject: [PATCH 5/7] Fix unit test as testarch has 2 lut bels Small improvements to RR graph generation Signed-off-by: Maciej Dudek --- vpr/src/route/rr_graph_fpga_interchange.cpp | 30 ++++++++++++++++----- vpr/test/test_interchange_device.cpp | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 2f5d415c529..115654c2286 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -770,7 +770,9 @@ struct RR_Graph_Builder { for (intermediate_node* root : roots) { if (root == last) continue; + VTR_ASSERT(redirect_.find(std::make_tuple(node_id, last->loc)) != redirect_.end()); auto key1 = redirect_[std::make_tuple(node_id, last->loc)]; + VTR_ASSERT(redirect_.find(std::make_tuple(node_id, root->loc)) != redirect_.end()); auto key2 = redirect_[std::make_tuple(node_id, root->loc)]; shorts_.emplace_back(std::get<0>(key1), std::get<1>(key1), std::get<2>(key1), @@ -1074,6 +1076,7 @@ struct RR_Graph_Builder { bool input; int node_id; std::tie(input, std::ignore, node_id) = pin_vec[j]; + VTR_ASSERT(redirect_.find(std::make_tuple(node_id, loc)) != redirect_.end()); auto chan_key = redirect_[std::make_tuple(node_id, loc)]; e_rr_type pin = input ? e_rr_type::SINK : e_rr_type::SOURCE; e_rr_type mux = input ? e_rr_type::IPIN : e_rr_type::OPIN; @@ -1116,6 +1119,14 @@ struct RR_Graph_Builder { } } + char* int_to_string(char* buff, int value) { + if (value < 10) { + return &(*buff = '0' + value) + 1; + } else { + return &(*int_to_string(buff, value / 10) = '0' + value % 10) + 1; + } + } + void pack_pips() { for (auto& i : pips_) { int node1, node2; @@ -1128,7 +1139,9 @@ struct RR_Graph_Builder { bool forward; std::tie(name, wire0, wire1, forward) = metadata; location loc = tile_to_loc_[tile_id]; + VTR_ASSERT(redirect_.find(std::make_tuple(node1, loc)) != redirect_.end()); auto key1 = redirect_[std::make_tuple(node1, loc)]; + VTR_ASSERT(redirect_.find(std::make_tuple(node2, loc)) != redirect_.end()); auto key2 = redirect_[std::make_tuple(node2, loc)]; VTR_ASSERT(loc_type_idx_to_rr_idx_.find(key1) != loc_type_idx_to_rr_idx_.end()); @@ -1139,14 +1152,19 @@ struct RR_Graph_Builder { sink = loc_type_idx_to_rr_idx_[key2]; device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(src), RRNodeId(sink), sw_id); - vtr::interned_string name_(empty_); - vtr::interned_string value_(empty_); - char metadata_[100]; - sprintf(metadata_, "%d,%d,%d,%d", name, wire0, wire1, forward ? 1 : 0); + char* temp = int_to_string(metadata_, name); + *temp++ = ','; + temp = int_to_string(temp, wire0); + *temp++ = ','; + temp = int_to_string(temp, wire1); + *temp++ = ','; + temp = int_to_string(temp, forward ? 1 : 0); + *temp++ = 0; + + vtr::interned_string name_(device_ctx_.arch->strings.intern_string(vtr::string_view("FPGAInterchange"))); + vtr::interned_string value_(device_ctx_.arch->strings.intern_string(vtr::string_view(metadata_))); - name_ = device_ctx_.arch->strings.intern_string(vtr::string_view("FPGAInterchange")); - value_ = device_ctx_.arch->strings.intern_string(vtr::string_view(metadata_)); vpr::add_rr_edge_metadata(src, sink, sw_id, name_, value_); } } diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp index 21409290aa7..22a04eaca1e 100644 --- a/vpr/test/test_interchange_device.cpp +++ b/vpr/test/test_interchange_device.cpp @@ -91,7 +91,7 @@ TEST_CASE("read_interchange_luts", "[vpr]") { const auto& lut_elements = it.second; for (const auto& lut_element : lut_elements) { - REQUIRE(lut_element.lut_bels.size() == 1); + REQUIRE(lut_element.lut_bels.size() == 2); for (auto lut_bel : lut_element.lut_bels) { CHECK(std::find(lut_bels.begin(), lut_bels.end(), lut_bel.name) != lut_bels.end()); From 68807bf247b080653e3de1fc80967026674eed87 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 27 Dec 2021 17:45:19 +0100 Subject: [PATCH 6/7] Get Xilinx 7 series to work Signed-off-by: Maciej Dudek --- vpr/src/route/rr_graph_fpga_interchange.cpp | 104 +++++++++++--------- vpr/src/route/rr_graph_fpga_interchange.h | 51 ++++++++++ 2 files changed, 108 insertions(+), 47 deletions(-) diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 115654c2286..62171cce3d4 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -55,6 +55,7 @@ struct intermediate_node { visited = nullptr; on_route = false; has_pins = false; + segment_id = -1; } }; @@ -83,14 +84,6 @@ struct RR_Graph_Builder { wire_name_to_seg_idx_[segment_inf_[RRSegmentId(i)].name] = i; } - for (size_t i = 1; i < grid.width() - 1; ++i) { - for (size_t j = 1; j < grid.height() - 1; ++j) { - location loc(i, j); - if (grid[i][j].type->index == 0) - empty_tiles.insert(loc); - } - } - auto primLib = ar_.getPrimLibs(); auto portList = primLib.getPortList(); @@ -158,15 +151,13 @@ struct RR_Graph_Builder { std::unordered_map wire_name_to_seg_idx_; - std::set empty_tiles; - std::set bel_cell_mappings_; std::unordered_map tile_to_loc_; - std::map loc_to_tile_; + std::unordered_map> loc_to_tile_; - std::map, int /*idx*/> pips_models_; - std::map, std::tuple>> pips_; + std::unordered_map, int /*idx*/, hash_tuple::hash>> pips_models_; + std::unordered_map, std::tuple>, hash_tuple::hash>> pips_; std::map, int> wire_to_node_; std::unordered_map> node_to_locs_; @@ -192,9 +183,9 @@ struct RR_Graph_Builder { * Sets contain tuples of node ids and location. * Each value correspondence to node id n being used by either pip or pin at location l. */ - std::set> used_by_pip_; - std::set> used_by_pin_; - std::set usefull_node_; + std::unordered_set, hash_tuple::hash>> used_by_pip_; + std::unordered_set, hash_tuple::hash>> used_by_pin_; + std::unordered_set usefull_node_; /* Sink_source_loc_map is used to create ink/source and ipin/opin rr_nodes, * rr_edges from sink/source to ipin/opin and from ipin/opin to their coresponding segments @@ -423,6 +414,10 @@ struct RR_Graph_Builder { const auto& wire = ar_.getWires()[wire_id_]; int tile_id = wire.getTile(); int wire_id = wire.getWire(); + if (tile_id == 6038 && wire_id == 5471) { + VTR_LOG("id:%d, wire_id_:%d\n", id, (int)wire_id_); + VTR_LOG("tile:%s wire:%s\n", str(tile_id).c_str(), str(wire_id).c_str()); + } wire_to_node_[std::make_tuple(tile_id, wire_id)] = id; node_to_locs_[id].insert(tile_to_loc_[tile_id]); if (wire_name_to_seg_idx_.find(str(wire_id)) == wire_name_to_seg_idx_.end()) @@ -494,7 +489,9 @@ struct RR_Graph_Builder { * Build graph of FPGA Interchange node for further computations */ std::set build_node_graph(int node_id, - std::set nodes) { + std::set nodes, + int& seg_id) { + std::set roots; std::map existing_nodes; do { @@ -502,7 +499,9 @@ struct RR_Graph_Builder { (*nodes.begin()).second); location key = *nodes.begin(); - root_node->segment_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[key])]; + if (node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[key])] != -1) + seg_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[key])]; + nodes.erase(key); existing_nodes.emplace(key, root_node); @@ -523,7 +522,8 @@ struct RR_Graph_Builder { temp = existing_nodes[other_loc]; } else if (nodes.find(other_loc) != nodes.end()) { temp = new intermediate_node(other_loc.first, other_loc.second); - temp->segment_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[other_loc])]; + if (node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[other_loc])] != -1) + seg_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[other_loc])]; nodes.erase(other_loc); existing_nodes.emplace(other_loc, temp); builder.push(temp); @@ -550,7 +550,7 @@ struct RR_Graph_Builder { * Removes dangling nodes from a graph represented by the root node. * Dangling nodes are nodes that do not connect to a pin, a pip or other non-dangling node. */ - int reduce_graph_and_count_nodes_left(std::set& roots) { + int reduce_graph_and_count_nodes_left(std::set& roots, int seg_id) { int cnt = 0; std::queue walker; std::stack back_walker; @@ -564,6 +564,7 @@ struct RR_Graph_Builder { intermediate_node* vertex = walker.front(); walker.pop(); back_walker.push(vertex); + vertex->segment_id = seg_id; for (auto i : NODESIDES) { if (vertex->links[i] != nullptr) { all_nulls = false; @@ -581,9 +582,6 @@ struct RR_Graph_Builder { intermediate_node* vertex = back_walker.top(); back_walker.pop(); if (!vertex->has_pins && !vertex->on_route) { - if (vertex->visited->segment_id == -1) { - vertex->visited->segment_id = vertex->segment_id; - } for (auto i : NODESIDES) { intermediate_node* temp = nullptr; if (vertex->links[i] != nullptr) { @@ -598,8 +596,6 @@ struct RR_Graph_Builder { delete vertex; } else if (vertex->on_route) { vertex->visited->on_route = true; - if (vertex->visited->segment_id == -1) - vertex->visited->segment_id = vertex->segment_id; } } return cnt; @@ -642,7 +638,10 @@ struct RR_Graph_Builder { std::tuple key1(vertex->loc, e_rr_type::CHANY); std::tuple key2(location(vertex->loc.first, vertex->loc.second - 1), e_rr_type::CHANX); - VTR_ASSERT(vertex->segment_id != -1); + if (vertex->segment_id == -1) { + VTR_LOG("node_id:%d X:%d Y:%d\n", node_id, vertex->loc.first, vertex->loc.second); + VTR_ASSERT(false); + } if (is_chany) { loc_chan_idx[key1] = chan_loc_map_[key1].size(); @@ -818,13 +817,14 @@ struct RR_Graph_Builder { for (auto const& node : ar_.getNodes()) { std::tuple base_wire_(wires[node.getWires()[0]].getTile(), wires[node.getWires()[0]].getWire()); int node_id = wire_to_node_[base_wire_]; + int seg_id; if (usefull_node_.find(node_id) == usefull_node_.end()) { continue; } std::set all_possible_tiles = node_to_locs_[node_id]; - std::set roots = build_node_graph(node_id, all_possible_tiles); - int div = reduce_graph_and_count_nodes_left(roots); + std::set roots = build_node_graph(node_id, all_possible_tiles, seg_id); + int div = reduce_graph_and_count_nodes_left(roots, seg_id); if (roots.empty()) { continue; } @@ -900,8 +900,11 @@ struct RR_Graph_Builder { int pin_id = sub_tile.sub_tile_to_tile_pin_indices[port.index]; std::tuple key{tile_type_id, it, std::string(port.name)}; std::tie(value, wire_id) = tile_type_site_num_pin_name_model[key]; - VTR_ASSERT(wire_to_node_.find(std::make_tuple(tile_id, wire_id)) != wire_to_node_.end()); - node_id = wire_to_node_[std::make_tuple(tile_id, wire_id)]; + if (wire_to_node_.find(std::make_tuple(tile_id, wire_id)) == wire_to_node_.end()) { + node_id = -1; + } else { + node_id = wire_to_node_[std::make_tuple(tile_id, wire_id)]; + } location loc = tile_to_loc_[tile_id]; @@ -965,18 +968,23 @@ struct RR_Graph_Builder { float R, C; std::tie(R, C) = input ? std::tuple(0, RC) : std::tuple(RC, 0); - location track_loc; - e_rr_type track_type; - int track_idx; - VTR_ASSERT(redirect_.find(std::make_tuple(FPGA_Interchange_node_id, loc)) != redirect_.end()); - std::tie(track_loc, track_type, track_idx) = redirect_[std::make_tuple(FPGA_Interchange_node_id, loc)]; - auto val = chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx]; - int track_seg; - float track_R, track_C; - std::tie(track_seg, track_R, track_C) = val; - track_R += R; - track_C += C; - chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx] = std::make_tuple(track_seg, track_R, track_C); + if(FPGA_Interchange_node_id != -1) { + location track_loc; + e_rr_type track_type; + int track_idx; + if (redirect_.find(std::make_tuple(FPGA_Interchange_node_id, loc)) == redirect_.end()) { + VTR_LOG("node_id:%d, loc->X:%d Y:%d\n", FPGA_Interchange_node_id, loc.first, loc.second); + VTR_ASSERT(false); + } + std::tie(track_loc, track_type, track_idx) = redirect_[std::make_tuple(FPGA_Interchange_node_id, loc)]; + auto val = chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx]; + int track_seg; + float track_R, track_C; + std::tie(track_seg, track_R, track_C) = val; + track_R += R; + track_C += C; + chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx] = std::make_tuple(track_seg, track_R, track_C); + } device_ctx_.rr_nodes.make_room_for_node(RRNodeId(rr_idx)); loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, j)] = rr_idx; @@ -996,7 +1004,10 @@ struct RR_Graph_Builder { } for (size_t j = 0; j < pin_vec.size(); ++j) { bool input; - std::tie(input, std::ignore, std::ignore) = pin_vec[j]; + int FPGA_Interchange_node_id; + std::tie(input, std::ignore, FPGA_Interchange_node_id) = pin_vec[j]; + if (FPGA_Interchange_node_id == -1) + continue; e_rr_type pin = input ? e_rr_type::IPIN : e_rr_type::OPIN; device_ctx_.rr_nodes.make_room_for_node(RRNodeId(rr_idx)); @@ -1076,6 +1087,8 @@ struct RR_Graph_Builder { bool input; int node_id; std::tie(input, std::ignore, node_id) = pin_vec[j]; + if (node_id == -1) + continue; VTR_ASSERT(redirect_.find(std::make_tuple(node_id, loc)) != redirect_.end()); auto chan_key = redirect_[std::make_tuple(node_id, loc)]; e_rr_type pin = input ? e_rr_type::SINK : e_rr_type::SOURCE; @@ -1162,10 +1175,7 @@ struct RR_Graph_Builder { temp = int_to_string(temp, forward ? 1 : 0); *temp++ = 0; - vtr::interned_string name_(device_ctx_.arch->strings.intern_string(vtr::string_view("FPGAInterchange"))); - vtr::interned_string value_(device_ctx_.arch->strings.intern_string(vtr::string_view(metadata_))); - - vpr::add_rr_edge_metadata(src, sink, sw_id, name_, value_); + vpr::add_rr_edge_metadata(src, sink, sw_id, vtr::string_view("FPGAInterchange"), vtr::string_view(metadata_)); } } diff --git a/vpr/src/route/rr_graph_fpga_interchange.h b/vpr/src/route/rr_graph_fpga_interchange.h index ab38b5b881c..628526dbd22 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.h +++ b/vpr/src/route/rr_graph_fpga_interchange.h @@ -9,6 +9,7 @@ #include "LogicalNetlist.capnp.h" #include "capnp/serialize.h" #include "capnp/serialize-packed.h" +#include void build_rr_graph_fpga_interchange(const t_graph_type graph_type, const DeviceGrid& grid, @@ -17,4 +18,54 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, int* wire_to_rr_ipin_switch, bool do_check_rr_graph); +namespace hash_tuple{ + namespace { + template + struct hash { + size_t operator()(TT const& tt) const { + return std::hash()(tt); + } + }; + + template + struct hash > { + size_t operator()(const std::pair &p) const noexcept(true) { + size_t lhs, rhs; + lhs = std::hash()(p.first); + rhs = std::hash()(p.second); + lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); + return lhs; + } + }; + template + inline void hash_combine(std::size_t& seed, T const& v) { + seed ^= hash_tuple::hash()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + // Recursive template code derived from Matthieu M. + template ::value - 1> + struct HashValueImpl { + static void apply(size_t& seed, Tuple const& tuple) { + HashValueImpl::apply(seed, tuple); + hash_combine(seed, std::get(tuple)); + } + }; + + template + struct HashValueImpl { + static void apply(size_t& seed, Tuple const& tuple) { + hash_combine(seed, std::get<0>(tuple)); + } + }; + } + + template + struct hash> { + size_t operator()(std::tuple const& tt) const { + size_t seed = 0; + HashValueImpl >::apply(seed, tt); + return seed; + } + }; +} + #endif /* RR_GRAPH_FPGA_INTERCHANGE_H */ From d8530bc73531cd35c92b7a341e08cf7628a2719f Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Fri, 31 Dec 2021 11:09:00 +0100 Subject: [PATCH 7/7] Reducing rr_node and rr_edge count in fpga_interchange node translation step Combine node forest to single tree Signed-off-by: Maciej Dudek --- .../src/read_fpga_interchange_arch.cpp | 52 +- vpr/src/route/rr_graph_fpga_interchange.cpp | 969 +++++++++++------- vpr/src/route/rr_graph_fpga_interchange.h | 90 +- 3 files changed, 696 insertions(+), 415 deletions(-) diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 8728f52c79d..475c3af82cf 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -253,7 +253,6 @@ struct ArchReader { // Bel Cell mappings std::unordered_map> bel_cell_mappings_; - std::unordered_map segment_name_to_segment_idx; // Utils @@ -2151,6 +2150,29 @@ struct ArchReader { } } + void add_segment_with_default_values(t_segment_inf& seg, std::string name) { + // Use default values as we will populate rr_graph with correct values + // This segments are just declaration of future use + seg.name = name; + seg.length = 1; + seg.frequency = 1; + seg.Rmetal = 1e-12; + seg.Cmetal = 1e-12; + seg.parallel_axis = BOTH_AXIS; + + // TODO: Only bi-directional segments are created, but it the interchange format + // has directionality information on PIPs, which may be used to infer the + // segments' directonality. + seg.directionality = BI_DIRECTIONAL; + seg.arch_wire_switch = 1; + seg.arch_opin_switch = 1; + seg.cb.resize(1); + seg.cb[0] = true; + seg.sb.resize(2); + seg.sb[0] = true; + seg.sb[1] = true; + } + void process_segments() { // Segment names will be taken from wires connected to pips // They are good representation for nodes @@ -2163,32 +2185,14 @@ struct ArchReader { } } - int num_seg = wire_names.size(); + int num_seg = wire_names.size() + 1; arch_->Segments.resize(num_seg); - size_t index = 0; + + size_t index = 1; + add_segment_with_default_values(arch_->Segments[0], std::string("__generic__")); for (auto i : wire_names) { - // Use default values as we will populate rr_graph with correct values - // This segments are just declaration of future use - arch_->Segments[index].name = str(i); - arch_->Segments[index].length = 1; - arch_->Segments[index].frequency = 1; - arch_->Segments[index].Rmetal = 1e-12; - arch_->Segments[index].Cmetal = 1e-12; - arch_->Segments[index].parallel_axis = BOTH_AXIS; - - // TODO: Only bi-directional segments are created, but it the interchange format - // has directionality information on PIPs, which may be used to infer the - // segments' directonality. - arch_->Segments[index].directionality = BI_DIRECTIONAL; - arch_->Segments[index].arch_wire_switch = 1; - arch_->Segments[index].arch_opin_switch = 1; - arch_->Segments[index].cb.resize(1); - arch_->Segments[index].cb[0] = true; - arch_->Segments[index].sb.resize(2); - arch_->Segments[index].sb[0] = true; - arch_->Segments[index].sb[1] = true; - segment_name_to_segment_idx[str(i)] = index; + add_segment_with_default_values(arch_->Segments[index], str(i)); ++index; } } diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 62171cce3d4..e9462a702d4 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -33,6 +33,7 @@ enum node_sides { }; auto NODESIDES = {LEFT_EDGE, TOP_EDGE, RIGHT_EDGE, BOTTOM_EDGE}; +enum node_sides OPPOSITE_SIDE[] = {RIGHT_EDGE, BOTTOM_EDGE, LEFT_EDGE, TOP_EDGE}; /* * Intermediate data type. @@ -43,18 +44,32 @@ struct intermediate_node { public: location loc; int segment_id; - intermediate_node* links[4]; //left, up, right, down + std::vector links; //left, up, right, down intermediate_node* visited; + bool back_bone; bool on_route; bool has_pins; intermediate_node() = delete; intermediate_node(int x, int y) : loc{x, y} { + links.resize(4); for (auto i : NODESIDES) - links[i] = nullptr; + links[i] = false; visited = nullptr; on_route = false; has_pins = false; + back_bone = false; + segment_id = -1; + } + intermediate_node(location loc_) + : loc(loc_) { + links.resize(4); + for (auto i : NODESIDES) + links[i] = false; + visited = nullptr; + on_route = false; + has_pins = false; + back_bone = false; segment_id = -1; } }; @@ -131,6 +146,10 @@ struct RR_Graph_Builder { create_sink_source_nodes(); create_pips_(); process_nodes(); +#ifdef DEBUG + print_virtual(); +#endif + virtual_to_real_(); pack_to_rr_graph(); #ifdef DEBUG print(); @@ -170,14 +189,32 @@ struct RR_Graph_Builder { /* * Intermediate data storage. - * - Shorts represent connections between rr_nodes in a single FPGA Interchange node. - * - Redirect is map from node_id at location to location channel and index of track in that channel. + * - virtual_shorts_ represent connections between rr_nodes in a single FPGA Interchange node. + * - virtual_redirect_ is map from node_id at location to location channel and index of virtual_track in that channel. * It's useful for ends of the nodes (FPGA Interchange) that don't have representation in CHANS. - * - CHAN_loc_map maps from location and CHAN to vector containing tracks description. + * - virtual_chan_loc_map maps from location and CHAN to vector containing virtual_tracks description. + * - virtual_beg_to_real_ maps from virtual track to physical one + * - chan_loc_map maps from location and CHAN to vector containing tracks description. + * - node_id_count_ maps from node_id to its tile count */ - std::vector> shorts_; - std::map, std::tuple> redirect_; - std::map, std::vector> /*idx = ptc,*/> chan_loc_map_; + std::vector> virtual_shorts_; + std::map, std::tuple> virtual_redirect_; + std::unordered_map, + int, + hash_tuple::hash>> + virtual_beg_to_real_; + + std::unordered_map, + std::vector>, + hash_tuple::hash>> + virtual_chan_loc_map_; + + std::unordered_map, + std::unordered_map>, + hash_tuple::hash>> + chan_loc_map_; + + std::unordered_map node_id_count_; /* * Sets contain tuples of node ids and location. @@ -200,6 +237,14 @@ struct RR_Graph_Builder { std::map, int> loc_type_idx_to_rr_idx_; int rr_idx = 0; // Do not decrement! + location add_vec(location x, location dx) { + return location(x.first + dx.first, x.second + dx.second); + } + + location mul_vec_scal(location x, int s) { + return location(x.first * s, x.second * s); + } + std::string str(int idx) { return std::string(ar_.getStrList()[idx].cStr()); } @@ -207,7 +252,8 @@ struct RR_Graph_Builder { /* * Debug print function */ - void print() { + void print_virtual() { + VTR_LOG("Virtual\n"); for (const auto& entry : sink_source_loc_map_) { const auto& key = entry.first; const auto& value = entry.second; @@ -232,18 +278,19 @@ struct RR_Graph_Builder { VTR_LOG("Switch: %d Name:%s\n", it++, switch_id.name); } - for (auto& entry : chan_loc_map_) { - location loc; + for (auto& entry : virtual_chan_loc_map_) { + location loc, loc2; e_rr_type type; std::tie(loc, type) = entry.first; VTR_LOG("CHAN%c X:%d Y:%d\n", type == e_rr_type::CHANX ? 'X' : 'Y', loc.first, loc.second); for (auto& seg : entry.second) { - VTR_LOG("\tSegment id:%d name:%s\n", std::get<0>(seg), segment_inf_[RRSegmentId(std::get<0>(seg))].name.c_str()); + loc2 = std::get<3>(seg); + VTR_LOG("\tSegment id:%d name:%s -> X:%d Y:%d\n", std::get<0>(seg), segment_inf_[RRSegmentId(std::get<0>(seg))].name.c_str(), loc2.first, loc2.second); } } VTR_LOG("Redirects:\n"); - for (auto& entry : redirect_) { + for (auto& entry : virtual_redirect_) { int node; location loc; std::tie(node, loc) = entry.first; @@ -257,41 +304,32 @@ struct RR_Graph_Builder { } VTR_LOG("Shorts:\n"); - for (auto& entry : shorts_) { + for (auto& entry : virtual_shorts_) { location loc1, loc2; e_rr_type type1, type2; int id1, id2; std::tie(loc1, type1, id1, loc2, type2, id2) = entry; VTR_LOG("\tCHAN%c X:%d Y%d", type1 == e_rr_type::CHANX ? 'X' : 'Y', loc1.first, loc1.second); VTR_LOG(" Segment id:%d name:%s ->", id1, - segment_inf_[RRSegmentId(std::get<0>(chan_loc_map_[std::make_tuple(loc1, type1)][id1]))].name.c_str()); + segment_inf_[RRSegmentId(std::get<0>(virtual_chan_loc_map_[std::make_tuple(loc1, type1)][id1]))].name.c_str()); VTR_LOG(" CHAN%c X:%d Y:%d", type2 == e_rr_type::CHANX ? 'X' : 'Y', loc2.first, loc2.second); VTR_LOG(" Segment id:%d name:%s\n", id2, - segment_inf_[RRSegmentId(std::get<0>(chan_loc_map_[std::make_tuple(loc2, type2)][id2]))].name.c_str()); + segment_inf_[RRSegmentId(std::get<0>(virtual_chan_loc_map_[std::make_tuple(loc2, type2)][id2]))].name.c_str()); } + } - for (auto& pip : pips_) { - auto& key = pip.first; - auto& value = pip.second; - std::tuple meta; - int switch_id, tile_id, name, wire0, wire1; - bool forward; - std::tie(switch_id, tile_id, meta) = value; - std::tie(name, wire0, wire1, forward) = meta; - auto& r1 = redirect_[std::make_tuple(std::get<0>(key), tile_to_loc_[tile_id])]; - auto& r2 = redirect_[std::make_tuple(std::get<1>(key), tile_to_loc_[tile_id])]; - VTR_LOG("Switch_type: %d, %s.%s %s.%s: forward:%s\n", - switch_id, str(tile_id).c_str(), str(wire0).c_str(), str(name).c_str(), str(wire1).c_str(), forward ? "yes" : "no"); - VTR_LOG("Edge: CHAN%c X:%d Y:%d idx:%d -> CHAN%c X:%d Y:%d idx%d\n", - std::get<1>(r1) == e_rr_type::CHANX ? 'X' : 'Y', - std::get<0>(r1).first, - std::get<0>(r1).second, - std::get<2>(r1), - std::get<1>(r2) == e_rr_type::CHANX ? 'X' : 'Y', - std::get<0>(r2).first, - std::get<0>(r2).second, - std::get<2>(r2)); + void print() { + VTR_LOG("Real\n"); + + VTR_LOG("Virtual to real mapping:\n"); + for (auto i : virtual_beg_to_real_) { + e_rr_type type; + location loc; + int virt_idx; + std::tie(loc, type, virt_idx) = i.first; + VTR_LOG("CHAN%c X:%d Y:%d virt_idx:%d -> idx:%d\n", type == CHANX ? 'X' : 'Y', loc.first, loc.second, virt_idx, i.second); } + VTR_LOG("RR_Nodes\n"); for (auto i : loc_type_idx_to_rr_idx_) { location loc; @@ -302,6 +340,27 @@ struct RR_Graph_Builder { } } + void fill_switch(t_rr_switch_inf& switch_, + float R, + float Cin, + float Cout, + float Cinternal, + float Tdel, + float mux_trans_size, + float buf_size, + char* name, + SwitchType type) { + switch_.R = R; + switch_.Cin = Cin; + switch_.Cout = Cout; + switch_.Cinternal = Cinternal; + switch_.Tdel = Tdel; + switch_.mux_trans_size = mux_trans_size; + switch_.buf_size = buf_size; + switch_.name = name; + switch_.set_type(type); + } + /* * Fill device_ctx rr_switch_inf structure and store id of each PIP type for future use */ @@ -320,25 +379,11 @@ struct RR_Graph_Builder { device_ctx_.rr_switch_inf.reserve(seen.size() + 1); int id = 2; make_room_in_vector(&device_ctx_.rr_switch_inf, 0); - device_ctx_.rr_switch_inf[0].R = 0; - device_ctx_.rr_switch_inf[0].Cin = 0; - device_ctx_.rr_switch_inf[0].Cout = 0; - device_ctx_.rr_switch_inf[0].Cinternal = 0; - device_ctx_.rr_switch_inf[0].Tdel = 0; - device_ctx_.rr_switch_inf[0].buf_size = 0; - device_ctx_.rr_switch_inf[0].mux_trans_size = 0; - device_ctx_.rr_switch_inf[0].name = "short"; - device_ctx_.rr_switch_inf[0].set_type(SwitchType::SHORT); + fill_switch(device_ctx_.rr_switch_inf[0], 0, 0, 0, 0, 0, 0, 0, + vtr::strdup("short"), SwitchType::SHORT); make_room_in_vector(&device_ctx_.rr_switch_inf, 1); - device_ctx_.rr_switch_inf[1].R = 0; - device_ctx_.rr_switch_inf[1].Cin = 0; - device_ctx_.rr_switch_inf[1].Cout = 0; - device_ctx_.rr_switch_inf[1].Cinternal = 0; - device_ctx_.rr_switch_inf[1].Tdel = 0; - device_ctx_.rr_switch_inf[1].buf_size = 0; - device_ctx_.rr_switch_inf[1].mux_trans_size = 0; - device_ctx_.rr_switch_inf[1].name = "generic"; - device_ctx_.rr_switch_inf[1].set_type(SwitchType::MUX); + fill_switch(device_ctx_.rr_switch_inf[1], 0, 0, 0, 0, 0, 0, 0, + vtr::strdup("generic"), SwitchType::MUX); const auto& pip_models = ar_.getPipTimings(); float R, Cin, Cout, Cint, Tdel; std::string switch_name; @@ -346,6 +391,7 @@ struct RR_Graph_Builder { for (const auto& value : seen) { make_room_in_vector(&device_ctx_.rr_switch_inf, id); int timing_model_id; + int mux_trans_size; bool buffered; std::tie(timing_model_id, buffered) = value; const auto& model = pip_models[timing_model_id]; @@ -375,18 +421,10 @@ struct RR_Graph_Builder { switch_name = name.str(); type = buffered ? SwitchType::MUX : SwitchType::PASS_GATE; + mux_trans_size = buffered ? 1 : 0; - auto& as = device_ctx_.rr_switch_inf[id]; - as.name = vtr::strdup(switch_name.c_str()); - as.set_type(type); - as.mux_trans_size = type == SwitchType::MUX ? 1 : 0; - - as.R = R; - as.Cin = Cin; - as.Cout = Cout; - as.Cinternal = Cint; - as.Tdel = Tdel; - as.buf_size = 0.; + fill_switch(device_ctx_.rr_switch_inf[id], R, Cin, Cout, Cint, Tdel, + mux_trans_size, 0, vtr::strdup(switch_name.c_str()), type); id++; } @@ -414,10 +452,6 @@ struct RR_Graph_Builder { const auto& wire = ar_.getWires()[wire_id_]; int tile_id = wire.getTile(); int wire_id = wire.getWire(); - if (tile_id == 6038 && wire_id == 5471) { - VTR_LOG("id:%d, wire_id_:%d\n", id, (int)wire_id_); - VTR_LOG("tile:%s wire:%s\n", str(tile_id).c_str(), str(wire_id).c_str()); - } wire_to_node_[std::make_tuple(tile_id, wire_id)] = id; node_to_locs_[id].insert(tile_to_loc_[tile_id]); if (wire_name_to_seg_idx_.find(str(wire_id)) == wire_name_to_seg_idx_.end()) @@ -485,19 +519,37 @@ struct RR_Graph_Builder { } } + bool pip_uses_node_loc(int node_id, location loc) { + return used_by_pip_.find(std::make_tuple(node_id, loc)) != used_by_pip_.end(); + } + + bool pin_uses_node_loc(int node_id, location loc) { + return used_by_pin_.find(std::make_tuple(node_id, loc)) != used_by_pin_.end(); + } + /* * Build graph of FPGA Interchange node for further computations */ std::set build_node_graph(int node_id, std::set nodes, + std::map& existing_nodes, int& seg_id) { - std::set roots; - std::map existing_nodes; do { - intermediate_node* root_node = new intermediate_node((*nodes.begin()).first, - (*nodes.begin()).second); - location key = *nodes.begin(); + intermediate_node* root_node = nullptr; + location key; + for (const auto& it : nodes) { + if (pip_uses_node_loc(node_id, it) || pin_uses_node_loc(node_id, it)) { + root_node = new intermediate_node(it); + key = it; + break; + } + } + + if (root_node == nullptr) { + nodes.clear(); + break; + } if (node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[key])] != -1) seg_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[key])]; @@ -515,30 +567,30 @@ struct RR_Graph_Builder { builder.pop(); location loc = vertex->loc; for (auto i : NODESIDES) { - location offset = offsets[i]; - location other_loc(loc.first + offset.first, loc.second + offset.second); - intermediate_node* temp = nullptr; + location other_loc = add_vec(loc, offsets[i]); + bool temp = false; if (existing_nodes.find(other_loc) != existing_nodes.end()) { - temp = existing_nodes[other_loc]; + temp = true; } else if (nodes.find(other_loc) != nodes.end()) { - temp = new intermediate_node(other_loc.first, other_loc.second); + intermediate_node* new_node = new intermediate_node(other_loc); + temp = true; if (node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[other_loc])] != -1) seg_id = node_tile_to_segment_[std::make_tuple(node_id, loc_to_tile_[other_loc])]; nodes.erase(other_loc); - existing_nodes.emplace(other_loc, temp); - builder.push(temp); + existing_nodes.emplace(other_loc, new_node); + builder.push(new_node); } vertex->links[i] = temp; } } - roots.emplace(root_node); + roots.insert(root_node); } while (!nodes.empty()); for (auto& node : existing_nodes) { - if (used_by_pip_.find(std::make_tuple(node_id, node.second->loc)) != used_by_pip_.end()) { + if (pip_uses_node_loc(node_id, node.second->loc)) { node.second->on_route = true; } - if (used_by_pin_.find(std::make_tuple(node_id, node.second->loc)) != used_by_pin_.end()) { + if (pin_uses_node_loc(node_id, node.second->loc)) { node.second->has_pins = true; node.second->on_route = true; } @@ -546,267 +598,439 @@ struct RR_Graph_Builder { return roots; } + intermediate_node* update_end(intermediate_node* end, intermediate_node* node, enum node_sides side) { + intermediate_node* res = end; + int x, y; + x = end->loc.first > node->loc.first ? -1 : end->loc.first < node->loc.first ? 1 : 0; + y = end->loc.second > node->loc.second ? -1 : end->loc.second < node->loc.second ? 1 : 0; + switch (side) { + case LEFT_EDGE: + if (x < 0 || (x == 0 && y < 0)) + res = node; + break; + case TOP_EDGE: + if (y > 0 || (y == 0 && x > 0)) + res = node; + break; + case RIGHT_EDGE: + if (x > 0 || (x == 0 && y > 0)) + res = node; + break; + case BOTTOM_EDGE: + if (y < 0 || (y == 0 && x < 0)) + res = node; + break; + default: + VTR_ASSERT(false); + break; + } + return res; + } + + void add_line(std::map& existing_nodes, + location start, + location end, + std::initializer_list connections) { + int range_start, range_end; + bool horizontal; + if (start.second == end.second) { + range_start = start.first + 1; + range_end = end.first; + horizontal = true; + } else { + range_start = start.second + 1; + range_end = end.second; + horizontal = false; + } + for (int i = range_start; i < range_end; i++) { + location temp; + if (horizontal) + temp = location(i, end.second); + else + temp = location(end.first, i); + if (existing_nodes.find(temp) == existing_nodes.end()) + existing_nodes[temp] = new intermediate_node(temp); + intermediate_node* temp_ = existing_nodes[temp]; + temp_->back_bone = true; + for (auto j : connections) + temp_->links[j] = true; + } + } + + void add_comb_node(std::map& existing_nodes, + intermediate_node* ends[], + location node, + bool up, + enum node_sides node_side, + enum node_sides end_side) { + intermediate_node* temp; + if (existing_nodes.find(node) == existing_nodes.end()) + existing_nodes[node] = new intermediate_node(node); + temp = existing_nodes[node]; + temp->back_bone = true; + if (temp != ends[node_side]) { + temp->links[node_side] = true; + ends[node_side]->links[end_side] = true; + if (temp->loc.first < ends[node_side]->loc.first) + add_line(existing_nodes, temp->loc, ends[node_side]->loc, {RIGHT_EDGE, LEFT_EDGE}); + else + add_line(existing_nodes, ends[node_side]->loc, temp->loc, {RIGHT_EDGE, LEFT_EDGE}); + } + if (up && temp->loc.second != ends[TOP_EDGE]->loc.second) { + temp->links[TOP_EDGE] = true; + ends[TOP_EDGE]->links[BOTTOM_EDGE] = true; + add_line(existing_nodes, temp->loc, ends[TOP_EDGE]->loc, {TOP_EDGE, BOTTOM_EDGE}); + } else if (!up && temp->loc.second != ends[BOTTOM_EDGE]->loc.second) { + temp->links[BOTTOM_EDGE] = true; + ends[BOTTOM_EDGE]->links[TOP_EDGE] = true; + add_line(existing_nodes, ends[BOTTOM_EDGE]->loc, temp->loc, {TOP_EDGE, BOTTOM_EDGE}); + } + } + + intermediate_node* create_final_root_node(std::map& existing_nodes, + bool left_node, + location root_node_, + location up_node_, + location comb_node_, + enum node_sides side) { + intermediate_node* temp; + if (existing_nodes.find(root_node_) == existing_nodes.end()) + existing_nodes[root_node_] = new intermediate_node(root_node_); + temp = existing_nodes[root_node_]; + temp->back_bone = true; + temp->links[side] = true; + temp->links[BOTTOM_EDGE] = true; + add_line(existing_nodes, up_node_, root_node_, {TOP_EDGE, BOTTOM_EDGE}); + existing_nodes[up_node_]->links[TOP_EDGE] = true; + existing_nodes[comb_node_]->links[OPPOSITE_SIDE[side]] = true; + if (left_node) { + add_line(existing_nodes, root_node_, comb_node_, {RIGHT_EDGE, LEFT_EDGE}); + } else { + add_line(existing_nodes, comb_node_, root_node_, {RIGHT_EDGE, LEFT_EDGE}); + } + return temp; + } + + void connect_dangling_roots(std::set& roots, + std::map& existing_nodes, + intermediate_node* ends[]) { + /* connect not yet connected roots */ + std::vector del_list; + + int x_, y_; + x_ = ends[RIGHT_EDGE]->loc.first - ends[LEFT_EDGE]->loc.first; + y_ = ends[TOP_EDGE]->loc.second - ends[BOTTOM_EDGE]->loc.second; + + int max_length = std::max(x_, y_); + + for (const auto& i : roots) { + bool done = i->back_bone; + for (int j = 1; j < max_length; j++) { + if (done) { + del_list.push_back(i); + break; + } + for (const auto& k : NODESIDES) { + location offset = mul_vec_scal(offsets[k], j); + location other_node_loc = add_vec(i->loc, offset); + if (existing_nodes.find(other_node_loc) == existing_nodes.end()) + continue; + intermediate_node* other_node; + other_node = existing_nodes[other_node_loc]; + if (!other_node->back_bone) + continue; + i->links[k] = true; + i->back_bone = true; + other_node->links[OPPOSITE_SIDE[k]] = true; + if (0 < k && k < 3) + add_line(existing_nodes, i->loc, other_node_loc, {k, OPPOSITE_SIDE[k]}); + else + add_line(existing_nodes, other_node_loc, i->loc, {k, OPPOSITE_SIDE[k]}); + done = true; + break; + } + } + } + + /* remove already done roots */ + for (const auto& i : del_list) { + if (roots.find(i) != roots.end()) + roots.erase(i); + } + del_list.clear(); + } + + intermediate_node* connect_roots(std::set& roots, + std::map& existing_nodes) { + if (roots.size() == 1) { + intermediate_node* root = *roots.begin(); + root->back_bone = true; + roots.clear(); + return root; + } + intermediate_node* ends[4]; + ends[0] = ends[1] = ends[2] = ends[3] = *roots.begin(); + + for (auto root : roots) { + for (auto const& i : NODESIDES) + ends[i] = update_end(ends[i], root, i); + } + + for (auto const& i : NODESIDES) { + ends[i]->back_bone = true; + if (roots.find(ends[i]) != roots.end()) + roots.erase(ends[i]); + } + + bool right_to_top = ends[TOP_EDGE]->loc.first >= ends[BOTTOM_EDGE]->loc.first; + bool left_to_top = ends[TOP_EDGE]->loc.first < ends[BOTTOM_EDGE]->loc.first; + + location right_comb_node(right_to_top ? ends[TOP_EDGE]->loc.first : ends[BOTTOM_EDGE]->loc.first, ends[RIGHT_EDGE]->loc.second); + location left_comb_node(left_to_top ? ends[TOP_EDGE]->loc.first : ends[BOTTOM_EDGE]->loc.first, ends[LEFT_EDGE]->loc.second); + + add_comb_node(existing_nodes, ends, right_comb_node, right_to_top, RIGHT_EDGE, LEFT_EDGE); + add_comb_node(existing_nodes, ends, left_comb_node, left_to_top, LEFT_EDGE, RIGHT_EDGE); + + int y = std::max(right_comb_node.second, left_comb_node.second); + location loc1(left_comb_node.first, y); + location loc2(right_comb_node.first, y); + + intermediate_node* true_root = nullptr; + + if (loc1 != left_comb_node && loc1 != loc2) { + true_root = create_final_root_node(existing_nodes, true, loc1, left_comb_node, right_comb_node, RIGHT_EDGE); + } else if (loc2 != right_comb_node && loc1 != loc2) { + true_root = create_final_root_node(existing_nodes, false, loc2, right_comb_node, left_comb_node, LEFT_EDGE); + } else if (loc1 != loc2) { + add_line(existing_nodes, loc1, loc2, {RIGHT_EDGE, LEFT_EDGE}); + existing_nodes[loc1]->links[RIGHT_EDGE] = true; + existing_nodes[loc2]->links[LEFT_EDGE] = true; + true_root = existing_nodes[loc1]; + } else { + loc1 = right_comb_node; + loc2 = left_comb_node; + if (right_comb_node.second > left_comb_node.second) { + std::swap(loc1, loc2); + } + add_line(existing_nodes, loc1, loc2, {TOP_EDGE, BOTTOM_EDGE}); + existing_nodes[loc1]->links[TOP_EDGE] = true; + existing_nodes[loc2]->links[BOTTOM_EDGE] = true; + true_root = existing_nodes[loc1]; + } + + connect_dangling_roots(roots, existing_nodes, ends); + + VTR_ASSERT(true_root != nullptr); + return true_root; + } + /* * Removes dangling nodes from a graph represented by the root node. * Dangling nodes are nodes that do not connect to a pin, a pip or other non-dangling node. */ - int reduce_graph_and_count_nodes_left(std::set& roots, int seg_id) { + int reduce_graph_and_count_nodes_left(intermediate_node* root, + std::map& existing_nodes, + int seg_id) { int cnt = 0; std::queue walker; std::stack back_walker; - for (intermediate_node* root : roots) { - walker.push(root); - root->visited = root; - } - bool all_nulls; + walker.push(root); + root->visited = root; + bool has_chanx; + bool single_node; while (!walker.empty()) { - all_nulls = true; intermediate_node* vertex = walker.front(); walker.pop(); back_walker.push(vertex); vertex->segment_id = seg_id; + single_node = true; for (auto i : NODESIDES) { - if (vertex->links[i] != nullptr) { - all_nulls = false; - if (i == node_sides::LEFT_EDGE || i == node_sides::TOP_EDGE) - cnt++; - if (vertex->links[i]->visited == nullptr) { - vertex->links[i]->visited = vertex; - walker.push(vertex->links[i]); + if (vertex->links[i]) { + single_node = false; + has_chanx = false; + intermediate_node* other_node = existing_nodes[add_vec(vertex->loc, offsets[i])]; + if (other_node->visited == nullptr || vertex->visited == other_node) { + if (i == node_sides::LEFT_EDGE) { + has_chanx = true; + cnt++; + } else if (i == node_sides::TOP_EDGE) { + cnt++; + } + if (other_node->visited == nullptr) { + other_node->visited = vertex; + walker.push(other_node); + } + } else { + vertex->links[i] = false; } } } - cnt += all_nulls; + if ((vertex->has_pins && !has_chanx) || single_node) + cnt++; } while (!back_walker.empty()) { intermediate_node* vertex = back_walker.top(); back_walker.pop(); + single_node = true; + has_chanx = false; if (!vertex->has_pins && !vertex->on_route) { for (auto i : NODESIDES) { - intermediate_node* temp = nullptr; - if (vertex->links[i] != nullptr) { - if (i == node_sides::RIGHT_EDGE || i == node_sides::BOTTOM_EDGE) + if (vertex->links[i]) { + if (i == node_sides::LEFT_EDGE || i == node_sides::TOP_EDGE) cnt--; - temp = vertex->links[i]; - temp->links[(i + 2) % 4] = nullptr; } } - if (roots.find(vertex) != roots.end()) - roots.erase(vertex); + existing_nodes.erase(vertex->loc); delete vertex; - } else if (vertex->on_route) { + } else { vertex->visited->on_route = true; + for (auto i : NODESIDES) { + if (vertex->links[i]) { + if (existing_nodes.find(add_vec(vertex->loc, offsets[i])) == existing_nodes.end()) { + vertex->links[i] = false; + if ((i == node_sides::LEFT_EDGE && !vertex->has_pins) || i == node_sides::TOP_EDGE) + cnt--; + } + } + } + for (auto i : NODESIDES) + single_node &= !vertex->links[i]; + if (!vertex->has_pins && single_node) + cnt++; } } return cnt; } - void populate_chan_loc_map(int node_id, std::set roots, float R, float C) { - /* - * Create future rr_nodes that corespond to given FPGA Interchange node - */ - std::map, int /*index*/> loc_chan_idx; - - std::queue walker; - std::stack back_walker; - - for (intermediate_node* root : roots) { - walker.push(root); + void process_set(std::unordered_set& set, + std::map& existing_nodes, + std::map>& local_redirect, + float R, + float C, + location offset, + node_sides side, + e_rr_type chan_type) { + int len; + int idx; + intermediate_node *start, *end; + for (auto const i : set) { + len = 0; + start = i; + end = i; + auto key = std::make_tuple(add_vec(start->loc, offset), chan_type); + idx = virtual_chan_loc_map_[key].size(); + do { + len++; + local_redirect.emplace(end->loc, std::make_tuple(add_vec(start->loc, offset), chan_type, idx)); + if (!end->links[side]) + break; + intermediate_node* neighbour = existing_nodes[add_vec(end->loc, offsets[side])]; + if (set.find(neighbour) != set.end()) + break; + end = neighbour; + } while (true); + virtual_chan_loc_map_[key].emplace_back(start->segment_id, R * len, C * len, add_vec(end->loc, offset)); } + } - bool all_nulls, is_chanx, is_chany; - while (!walker.empty()) { - intermediate_node* vertex = walker.front(); - walker.pop(); - back_walker.push(vertex); - is_chany = false; - is_chanx = vertex->has_pins; - all_nulls = true; - for (auto i : NODESIDES) { - if (vertex->links[i] != nullptr) { - all_nulls = false; - if (vertex->links[i]->visited == vertex || vertex->links[i] == vertex->visited) { - is_chanx = i == node_sides::LEFT_EDGE ? true : is_chanx; - is_chany = i == node_sides::TOP_EDGE ? true : is_chany; - } - if (vertex->links[i]->visited == vertex) { - walker.push(vertex->links[i]); - } - } - } - is_chanx = all_nulls ? all_nulls : is_chanx; - std::tuple key1(vertex->loc, e_rr_type::CHANY); - std::tuple key2(location(vertex->loc.first, vertex->loc.second - 1), e_rr_type::CHANX); + void add_short(location n1, + location n2, + std::map>& r1, + std::map>& r2) { + auto red1 = r1[n1]; + auto red2 = r2[n2]; + virtual_shorts_.emplace_back(std::get<0>(red1), std::get<1>(red1), std::get<2>(red1), + std::get<0>(red2), std::get<1>(red2), std::get<2>(red2)); + } - if (vertex->segment_id == -1) { - VTR_LOG("node_id:%d X:%d Y:%d\n", node_id, vertex->loc.first, vertex->loc.second); - VTR_ASSERT(false); - } + void connect_base_on_redirects(std::unordered_set& set, + node_sides side, + std::map>& src, + std::map>& dist) { + for (auto const node : set) { + if (!node->links[side]) + continue; + location other_node = add_vec(node->loc, offsets[side]); + if (dist.find(other_node) == dist.end()) + continue; + add_short(node->loc, other_node, src, dist); + } + } - if (is_chany) { - loc_chan_idx[key1] = chan_loc_map_[key1].size(); - chan_loc_map_[key1].emplace_back(vertex->segment_id, R, C); - } - if (is_chanx) { - loc_chan_idx[key2] = chan_loc_map_[key2].size(); - chan_loc_map_[key2].emplace_back(vertex->segment_id, R, C); + void graph_reduction_stage2(int node_id, + std::map& existing_nodes, + float R, + float C) { + std::unordered_set chanxs, chanys; + bool chanx_start, chany_start, single_node; + for (auto const& i : existing_nodes) { + single_node = true; + for (auto j : NODESIDES) + single_node &= !i.second->links[j]; + chanx_start = chany_start = false; + if (i.second->links[LEFT_EDGE]) { + intermediate_node* left_node = existing_nodes[add_vec(i.second->loc, offsets[LEFT_EDGE])]; + chanx_start = pip_uses_node_loc(node_id, left_node->loc) || left_node->links[TOP_EDGE] || left_node->links[BOTTOM_EDGE]; + } else { + chanx_start = i.second->has_pins || single_node; } - if (is_chanx && is_chany) { - shorts_.emplace_back(std::get<0>(key1), std::get<1>(key1), loc_chan_idx[key1], - std::get<0>(key2), std::get<1>(key2), loc_chan_idx[key2]); + if (i.second->links[TOP_EDGE]) { + intermediate_node* top_node = existing_nodes[add_vec(i.second->loc, offsets[TOP_EDGE])]; + chany_start = pip_uses_node_loc(node_id, top_node->loc) || pin_uses_node_loc(node_id, top_node->loc); + chany_start |= top_node->links[LEFT_EDGE] || top_node->links[RIGHT_EDGE]; } + if (chanx_start) + chanxs.insert(i.second); + if (chany_start) + chanys.insert(i.second); } - /* - * Create edges between future rr_nodes that correspond to the given FPGA Interchange node - */ - while (!back_walker.empty()) { - intermediate_node* vertex = back_walker.top(); - back_walker.pop(); - location loc = vertex->loc; - location down(vertex->loc.first, vertex->loc.second - 1); // lower tile CHANY, our CHANX - location right(down.first + 1, down.second); // CHANX of tile to the right - - bool left_node_exist = vertex->links[LEFT_EDGE] != nullptr, - top_node_exist = vertex->links[TOP_EDGE] != nullptr, - right_node_exist = vertex->links[RIGHT_EDGE] != nullptr, - bottom_node_exist = vertex->links[BOTTOM_EDGE] != nullptr; - - bool right_node_valid = false; - if (right_node_exist) - right_node_valid = vertex->links[RIGHT_EDGE]->visited == vertex || vertex->links[RIGHT_EDGE] == vertex->visited; - - bool bottom_node_valid = false; - if (bottom_node_exist) - bottom_node_valid = vertex->links[BOTTOM_EDGE]->visited == vertex || vertex->links[BOTTOM_EDGE] == vertex->visited; - - if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) != loc_chan_idx.end()) { - redirect_.emplace(std::make_tuple(node_id, loc), - std::make_tuple(down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)])); - - } else if (loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) != loc_chan_idx.end()) { - redirect_.emplace(std::make_tuple(node_id, loc), - std::make_tuple(loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)])); - - } else if (right_node_exist && right_node_valid && loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) { - redirect_.emplace(std::make_tuple(node_id, loc), - std::make_tuple(right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)])); - - } else if (bottom_node_exist && bottom_node_valid && loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) { - redirect_.emplace(std::make_tuple(node_id, loc), - std::make_tuple(down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)])); + std::map> local_redirect_x, local_redirect_y; + + process_set(chanys, existing_nodes, local_redirect_y, R, C, location(0, 0), BOTTOM_EDGE, CHANY); + process_set(chanxs, existing_nodes, local_redirect_x, R, C, offsets[BOTTOM_EDGE], RIGHT_EDGE, CHANX); + connect_base_on_redirects(chanys, TOP_EDGE, local_redirect_y, local_redirect_y); + connect_base_on_redirects(chanxs, LEFT_EDGE, local_redirect_x, local_redirect_y); + connect_base_on_redirects(chanxs, LEFT_EDGE, local_redirect_x, local_redirect_x); + connect_base_on_redirects(chanys, TOP_EDGE, local_redirect_y, local_redirect_x); + + bool ry, rx; + for (auto const node : existing_nodes) { + ry = local_redirect_y.find(node.first) != local_redirect_y.end(); + rx = local_redirect_x.find(node.first) != local_redirect_x.end(); + if (node.second->links[RIGHT_EDGE] && node.second->links[BOTTOM_EDGE]) { + location bottom_node = add_vec(node.first, offsets[BOTTOM_EDGE]); + location right_node = add_vec(node.first, offsets[RIGHT_EDGE]); + add_short(bottom_node, right_node, local_redirect_y, local_redirect_x); } - - /* Check if node x is up-right corner: - * +-+-+ - * |x|b| - * +-+-+ - * |a| - * +-+ - * If so it does not use CHANX or CHANY, and it must connect CHANY from a with CHANX from b - */ - - if (right_node_exist && right_node_valid && bottom_node_exist && bottom_node_valid && loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) == loc_chan_idx.end() && loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) == loc_chan_idx.end()) { - shorts_.emplace_back(down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)], - right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)]); - continue; + if (ry && rx) { + add_short(node.first, node.first, local_redirect_y, local_redirect_x); } - - /* If tile has CHANX, try to cannect it to CHANY of tile below, - * CHANY and CHANX of tile to the left and CHANX of tile to the right - */ - if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANX)) != loc_chan_idx.end()) { - if (left_node_exist && vertex->links[0]->visited == vertex) { - location left(down.first - 1, down.second); // CHANX - location left_up(loc.first - 1, loc.second); // CHANY - if (loc_chan_idx.find(std::make_tuple(left, e_rr_type::CHANX)) != loc_chan_idx.end()) - shorts_.emplace_back(left, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(left, e_rr_type::CHANX)], - down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)]); - if (loc_chan_idx.find(std::make_tuple(left_up, e_rr_type::CHANY)) != loc_chan_idx.end()) - shorts_.emplace_back(left_up, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(left_up, e_rr_type::CHANY)], - down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)]); - } - if (right_node_exist && vertex->links[2]->visited == vertex) { - if (loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) - shorts_.emplace_back(right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)], - down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)]); - } - if (bottom_node_exist && vertex->links[3]->visited == vertex) { - if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) - shorts_.emplace_back(down, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANX)], - down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)]); - } - } - - /* If tile has CHANY, try to cannect it to CHANY of tile below, - * CHANY and CHANX of tile above and CHANX of tile to the right - */ - if (loc_chan_idx.find(std::make_tuple(loc, e_rr_type::CHANY)) != loc_chan_idx.end()) { - if (top_node_exist && vertex->links[1]->visited == vertex) { - location up(down.first, down.second + 1); // CHANX - location up_up(loc.first, loc.second + 1); // CHANY - if (loc_chan_idx.find(std::make_tuple(up, e_rr_type::CHANX)) != loc_chan_idx.end()) - shorts_.emplace_back(up, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(up, e_rr_type::CHANX)], - loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); - if (loc_chan_idx.find(std::make_tuple(up_up, e_rr_type::CHANY)) != loc_chan_idx.end()) - shorts_.emplace_back(up_up, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(up_up, e_rr_type::CHANY)], - loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); - } - if (right_node_exist && vertex->links[2]->visited == vertex) { - if (loc_chan_idx.find(std::make_tuple(right, e_rr_type::CHANX)) != loc_chan_idx.end()) - shorts_.emplace_back(right, e_rr_type::CHANX, loc_chan_idx[std::make_tuple(right, e_rr_type::CHANX)], - loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); - } - if (bottom_node_exist && vertex->links[3]->visited == vertex) { - if (loc_chan_idx.find(std::make_tuple(down, e_rr_type::CHANY)) != loc_chan_idx.end()) - shorts_.emplace_back(down, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(down, e_rr_type::CHANY)], - loc, e_rr_type::CHANY, loc_chan_idx[std::make_tuple(loc, e_rr_type::CHANY)]); - } + if (rx) { + virtual_redirect_.emplace(std::make_tuple(node_id, node.first), local_redirect_x[node.first]); + } else if (ry) { + virtual_redirect_.emplace(std::make_tuple(node_id, node.first), local_redirect_y[node.first]); + } else if (node.second->links[RIGHT_EDGE]) { + location right_node = add_vec(node.first, offsets[RIGHT_EDGE]); + virtual_redirect_.emplace(std::make_tuple(node_id, node.first), local_redirect_x[right_node]); + } else if (node.second->links[BOTTOM_EDGE]) { + location bottom_node = add_vec(node.first, offsets[BOTTOM_EDGE]); + virtual_redirect_.emplace(std::make_tuple(node_id, node.first), local_redirect_y[bottom_node]); + } else { + VTR_ASSERT(false); } } - - intermediate_node* last = *roots.begin(); - for (intermediate_node* root : roots) { - if (root == last) - continue; - VTR_ASSERT(redirect_.find(std::make_tuple(node_id, last->loc)) != redirect_.end()); - auto key1 = redirect_[std::make_tuple(node_id, last->loc)]; - VTR_ASSERT(redirect_.find(std::make_tuple(node_id, root->loc)) != redirect_.end()); - auto key2 = redirect_[std::make_tuple(node_id, root->loc)]; - - shorts_.emplace_back(std::get<0>(key1), std::get<1>(key1), std::get<2>(key1), - std::get<0>(key2), std::get<1>(key2), std::get<2>(key2)); - - last = root; - } } /* * Remove a graph represented by a root node. * Clean up of earlier stages */ - void delete_nodes(std::set roots) { - std::queue walker; - - for (intermediate_node* root : roots) { - walker.push(root); - } - - while (!walker.empty()) { - intermediate_node* vertex = walker.front(); - walker.pop(); - for (auto i : NODESIDES) { - intermediate_node* temp = nullptr; - if (vertex->links[i] != nullptr) { - if (vertex->links[i]->visited == vertex) { - walker.push(vertex->links[i]); - } - temp = vertex->links[i]; - temp->links[(i + 2) % 4] = nullptr; - } - } - delete vertex; + void delete_nodes(std::map& existing_nodes) { + for (auto i : existing_nodes) { + delete i.second; } + existing_nodes.clear(); } /* @@ -823,19 +1047,27 @@ struct RR_Graph_Builder { } std::set all_possible_tiles = node_to_locs_[node_id]; - std::set roots = build_node_graph(node_id, all_possible_tiles, seg_id); - int div = reduce_graph_and_count_nodes_left(roots, seg_id); - if (roots.empty()) { + std::map existing_nodes; + + std::set roots = build_node_graph(node_id, all_possible_tiles, existing_nodes, seg_id); + intermediate_node* root = connect_roots(roots, existing_nodes); + + VTR_ASSERT(roots.empty()); + + int div = reduce_graph_and_count_nodes_left(root, existing_nodes, seg_id); + if (existing_nodes.empty()) { continue; } + node_id_count_[node_id] = div; + VTR_ASSERT(div > 0); float capacitance = 0.000000001, resistance = 5.7; // Some random data if (ar_.hasNodeTimings()) { auto model = ar_.getNodeTimings()[node.getNodeTiming()]; capacitance = get_corner_value(model.getCapacitance(), "slow", "typ") / div; resistance = get_corner_value(model.getResistance(), "slow", "typ") / div; } - populate_chan_loc_map(node_id, roots, resistance, capacitance); - delete_nodes(roots); + graph_reduction_stage2(node_id, existing_nodes, resistance, capacitance); + delete_nodes(existing_nodes); } } @@ -917,33 +1149,68 @@ struct RR_Graph_Builder { } } + void sweep(location loc, + e_rr_type type, + int& used_tracks, + std::unordered_map>& sweeper, + std::list& avaiable_tracks) { + for (size_t k = 0; k < virtual_chan_loc_map_[std::make_tuple(loc, type)].size(); ++k) { + auto track = virtual_chan_loc_map_[std::make_tuple(loc, type)][k]; + int new_id; + if (!avaiable_tracks.empty()) { + new_id = avaiable_tracks.front(); + avaiable_tracks.pop_front(); + } else { + new_id = used_tracks++; + } + virtual_beg_to_real_[std::make_tuple(loc, type, k)] = new_id; + chan_loc_map_[std::make_tuple(loc, type)][new_id] = track; + int stop = type == CHANX ? std::get<3>(track).first : std::get<3>(track).second; + sweeper[stop].push_back(new_id); + } + int pos = type == CHANX ? loc.first : loc.second; + while (!sweeper[pos].empty()) { + avaiable_tracks.push_back(sweeper[pos].front()); + sweeper[pos].pop_front(); + } + virtual_chan_loc_map_[std::make_tuple(loc, type)].clear(); + } + /* - * This function populate device chan_width structures, they are needed in later stages of VPR flow + * Create mapping from virtual to physical tracks. + * It should work in O(N*M + L), where n,m are device dims and l is number of used segments in CHANs */ - void pack_chan_width() { + void virtual_to_real_() { device_ctx_.chan_width.x_list.resize(grid_.height(), 0); device_ctx_.chan_width.y_list.resize(grid_.width(), 0); + int min_x, min_y, max_x, max_y; min_x = min_y = 0x7fffffff; max_x = max_y = 0; - for (auto i : chan_loc_map_) { - auto key = i.first; - auto vec = i.second; - location loc; - e_rr_type type; - std::tie(loc, type) = key; - int x, y; - std::tie(x, y) = loc; - if (type == e_rr_type::CHANX) { - min_x = std::min(min_x, (int)vec.size()); - max_x = std::max(max_x, (int)vec.size()); - device_ctx_.chan_width.x_list[y] = std::max((int)device_ctx_.chan_width.x_list[y], (int)vec.size()); - } else { - min_y = std::min(min_y, (int)vec.size()); - max_y = std::max(max_y, (int)vec.size()); - device_ctx_.chan_width.y_list[x] = std::max((int)device_ctx_.chan_width.y_list[x], (int)vec.size()); - } + + std::unordered_map> sweeper; + std::list avaiable_tracks; + int used_tracks; + for (size_t i = 0; i < grid_.height(); ++i) { + used_tracks = 0; + avaiable_tracks.clear(); + for (size_t j = 0; j < grid_.width(); ++j) + sweep(location(j, i), CHANX, used_tracks, sweeper, avaiable_tracks); + min_x = std::min(min_x, used_tracks); + max_x = std::max(max_x, used_tracks); + device_ctx_.chan_width.x_list[i] = used_tracks; } + + for (size_t i = 0; i < grid_.width(); ++i) { + used_tracks = 0; + avaiable_tracks.clear(); + for (size_t j = grid_.height() - 1; j < (size_t)-1; --j) + sweep(location(i, j), CHANY, used_tracks, sweeper, avaiable_tracks); + min_y = std::min(min_y, used_tracks); + max_y = std::max(max_y, used_tracks); + device_ctx_.chan_width.y_list[i] = used_tracks; + } + device_ctx_.chan_width.max = std::max(max_y, max_x); device_ctx_.chan_width.x_min = min_x; device_ctx_.chan_width.x_max = max_x; @@ -968,22 +1235,28 @@ struct RR_Graph_Builder { float R, C; std::tie(R, C) = input ? std::tuple(0, RC) : std::tuple(RC, 0); - if(FPGA_Interchange_node_id != -1) { + if (FPGA_Interchange_node_id != -1) { location track_loc; e_rr_type track_type; int track_idx; - if (redirect_.find(std::make_tuple(FPGA_Interchange_node_id, loc)) == redirect_.end()) { + if (virtual_redirect_.find(std::make_tuple(FPGA_Interchange_node_id, loc)) == virtual_redirect_.end()) { VTR_LOG("node_id:%d, loc->X:%d Y:%d\n", FPGA_Interchange_node_id, loc.first, loc.second); VTR_ASSERT(false); } - std::tie(track_loc, track_type, track_idx) = redirect_[std::make_tuple(FPGA_Interchange_node_id, loc)]; + auto virtual_key = virtual_redirect_[std::make_tuple(FPGA_Interchange_node_id, loc)]; + track_idx = virtual_beg_to_real_[virtual_key]; + std::tie(track_loc, track_type, std::ignore) = virtual_key; + auto val = chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx]; int track_seg; float track_R, track_C; - std::tie(track_seg, track_R, track_C) = val; + location end; + std::tie(track_seg, track_R, track_C, end) = val; track_R += R; track_C += C; - chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx] = std::make_tuple(track_seg, track_R, track_C); + if (node_id_count_[FPGA_Interchange_node_id] == 1) + track_seg = 0; //set __generic__ segment type for wires going to/from site and that have pips in tile + chan_loc_map_[std::make_tuple(track_loc, track_type)][track_idx] = std::make_tuple(track_seg, track_R, track_C, end); } device_ctx_.rr_nodes.make_room_for_node(RRNodeId(rr_idx)); @@ -1038,13 +1311,16 @@ struct RR_Graph_Builder { int x, y; std::tie(x, y) = loc; auto tracks = i.second; - for (size_t j = 0; j < tracks.size(); ++j) { + for (auto track : tracks) { int seg; float R, C; - std::tie(seg, R, C) = tracks[j]; + location end; + int x1, y1; + std::tie(seg, R, C, end) = track.second; + std::tie(x1, y1) = end; device_ctx_.rr_nodes.make_room_for_node(RRNodeId(rr_idx)); - loc_type_idx_to_rr_idx_[std::make_tuple(loc, type, j)] = rr_idx; + loc_type_idx_to_rr_idx_[std::make_tuple(loc, type, track.first)] = rr_idx; auto node = device_ctx_.rr_nodes[rr_idx]; RRNodeId node_id = node.id(); @@ -1053,8 +1329,8 @@ struct RR_Graph_Builder { device_ctx_.rr_graph_builder.set_node_rc_index(node_id, NodeRCIndex(find_create_rr_rc_data(R, C))); device_ctx_.rr_graph_builder.set_node_direction(node_id, Direction::BIDIR); - device_ctx_.rr_graph_builder.set_node_coordinates(node_id, x, y, x, y); - device_ctx_.rr_graph_builder.set_node_ptc_num(node_id, j); + device_ctx_.rr_graph_builder.set_node_coordinates(node_id, x, y, x1, y1); + device_ctx_.rr_graph_builder.set_node_ptc_num(node_id, track.first); if (type == e_rr_type::CHANX) { device_ctx_.rr_graph_builder.set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + seg)); @@ -1089,14 +1365,14 @@ struct RR_Graph_Builder { std::tie(input, std::ignore, node_id) = pin_vec[j]; if (node_id == -1) continue; - VTR_ASSERT(redirect_.find(std::make_tuple(node_id, loc)) != redirect_.end()); - auto chan_key = redirect_[std::make_tuple(node_id, loc)]; + auto virtual_chan_key = virtual_redirect_[std::make_tuple(node_id, loc)]; e_rr_type pin = input ? e_rr_type::SINK : e_rr_type::SOURCE; e_rr_type mux = input ? e_rr_type::IPIN : e_rr_type::OPIN; + auto chan_key = std::make_tuple(std::get<0>(virtual_chan_key), + std::get<1>(virtual_chan_key), + virtual_beg_to_real_[virtual_chan_key]); int pin_id, mux_id, track_id; - VTR_ASSERT(loc_type_idx_to_rr_idx_.find(std::make_tuple(loc, pin, j)) != loc_type_idx_to_rr_idx_.end()); - VTR_ASSERT(loc_type_idx_to_rr_idx_.find(std::make_tuple(loc, mux, j)) != loc_type_idx_to_rr_idx_.end()); pin_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, j)]; mux_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, mux, j)]; track_id = loc_type_idx_to_rr_idx_[chan_key]; @@ -1113,15 +1389,15 @@ struct RR_Graph_Builder { } void pack_chans_edges() { - for (auto& i : shorts_) { + for (auto& i : virtual_shorts_) { location l1, l2; e_rr_type t1, t2; - int idx1, idx2; - std::tie(l1, t1, idx1, l2, t2, idx2) = i; - std::tuple key1(l1, t1, idx1), key2(l2, t2, idx2); + int virtual_idx1, idx1, virtual_idx2, idx2; + std::tie(l1, t1, virtual_idx1, l2, t2, virtual_idx2) = i; + idx1 = virtual_beg_to_real_[std::make_tuple(l1, t1, virtual_idx1)]; + idx2 = virtual_beg_to_real_[std::make_tuple(l2, t2, virtual_idx2)]; - VTR_ASSERT(loc_type_idx_to_rr_idx_.find(key1) != loc_type_idx_to_rr_idx_.end()); - VTR_ASSERT(loc_type_idx_to_rr_idx_.find(key2) != loc_type_idx_to_rr_idx_.end()); + std::tuple key1(l1, t1, idx1), key2(l2, t2, idx2); int src, sink; src = loc_type_idx_to_rr_idx_[key1]; @@ -1152,13 +1428,15 @@ struct RR_Graph_Builder { bool forward; std::tie(name, wire0, wire1, forward) = metadata; location loc = tile_to_loc_[tile_id]; - VTR_ASSERT(redirect_.find(std::make_tuple(node1, loc)) != redirect_.end()); - auto key1 = redirect_[std::make_tuple(node1, loc)]; - VTR_ASSERT(redirect_.find(std::make_tuple(node2, loc)) != redirect_.end()); - auto key2 = redirect_[std::make_tuple(node2, loc)]; + auto virtual_key1 = virtual_redirect_[std::make_tuple(node1, loc)]; + auto virtual_key2 = virtual_redirect_[std::make_tuple(node2, loc)]; - VTR_ASSERT(loc_type_idx_to_rr_idx_.find(key1) != loc_type_idx_to_rr_idx_.end()); - VTR_ASSERT(loc_type_idx_to_rr_idx_.find(key2) != loc_type_idx_to_rr_idx_.end()); + auto key1 = std::make_tuple(std::get<0>(virtual_key1), + std::get<1>(virtual_key1), + virtual_beg_to_real_[virtual_key1]); + auto key2 = std::make_tuple(std::get<0>(virtual_key2), + std::get<1>(virtual_key2), + virtual_beg_to_real_[virtual_key2]); int src, sink; src = loc_type_idx_to_rr_idx_[key1]; @@ -1191,7 +1469,6 @@ struct RR_Graph_Builder { * Fill device rr_graph informations. */ void pack_to_rr_graph() { - pack_chan_width(); // switches are already packed // segments are already packed before rr_generation // physical_tile_types are already packed before rr_generation diff --git a/vpr/src/route/rr_graph_fpga_interchange.h b/vpr/src/route/rr_graph_fpga_interchange.h index 628526dbd22..5a79c7bde1d 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.h +++ b/vpr/src/route/rr_graph_fpga_interchange.h @@ -18,54 +18,54 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, int* wire_to_rr_ipin_switch, bool do_check_rr_graph); -namespace hash_tuple{ - namespace { - template - struct hash { - size_t operator()(TT const& tt) const { - return std::hash()(tt); - } - }; +namespace hash_tuple { +namespace { +template +struct hash { + size_t operator()(TT const& tt) const { + return std::hash()(tt); + } +}; - template - struct hash > { - size_t operator()(const std::pair &p) const noexcept(true) { - size_t lhs, rhs; - lhs = std::hash()(p.first); - rhs = std::hash()(p.second); - lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); - return lhs; - } - }; - template - inline void hash_combine(std::size_t& seed, T const& v) { - seed ^= hash_tuple::hash()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); - } - // Recursive template code derived from Matthieu M. - template ::value - 1> - struct HashValueImpl { - static void apply(size_t& seed, Tuple const& tuple) { - HashValueImpl::apply(seed, tuple); - hash_combine(seed, std::get(tuple)); - } - }; +template +struct hash> { + size_t operator()(const std::pair& p) const noexcept(true) { + size_t lhs, rhs; + lhs = std::hash()(p.first); + rhs = std::hash()(p.second); + lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); + return lhs; + } +}; +template +inline void hash_combine(std::size_t& seed, T const& v) { + seed ^= hash_tuple::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} +// Recursive template code derived from Matthieu M. +template::value - 1> +struct HashValueImpl { + static void apply(size_t& seed, Tuple const& tuple) { + HashValueImpl::apply(seed, tuple); + hash_combine(seed, std::get(tuple)); + } +}; - template - struct HashValueImpl { - static void apply(size_t& seed, Tuple const& tuple) { - hash_combine(seed, std::get<0>(tuple)); - } - }; +template +struct HashValueImpl { + static void apply(size_t& seed, Tuple const& tuple) { + hash_combine(seed, std::get<0>(tuple)); } +}; +} // namespace - template - struct hash> { - size_t operator()(std::tuple const& tt) const { - size_t seed = 0; - HashValueImpl >::apply(seed, tt); - return seed; - } - }; -} +template +struct hash> { + size_t operator()(std::tuple const& tt) const { + size_t seed = 0; + HashValueImpl>::apply(seed, tt); + return seed; + } +}; +} // namespace hash_tuple #endif /* RR_GRAPH_FPGA_INTERCHANGE_H */