From 46af2d168dc97ddc6b165d415907a91158897e4c Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 22 Nov 2021 09:33:33 +0100 Subject: [PATCH 01/38] Initial FPGA Interchange rr_graph builder Signed-off-by: Maciej Dudek --- vpr/src/base/SetupVPR.cpp | 1 + vpr/src/route/rr_graph.cpp | 23 ++++++++++++++++++++- vpr/src/route/rr_graph_fpga_interchange.cpp | 21 +++++++++++++++++++ vpr/src/route/rr_graph_fpga_interchange.h | 18 ++++++++++++++++ 4 files changed, 62 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 f75745facae..11543c56bc9 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -422,6 +422,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 5172a66e2be..844ae803b65 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" @@ -315,7 +316,8 @@ void create_rr_graph(const t_graph_type graph_type, int* Warnings) { const auto& device_ctx = g_vpr_ctx.device(); auto& mutable_device_ctx = g_vpr_ctx.mutable_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(); @@ -332,6 +334,25 @@ void create_rr_graph(const t_graph_type graph_type, router_opts.reorder_rr_graph_nodes_threshold, router_opts.reorder_rr_graph_nodes_seed); } + } 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); + if (router_opts.reorder_rr_graph_nodes_algorithm != DONT_REORDER) { + mutable_device_ctx.rr_graph_builder.reorder_nodes(router_opts.reorder_rr_graph_nodes_algorithm, + router_opts.reorder_rr_graph_nodes_threshold, + router_opts.reorder_rr_graph_nodes_seed); + } } } else { if (channel_widths_unchanged(device_ctx.chan_width, nodes_per_chan) && !device_ctx.rr_graph.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 8dec6b06dade9a0a9a08d25776d01d51a6b2751c Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Thu, 25 Nov 2021 12:47:45 +0100 Subject: [PATCH 02/38] RR_graph building for fpga interchange Signed-off-by: Maciej Dudek --- .../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 +- 8 files changed, 1311 insertions(+), 96 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/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 4d56a3f535b..46555702b33 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}) { @@ -2457,17 +2382,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 11543c56bc9..c03fef2e119 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -422,7 +422,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 f469e76dbc4..f4c72fdcadd 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1250,6 +1250,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 844ae803b65..3ff6b3c561a 100644 --- a/vpr/src/route/rr_graph.cpp +++ b/vpr/src/route/rr_graph.cpp @@ -318,7 +318,7 @@ void create_rr_graph(const t_graph_type graph_type, auto& mutable_device_ctx = g_vpr_ctx.mutable_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, @@ -343,11 +343,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); if (router_opts.reorder_rr_graph_nodes_algorithm != DONT_REORDER) { mutable_device_ctx.rr_graph_builder.reorder_nodes(router_opts.reorder_rr_graph_nodes_algorithm, router_opts.reorder_rr_graph_nodes_threshold, 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 b3bc56758843fc972527aa04949afc05e28da378 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 20 Dec 2021 13:26:11 +0100 Subject: [PATCH 03/38] 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 eab6266923f8ab5ddfc70240f7f48361b842807e Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 20 Dec 2021 15:40:13 +0100 Subject: [PATCH 04/38] 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 7fc76c66d3b4ea1cacb1ddc4044d4b3f1a8459ee Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Thu, 23 Dec 2021 16:11:59 +0100 Subject: [PATCH 05/38] 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 ++++++++++++++++----- 1 file changed, 24 insertions(+), 6 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_); } } From 17298bc10f078c0cedd7d3f8608b241aab0b7707 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 27 Dec 2021 17:45:19 +0100 Subject: [PATCH 06/38] 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 ceaf604cc01582233e7353177024fab390fa2d20 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Fri, 31 Dec 2021 11:09:00 +0100 Subject: [PATCH 07/38] 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 46555702b33..80292c175cc 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -259,7 +259,6 @@ struct ArchReader { // Bel Cell mappings std::unordered_map> bel_cell_mappings_; - std::unordered_map segment_name_to_segment_idx; // Utils @@ -2370,6 +2369,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 @@ -2382,32 +2404,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 */ From 96a25201b2b659eb7293f9de591ab316b86bbded Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 24 Jan 2022 19:19:04 +0100 Subject: [PATCH 08/38] Add support for constant networks Update to new mainline Signed-off-by: Maciej Dudek --- .../src/read_fpga_interchange_arch.cpp | 7 +- vpr/src/route/rr_graph_fpga_interchange.cpp | 194 ++++++++++++++---- vpr/src/route/rr_graph_fpga_interchange.h | 5 +- 3 files changed, 164 insertions(+), 42 deletions(-) diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 80292c175cc..aafe9b02970 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -2129,6 +2129,7 @@ struct ArchReader { t_sub_tile sub_tile; sub_tile.index = 0; sub_tile.name = vtr::strdup(const_block_.c_str()); + sub_tile.capacity.set(0, 0); int count = 0; for (auto const_cell : const_cells) { sub_tile.sub_tile_to_tile_pin_indices.push_back(count); @@ -2139,7 +2140,7 @@ struct ArchReader { port.name = vtr::strdup((const_cell.first + "_" + const_cell.second).c_str()); - port.index = port.absolute_first_pin_index = port.port_index_by_type = 0; + port.index = port.absolute_first_pin_index = port.port_index_by_type = count; sub_tile.ports.push_back(port); @@ -2219,10 +2220,10 @@ struct ArchReader { grid_def.loc_defs.emplace_back(std::move(single)); } - // The constant source tile will be placed at (0, 0) + // The constant source tile will be placed at (1, max_height) t_grid_loc_def constant(const_block_, 1); constant.x.start_expr = std::to_string(1); - constant.y.start_expr = std::to_string(1); + constant.y.start_expr = std::to_string(grid_def.height -1); constant.x.end_expr = constant.x.start_expr + " + w - 1"; constant.y.end_expr = constant.y.start_expr + " + h - 1"; diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index e9462a702d4..2b7979b2adf 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -178,9 +178,15 @@ struct RR_Graph_Builder { 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, + int, + hash_tuple::hash>> wire_to_node_; std::unordered_map> node_to_locs_; - std::map /**/, int /*segment type*/> node_tile_to_segment_; + std::unordered_map /**/, + int /*segment type*/, + hash_tuple::hash>> node_tile_to_segment_; + std::unordered_map> node_to_RC_; + int total_node_count_; /* * Offsets for FPGA Interchange node processing @@ -246,6 +252,8 @@ struct RR_Graph_Builder { } std::string str(int idx) { + if (idx == -1) + return std::string("constant_block"); return std::string(ar_.getStrList()[idx].cStr()); } @@ -379,10 +387,10 @@ 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); - fill_switch(device_ctx_.rr_switch_inf[0], 0, 0, 0, 0, 0, 0, 0, + fill_switch(device_ctx_.rr_switch_inf[RRSwitchId(0)], 0, 0, 0, 0, 0, 0, 0, vtr::strdup("short"), SwitchType::SHORT); make_room_in_vector(&device_ctx_.rr_switch_inf, 1); - fill_switch(device_ctx_.rr_switch_inf[1], 0, 0, 0, 0, 0, 0, 0, + fill_switch(device_ctx_.rr_switch_inf[RRSwitchId(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; @@ -423,7 +431,7 @@ struct RR_Graph_Builder { type = buffered ? SwitchType::MUX : SwitchType::PASS_GATE; mux_trans_size = buffered ? 1 : 0; - fill_switch(device_ctx_.rr_switch_inf[id], R, Cin, Cout, Cint, Tdel, + fill_switch(device_ctx_.rr_switch_inf[RRSwitchId(id)], R, Cin, Cout, Cint, Tdel, mux_trans_size, 0, vtr::strdup(switch_name.c_str()), type); id++; @@ -434,10 +442,15 @@ struct RR_Graph_Builder { * Create mapping form tile_id to its location */ void create_tile_id_to_loc() { + int max_height = 0; for (const auto& tile : ar_.getTileList()) { tile_to_loc_[tile.getName()] = location(tile.getCol() + 1, tile.getRow() + 1); + max_height = std::max(max_height, (int)(tile.getRow() +1)); loc_to_tile_[location(tile.getCol() + 1, tile.getRow() + 1)] = tile.getName(); } + /* tile with name -1 is assosiated with constant source tile */ + tile_to_loc_[-1] = location(1, max_height + 1); + loc_to_tile_[location(1, max_height + 1)] = -1; } /* @@ -445,21 +458,105 @@ struct RR_Graph_Builder { * 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 process_const_nodes() { + for (const auto& tile : ar_.getTileList()) { + int tile_id = tile.getName(); + auto tile_type = ar_.getTileTypeList()[tile.getType()]; + auto wires = tile_type.getWires(); + for (const auto& constant : tile_type.getConstants()){ + int const_id = constant.getConstant() == Device::ConstantType::VCC ? 1 : 0; + for (const auto wire_id : constant.getWires()) { + wire_to_node_[std::make_tuple(tile_id, wires[wire_id])] = const_id; + node_to_locs_[const_id].insert(tile_to_loc_[tile_id]); + if (wire_name_to_seg_idx_.find(str(wires[wire_id])) == wire_name_to_seg_idx_.end()) + wire_name_to_seg_idx_[str(wires[wire_id])] = -1; + node_tile_to_segment_[std::make_tuple(const_id, tile_id)] = wire_name_to_seg_idx_[str(wires[wire_id])]; + } + } + } + for (const auto& node_source : ar_.getConstants().getNodeSources()) { + int tile_id = node_source.getTile(); + int wire_id = node_source.getWire(); + int const_id = node_source.getConstant() == Device::ConstantType::VCC ? 1 : 0; + wire_to_node_[std::make_tuple(tile_id, wire_id)] = const_id; + node_to_locs_[const_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(const_id, tile_id)] = wire_name_to_seg_idx_[str(wire_id)]; + } + for (int i = 0; i < 2; ++i) { + wire_to_node_[std::make_tuple(-1, i)] = i; + node_to_locs_[i].insert(tile_to_loc_[-1]); + node_tile_to_segment_[std::make_tuple(i, -1)] = -1; + } + } + void create_uniq_node_ids() { - int id = 0; + /* + Process constant sources + */ + process_const_nodes(); + /* + Process nodes + */ + int id = 2; + bool constant_node; + int node_id; for (const auto& node : ar_.getNodes()) { + constant_node = false; + /* + Nodes connected to constant sources should be combined into single constant node + */ 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_to_node_.find(std::make_tuple(tile_id, wire_id)) != wire_to_node_.end() && + wire_to_node_[std::make_tuple(tile_id, wire_id)] < 2) { + constant_node = true; + node_id = wire_to_node_[std::make_tuple(tile_id, wire_id)]; + } + } + if (!constant_node){ + node_id = id++; + } + float capacitance = 0.0, resistance = 0.0; // Some random data + if (node_to_RC_.find(node_id) == node_to_RC_.end()) { + node_to_RC_[node_id] = std::pair(0, 0); + capacitance = 0.000000001; resistance = 5.7; + } + if (ar_.hasNodeTimings()) { + auto model = ar_.getNodeTimings()[node.getNodeTiming()]; + capacitance = get_corner_value(model.getCapacitance(), "slow", "typ"); + resistance = get_corner_value(model.getResistance(), "slow", "typ"); + } + node_to_RC_[node_id].first += resistance; + node_to_RC_[node_id].second += capacitance; +#ifdef DEBUG + if (constant_node) { + const auto& wires = ar_.getWires(); + std::tuple base_wire_(wires[node.getWires()[0]].getTile(), wires[node.getWires()[0]].getWire()); + VTR_LOG("Constant_node: %s\n", wire_to_node_[base_wire_] == 1 ? "VCC\0": "GND\0"); + for (const auto& wire_id_ : node.getWires()) { + const auto& wire = ar_.getWires()[wire_id_]; + int tile_id = wire.getTile(); + int wire_id = wire.getWire(); + VTR_LOG("tile:%s wire:%s\n", str(tile_id).c_str(), str(wire_id).c_str()); + } + } +#endif + 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)] = node_id; + node_to_locs_[node_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)]; + node_tile_to_segment_[std::make_tuple(node_id, tile_id)] = wire_name_to_seg_idx_[str(wire_id)]; } - id++; } + total_node_count_ = id; } /* @@ -473,8 +570,6 @@ struct RR_Graph_Builder { const auto& tile_type = ar_.getTileTypeList()[tile.getType()]; for (const auto& pip : tile_type.getPips()) { - if (pip.isPseudoCells()) - continue; int wire0_name, wire1_name; int node0, node1; int switch_id; @@ -487,6 +582,9 @@ struct RR_Graph_Builder { continue; node0 = wire_to_node_[std::make_tuple(tile_id, wire0_name)]; node1 = wire_to_node_[std::make_tuple(tile_id, wire1_name)]; + // Allow for pseudopips that connect from/to VCC/GND + if (pip.isPseudoCells() && (node0 > 1 && node1> 1)) + continue; used_by_pip_.emplace(node0, loc); used_by_pip_.emplace(node1, loc); @@ -903,6 +1001,7 @@ struct RR_Graph_Builder { } void process_set(std::unordered_set& set, + std::unordered_set>& nodes_used, std::map& existing_nodes, std::map>& local_redirect, float R, @@ -920,6 +1019,7 @@ struct RR_Graph_Builder { auto key = std::make_tuple(add_vec(start->loc, offset), chan_type); idx = virtual_chan_loc_map_[key].size(); do { + nodes_used.insert(end->loc); len++; local_redirect.emplace(end->loc, std::make_tuple(add_vec(start->loc, offset), chan_type, idx)); if (!end->links[side]) @@ -962,6 +1062,7 @@ struct RR_Graph_Builder { float R, float C) { std::unordered_set chanxs, chanys; + std::unordered_set> nodes_in_chanxs, nodes_in_chanys; bool chanx_start, chany_start, single_node; for (auto const& i : existing_nodes) { single_node = true; @@ -973,11 +1074,15 @@ struct RR_Graph_Builder { 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 (i.second->links[RIGHT_EDGE]) + nodes_in_chanxs.insert(i.first); } 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]; + } else if (i.second->links[BOTTOM_EDGE]) { + nodes_in_chanys.insert(i.first); } if (chanx_start) chanxs.insert(i.second); @@ -987,25 +1092,35 @@ struct RR_Graph_Builder { 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); + process_set(chanys, nodes_in_chanys, existing_nodes, local_redirect_y, R, C, location(0, 0), BOTTOM_EDGE, CHANY); + process_set(chanxs, nodes_in_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_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); + //connect_base_on_redirects(chanys, TOP_EDGE, local_redirect_y, local_redirect_x); bool ry, rx; + for (auto i : nodes_in_chanys) { + location node = i; + if (nodes_in_chanxs.find(i) != nodes_in_chanxs.end()){ + ry = local_redirect_y.find(node) != local_redirect_y.end(); + rx = local_redirect_x.find(node) != local_redirect_x.end(); + location x,y; + if (rx) + x = node; + else + x = add_vec(node, offsets[RIGHT_EDGE]); + if (ry) + y = node; + else + y = add_vec(node, offsets[BOTTOM_EDGE]); + add_short(y, x, local_redirect_y, local_redirect_x); + } + } + 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); - } - if (ry && rx) { - add_short(node.first, node.first, local_redirect_y, local_redirect_x); - } if (rx) { virtual_redirect_.emplace(std::make_tuple(node_id, node.first), local_redirect_x[node.first]); } else if (ry) { @@ -1037,10 +1152,7 @@ struct RR_Graph_Builder { * 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_]; + for (int node_id = 0; node_id < total_node_count_; ++node_id) { int seg_id; if (usefull_node_.find(node_id) == usefull_node_.end()) { continue; @@ -1060,12 +1172,8 @@ struct RR_Graph_Builder { } 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; - } + float resistance = node_to_RC_[node_id].first / div; + float capacitance = node_to_RC_[node_id].second / div; graph_reduction_stage2(node_id, existing_nodes, resistance, capacitance); delete_nodes(existing_nodes); } @@ -1147,6 +1255,16 @@ struct RR_Graph_Builder { it = next_good_site(it + 1, tile); } } + /* + Add constant ource + */ + sink_source_loc_map_[-1].resize(2); + for ( auto i : {0, 1}) { + location loc = tile_to_loc_[-1]; + used_by_pin_.insert(std::make_tuple(i, loc)); + usefull_node_.insert(i); + sink_source_loc_map_[-1][i] = std::make_tuple(false, 0, i); + } } void sweep(location loc, @@ -1382,8 +1500,12 @@ struct RR_Graph_Builder { 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); + device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(src), + RRNodeId(sink_src), + src == track_id ? 1 :0); + device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(sink_src), + RRNodeId(sink), + sink == track_id ? 1 : 0); } } } diff --git a/vpr/src/route/rr_graph_fpga_interchange.h b/vpr/src/route/rr_graph_fpga_interchange.h index 5a79c7bde1d..f6b1af49cbc 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.h +++ b/vpr/src/route/rr_graph_fpga_interchange.h @@ -33,13 +33,12 @@ struct hash> { size_t lhs, rhs; lhs = std::hash()(p.first); rhs = std::hash()(p.second); - lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); - return lhs; + return (lhs * lhs + 3UL * lhs + 2UL * lhs * rhs + rhs + rhs * rhs) / 2UL; } }; template inline void hash_combine(std::size_t& seed, T const& v) { - seed ^= hash_tuple::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= hash_tuple::hash()(v) + 0x9e3779b97f4a7c15UL + (seed << 12) + (seed >> 4); } // Recursive template code derived from Matthieu M. template::value - 1> From eb17d62c08f40f178601b7af97bb8dabd97a6774 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Tue, 25 Jan 2022 14:28:15 +0100 Subject: [PATCH 09/38] Format code Signed-off-by: Maciej Dudek --- .../src/read_fpga_interchange_arch.cpp | 2 +- vpr/src/route/rr_graph_fpga_interchange.cpp | 46 ++++++++++--------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index aafe9b02970..17b3e7dcee1 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -2223,7 +2223,7 @@ struct ArchReader { // The constant source tile will be placed at (1, max_height) t_grid_loc_def constant(const_block_, 1); constant.x.start_expr = std::to_string(1); - constant.y.start_expr = std::to_string(grid_def.height -1); + constant.y.start_expr = std::to_string(grid_def.height - 1); constant.x.end_expr = constant.x.start_expr + " + w - 1"; constant.y.end_expr = constant.y.start_expr + " + h - 1"; diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 2b7979b2adf..2ca7ee6b7ea 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -180,11 +180,13 @@ struct RR_Graph_Builder { std::unordered_map, int, - hash_tuple::hash>> wire_to_node_; + hash_tuple::hash>> + wire_to_node_; std::unordered_map> node_to_locs_; std::unordered_map /**/, int /*segment type*/, - hash_tuple::hash>> node_tile_to_segment_; + hash_tuple::hash>> + node_tile_to_segment_; std::unordered_map> node_to_RC_; int total_node_count_; @@ -445,7 +447,7 @@ struct RR_Graph_Builder { int max_height = 0; for (const auto& tile : ar_.getTileList()) { tile_to_loc_[tile.getName()] = location(tile.getCol() + 1, tile.getRow() + 1); - max_height = std::max(max_height, (int)(tile.getRow() +1)); + max_height = std::max(max_height, (int)(tile.getRow() + 1)); loc_to_tile_[location(tile.getCol() + 1, tile.getRow() + 1)] = tile.getName(); } /* tile with name -1 is assosiated with constant source tile */ @@ -463,7 +465,7 @@ struct RR_Graph_Builder { int tile_id = tile.getName(); auto tile_type = ar_.getTileTypeList()[tile.getType()]; auto wires = tile_type.getWires(); - for (const auto& constant : tile_type.getConstants()){ + for (const auto& constant : tile_type.getConstants()) { int const_id = constant.getConstant() == Device::ConstantType::VCC ? 1 : 0; for (const auto wire_id : constant.getWires()) { wire_to_node_[std::make_tuple(tile_id, wires[wire_id])] = const_id; @@ -493,37 +495,37 @@ struct RR_Graph_Builder { void create_uniq_node_ids() { /* - Process constant sources - */ + * Process constant sources + */ process_const_nodes(); /* - Process nodes - */ + * Process nodes + */ int id = 2; bool constant_node; int node_id; for (const auto& node : ar_.getNodes()) { constant_node = false; /* - Nodes connected to constant sources should be combined into single constant node - */ + * Nodes connected to constant sources should be combined into single constant node + */ for (const auto& wire_id_ : node.getWires()) { const auto& wire = ar_.getWires()[wire_id_]; int tile_id = wire.getTile(); int wire_id = wire.getWire(); - if (wire_to_node_.find(std::make_tuple(tile_id, wire_id)) != wire_to_node_.end() && - wire_to_node_[std::make_tuple(tile_id, wire_id)] < 2) { + if (wire_to_node_.find(std::make_tuple(tile_id, wire_id)) != wire_to_node_.end() && wire_to_node_[std::make_tuple(tile_id, wire_id)] < 2) { constant_node = true; node_id = wire_to_node_[std::make_tuple(tile_id, wire_id)]; } } - if (!constant_node){ + if (!constant_node) { node_id = id++; } float capacitance = 0.0, resistance = 0.0; // Some random data if (node_to_RC_.find(node_id) == node_to_RC_.end()) { node_to_RC_[node_id] = std::pair(0, 0); - capacitance = 0.000000001; resistance = 5.7; + capacitance = 0.000000001; + resistance = 5.7; } if (ar_.hasNodeTimings()) { auto model = ar_.getNodeTimings()[node.getNodeTiming()]; @@ -536,7 +538,7 @@ struct RR_Graph_Builder { if (constant_node) { const auto& wires = ar_.getWires(); std::tuple base_wire_(wires[node.getWires()[0]].getTile(), wires[node.getWires()[0]].getWire()); - VTR_LOG("Constant_node: %s\n", wire_to_node_[base_wire_] == 1 ? "VCC\0": "GND\0"); + VTR_LOG("Constant_node: %s\n", wire_to_node_[base_wire_] == 1 ? "VCC\0" : "GND\0"); for (const auto& wire_id_ : node.getWires()) { const auto& wire = ar_.getWires()[wire_id_]; int tile_id = wire.getTile(); @@ -583,7 +585,7 @@ struct RR_Graph_Builder { node0 = wire_to_node_[std::make_tuple(tile_id, wire0_name)]; node1 = wire_to_node_[std::make_tuple(tile_id, wire1_name)]; // Allow for pseudopips that connect from/to VCC/GND - if (pip.isPseudoCells() && (node0 > 1 && node1> 1)) + if (pip.isPseudoCells() && (node0 > 1 && node1 > 1)) continue; used_by_pip_.emplace(node0, loc); @@ -1102,10 +1104,10 @@ struct RR_Graph_Builder { bool ry, rx; for (auto i : nodes_in_chanys) { location node = i; - if (nodes_in_chanxs.find(i) != nodes_in_chanxs.end()){ + if (nodes_in_chanxs.find(i) != nodes_in_chanxs.end()) { ry = local_redirect_y.find(node) != local_redirect_y.end(); rx = local_redirect_x.find(node) != local_redirect_x.end(); - location x,y; + location x, y; if (rx) x = node; else @@ -1256,10 +1258,10 @@ struct RR_Graph_Builder { } } /* - Add constant ource - */ + * Add constant ource + */ sink_source_loc_map_[-1].resize(2); - for ( auto i : {0, 1}) { + for (auto i : {0, 1}) { location loc = tile_to_loc_[-1]; used_by_pin_.insert(std::make_tuple(i, loc)); usefull_node_.insert(i); @@ -1502,7 +1504,7 @@ struct RR_Graph_Builder { device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(src), RRNodeId(sink_src), - src == track_id ? 1 :0); + src == track_id ? 1 : 0); device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(sink_src), RRNodeId(sink), sink == track_id ? 1 : 0); From 6135e8ffb33e3b0a69219e38d90b2435437376b1 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Thu, 27 Jan 2022 13:32:40 +0100 Subject: [PATCH 10/38] Squashed 'libs/EXTERNAL/libinterchange/' changes from 53e3feda4..54f6208f3 54f6208f3 Merge pull request #69 from antmicro/mdudel/fix_annotations d8a0567a5 Fix annotations in DeviceResources git-subtree-dir: libs/EXTERNAL/libinterchange git-subtree-split: 54f6208f32e20b746863da6ea54e3051fc9a830e --- .../interchange/DeviceResources.capnp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp b/libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp index 09c8d438555..111cf20f994 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,15 @@ 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 +627,7 @@ struct Device { } struct PinDelay { - pin @0 : BELPinIdx $belPinRef(); + pin @0 : BELPinIdx; union { noClock @1 : Void; clockEdge @2 : ClockEdge; From 4153d2be81a932bdadb6490f03fcaede01223813 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Thu, 27 Jan 2022 16:00:39 +0100 Subject: [PATCH 11/38] Misc fixes and comments Signed-off-by: Maciej Dudek --- .../src/fpga_interchange_arch_utils.cpp | 21 +++ .../src/fpga_interchange_arch_utils.h | 11 ++ .../src/read_fpga_interchange_arch.cpp | 46 +++--- vpr/src/route/rr_graph.cpp | 3 +- vpr/src/route/rr_graph_fpga_interchange.cpp | 145 ++++++++++-------- vpr/src/route/rr_graph_fpga_interchange.h | 1 + 6 files changed, 139 insertions(+), 88 deletions(-) diff --git a/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp b/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp index 8184deb1ade..677852849d3 100644 --- a/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp +++ b/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp @@ -76,3 +76,24 @@ float get_corner_value(DeviceResources::Device::CornerModel::Reader model, const } return 0.; } + +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); +} diff --git a/libs/libarchfpga/src/fpga_interchange_arch_utils.h b/libs/libarchfpga/src/fpga_interchange_arch_utils.h index 6de305bec40..1c3f3a8adf5 100644 --- a/libs/libarchfpga/src/fpga_interchange_arch_utils.h +++ b/libs/libarchfpga/src/fpga_interchange_arch_utils.h @@ -34,6 +34,17 @@ extern "C" { float get_corner_value(DeviceResources::Device::CornerModel::Reader model, const char* speed_model, const char* value); +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); + #ifdef __cplusplus } #endif diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 17b3e7dcee1..1069c78d7a1 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -183,6 +183,29 @@ static t_pin_to_pin_annotation get_pack_pattern(std::string pp_name, std::string return pp; } +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; +} + /****************** End Utility functions ******************/ struct ArchReader { @@ -2370,29 +2393,6 @@ 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 diff --git a/vpr/src/route/rr_graph.cpp b/vpr/src/route/rr_graph.cpp index 3ff6b3c561a..fb69a8e7190 100644 --- a/vpr/src/route/rr_graph.cpp +++ b/vpr/src/route/rr_graph.cpp @@ -318,7 +318,7 @@ void create_rr_graph(const t_graph_type graph_type, auto& mutable_device_ctx = g_vpr_ctx.mutable_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 && !router_opts.FPGAInterchange) { + if (device_ctx.read_rr_graph_filename != det_routing_arch->read_rr_graph_filename) { free_rr_graph(); load_rr_file(graph_type, @@ -343,6 +343,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, router_opts.do_check_rr_graph); if (router_opts.reorder_rr_graph_nodes_algorithm != DONT_REORDER) { mutable_device_ctx.rr_graph_builder.reorder_nodes(router_opts.reorder_rr_graph_nodes_algorithm, diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 2ca7ee6b7ea..6935a2f729b 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -34,6 +34,10 @@ 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}; +enum constant { + GND = 0, + VCC, +}; /* * Intermediate data type. @@ -89,8 +93,7 @@ struct RR_Graph_Builder { , 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(""))) { + , base_cost_type_(base_cost_type) { for (auto& type : physical_tile_types) { tile_type_to_pb_type_[std::string(type.name)] = &type; } @@ -144,12 +147,12 @@ struct RR_Graph_Builder { create_tile_id_to_loc(); create_uniq_node_ids(); create_sink_source_nodes(); - create_pips_(); + create_pips(); process_nodes(); #ifdef DEBUG print_virtual(); #endif - virtual_to_real_(); + virtual_to_real(); pack_to_rr_graph(); #ifdef DEBUG print(); @@ -164,8 +167,6 @@ struct RR_Graph_Builder { 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_; @@ -230,7 +231,7 @@ struct RR_Graph_Builder { */ std::unordered_set, hash_tuple::hash>> used_by_pip_; std::unordered_set, hash_tuple::hash>> used_by_pin_; - std::unordered_set usefull_node_; + std::unordered_set useful_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 @@ -259,6 +260,22 @@ struct RR_Graph_Builder { return std::string(ar_.getStrList()[idx].cStr()); } + /* + * This code iterates over each node independently when transforming + * an FPGA Interchange device resource into a VTR RR Graph. + * During the transformation, segments are created and they require + * a starting and ending coordinates as well as their track location. + * At any given time only one segment can occupy a single physical track. + * Solving the track assignment with an online algorithm would yield a huge + * slow down with a suboptimal maximal number of tracks in a single channel. + * This is solved by only storing a starting and ending location and + * the track number in the starting position, thus it is named "virtual". + * Then, when all FPGA Interchange nodes are taken care of, + * the physical track placement is computed. + * Much of the code runs on virtual assignment, as it's fast to create, + * use and is only translated when creating the final rr_graph. + */ + /* * Debug print function */ @@ -350,27 +367,6 @@ 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 */ @@ -378,15 +374,13 @@ struct RR_Graph_Builder { 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); + device_ctx_.rr_switch_inf.reserve(seen.size() + 2); int id = 2; make_room_in_vector(&device_ctx_.rr_switch_inf, 0); fill_switch(device_ctx_.rr_switch_inf[RRSwitchId(0)], 0, 0, 0, 0, 0, 0, 0, @@ -456,9 +450,7 @@ struct RR_Graph_Builder { } /* - * 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) + * Helper function for create_uniq_node_ids. It process constant sources into single node. */ void process_const_nodes() { for (const auto& tile : ar_.getTileList()) { @@ -466,7 +458,7 @@ struct RR_Graph_Builder { auto tile_type = ar_.getTileTypeList()[tile.getType()]; auto wires = tile_type.getWires(); for (const auto& constant : tile_type.getConstants()) { - int const_id = constant.getConstant() == Device::ConstantType::VCC ? 1 : 0; + int const_id = constant.getConstant() == Device::ConstantType::VCC ? VCC : GND; for (const auto wire_id : constant.getWires()) { wire_to_node_[std::make_tuple(tile_id, wires[wire_id])] = const_id; node_to_locs_[const_id].insert(tile_to_loc_[tile_id]); @@ -479,54 +471,62 @@ struct RR_Graph_Builder { for (const auto& node_source : ar_.getConstants().getNodeSources()) { int tile_id = node_source.getTile(); int wire_id = node_source.getWire(); - int const_id = node_source.getConstant() == Device::ConstantType::VCC ? 1 : 0; + int const_id = node_source.getConstant() == Device::ConstantType::VCC ? VCC : GND; wire_to_node_[std::make_tuple(tile_id, wire_id)] = const_id; node_to_locs_[const_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(const_id, tile_id)] = wire_name_to_seg_idx_[str(wire_id)]; } - for (int i = 0; i < 2; ++i) { + for (auto const i : {GND, VCC}) { wire_to_node_[std::make_tuple(-1, i)] = i; node_to_locs_[i].insert(tile_to_loc_[-1]); node_tile_to_segment_[std::make_tuple(i, -1)] = -1; } } + /* + * 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() { - /* - * Process constant sources - */ process_const_nodes(); - /* - * Process nodes - */ - int id = 2; + + // Process nodes + int id = 2; // ids 0 and 1 are reserved respectively for GND and VCC constants bool constant_node; int node_id; for (const auto& node : ar_.getNodes()) { constant_node = false; - /* - * Nodes connected to constant sources should be combined into single constant node - */ + // Nodes connected to constant sources should be combined into single constant node for (const auto& wire_id_ : node.getWires()) { const auto& wire = ar_.getWires()[wire_id_]; int tile_id = wire.getTile(); int wire_id = wire.getWire(); if (wire_to_node_.find(std::make_tuple(tile_id, wire_id)) != wire_to_node_.end() && wire_to_node_[std::make_tuple(tile_id, wire_id)] < 2) { + /* + * At least one of node wires is marked as constant source, that make whole node constant source. + * Give this node id equal to const source type. + */ constant_node = true; node_id = wire_to_node_[std::make_tuple(tile_id, wire_id)]; } } + + //If node is not part of constant network give it uniq id. if (!constant_node) { node_id = id++; } - float capacitance = 0.0, resistance = 0.0; // Some random data + float capacitance = 0.0, resistance = 0.0; if (node_to_RC_.find(node_id) == node_to_RC_.end()) { node_to_RC_[node_id] = std::pair(0, 0); + // Some random data capacitance = 0.000000001; resistance = 5.7; } + + // Check if device has timing model. If it does, use it if (ar_.hasNodeTimings()) { auto model = ar_.getNodeTimings()[node.getNodeTiming()]; capacitance = get_corner_value(model.getCapacitance(), "slow", "typ"); @@ -565,7 +565,7 @@ struct RR_Graph_Builder { * 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_() { + void create_pips() { for (const auto& tile : ar_.getTileList()) { int tile_id = tile.getName(); location loc = tile_to_loc_[tile_id]; @@ -584,15 +584,21 @@ struct RR_Graph_Builder { continue; node0 = wire_to_node_[std::make_tuple(tile_id, wire0_name)]; node1 = wire_to_node_[std::make_tuple(tile_id, wire1_name)]; - // Allow for pseudopips that connect from/to VCC/GND - if (pip.isPseudoCells() && (node0 > 1 && node1 > 1)) - continue; - + /* + * Allow for pseudopips that connect from/to VCC/GND. + * Temporary workaround for nexus family. + */ + /* + * FIXME + * right now we allow for pseudo pips even tho we don;t check if they are avaiable + * if (pip.isPseudoCells() && (node0 > 1 && node1 > 1)) + * continue; + */ used_by_pip_.emplace(node0, loc); used_by_pip_.emplace(node1, loc); - usefull_node_.insert(node0); - usefull_node_.insert(node1); + useful_node_.insert(node0); + useful_node_.insert(node1); int name = tile_id; if (tile.hasSubTilesPrefices()) @@ -698,6 +704,10 @@ struct RR_Graph_Builder { return roots; } + /* + * Given end, node and side check if node is more to the given side than end, + * if so it updates end. It's used to create bounding boxes for FPGA Interchange nodes. + */ intermediate_node* update_end(intermediate_node* end, intermediate_node* node, enum node_sides side) { intermediate_node* res = end; int x, y; @@ -727,6 +737,13 @@ struct RR_Graph_Builder { return res; } + /* + * Given start, end, existing nodes and connections (list of sides that line uses) + * it creates straight line connections from start to end. + * If there are missing nodes on the way, it will create new nodes and store them in + * existing nodes. + */ + void add_line(std::map& existing_nodes, location start, location end, @@ -944,10 +961,10 @@ struct RR_Graph_Builder { back_walker.push(vertex); vertex->segment_id = seg_id; single_node = true; + has_chanx = false; for (auto i : NODESIDES) { 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) { @@ -1097,9 +1114,7 @@ struct RR_Graph_Builder { process_set(chanys, nodes_in_chanys, existing_nodes, local_redirect_y, R, C, location(0, 0), BOTTOM_EDGE, CHANY); process_set(chanxs, nodes_in_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 i : nodes_in_chanys) { @@ -1120,7 +1135,7 @@ struct RR_Graph_Builder { } } - for (auto const node : existing_nodes) { + 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 (rx) { @@ -1156,7 +1171,7 @@ struct RR_Graph_Builder { void process_nodes() { for (int node_id = 0; node_id < total_node_count_; ++node_id) { int seg_id; - if (usefull_node_.find(node_id) == usefull_node_.end()) { + if (useful_node_.find(node_id) == useful_node_.end()) { continue; } std::set all_possible_tiles = node_to_locs_[node_id]; @@ -1251,7 +1266,7 @@ struct RR_Graph_Builder { location loc = tile_to_loc_[tile_id]; used_by_pin_.insert(std::make_tuple(node_id, loc)); - usefull_node_.insert(node_id); + useful_node_.insert(node_id); sink_source_loc_map_[tile_id][pin_id] = std::make_tuple(port.type == IN_PORT, value, node_id); } it = next_good_site(it + 1, tile); @@ -1264,7 +1279,7 @@ struct RR_Graph_Builder { for (auto i : {0, 1}) { location loc = tile_to_loc_[-1]; used_by_pin_.insert(std::make_tuple(i, loc)); - usefull_node_.insert(i); + useful_node_.insert(i); sink_source_loc_map_[-1][i] = std::make_tuple(false, 0, i); } } @@ -1300,12 +1315,12 @@ struct RR_Graph_Builder { * 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 virtual_to_real_() { + 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; + min_x = min_y = std::numeric_limits::max(); max_x = max_y = 0; std::unordered_map> sweeper; @@ -1649,6 +1664,7 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, const std::vector& segment_inf, const enum e_base_cost_type base_cost_type, int* wire_to_rr_ipin_switch, + std::string& read_rr_graph_filename, bool do_check_rr_graph) { vtr::ScopedStartFinishTimer timer("Building RR Graph from device database"); @@ -1699,6 +1715,7 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, builder.build_rr_graph(); *wire_to_rr_ipin_switch = 0; device_ctx.read_rr_graph_filename.assign(get_arch_file_name()); + 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 f6b1af49cbc..f89487dd5c9 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.h +++ b/vpr/src/route/rr_graph_fpga_interchange.h @@ -16,6 +16,7 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, const std::vector& segment_inf, const enum e_base_cost_type base_cost_type, int* wire_to_rr_ipin_switch, + std::string& read_rr_graph_filename, bool do_check_rr_graph); namespace hash_tuple { From 5bc078d3cf3bc9db4099dfaf19d12de357f387db Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Fri, 28 Jan 2022 19:10:12 +0100 Subject: [PATCH 12/38] Move common code to fpga_interchange_arch_utils Signed-off-by: Maciej Dudek --- .../src/fpga_interchange_arch_utils.cpp | 106 ++++++++++- .../src/fpga_interchange_arch_utils.h | 54 +++++- .../src/fpga_interchange_arch_utils.impl.h | 93 +++++++++ .../src/read_fpga_interchange_arch.cpp | 166 +--------------- vpr/src/route/rr_graph_fpga_interchange.cpp | 180 +++++------------- vpr/src/route/rr_graph_fpga_interchange.h | 17 +- 6 files changed, 313 insertions(+), 303 deletions(-) create mode 100644 libs/libarchfpga/src/fpga_interchange_arch_utils.impl.h diff --git a/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp b/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp index 677852849d3..ccc404a7172 100644 --- a/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp +++ b/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp @@ -77,7 +77,33 @@ float get_corner_value(DeviceResources::Device::CornerModel::Reader model, const return 0.; } -void fill_switch(t_rr_switch_inf& switch_, +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 pip_types(std::set>& seen, + DeviceResources::Device::Reader ar_) { + for (const auto& tile : ar_.getTileTypeList()) { + for (const auto& pip : tile.getPips()) { + /* + * FIXME + * right now we allow for pseudo pips even tho we don't check if they are avaiable + * if (pip.isPseudoCells()) + * continue; + */ + seen.emplace(pip.getTiming(), pip.getBuffered21()); + if (!pip.getDirectional()) { + seen.emplace(pip.getTiming(), pip.getBuffered20()); + } + } + } +} + +void fill_switch(t_arch_switch_inf* switch_, float R, float Cin, float Cout, @@ -87,13 +113,73 @@ void fill_switch(t_rr_switch_inf& switch_, 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); + switch_->R = R; + switch_->Cin = Cin; + switch_->Cout = Cout; + switch_->Cinternal = Cinternal; + switch_->set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel); + switch_->mux_trans_size = mux_trans_size; + switch_->buf_size = buf_size; + switch_->name = name; + switch_->set_type(type); +} + +void process_cell_bel_mappings(DeviceResources::Device::Reader ar_, + std::unordered_map>& bel_cell_mappings_, + std::function str) { + 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()) { + std::vector> pins; + + for (auto pin_map : common_pins.getPins()) + pins.emplace_back(pin_map.getCellPin(), pin_map.getBelPin()); + + for (auto site_type_entry : common_pins.getSiteTypes()) { + size_t site_type = site_type_entry.getSiteType(); + + for (auto bel : site_type_entry.getBels()) { + t_bel_cell_mapping mapping; + + mapping.cell = cell_name; + mapping.site = site_type; + mapping.pins = pins; + + std::set maps{mapping}; + auto res = bel_cell_mappings_.emplace(bel, maps); + if (!res.second) { + res.first->second.insert(mapping); + } + } + } + } + } } diff --git a/libs/libarchfpga/src/fpga_interchange_arch_utils.h b/libs/libarchfpga/src/fpga_interchange_arch_utils.h index 1c3f3a8adf5..40d95ed6918 100644 --- a/libs/libarchfpga/src/fpga_interchange_arch_utils.h +++ b/libs/libarchfpga/src/fpga_interchange_arch_utils.h @@ -2,8 +2,10 @@ #define FPGAINTERCHANGE_ARCH_UTILS_FILE_H #include "arch_types.h" +#include "arch_util.h" #include "arch_error.h" #include "vtr_error.h" +#include "vtr_util.h" #include "DeviceResources.capnp.h" #include "LogicalNetlist.capnp.h" @@ -11,11 +13,28 @@ #include "capnp/serialize-packed.h" #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif +// Necessary to reduce code verbosity when getting the pin directions +static const auto INPUT = LogicalNetlist::Netlist::Direction::INPUT; +static const auto OUTPUT = LogicalNetlist::Netlist::Direction::OUTPUT; +static const auto INOUT = LogicalNetlist::Netlist::Direction::INOUT; + +struct t_bel_cell_mapping { + size_t cell; + size_t site; + std::vector> pins; + + bool operator<(const t_bel_cell_mapping& other) const { + return cell < other.cell || (cell == other.cell && site < other.site); + } +}; + /****************** Utility functions ******************/ /** @@ -34,7 +53,20 @@ extern "C" { float get_corner_value(DeviceResources::Device::CornerModel::Reader model, const char* speed_model, const char* value); -void fill_switch(t_rr_switch_inf& switch_, +char* int_to_string(char* buff, int value); + +void pip_types(std::set>& seen, + DeviceResources::Device::Reader ar_); + +void process_cell_bel_mappings(DeviceResources::Device::Reader ar_, + std::unordered_map>& bel_cell_mappings_, + std::function str); + +#ifdef __cplusplus +} +#endif +template +void fill_switch(T* switch_, float R, float Cin, float Cout, @@ -45,8 +77,22 @@ void fill_switch(t_rr_switch_inf& switch_, char* name, SwitchType type); -#ifdef __cplusplus -} -#endif +void fill_switch(t_arch_switch_inf* switch_, + float R, + float Cin, + float Cout, + float Cinternal, + float Tdel, + float mux_trans_size, + float buf_size, + char* name, + SwitchType type); + +template +void process_switches_array(DeviceResources::Device::Reader ar_, + std::set>& seen, + T1 switch_array, + std::vector, int>>& pips_models_); +#include "fpga_interchange_arch_utils.impl.h" #endif diff --git a/libs/libarchfpga/src/fpga_interchange_arch_utils.impl.h b/libs/libarchfpga/src/fpga_interchange_arch_utils.impl.h new file mode 100644 index 00000000000..52fe9206c55 --- /dev/null +++ b/libs/libarchfpga/src/fpga_interchange_arch_utils.impl.h @@ -0,0 +1,93 @@ +/****************** 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 + */ + +template +void fill_switch(T* 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); +} + +template +void process_switches_array(DeviceResources::Device::Reader ar_, + std::set>& seen, + T1 switch_array, + std::vector, int>>& pips_models_) { + fill_switch(&switch_array[T2(0)], 0, 0, 0, 0, 0, 0, 0, + vtr::strdup("short"), SwitchType::SHORT); + fill_switch(&switch_array[T2(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; + SwitchType type; + int id = 2; + for (const auto& value : seen) { + 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]; + pips_models_.emplace_back(std::make_tuple(value, 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; + mux_trans_size = buffered ? 1 : 0; + + fill_switch(&switch_array[T2(id)], R, Cin, Cout, Cint, Tdel, + mux_trans_size, 0, vtr::strdup(switch_name.c_str()), type); + + id++; + } +} diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 1069c78d7a1..ace82ab7e79 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -37,11 +37,6 @@ using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; -// Necessary to reduce code verbosity when getting the pin directions -static const auto INPUT = LogicalNetlist::Netlist::Direction::INPUT; -static const auto OUTPUT = LogicalNetlist::Netlist::Direction::OUTPUT; -static const auto INOUT = LogicalNetlist::Netlist::Direction::INOUT; - static const auto LOGIC = Device::BELCategory::LOGIC; static const auto ROUTING = Device::BELCategory::ROUTING; static const auto SITE_PORT = Device::BELCategory::SITE_PORT; @@ -59,16 +54,6 @@ struct t_package_pin { std::string bel_name; }; -struct t_bel_cell_mapping { - size_t cell; - size_t site; - std::vector> pins; - - bool operator<(const t_bel_cell_mapping& other) const { - return cell < other.cell || (cell == other.cell && site < other.site); - } -}; - // Intermediate data type to store information on interconnects to be created struct t_ic_data { std::string input; @@ -232,7 +217,8 @@ struct ArchReader { // Preprocess arch information process_luts(); process_package_pins(); - process_cell_bel_mappings(); + auto func = std::bind(&ArchReader::str, this, std::placeholders::_1); + process_cell_bel_mappings(ar_, bel_cell_mappings_, func); process_constants(); process_bels_and_sites(); @@ -783,64 +769,6 @@ struct ArchReader { } } - void process_cell_bel_mappings() { - 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()) { - std::vector> pins; - - for (auto pin_map : common_pins.getPins()) - pins.emplace_back(pin_map.getCellPin(), pin_map.getBelPin()); - - for (auto site_type_entry : common_pins.getSiteTypes()) { - size_t site_type = site_type_entry.getSiteType(); - - for (auto bel : site_type_entry.getBels()) { - t_bel_cell_mapping mapping; - - mapping.cell = cell_name; - mapping.site = site_type; - mapping.pins = pins; - - std::set maps{mapping}; - auto res = bel_cell_mappings_.emplace(bel, maps); - if (!res.second) { - res.first->second.insert(mapping); - } - } - } - } - } - } - void process_constants() { auto consts = ar_.getConstants(); @@ -2293,101 +2221,27 @@ struct ArchReader { } void process_switches() { - std::set> pip_timing_models; - for (auto tile_type : ar_.getTileTypeList()) { - for (auto pip : tile_type.getPips()) { - pip_timing_models.insert(std::pair(pip.getBuffered21(), pip.getTiming())); - if (!pip.getDirectional()) - pip_timing_models.insert(std::pair(pip.getBuffered20(), pip.getTiming())); - } - } + std::set> seen; + pip_types(seen, ar_); - auto timing_data = ar_.getPipTimings(); + arch_->num_switches = seen.size() + 2; - std::vector> pip_timing_models_list; - pip_timing_models_list.reserve(pip_timing_models.size()); - - for (auto entry : pip_timing_models) { - pip_timing_models_list.push_back(entry); + if (arch_->num_switches > 0) { + arch_->Switches = new t_arch_switch_inf[arch_->num_switches]; } - size_t num_switches = pip_timing_models.size() + 2; - std::string switch_name; - - arch_->num_switches = num_switches; + std::vector, int>> pips_models_; + process_switches_array(ar_, seen, arch_->Switches, pips_models_); - if (num_switches > 0) { - arch_->Switches = new t_arch_switch_inf[num_switches]; - } - - float R, Cin, Cint, Cout, Tdel; - for (size_t i = 0; i < num_switches; ++i) { + for (int i = 0; i < arch_->num_switches; ++i) { t_arch_switch_inf* as = &arch_->Switches[i]; - R = Cin = Cint = Cout = Tdel = 0.0; - SwitchType type; - - if (i == 0) { - switch_name = "short"; - type = SwitchType::SHORT; - R = 0.0; - } else if (i == 1) { - switch_name = "generic"; - type = SwitchType::MUX; - R = 0.0; - } else { - auto entry = pip_timing_models_list[i - 2]; - auto model = timing_data[entry.second]; - std::stringstream name; - std::string mux_type_string = entry.first ? "mux_" : "passGate_"; - name << mux_type_string; - - // FIXME: allow to dynamically choose different speed models and corners - 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 (entry.first) { - 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() + std::to_string(i); - type = entry.first ? SwitchType::MUX : SwitchType::PASS_GATE; - } - - /* Should never happen */ - if (switch_name == std::string(VPR_DELAYLESS_SWITCH_NAME)) { - archfpga_throw(arch_file_, __LINE__, - "Switch name '%s' is a reserved name for VPR internal usage!", switch_name.c_str()); - } - - as->name = vtr::strdup(switch_name.c_str()); - as->set_type(type); - as->mux_trans_size = as->type() == SwitchType::MUX ? 1 : 0; - - as->R = R; - as->Cin = Cin; - as->Cout = Cout; - as->Cinternal = Cint; - as->set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel); - if (as->type() == SwitchType::SHORT || as->type() == SwitchType::PASS_GATE) { as->buf_size_type = BufferSize::ABSOLUTE; - as->buf_size = 0; as->power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; as->power_buffer_size = 0.; } else { as->buf_size_type = BufferSize::AUTO; - as->buf_size = 0.; as->power_buffer_type = POWER_BUFFER_TYPE_AUTO; } } diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 6935a2f729b..b15e76fedf3 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -21,10 +21,11 @@ using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; -static const auto INOUT = LogicalNetlist::Netlist::Direction::INOUT; - typedef std::pair location; +//> +typedef std::tuple> pip_; + enum node_sides { LEFT_EDGE = 0, TOP_EDGE, @@ -102,44 +103,12 @@ struct RR_Graph_Builder { wire_name_to_seg_idx_[segment_inf_[RRSegmentId(i)].name] = i; } - 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; - } - } + std::unordered_map> temp_; + auto func = std::bind(&RR_Graph_Builder::str, this, std::placeholders::_1); + process_cell_bel_mappings(ar_, temp_, func); - 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); - } - } - } - } + for (auto i : temp_) + bel_cell_mappings_.insert(i.first); } void build_rr_graph() { @@ -177,7 +146,7 @@ struct RR_Graph_Builder { std::unordered_map> loc_to_tile_; std::unordered_map, int /*idx*/, hash_tuple::hash>> pips_models_; - std::unordered_map, std::tuple>, hash_tuple::hash>> pips_; + std::unordered_map, pip_, hash_tuple::hash>> pips_; std::unordered_map, int, @@ -207,7 +176,11 @@ struct RR_Graph_Builder { * - node_id_count_ maps from node_id to its tile count */ std::vector> virtual_shorts_; - std::map, std::tuple> virtual_redirect_; + std::unordered_map, + std::tuple, + hash_tuple::hash>> + virtual_redirect_; + std::unordered_map, int, hash_tuple::hash>> @@ -229,8 +202,14 @@ 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::unordered_set, hash_tuple::hash>> used_by_pip_; - std::unordered_set, hash_tuple::hash>> used_by_pin_; + std::unordered_set, + hash_tuple::hash>> + used_by_pip_; + + std::unordered_set, + hash_tuple::hash>> + used_by_pin_; + std::unordered_set useful_node_; /* Sink_source_loc_map is used to create ink/source and ipin/opin rr_nodes, @@ -243,7 +222,10 @@ struct RR_Graph_Builder { * RR generation stuff */ vtr::vector seg_index_; - std::map, int> loc_type_idx_to_rr_idx_; + std::unordered_map, + int, + hash_tuple::hash>> + loc_type_idx_to_rr_idx_; int rr_idx = 0; // Do not decrement! location add_vec(location x, location dx) { @@ -372,65 +354,14 @@ struct RR_Graph_Builder { */ void create_switch_inf() { std::set> seen; - for (const auto& tile : ar_.getTileTypeList()) { - for (const auto& pip : tile.getPips()) { - seen.emplace(pip.getTiming(), pip.getBuffered21()); - if (!pip.getDirectional()) { - seen.emplace(pip.getTiming(), pip.getBuffered20()); - } - } - } - device_ctx_.rr_switch_inf.reserve(seen.size() + 2); - int id = 2; - make_room_in_vector(&device_ctx_.rr_switch_inf, 0); - fill_switch(device_ctx_.rr_switch_inf[RRSwitchId(0)], 0, 0, 0, 0, 0, 0, 0, - vtr::strdup("short"), SwitchType::SHORT); - make_room_in_vector(&device_ctx_.rr_switch_inf, 1); - fill_switch(device_ctx_.rr_switch_inf[RRSwitchId(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; - SwitchType type; - 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]; - 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; - } + pip_types(seen, ar_); - Tdel = get_corner_value(model.getInternalDelay(), "slow", "min"); - name << "Tdel" << std::scientific << Tdel; + device_ctx_.rr_switch_inf.resize(seen.size() + 2); - switch_name = name.str(); - type = buffered ? SwitchType::MUX : SwitchType::PASS_GATE; - mux_trans_size = buffered ? 1 : 0; - - fill_switch(device_ctx_.rr_switch_inf[RRSwitchId(id)], R, Cin, Cout, Cint, Tdel, - mux_trans_size, 0, vtr::strdup(switch_name.c_str()), type); - - id++; + std::vector, int>> temp_; + process_switches_array(ar_, seen, device_ctx_.rr_switch_inf.data(), temp_); + for (auto i : temp_) { + pips_models_[std::get<0>(i)] = std::get<1>(i); } } @@ -439,6 +370,8 @@ struct RR_Graph_Builder { */ void create_tile_id_to_loc() { int max_height = 0; + tile_to_loc_.reserve(ar_.getTileList().size() + 1); + loc_to_tile_.reserve(ar_.getTileList().size() + 1); for (const auto& tile : ar_.getTileList()) { tile_to_loc_[tile.getName()] = location(tile.getCol() + 1, tile.getRow() + 1); max_height = std::max(max_height, (int)(tile.getRow() + 1)); @@ -534,19 +467,6 @@ struct RR_Graph_Builder { } node_to_RC_[node_id].first += resistance; node_to_RC_[node_id].second += capacitance; -#ifdef DEBUG - if (constant_node) { - const auto& wires = ar_.getWires(); - std::tuple base_wire_(wires[node.getWires()[0]].getTile(), wires[node.getWires()[0]].getWire()); - VTR_LOG("Constant_node: %s\n", wire_to_node_[base_wire_] == 1 ? "VCC\0" : "GND\0"); - for (const auto& wire_id_ : node.getWires()) { - const auto& wire = ar_.getWires()[wire_id_]; - int tile_id = wire.getTile(); - int wire_id = wire.getWire(); - VTR_LOG("tile:%s wire:%s\n", str(tile_id).c_str(), str(wire_id).c_str()); - } - } -#endif for (const auto& wire_id_ : node.getWires()) { const auto& wire = ar_.getWires()[wire_id_]; int tile_id = wire.getTile(); @@ -566,6 +486,13 @@ struct RR_Graph_Builder { * also store meta data related to that pip such as: tile_name, wire names, and its direction. */ void create_pips() { + int pip_count = 0; + for (const auto& tile : ar_.getTileList()) { + const auto& tile_type = ar_.getTileTypeList()[tile.getType()]; + pip_count += tile_type.getPips().size(); + } + pips_.reserve((int)(pip_count * 1.05)); + for (const auto& tile : ar_.getTileList()) { int tile_id = tile.getName(); location loc = tile_to_loc_[tile_id]; @@ -590,7 +517,7 @@ struct RR_Graph_Builder { */ /* * FIXME - * right now we allow for pseudo pips even tho we don;t check if they are avaiable + * right now we allow for pseudo pips even tho we don't check if they are avaiable * if (pip.isPseudoCells() && (node0 > 1 && node1 > 1)) * continue; */ @@ -604,21 +531,22 @@ struct RR_Graph_Builder { if (tile.hasSubTilesPrefices()) name = tile.getSubTilesPrefices()[pip.getSubTile()]; + VTR_ASSERT(pips_models_.find(std::make_tuple(pip.getTiming(), pip.getBuffered21())) != pips_models_.end()); 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, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, pip_(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, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, pip_(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, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, pip_(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, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, pip_(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); } } } @@ -1273,7 +1201,7 @@ struct RR_Graph_Builder { } } /* - * Add constant ource + * Add constant source */ sink_source_loc_map_[-1].resize(2); for (auto i : {0, 1}) { @@ -1547,14 +1475,6 @@ 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; @@ -1584,11 +1504,11 @@ struct RR_Graph_Builder { char metadata_[100]; char* temp = int_to_string(metadata_, name); - *temp++ = ','; + *temp++ = '.'; temp = int_to_string(temp, wire0); *temp++ = ','; temp = int_to_string(temp, wire1); - *temp++ = ','; + *temp++ = '.'; temp = int_to_string(temp, forward ? 1 : 0); *temp++ = 0; diff --git a/vpr/src/route/rr_graph_fpga_interchange.h b/vpr/src/route/rr_graph_fpga_interchange.h index f89487dd5c9..e24127bc300 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.h +++ b/vpr/src/route/rr_graph_fpga_interchange.h @@ -21,10 +21,21 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, namespace hash_tuple { namespace { + +// https://nullprogram.com/blog/2018/07/31/ +inline size_t splittable64(size_t x) { + x ^= x >> 30; + x *= 0xbf58476d1ce4e5b9U; + x ^= x >> 27; + x *= 0x94d049bb133111ebU; + x ^= x >> 31; + return x; +} + template struct hash { size_t operator()(TT const& tt) const { - return std::hash()(tt); + return splittable64(std::hash()(tt)); } }; @@ -32,8 +43,8 @@ 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 = splittable64(std::hash()(p.first)); + rhs = splittable64(std::hash()(p.second)); return (lhs * lhs + 3UL * lhs + 2UL * lhs * rhs + rhs + rhs * rhs) / 2UL; } }; From 94d2164f0958a137ae3fde4c15b405de79f2272d Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Thu, 23 Dec 2021 16:16:13 +0100 Subject: [PATCH 13/38] vpr: interchange: rr graph: use only routing segments Signed-off-by: Alessandro Comodi --- .../src/read_fpga_interchange_arch.cpp | 27 +++++++++++++++---- vpr/src/route/rr_graph_fpga_interchange.cpp | 9 +++++-- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index ace82ab7e79..30385b9022e 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -174,8 +174,8 @@ void add_segment_with_default_values(t_segment_inf& seg, std::string name) { seg.name = name; seg.length = 1; seg.frequency = 1; - seg.Rmetal = 1e-12; - seg.Cmetal = 1e-12; + seg.Rmetal = 1e2; + seg.Cmetal = 1e-15; seg.parallel_axis = BOTH_AXIS; // TODO: Only bi-directional segments are created, but it the interchange format @@ -2250,12 +2250,29 @@ struct ArchReader { void process_segments() { // Segment names will be taken from wires connected to pips // They are good representation for nodes + + auto wires = ar_.getWires(); + auto wire_types = ar_.getWireTypes(); + + std::unordered_map wire_map; + for (auto wire : wires) { + auto type = wire_types[wire.getType()]; + wire_map.emplace(wire.getWire(), type.getCategory()); + } + std::set wire_names; for (auto tile_type : ar_.getTileTypeList()) { - auto wires = tile_type.getWires(); + auto tile_wires = tile_type.getWires(); + for (auto pip : tile_type.getPips()) { - wire_names.insert(wires[pip.getWire0()]); - wire_names.insert(wires[pip.getWire1()]); + auto wire0 = tile_wires[pip.getWire0()]; + auto wire1 = tile_wires[pip.getWire1()]; + + if (wire_map[wire0] == Device::WireCategory::GENERAL) + wire_names.insert(wire0); + + if (wire_map[wire1] == Device::WireCategory::GENERAL) + wire_names.insert(wire1); } } diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index b15e76fedf3..50a3f3f9028 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -99,9 +99,14 @@ struct RR_Graph_Builder { tile_type_to_pb_type_[std::string(type.name)] = &type; } - for (size_t i = 0; i < segment_inf.size(); ++i) { + auto wire_types = ar_.getWireTypes(); + for (auto wire : ar_.getWires()) + if (wire_types[wire.getType()].getCategory() != Device::WireCategory::GENERAL) + wire_name_to_seg_idx_[str(wire.getWire())] = 0; + + // segment at index 0 is the generic one + for (size_t i = 1; i < segment_inf.size(); ++i) wire_name_to_seg_idx_[segment_inf_[RRSegmentId(i)].name] = i; - } std::unordered_map> temp_; auto func = std::bind(&RR_Graph_Builder::str, this, std::placeholders::_1); From 09c771c0f6c1f3f7cdfcf28bd2bad636a485c88b Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Tue, 15 Mar 2022 16:25:17 +0100 Subject: [PATCH 14/38] interchange: rr graph: adapt to use new RR graph library Signed-off-by: Alessandro Comodi --- vpr/src/route/rr_graph_fpga_interchange.cpp | 57 ++++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 50a3f3f9028..03aef093e49 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -87,11 +87,13 @@ struct RR_Graph_Builder { RR_Graph_Builder(Device::Reader& arch_reader, const DeviceGrid& grid, DeviceContext& device_ctx, - const std::vector& physical_tile_types, + t_rr_graph_storage& rr_nodes, const vtr::vector& segment_inf, + const std::vector& physical_tile_types, const enum e_base_cost_type base_cost_type) : ar_(arch_reader) , device_ctx_(device_ctx) + , rr_nodes_(rr_nodes) , segment_inf_(segment_inf) , grid_(grid) , base_cost_type_(base_cost_type) { @@ -137,6 +139,7 @@ struct RR_Graph_Builder { private: Device::Reader& ar_; DeviceContext& device_ctx_; + t_rr_graph_storage& rr_nodes_; const vtr::vector& segment_inf_; const DeviceGrid& grid_; const enum e_base_cost_type base_cost_type_; @@ -288,7 +291,7 @@ struct RR_Graph_Builder { int it = 0; - for (auto const& switch_id : device_ctx_.rr_switch_inf) { + for (auto const& switch_id : device_ctx_.rr_graph.rr_switch()) { VTR_LOG("Switch: %d Name:%s\n", it++, switch_id.name); } @@ -361,13 +364,20 @@ struct RR_Graph_Builder { std::set> seen; pip_types(seen, ar_); - device_ctx_.rr_switch_inf.resize(seen.size() + 2); + std::vector switches; + switches.resize(seen.size() + 2); std::vector, int>> temp_; - process_switches_array(ar_, seen, device_ctx_.rr_switch_inf.data(), temp_); - for (auto i : temp_) { + process_switches_array&, int>(ar_, seen, switches, temp_); + + auto& rr_switches = device_ctx_.rr_graph_builder.rr_switch(); + rr_switches.resize(seen.size() + 2); + + for (int i = 0; i < (int)switches.size(); i++) + rr_switches[RRSwitchId(i)] = switches[i]; + + for (auto i : temp_) pips_models_[std::get<0>(i)] = std::get<1>(i); - } } /* @@ -1327,9 +1337,9 @@ struct RR_Graph_Builder { 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)); + 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]; + auto node = 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); @@ -1351,9 +1361,9 @@ struct RR_Graph_Builder { 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)); + 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]; + auto node = 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); @@ -1387,9 +1397,9 @@ struct RR_Graph_Builder { std::tie(seg, R, C, end) = track.second; std::tie(x1, y1) = end; - device_ctx_.rr_nodes.make_room_for_node(RRNodeId(rr_idx)); + rr_nodes_.make_room_for_node(RRNodeId(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]; + auto node = rr_nodes_[rr_idx]; RRNodeId node_id = node.id(); device_ctx_.rr_graph_builder.set_node_type(node_id, type); @@ -1413,11 +1423,11 @@ struct RR_Graph_Builder { } void pack_rr_nodes() { - device_ctx_.rr_nodes.clear(); + 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(); + rr_nodes_.shrink_to_fit(); } void pack_tiles_edges() { @@ -1553,8 +1563,8 @@ struct RR_Graph_Builder { 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]; + for (size_t node = 0; node < rr_nodes_.size(); node++) { + auto rr_node = rr_nodes_[node]; device_ctx_.rr_graph_builder.add_node_to_all_locs(rr_node.id()); } @@ -1595,9 +1605,11 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, auto& device_ctx = g_vpr_ctx.mutable_device(); size_t num_segments = segment_inf.size(); - device_ctx.rr_segments.reserve(num_segments); + + auto& rr_segments = device_ctx.rr_graph_builder.rr_segments(); + rr_segments.reserve(num_segments); for (long unsigned int iseg = 0; iseg < num_segments; ++iseg) { - device_ctx.rr_segments.push_back(segment_inf[(iseg)]); + rr_segments.push_back(segment_inf[(iseg)]); } // Decompress GZipped capnproto device file @@ -1636,7 +1648,14 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, 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); + RR_Graph_Builder builder( + device_reader, + grid, + device_ctx, + device_ctx.rr_graph_builder.rr_nodes(), + device_ctx.rr_graph.rr_segments(), + device_ctx.physical_tile_types, + base_cost_type); builder.build_rr_graph(); *wire_to_rr_ipin_switch = 0; device_ctx.read_rr_graph_filename.assign(get_arch_file_name()); From 9264618b1dcc683e14f654455a38ecc45bae65e4 Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Thu, 17 Mar 2022 16:55:12 +0100 Subject: [PATCH 15/38] interchange: rr graph: cleaning code and adding comments Signed-off-by: Alessandro Comodi --- vpr/src/route/rr_graph_fpga_interchange.cpp | 1179 +++++++++++-------- 1 file changed, 686 insertions(+), 493 deletions(-) diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 03aef093e49..676c823e65c 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -21,10 +21,20 @@ using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; -typedef std::pair location; +// Helper types definitions -//> -typedef std::tuple> pip_; +// t_location in the grid: +typedef std::pair t_location; + +// edge between two wires: > +typedef std::tuple> t_pip; + +// Helper enumerations + +enum constant { + GND = 0, + VCC, +}; enum node_sides { LEFT_EDGE = 0, @@ -33,64 +43,48 @@ enum node_sides { BOTTOM_EDGE, }; -auto NODESIDES = {LEFT_EDGE, TOP_EDGE, RIGHT_EDGE, BOTTOM_EDGE}; -enum node_sides OPPOSITE_SIDE[] = {RIGHT_EDGE, BOTTOM_EDGE, LEFT_EDGE, TOP_EDGE}; -enum constant { - GND = 0, - VCC, -}; +const std::vector NODE_SIDES = {LEFT_EDGE, TOP_EDGE, RIGHT_EDGE, BOTTOM_EDGE}; +const std::vector OPPOSITE_NODE_SIDES = {RIGHT_EDGE, BOTTOM_EDGE, LEFT_EDGE, TOP_EDGE}; -/* - * Intermediate data type. - * It is used to build and explore FPGA Interchange - * nodes graph and later convert them to rr_nodes. +/** @brief Intermediate data type used to build and explore FPGA Interchange + * nodes graph and later convert them to VTR RR Nodes. */ struct intermediate_node { public: - location loc; - int segment_id; - std::vector links; //left, up, right, down - intermediate_node* visited; - bool back_bone; - bool on_route; - bool has_pins; + t_location loc; + int segment_id = -1; + std::vector links{false, false, false, false}; // left, up, right, down + intermediate_node* visited = nullptr; + bool back_bone = false; // Indicates whether the node is in the connecting line between two ends + bool on_route = false; + bool has_pins = false; + intermediate_node() = delete; + intermediate_node(int x, int y) - : loc{x, y} { - links.resize(4); - for (auto i : NODESIDES) - 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; - } + : loc{x, y} {} + + intermediate_node(t_location loc_) + : loc(loc_) {} }; -/* - * Main class for FPGA Interchange device to rr_graph flow +/** @brief Interchange RR Graph builder class + * + * This class contains methods and attributes to translate from the Interchange + * RR graph collection of nodes and wires to the VTR RR Graph representation. + * + * First it builds an intermediate representation, which is necessary to store all the + * information in a convenient way to use the VTR RR Graph builder API. */ -struct RR_Graph_Builder { +struct InterchangeRRGraphBuilder { public: - RR_Graph_Builder(Device::Reader& arch_reader, - const DeviceGrid& grid, - DeviceContext& device_ctx, - t_rr_graph_storage& rr_nodes, - const vtr::vector& segment_inf, - const std::vector& physical_tile_types, - const enum e_base_cost_type base_cost_type) + InterchangeRRGraphBuilder(Device::Reader& arch_reader, + const DeviceGrid& grid, + DeviceContext& device_ctx, + t_rr_graph_storage& rr_nodes, + const vtr::vector& segment_inf, + const std::vector& physical_tile_types, + const enum e_base_cost_type base_cost_type) : ar_(arch_reader) , device_ctx_(device_ctx) , rr_nodes_(rr_nodes) @@ -111,7 +105,7 @@ struct RR_Graph_Builder { wire_name_to_seg_idx_[segment_inf_[RRSegmentId(i)].name] = i; std::unordered_map> temp_; - auto func = std::bind(&RR_Graph_Builder::str, this, std::placeholders::_1); + auto func = std::bind(&InterchangeRRGraphBuilder::str, this, std::placeholders::_1); process_cell_bel_mappings(ar_, temp_, func); for (auto i : temp_) @@ -126,12 +120,12 @@ struct RR_Graph_Builder { create_pips(); process_nodes(); #ifdef DEBUG - print_virtual(); + print_virtual_rr_graph(); #endif virtual_to_real(); pack_to_rr_graph(); #ifdef DEBUG - print(); + print_real_rr_graph(); #endif finish_rr_generation(); } @@ -150,72 +144,68 @@ struct RR_Graph_Builder { std::set bel_cell_mappings_; - std::unordered_map tile_to_loc_; - std::unordered_map> loc_to_tile_; + // Tile location maps + vtr::bimap tile_loc_bimap_; + // PIP maps std::unordered_map, int /*idx*/, hash_tuple::hash>> pips_models_; - std::unordered_map, pip_, hash_tuple::hash>> pips_; + std::unordered_map, t_pip, hash_tuple::hash>> pips_; - std::unordered_map, - int, - hash_tuple::hash>> - wire_to_node_; - std::unordered_map> node_to_locs_; - std::unordered_map /**/, - int /*segment type*/, - hash_tuple::hash>> - node_tile_to_segment_; + // Node maps + std::unordered_map, int, hash_tuple::hash>> wire_to_node_; + std::unordered_map> node_to_locs_; + std::unordered_map /**/, int /*segment type*/, hash_tuple::hash>> node_tile_to_segment_; std::unordered_map> node_to_RC_; int total_node_count_; /* * Offsets for FPGA Interchange node processing */ - location offsets[4] = {location(-1, 0), location(0, 1), location(1, 0), location(0, -1)}; + t_location offsets[4] = {t_location(-1, 0), t_location(0, 1), t_location(1, 0), t_location(0, -1)}; /* * Intermediate data storage. - * - 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. + * - virtual_shorts_ represents connections between rr_nodes in a single FPGA Interchange node. + * - virtual_redirect_ is map from node_id at t_location to t_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. - * - virtual_chan_loc_map maps from location and CHAN to vector containing virtual_tracks description. + * - virtual_chan_loc_map maps from t_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. + * - chan_loc_map_ maps from t_location and CHAN to vector containing tracks description. * - node_id_count_ maps from node_id to its tile count */ - std::vector> virtual_shorts_; - std::unordered_map, - std::tuple, - hash_tuple::hash>> + std::vector> virtual_shorts_; + std::unordered_map, + std::tuple, + hash_tuple::hash>> virtual_redirect_; - std::unordered_map, + std::unordered_map, int, - hash_tuple::hash>> + hash_tuple::hash>> virtual_beg_to_real_; - std::unordered_map, - std::vector>, - hash_tuple::hash>> + std::unordered_map, + std::vector>, + hash_tuple::hash>> virtual_chan_loc_map_; - std::unordered_map, - std::unordered_map>, - hash_tuple::hash>> + 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. - * Each value correspondence to node id n being used by either pip or pin at location l. + * Sets contain tuples of node ids and t_location. + * Each value correspondence to node id n being used by either pip or pin at t_location l. */ - std::unordered_set, - hash_tuple::hash>> + std::unordered_set, + hash_tuple::hash>> used_by_pip_; - std::unordered_set, - hash_tuple::hash>> + std::unordered_set, + hash_tuple::hash>> used_by_pin_; std::unordered_set useful_node_; @@ -230,20 +220,23 @@ struct RR_Graph_Builder { * RR generation stuff */ vtr::vector seg_index_; - std::unordered_map, + std::unordered_map, int, - hash_tuple::hash>> + hash_tuple::hash>> 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); + /// @brief Returns the sum of a location and a vector, resulting in a new location + t_location add_vec(t_location x, t_location dx) { + return t_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); + /// @brief Returns the multiplication of a location and a scalar + t_location mul_vec_scal(t_location x, int s) { + return t_location(x.first * s, x.second * s); } + /// @brief Returns the string corresponding to an entry in the string list of the interchange database std::string str(int idx) { if (idx == -1) return std::string("constant_block"); @@ -251,32 +244,34 @@ struct RR_Graph_Builder { } /* - * This code iterates over each node independently when transforming + * The following code iterates over each node independently when transforming * an FPGA Interchange device resource into a VTR RR Graph. - * During the transformation, segments are created and they require - * a starting and ending coordinates as well as their track location. + * + * During the transformation, segments are created, which require + * starting and ending coordinates, as well as their track location. * At any given time only one segment can occupy a single physical track. - * Solving the track assignment with an online algorithm would yield a huge - * slow down with a suboptimal maximal number of tracks in a single channel. + * Solving the track assignment with an online algorithm would greatly impact + * on runtime, achieving a suboptimal maximal number of tracks in a single channel. + * * This is solved by only storing a starting and ending location and * the track number in the starting position, thus it is named "virtual". * Then, when all FPGA Interchange nodes are taken care of, * the physical track placement is computed. + * * Much of the code runs on virtual assignment, as it's fast to create, - * use and is only translated when creating the final rr_graph. + * use and is only translated when creating the final RR Graph. */ - /* - * Debug print function + /** @brief Debug print function to output the virtual RR graph information */ - void print_virtual() { + void print_virtual_rr_graph() { VTR_LOG("Virtual\n"); 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()); + std::tie(x, y) = tile_loc_bimap_[key]; + VTR_LOG("t_location x:%d y:%d Name:%s\n", x, y, str(key).c_str()); int it = 0; for (const auto& pin : value) { bool dir; @@ -296,7 +291,7 @@ struct RR_Graph_Builder { } for (auto& entry : virtual_chan_loc_map_) { - location loc, loc2; + t_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); @@ -309,10 +304,10 @@ struct RR_Graph_Builder { VTR_LOG("Redirects:\n"); for (auto& entry : virtual_redirect_) { int node; - location loc; + t_location loc; std::tie(node, loc) = entry.first; - location new_loc; + t_location new_loc; e_rr_type new_type; int new_idx; std::tie(new_loc, new_type, new_idx) = entry.second; @@ -322,7 +317,7 @@ struct RR_Graph_Builder { VTR_LOG("Shorts:\n"); for (auto& entry : virtual_shorts_) { - location loc1, loc2; + t_location loc1, loc2; e_rr_type type1, type2; int id1, id2; std::tie(loc1, type1, id1, loc2, type2, id2) = entry; @@ -335,30 +330,28 @@ struct RR_Graph_Builder { } } - void print() { - VTR_LOG("Real\n"); - + void print_real_rr_graph() { + VTR_LOG("Printing real RR graph\n"); VTR_LOG("Virtual to real mapping:\n"); for (auto i : virtual_beg_to_real_) { e_rr_type type; - location loc; + t_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"); + VTR_LOG("RR Nodes\n"); for (auto i : loc_type_idx_to_rr_idx_) { - location loc; + t_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", loc.first, loc.second, 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); } } - /* - * Fill device_ctx rr_switch_inf structure and store id of each PIP type for future use + /** @brief Fill device_ctx rr_switch_inf structure and store id of each PIP type for future use */ void create_switch_inf() { std::set> seen; @@ -380,25 +373,29 @@ struct RR_Graph_Builder { pips_models_[std::get<0>(i)] = std::get<1>(i); } - /* - * Create mapping form tile_id to its location + /** @brief Creates mapping from tile_id to its location */ void create_tile_id_to_loc() { int max_height = 0; - tile_to_loc_.reserve(ar_.getTileList().size() + 1); - loc_to_tile_.reserve(ar_.getTileList().size() + 1); for (const auto& tile : ar_.getTileList()) { - tile_to_loc_[tile.getName()] = location(tile.getCol() + 1, tile.getRow() + 1); + tile_loc_bimap_.insert(tile.getName(), t_location(tile.getCol() + 1, tile.getRow() + 1)); max_height = std::max(max_height, (int)(tile.getRow() + 1)); - loc_to_tile_[location(tile.getCol() + 1, tile.getRow() + 1)] = tile.getName(); } - /* tile with name -1 is assosiated with constant source tile */ - tile_to_loc_[-1] = location(1, max_height + 1); - loc_to_tile_[location(1, max_height + 1)] = -1; + /* tile with id name -1 is assosiated with constant source tile */ + tile_loc_bimap_.insert(-1, t_location(1, max_height + 1)); } - /* - * Helper function for create_uniq_node_ids. It process constant sources into single node. + void fill_node_mappings(int tile_id, int wire_id, int node_id) { + wire_to_node_[std::make_tuple(tile_id, wire_id)] = node_id; + node_to_locs_[node_id].insert(tile_loc_bimap_[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(node_id, tile_id)] = wire_name_to_seg_idx_[str(wire_id)]; + } + + /** @brief Helper function for create_uniq_node_ids. It process constant sources into single node. */ void process_const_nodes() { for (const auto& tile : ar_.getTileList()) { @@ -407,110 +404,109 @@ struct RR_Graph_Builder { auto wires = tile_type.getWires(); for (const auto& constant : tile_type.getConstants()) { int const_id = constant.getConstant() == Device::ConstantType::VCC ? VCC : GND; - for (const auto wire_id : constant.getWires()) { - wire_to_node_[std::make_tuple(tile_id, wires[wire_id])] = const_id; - node_to_locs_[const_id].insert(tile_to_loc_[tile_id]); - if (wire_name_to_seg_idx_.find(str(wires[wire_id])) == wire_name_to_seg_idx_.end()) - wire_name_to_seg_idx_[str(wires[wire_id])] = -1; - node_tile_to_segment_[std::make_tuple(const_id, tile_id)] = wire_name_to_seg_idx_[str(wires[wire_id])]; - } + + for (const auto wire_id : constant.getWires()) + fill_node_mappings(tile_id, wires[wire_id], const_id); } } + for (const auto& node_source : ar_.getConstants().getNodeSources()) { int tile_id = node_source.getTile(); int wire_id = node_source.getWire(); int const_id = node_source.getConstant() == Device::ConstantType::VCC ? VCC : GND; - wire_to_node_[std::make_tuple(tile_id, wire_id)] = const_id; - node_to_locs_[const_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(const_id, tile_id)] = wire_name_to_seg_idx_[str(wire_id)]; + fill_node_mappings(tile_id, wire_id, const_id); } - for (auto const i : {GND, VCC}) { - wire_to_node_[std::make_tuple(-1, i)] = i; - node_to_locs_[i].insert(tile_to_loc_[-1]); - node_tile_to_segment_[std::make_tuple(i, -1)] = -1; + + for (auto const const_node : {GND, VCC}) { + wire_to_node_[std::make_tuple(-1, const_node)] = const_node; + node_to_locs_[const_node].insert(tile_loc_bimap_[-1]); + node_tile_to_segment_[std::make_tuple(const_node, -1)] = -1; } } - /* - * 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) + /** @brief Create unique IDs for each FPGA Interchange node. + * + * In addition it creates mappings from wire to node ID and from node ID to its locations. + * These ids are used later in the RR graph generation for site pins and pip conections (rr_edges) */ void create_uniq_node_ids() { process_const_nodes(); // Process nodes int id = 2; // ids 0 and 1 are reserved respectively for GND and VCC constants - bool constant_node; - int node_id; for (const auto& node : ar_.getNodes()) { - constant_node = false; + bool is_constant_node = false; + int node_id = OPEN; + // Nodes connected to constant sources should be combined into single constant node for (const auto& wire_id_ : node.getWires()) { const auto& wire = ar_.getWires()[wire_id_]; int tile_id = wire.getTile(); int wire_id = wire.getWire(); if (wire_to_node_.find(std::make_tuple(tile_id, wire_id)) != wire_to_node_.end() && wire_to_node_[std::make_tuple(tile_id, wire_id)] < 2) { - /* - * At least one of node wires is marked as constant source, that make whole node constant source. - * Give this node id equal to const source type. - */ - constant_node = true; + // At least one of node wires is marked as constant source, that make whole node constant source. + // Give this node id equal to const source type. + is_constant_node = true; node_id = wire_to_node_[std::make_tuple(tile_id, wire_id)]; } } - //If node is not part of constant network give it uniq id. - if (!constant_node) { + // If the node is not part of constant network give it unique ID + if (!is_constant_node) node_id = id++; - } + + VTR_ASSERT(node_id != OPEN); + float capacitance = 0.0, resistance = 0.0; if (node_to_RC_.find(node_id) == node_to_RC_.end()) { node_to_RC_[node_id] = std::pair(0, 0); - // Some random data + // TODO: these values are only temporarily hard-coded in case there is no timing information. + // Need to handle this better. capacitance = 0.000000001; resistance = 5.7; } - // Check if device has timing model. If it does, use it + // Use timing model if present if (ar_.hasNodeTimings()) { auto model = ar_.getNodeTimings()[node.getNodeTiming()]; capacitance = get_corner_value(model.getCapacitance(), "slow", "typ"); resistance = get_corner_value(model.getResistance(), "slow", "typ"); } + node_to_RC_[node_id].first += resistance; node_to_RC_[node_id].second += capacitance; + 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)] = node_id; - node_to_locs_[node_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(node_id, tile_id)] = wire_name_to_seg_idx_[str(wire_id)]; + + fill_node_mappings(tile_id, wire_id, node_id); } } total_node_count_ = 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. + /** @brief Creates an entry for each pip in the FPGA device architecture, + * + * Additionally, it stores the following metadata for later physical netlist writing: + * - tile name + * - wire names + * - direction */ void create_pips() { int pip_count = 0; + for (const auto& tile : ar_.getTileList()) { const auto& tile_type = ar_.getTileTypeList()[tile.getType()]; pip_count += tile_type.getPips().size(); } + pips_.reserve((int)(pip_count * 1.05)); for (const auto& tile : ar_.getTileList()) { int tile_id = tile.getName(); - location loc = tile_to_loc_[tile_id]; + t_location loc = tile_loc_bimap_[tile_id]; const auto& tile_type = ar_.getTileTypeList()[tile.getType()]; for (const auto& pip : tile_type.getPips()) { @@ -520,22 +516,19 @@ struct RR_Graph_Builder { wire0_name = tile_type.getWires()[pip.getWire0()]; wire1_name = tile_type.getWires()[pip.getWire1()]; + 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(tile_id, wire1_name)) == wire_to_node_.end()) continue; + node0 = wire_to_node_[std::make_tuple(tile_id, wire0_name)]; node1 = wire_to_node_[std::make_tuple(tile_id, wire1_name)]; - /* - * Allow for pseudopips that connect from/to VCC/GND. - * Temporary workaround for nexus family. - */ - /* - * FIXME - * right now we allow for pseudo pips even tho we don't check if they are avaiable - * if (pip.isPseudoCells() && (node0 > 1 && node1 > 1)) - * continue; - */ + + // FIXME: Right now we allow for pseudo pips even tho we don't check if they are avaiable + //if (pip.isPseudoCells() && (node0 > 1 && node1 > 1)) + // continue; + used_by_pip_.emplace(node0, loc); used_by_pip_.emplace(node1, loc); @@ -547,46 +540,66 @@ struct RR_Graph_Builder { name = tile.getSubTilesPrefices()[pip.getSubTile()]; VTR_ASSERT(pips_models_.find(std::make_tuple(pip.getTiming(), pip.getBuffered21())) != pips_models_.end()); + 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, pip_(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); + + auto source_sink = std::make_tuple(node0, node1); + auto edge = t_pip(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true)); + pips_.emplace(source_sink, edge); + if (!pip.getBuffered21() && (pip.getDirectional() || pip.getBuffered20())) { source_sink = std::make_tuple(node1, node0); - pips_.emplace(source_sink, pip_(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, t_pip(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, pip_(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, t_pip(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, pip_(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); + pips_.emplace(source_sink, t_pip(switch_id, tile_id, std::make_tuple(name, wire0_name, wire1_name, true))); } } } } } - bool pip_uses_node_loc(int node_id, location loc) { + bool pip_uses_node_loc(int node_id, t_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) { + bool pin_uses_node_loc(int node_id, t_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 + /** @brief Builds graph of FPGA Interchange node for further computations + * + * An FPGA Interchange node graph can be interpreted as a set of wires + * that are electrically connected by non-configurable shorts. + * + * A direct translation of nodes from the Interchange Format to the VTR RR graph + * format is not possible, and some extra computation is required to build a node's + * representation suitable fro the RR graph. + * + * The node graph may be split in multiple unconnected sub-graphs, each with its own root nodes, + * and each sub-graph will need to be connected to each other, forming a single final node graph. + * + * The reason why a node graph is a forest, rather than a single tree (e.g. has multiple roots hence + * multiple unconnected sub-graphs), is that with absent information on the location of the various wires + * in the interchange format, the exact structure of the graph is not available and needs to be + * reconstructed to suite the VTR RR graph representation. + * + * All this procedure is to allow having nodes that represent complex-shaped wires, namely L-wires or U-wires. */ std::set build_node_graph(int node_id, - std::set nodes, - std::map& existing_nodes, + std::set nodes, + std::map& existing_nodes, int& seg_id) { std::set roots; do { intermediate_node* root_node = nullptr; - location key; + t_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); @@ -600,8 +613,8 @@ struct RR_Graph_Builder { 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])]; + if (node_tile_to_segment_[std::make_tuple(node_id, tile_loc_bimap_[key])] != -1) + seg_id = node_tile_to_segment_[std::make_tuple(node_id, tile_loc_bimap_[key])]; nodes.erase(key); @@ -614,17 +627,19 @@ struct RR_Graph_Builder { intermediate_node* vertex; vertex = builder.front(); builder.pop(); - location loc = vertex->loc; - for (auto i : NODESIDES) { - location other_loc = add_vec(loc, offsets[i]); + t_location loc = vertex->loc; + for (auto i : NODE_SIDES) { + t_location other_loc = add_vec(loc, offsets[i]); bool temp = false; if (existing_nodes.find(other_loc) != existing_nodes.end()) { temp = true; } else if (nodes.find(other_loc) != nodes.end()) { 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])]; + + if (node_tile_to_segment_[std::make_tuple(node_id, tile_loc_bimap_[other_loc])] != -1) + seg_id = node_tile_to_segment_[std::make_tuple(node_id, tile_loc_bimap_[other_loc])]; + nodes.erase(other_loc); existing_nodes.emplace(other_loc, new_node); builder.push(new_node); @@ -632,24 +647,29 @@ struct RR_Graph_Builder { vertex->links[i] = temp; } } + roots.insert(root_node); } while (!nodes.empty()); for (auto& node : existing_nodes) { - if (pip_uses_node_loc(node_id, node.second->loc)) { + if (pip_uses_node_loc(node_id, node.second->loc)) node.second->on_route = true; - } + if (pin_uses_node_loc(node_id, node.second->loc)) { node.second->has_pins = true; node.second->on_route = true; } } + return roots; } - /* - * Given end, node and side check if node is more to the given side than end, - * if so it updates end. It's used to create bounding boxes for FPGA Interchange nodes. + /** @brief This function is used to update the bounding box of a node graph. + * + * parameters: + * - end: node at a side's frontier + * - node: node that might replace the end node + * - side: side where to check the bounding box expansion */ intermediate_node* update_end(intermediate_node* end, intermediate_node* node, enum node_sides side) { intermediate_node* res = end; @@ -677,102 +697,166 @@ struct RR_Graph_Builder { VTR_ASSERT(false); break; } + return res; } - /* - * Given start, end, existing nodes and connections (list of sides that line uses) - * it creates straight line connections from start to end. - * If there are missing nodes on the way, it will create new nodes and store them in - * existing nodes. + /** @brief Creates a line of nodes connecting two points. + * + * parameters: + * - existing_nodes: mapping of nodes at locations + * - start: starting location + * - end: ending location + * - connections: sides that are used for this line + * + * If intermediate nodes are missing, they will be created. + * + * TODO: consider using segments of length > 1 and create one node only + * instead of multiple nodes on a straight line. */ - - void add_line(std::map& existing_nodes, - location start, - location end, + void add_line(std::map& existing_nodes, + t_location start, + t_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; - } + bool horizontal = start.second == end.second; + VTR_ASSERT(horizontal || start.first == end.first); + + int range_start = horizontal ? start.first + 1 : start.second + 1; + int range_end = horizontal ? end.first : end.second; + 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; + t_location loc = horizontal ? t_location(i, end.second) : t_location(end.first, i); + + if (existing_nodes.find(loc) == existing_nodes.end()) + existing_nodes[loc] = new intermediate_node(loc); + + intermediate_node* loc_ = existing_nodes[loc]; + loc_->back_bone = true; + for (auto j : connections) - temp_->links[j] = true; + loc_->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; + /** @brief Adds a middle node to allow connecting end nodes that would form a non-straight + * line of nodes. + * + * +----------+ + * | | +--------+ + * | End Node +----------+ Middle | + * | | | Node | + * +----------+ +---+----+ + * | + * | + * | + * +----+-----+ + * | | + * | End Node | + * | | + * +----------+ + */ + void add_middle_node(std::map& existing_nodes, + intermediate_node* ends[], + t_location node_loc, + bool is_up, + enum node_sides node_side, + enum node_sides end_side) { + if (existing_nodes.find(node_loc) == existing_nodes.end()) + existing_nodes[node_loc] = new intermediate_node(node_loc); + + intermediate_node* node = existing_nodes[node_loc]; + node->back_bone = true; + + if (node != ends[node_side]) { + node->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}); + + if (node->loc.first < ends[node_side]->loc.first) + add_line(existing_nodes, node->loc, ends[node_side]->loc, {RIGHT_EDGE, LEFT_EDGE}); else - add_line(existing_nodes, ends[node_side]->loc, temp->loc, {RIGHT_EDGE, LEFT_EDGE}); + add_line(existing_nodes, ends[node_side]->loc, node->loc, {RIGHT_EDGE, LEFT_EDGE}); } - if (up && temp->loc.second != ends[TOP_EDGE]->loc.second) { - temp->links[TOP_EDGE] = true; + + if (is_up && node->loc.second != ends[TOP_EDGE]->loc.second) { + node->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; + add_line(existing_nodes, node->loc, ends[TOP_EDGE]->loc, {TOP_EDGE, BOTTOM_EDGE}); + } else if (!is_up && node->loc.second != ends[BOTTOM_EDGE]->loc.second) { + node->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}); + add_line(existing_nodes, ends[BOTTOM_EDGE]->loc, node->loc, {TOP_EDGE, BOTTOM_EDGE}); } } - intermediate_node* create_final_root_node(std::map& existing_nodes, + /** @brief Creates the final root node that will be used to represent an Interchange node graph + * + * Given that there may be different root nodes for the same node graph, we need to elect one to + * be the new final root, so that we can than build the RR graph connections between all the + * generated nodes reliably. + */ + intermediate_node* create_final_root_node(std::map& existing_nodes, bool left_node, - location root_node_, - location up_node_, - location comb_node_, + t_location root_node, + t_location up_node, + t_location middle_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_]; + if (existing_nodes.find(root_node) == existing_nodes.end()) + existing_nodes[root_node] = new intermediate_node(root_node); + + intermediate_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}); - } + + add_line(existing_nodes, up_node, root_node, {TOP_EDGE, BOTTOM_EDGE}); + + existing_nodes[up_node]->links[TOP_EDGE] = true; + existing_nodes[middle_node]->links[OPPOSITE_NODE_SIDES[side]] = true; + + if (left_node) + add_line(existing_nodes, root_node, middle_node, {RIGHT_EDGE, LEFT_EDGE}); + else + add_line(existing_nodes, middle_node, root_node, {RIGHT_EDGE, LEFT_EDGE}); + return temp; } + /** @brief Connect all the remaining root nodes that are not yet connected to the corresponding + * node graph. + * + * This is to connect all the nodes that may be in the current situation: + * + * +---------+ + * | | + * | Node | + * | | + * +----+----+ + * | + * | +-------------+ + * | | Dangling | + * +----------+ | + * | | Root Node | + * | +-------------+ + * | + * +---------+ +----+----+ + * | | | Final | + * | Node +------------+ | + * | | | Root | + * +---------+ +----+----+ + * | + * | + * | + * | + * | + * +----+----+ + * | | + * | Node | + * | | + * +---------+ + */ void connect_dangling_roots(std::set& roots, - std::map& existing_nodes, + std::map& existing_nodes, intermediate_node* ends[]) { /* connect not yet connected roots */ std::vector del_list; @@ -783,95 +867,108 @@ struct RR_Graph_Builder { int max_length = std::max(x_, y_); - for (const auto& i : roots) { - bool done = i->back_bone; + for (const auto& root : roots) { + bool done = root->back_bone; for (int j = 1; j < max_length; j++) { if (done) { - del_list.push_back(i); + del_list.push_back(root); break; } - for (const auto& k : NODESIDES) { - location offset = mul_vec_scal(offsets[k], j); - location other_node_loc = add_vec(i->loc, offset); + + for (const auto& k : NODE_SIDES) { + t_location offset = mul_vec_scal(offsets[k], j); + t_location other_node_loc = add_vec(root->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; + root->links[k] = true; + root->back_bone = true; + other_node->links[OPPOSITE_NODE_SIDES[k]] = true; + if (0 < k && k < 3) - add_line(existing_nodes, i->loc, other_node_loc, {k, OPPOSITE_SIDE[k]}); + add_line(existing_nodes, root->loc, other_node_loc, {k, OPPOSITE_NODE_SIDES[k]}); else - add_line(existing_nodes, other_node_loc, i->loc, {k, OPPOSITE_SIDE[k]}); + add_line(existing_nodes, other_node_loc, root->loc, {k, OPPOSITE_NODE_SIDES[k]}); + done = true; break; } } } - /* remove already done roots */ + // Remove roots that have already been processed for (const auto& i : del_list) { if (roots.find(i) != roots.end()) roots.erase(i); } + del_list.clear(); } + /** @brief Connects to unconnected graphs into a single node graph. + * + * If the node graph is composed of multiple unconnected trees, this procedure will connect all + * of the various roots of the sub-trees together, so that a single node graph is present. + */ intermediate_node* connect_roots(std::set& roots, - std::map& existing_nodes) { + std::map& existing_nodes) { + // The node graph is fully connected, therefore no extra computation is required 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 root : roots) + for (auto const& side : NODE_SIDES) + ends[side] = update_end(ends[side], root, side); - for (auto const& i : NODESIDES) { - ends[i]->back_bone = true; - if (roots.find(ends[i]) != roots.end()) - roots.erase(ends[i]); + for (auto const& side : NODE_SIDES) { + ends[side]->back_bone = true; + if (roots.find(ends[side]) != roots.end()) + roots.erase(ends[side]); } 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); + t_location right_middle_node(right_to_top ? ends[TOP_EDGE]->loc.first : ends[BOTTOM_EDGE]->loc.first, ends[RIGHT_EDGE]->loc.second); + t_location left_middle_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); + add_middle_node(existing_nodes, ends, right_middle_node, right_to_top, RIGHT_EDGE, LEFT_EDGE); + add_middle_node(existing_nodes, ends, left_middle_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); + int y = std::max(right_middle_node.second, left_middle_node.second); + t_location loc1(left_middle_node.first, y); + t_location loc2(right_middle_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); + // The final root node is chosen among the possible different ones, depending on the various intermediate nodes + // locations. + if (loc1 != left_middle_node && loc1 != loc2) { + true_root = create_final_root_node(existing_nodes, true, loc1, left_middle_node, right_middle_node, RIGHT_EDGE); + } else if (loc2 != right_middle_node && loc1 != loc2) { + true_root = create_final_root_node(existing_nodes, false, loc2, right_middle_node, left_middle_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) { + loc1 = right_middle_node; + loc2 = left_middle_node; + + if (right_middle_node.second > left_middle_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; @@ -884,28 +981,30 @@ struct RR_Graph_Builder { 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. + /** @brief 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(intermediate_node* root, - std::map& existing_nodes, + std::map& existing_nodes, int seg_id) { int cnt = 0; std::queue walker; std::stack back_walker; + walker.push(root); root->visited = root; - bool has_chanx; - bool single_node; + while (!walker.empty()) { intermediate_node* vertex = walker.front(); walker.pop(); back_walker.push(vertex); vertex->segment_id = seg_id; - single_node = true; - has_chanx = false; - for (auto i : NODESIDES) { + + bool single_node = true; + bool has_chanx = false; + + for (auto i : NODE_SIDES) { if (vertex->links[i]) { single_node = false; intermediate_node* other_node = existing_nodes[add_vec(vertex->loc, offsets[i])]; @@ -916,6 +1015,7 @@ struct RR_Graph_Builder { } else if (i == node_sides::TOP_EDGE) { cnt++; } + if (other_node->visited == nullptr) { other_node->visited = vertex; walker.push(other_node); @@ -925,26 +1025,28 @@ struct RR_Graph_Builder { } } } + 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; + + bool single_node = true; + if (!vertex->has_pins && !vertex->on_route) { - for (auto i : NODESIDES) { - if (vertex->links[i]) { + for (auto i : NODE_SIDES) + if (vertex->links[i]) if (i == node_sides::LEFT_EDGE || i == node_sides::TOP_EDGE) cnt--; - } - } + existing_nodes.erase(vertex->loc); delete vertex; } else { vertex->visited->on_route = true; - for (auto i : NODESIDES) { + for (auto i : NODE_SIDES) { if (vertex->links[i]) { if (existing_nodes.find(add_vec(vertex->loc, offsets[i])) == existing_nodes.end()) { vertex->links[i] = false; @@ -953,143 +1055,191 @@ struct RR_Graph_Builder { } } } - for (auto i : NODESIDES) + + for (auto i : NODE_SIDES) single_node &= !vertex->links[i]; + if (!vertex->has_pins && single_node) cnt++; } } + return cnt; } + /** @brief Processes the set of nodes to populate the virtual CHAN loation maps + */ void process_set(std::unordered_set& set, - std::unordered_set>& nodes_used, - std::map& existing_nodes, - std::map>& local_redirect, + std::unordered_set>& nodes_used, + std::map& existing_nodes, + std::map>& local_redirect, float R, float C, - location offset, + t_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; + for (auto const inode : set) { + int len = 0; + intermediate_node* start = inode; + intermediate_node* end = inode; auto key = std::make_tuple(add_vec(start->loc, offset), chan_type); - idx = virtual_chan_loc_map_[key].size(); + int idx = virtual_chan_loc_map_[key].size(); do { nodes_used.insert(end->loc); len++; local_redirect.emplace(end->loc, std::make_tuple(add_vec(start->loc, offset), chan_type, idx)); + if (!end->links[side]) + // Reached the end of this set of nodes exiting the loop 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)); } } - void add_short(location n1, - location n2, - std::map>& r1, - std::map>& r2) { + /** @brief Adds a short between two node locations between two nodes existing in the + * same Interchange node graph. This populates the virtual shorts map. + */ + void add_short(t_location n1, + t_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)); } + /** @brief Adds connections between nodes belonging to the same Interchange node graph + */ void connect_base_on_redirects(std::unordered_set& set, node_sides side, - std::map>& src, - std::map>& dist) { + 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]); + + t_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); } } + /** @brief Translates the Interchange node graph representation in a format suitable + * to be translated into the VTR RR graph representation. + * + * In practice this means populating the mappings to better access data in the + * real RR graph building stage. + */ void graph_reduction_stage2(int node_id, - std::map& existing_nodes, + std::map& existing_nodes, float R, float C) { std::unordered_set chanxs, chanys; - std::unordered_set> nodes_in_chanxs, nodes_in_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])]; + std::unordered_set> nodes_in_chanxs, nodes_in_chanys; + + // Populate sets containing starting point where horizontal (CHANX) or vertical (CHANY) lines of + // nodes are linked together. + // + // In the following there will be four sets of nodes: + // - three sets for CHANY to build the three veritcal sets of nodes + // - one set for CHANX to connect together all the bottom nodes + // + // +---+ +---+ +---+ + // | | | | | | + // +-+-+ +-+-+ +-+-+ + // | | | + // | | | + // | | | + // | | | + // +-+-+ +-+-+ +-+-+ + // | +--------------------+ +--------------------+ | + // +---+ +---+ +---+ + // + for (auto const& node : existing_nodes) { + bool single_node = true; + for (auto side : NODE_SIDES) + single_node &= !node.second->links[side]; + + bool chanx_start = false; + bool chany_start = false; + + if (node.second->links[LEFT_EDGE]) { + intermediate_node* left_node = existing_nodes[add_vec(node.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 (i.second->links[RIGHT_EDGE]) - nodes_in_chanxs.insert(i.first); + chanx_start = node.second->has_pins || single_node; + if (node.second->links[RIGHT_EDGE]) + nodes_in_chanxs.insert(node.first); } - if (i.second->links[TOP_EDGE]) { - intermediate_node* top_node = existing_nodes[add_vec(i.second->loc, offsets[TOP_EDGE])]; + if (node.second->links[TOP_EDGE]) { + intermediate_node* top_node = existing_nodes[add_vec(node.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]; - } else if (i.second->links[BOTTOM_EDGE]) { - nodes_in_chanys.insert(i.first); + } else if (node.second->links[BOTTOM_EDGE]) { + nodes_in_chanys.insert(node.first); } + if (chanx_start) - chanxs.insert(i.second); + chanxs.insert(node.second); if (chany_start) - chanys.insert(i.second); + chanys.insert(node.second); } - std::map> local_redirect_x, local_redirect_y; + std::map> local_redirect_x, local_redirect_y; - process_set(chanys, nodes_in_chanys, existing_nodes, local_redirect_y, R, C, location(0, 0), BOTTOM_EDGE, CHANY); + process_set(chanys, nodes_in_chanys, existing_nodes, local_redirect_y, R, C, t_location(0, 0), BOTTOM_EDGE, CHANY); process_set(chanxs, nodes_in_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_x); - bool ry, rx; + // Connect CHANY with CHANX nodes for (auto i : nodes_in_chanys) { - location node = i; + t_location node = i; if (nodes_in_chanxs.find(i) != nodes_in_chanxs.end()) { - ry = local_redirect_y.find(node) != local_redirect_y.end(); - rx = local_redirect_x.find(node) != local_redirect_x.end(); - location x, y; + bool ry = local_redirect_y.find(node) != local_redirect_y.end(); + bool rx = local_redirect_x.find(node) != local_redirect_x.end(); + t_location x, y; if (rx) x = node; else x = add_vec(node, offsets[RIGHT_EDGE]); + if (ry) y = node; else y = add_vec(node, offsets[BOTTOM_EDGE]); + add_short(y, x, local_redirect_y, local_redirect_x); } } 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(); + bool ry = local_redirect_y.find(node.first) != local_redirect_y.end(); + bool rx = local_redirect_x.find(node.first) != local_redirect_x.end(); + 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]); + t_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]); + t_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); @@ -1097,19 +1247,20 @@ struct RR_Graph_Builder { } } - /* - * Remove a graph represented by a root node. - * Clean up of earlier stages + /** @brief Removes a node graph represented by a root node. + * + * This step is used to clean up of earlier intermediate stages. */ - void delete_nodes(std::map& existing_nodes) { + void delete_nodes(std::map& existing_nodes) { for (auto i : existing_nodes) { delete i.second; } existing_nodes.clear(); } - /* - * Process FPGA Interchange nodes + /** @brief Process FPGA Interchange nodes. + * + * This function processes all the wires that are connected together and are part of the same node graph. */ void process_nodes() { for (int node_id = 0; node_id < total_node_count_; ++node_id) { @@ -1117,9 +1268,9 @@ struct RR_Graph_Builder { if (useful_node_.find(node_id) == useful_node_.end()) { continue; } - std::set all_possible_tiles = node_to_locs_[node_id]; + std::set all_possible_tiles = node_to_locs_[node_id]; - std::map existing_nodes; + 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); @@ -1127,11 +1278,17 @@ struct RR_Graph_Builder { VTR_ASSERT(roots.empty()); int div = reduce_graph_and_count_nodes_left(root, existing_nodes, seg_id); - if (existing_nodes.empty()) { + + if (existing_nodes.empty()) continue; - } + node_id_count_[node_id] = div; VTR_ASSERT(div > 0); + + // Given that an Interchange graph node is composed of many electrically connected wires + // where each one is a separate VTR RR graph node, the resistance and capacitance values + // need to be split among all of the different nodes, as in the end, they are all part of + // the same Interchange "node". float resistance = node_to_RC_[node_id].first / div; float capacitance = node_to_RC_[node_id].second / div; graph_reduction_stage2(node_id, existing_nodes, resistance, capacitance); @@ -1139,44 +1296,51 @@ struct RR_Graph_Builder { } } + /** @brief Finds the next suitable site index to create SINK and SOURCE nodes + */ 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; - } + for (auto bel : site_type.getBels()) + found |= bel_cell_mappings_.find(bel.getName()) != bel_cell_mappings_.end(); + if (found) break; } + return ans; } - /* - * Create site sink/source ipin/opin mappins. + /** @brief Creates site SINK/SOURCE IPIN/OPIN mappings. */ void create_sink_source_nodes() { const auto& tile_types = ar_.getTileTypeList(); std::map, std::tuple> tile_type_site_num_pin_name_model; + + // Fill tile pin to wire map 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()) + + if (!tile_type_to_pb_type_.count(str(tileType.getName()))) 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()) { + + 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()) { + 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++; @@ -1184,29 +1348,35 @@ struct RR_Graph_Builder { 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()) + + if (!tile_type_to_pb_type_.count(tile_type_name)) continue; - sink_source_loc_map_[tile_id].resize(tile_type_to_pb_type_[tile_type_name]->num_pins); + int it = next_good_site(0, tile); + + sink_source_loc_map_[tile_id].resize(tile_type_to_pb_type_[tile_type_name]->num_pins); 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)}; + + float value; + int wire_id; std::tie(value, wire_id) = tile_type_site_num_pin_name_model[key]; - if (wire_to_node_.find(std::make_tuple(tile_id, wire_id)) == wire_to_node_.end()) { + + int node_id; + if (!wire_to_node_.count(std::make_tuple(tile_id, wire_id))) node_id = -1; - } else { + else node_id = wire_to_node_[std::make_tuple(tile_id, wire_id)]; - } - location loc = tile_to_loc_[tile_id]; + t_location loc = tile_loc_bimap_[tile_id]; used_by_pin_.insert(std::make_tuple(node_id, loc)); useful_node_.insert(node_id); @@ -1215,48 +1385,54 @@ struct RR_Graph_Builder { it = next_good_site(it + 1, tile); } } - /* - * Add constant source - */ + + // Add constant sources. Tile ID "-1" corresponds to the constant synthetic tile sink_source_loc_map_[-1].resize(2); - for (auto i : {0, 1}) { - location loc = tile_to_loc_[-1]; - used_by_pin_.insert(std::make_tuple(i, loc)); - useful_node_.insert(i); - sink_source_loc_map_[-1][i] = std::make_tuple(false, 0, i); + for (auto pin_id : {0, 1}) { + t_location loc = tile_loc_bimap_[-1]; + used_by_pin_.insert(std::make_tuple(pin_id, loc)); + useful_node_.insert(pin_id); + sink_source_loc_map_[-1][pin_id] = std::make_tuple(false, 0, pin_id); } } - void sweep(location loc, + void sweep(t_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]; + auto loc_type_tuple = std::make_tuple(loc, type); + for (size_t k = 0; k < virtual_chan_loc_map_[loc_type_tuple].size(); ++k) { + auto track = virtual_chan_loc_map_[loc_type_tuple][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; + chan_loc_map_[loc_type_tuple][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(); + + virtual_chan_loc_map_[loc_type_tuple].clear(); } - /* - * 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 + /** @brief Creates the mapping from the virtual to physical tracks. + * + * It works in O(N*M + L), where N and M are the device dimensions + * and L is the number of used segments in the various CHANs */ void virtual_to_real() { device_ctx_.chan_width.x_list.resize(grid_.height(), 0); @@ -1268,12 +1444,15 @@ struct RR_Graph_Builder { 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); + sweep(t_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; @@ -1282,8 +1461,10 @@ struct RR_Graph_Builder { 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); + sweep(t_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; @@ -1297,85 +1478,96 @@ struct RR_Graph_Builder { } void pack_tiles() { - for (auto& i : sink_source_loc_map_) { - int tile_id = i.first; + for (auto& node_loc : sink_source_loc_map_) { + int tile_id = node_loc.first; int x, y; - location loc = tile_to_loc_[tile_id]; + t_location loc = tile_loc_bimap_[tile_id]; std::tie(x, y) = loc; - auto pin_vec = i.second; - for (size_t j = 0; j < pin_vec.size(); ++j) { + + auto pin_vec = node_loc.second; + for (size_t pin_id = 0; pin_id < pin_vec.size(); ++pin_id) { bool input; float RC; - int FPGA_Interchange_node_id; - std::tie(input, RC, FPGA_Interchange_node_id) = pin_vec[j]; + int interchange_node_id; + std::tie(input, RC, interchange_node_id) = pin_vec[pin_id]; + 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); - if (FPGA_Interchange_node_id != -1) { - location track_loc; + if (interchange_node_id != -1) { + t_location track_loc; e_rr_type track_type; int track_idx; - 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); + if (virtual_redirect_.find(std::make_tuple(interchange_node_id, loc)) == virtual_redirect_.end()) { + VTR_LOG("node_id:%d, loc->X:%d Y:%d\n", interchange_node_id, loc.first, loc.second); VTR_ASSERT(false); } - auto virtual_key = virtual_redirect_[std::make_tuple(FPGA_Interchange_node_id, loc)]; + auto virtual_key = virtual_redirect_[std::make_tuple(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; - location end; + t_location end; std::tie(track_seg, track_R, track_C, end) = val; track_R += R; track_C += C; - if (node_id_count_[FPGA_Interchange_node_id] == 1) + + if (node_id_count_[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); } rr_nodes_.make_room_for_node(RRNodeId(rr_idx)); - loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, j)] = rr_idx; + loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, pin_id)] = rr_idx; auto node = 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); + device_ctx_.rr_graph_builder.set_node_ptc_num(node_id, pin_id); + + 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)); + rr_idx++; } - for (size_t j = 0; j < pin_vec.size(); ++j) { + + for (size_t pin_id = 0; pin_id < pin_vec.size(); ++pin_id) { bool input; - int FPGA_Interchange_node_id; - std::tie(input, std::ignore, FPGA_Interchange_node_id) = pin_vec[j]; - if (FPGA_Interchange_node_id == -1) + int interchange_node_id; + std::tie(input, std::ignore, interchange_node_id) = pin_vec[pin_id]; + + if (interchange_node_id == -1) continue; + e_rr_type pin = input ? e_rr_type::IPIN : e_rr_type::OPIN; rr_nodes_.make_room_for_node(RRNodeId(rr_idx)); - loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, j)] = rr_idx; + loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, pin_id)] = rr_idx; auto node = 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.set_node_ptc_num(node_id, pin_id); device_ctx_.rr_graph_builder.add_node_side(node_id, e_side::BOTTOM); + + 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)); + rr_idx++; } } @@ -1383,16 +1575,17 @@ struct RR_Graph_Builder { void pack_chans() { for (auto i : chan_loc_map_) { - location loc; + t_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 (auto track : tracks) { int seg; float R, C; - location end; + t_location end; int x1, y1; std::tie(seg, R, C, end) = track.second; std::tie(x1, y1) = end; @@ -1410,11 +1603,11 @@ struct RR_Graph_Builder { 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) { + if (type == e_rr_type::CHANX) device_ctx_.rr_graph_builder.set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + seg)); - } else { + 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++; @@ -1434,15 +1627,17 @@ struct RR_Graph_Builder { for (auto& i : sink_source_loc_map_) { int tile_id = i.first; int x, y; - location loc = tile_to_loc_[tile_id]; + t_location loc = tile_loc_bimap_[tile_id]; std::tie(x, y) = loc; auto pin_vec = i.second; - for (size_t j = 0; j < pin_vec.size(); ++j) { + for (size_t ipin = 0; ipin < pin_vec.size(); ++ipin) { bool input; int node_id; - std::tie(input, std::ignore, node_id) = pin_vec[j]; + std::tie(input, std::ignore, node_id) = pin_vec[ipin]; + if (node_id == -1) continue; + 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; @@ -1451,8 +1646,8 @@ struct RR_Graph_Builder { virtual_beg_to_real_[virtual_chan_key]); int pin_id, mux_id, track_id; - 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)]; + pin_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, ipin)]; + mux_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, mux, ipin)]; track_id = loc_type_idx_to_rr_idx_[chan_key]; int sink, sink_src, src; @@ -1472,14 +1667,14 @@ struct RR_Graph_Builder { void pack_chans_edges() { for (auto& i : virtual_shorts_) { - location l1, l2; + t_location l1, l2; e_rr_type t1, t2; 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)]; - std::tuple key1(l1, t1, idx1), key2(l2, t2, idx2); + std::tuple key1(l1, t1, idx1), key2(l2, t2, idx2); int src, sink; src = loc_type_idx_to_rr_idx_[key1]; @@ -1501,7 +1696,7 @@ struct RR_Graph_Builder { int name, wire0, wire1; bool forward; std::tie(name, wire0, wire1, forward) = metadata; - location loc = tile_to_loc_[tile_id]; + t_location loc = tile_loc_bimap_[tile_id]; auto virtual_key1 = virtual_redirect_[std::make_tuple(node1, loc)]; auto virtual_key2 = virtual_redirect_[std::make_tuple(node2, loc)]; @@ -1517,6 +1712,8 @@ 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); + // TODO: find a more efficient way to pack PIP information to build the physical netlist out + // of a routed design char metadata_[100]; char* temp = int_to_string(metadata_, name); *temp++ = '.'; @@ -1539,30 +1736,26 @@ struct RR_Graph_Builder { device_ctx_.rr_graph_builder.partition_edges(); } - /* - * Fill device rr_graph informations. + /** @brief Fills the device RR graph. */ void pack_to_rr_graph() { - // 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 + /** @brief Finilizes the RR graph generation. + * + * - Builds the node lookup + * - Inits fan_ins + * - Loads indexed data */ void finish_rr_generation() { - for (t_rr_type rr_type : RR_TYPES) { - if (rr_type == CHANX) { + 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 { + else device_ctx_.rr_graph_builder.node_lookup().resize_nodes(grid_.width(), grid_.height(), rr_type, NUM_SIDES); - } - } + for (size_t node = 0; node < rr_nodes_.size(); node++) { auto rr_node = rr_nodes_[node]; device_ctx_.rr_graph_builder.add_node_to_all_locs(rr_node.id()); @@ -1570,12 +1763,12 @@ struct RR_Graph_Builder { 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`` - */ + // Creates a temporary copy to convert from vtr::vector to sitd::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_) { @@ -1588,9 +1781,9 @@ struct RR_Graph_Builder { base_cost_type_); VTR_ASSERT(device_ctx_.rr_indexed_data.size() == seg_index_.size()); - for (size_t i = 0; i < seg_index_.size(); ++i) { + + for (size_t i = 0; i < seg_index_.size(); ++i) device_ctx_.rr_indexed_data[RRIndexedDataId(i)].seg_index = seg_index_[RRIndexedDataId(i)]; - } } }; @@ -1648,7 +1841,7 @@ void build_rr_graph_fpga_interchange(const t_graph_type graph_type, auto device_reader = message_reader.getRoot(); - RR_Graph_Builder builder( + InterchangeRRGraphBuilder builder( device_reader, grid, device_ctx, From 59a0c7e1aa7f42f82c560465caa8053d5da0b68e Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Fri, 4 Feb 2022 12:18:25 +0100 Subject: [PATCH 16/38] interchange: arch: add alternative sites types Signed-off-by: Maciej Dudek --- .../src/read_fpga_interchange_arch.cpp | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 30385b9022e..f3e4a7d129f 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -393,7 +393,13 @@ struct ArchReader { * - equivalent_sites * - tile_block_pin_directs_map **/ - void fill_sub_tile(t_physical_tile_type& type, t_sub_tile& sub_tile, int num_pins, int input_count, int output_count) { + void fill_sub_tile(t_physical_tile_type& type, + t_sub_tile& sub_tile, + int num_pins, + int input_count, + int output_count, + std::unordered_map* port_name_to_sub_tile_idx = nullptr, + Device::SiteTypeInTileType::Reader* site_in_tile = nullptr) { sub_tile.num_phy_pins += num_pins; type.num_pins += num_pins; type.num_inst_pins += num_pins; @@ -417,7 +423,7 @@ struct ArchReader { vtr::bimap directs_map; - for (int npin = 0; npin < type.num_pins; npin++) { + for (int npin = 0; npin < num_pins; npin++) { t_physical_pin physical_pin(npin); t_logical_pin logical_pin(npin); @@ -425,10 +431,37 @@ struct ArchReader { } auto ltype = get_type_by_name(sub_tile.name, ltypes_); - sub_tile.equivalent_sites.push_back(ltype); type.tile_block_pin_directs_map[ltype->index][sub_tile.index] = directs_map; + sub_tile.equivalent_sites.push_back(ltype); + + if (site_in_tile) { + auto site_types = ar_.getSiteTypeList(); + auto site_type = site_types[site_in_tile->getPrimaryType()]; + auto alt_sites_pins = site_in_tile->getAltPinsToPrimaryPins(); + + for (int i = 0; i < site_type.getAltSiteTypes().size(); ++i) { + auto alt_site = site_types[site_type.getAltSiteTypes()[i]]; + if (take_sites_.count(alt_site.getName()) == 0) + continue; + + auto ltype = get_type_by_name(str(alt_site.getName()).c_str(), ltypes_); + sub_tile.equivalent_sites.push_back(ltype); + + vtr::bimap directs_map; + auto pin_map = alt_sites_pins[i]; + for (int npin = 0; npin < ltype->pb_type->num_ports; npin++) { + auto pin = site_type.getPins()[pin_map.getPins()[npin]]; + auto idx = (*port_name_to_sub_tile_idx)[str(pin.getName())]; + t_physical_pin physical_pin(idx); + t_logical_pin logical_pin(npin); + + directs_map.insert(logical_pin, physical_pin); + } + type.tile_block_pin_directs_map[ltype->index][sub_tile.index] = directs_map; + } + } // Assign FC specs int iblk_pin = 0; for (const auto& port : sub_tile.ports) { @@ -1876,8 +1909,6 @@ struct ArchReader { if (take_sites_.count(site.getName()) == 0) continue; - auto pins_to_wires = site_in_tile.getPrimaryPinsToTileWires(); - sub_tile.index = type.capacity; sub_tile.name = vtr::strdup(str(site.getName()).c_str()); sub_tile.capacity.set(type.capacity, type.capacity); @@ -1888,8 +1919,7 @@ struct ArchReader { int icount = 0; int ocount = 0; - std::unordered_map port_name_to_wire_name; - int idx = 0; + std::unordered_map port_name_to_sub_tile_idx; for (auto dir : {INPUT, OUTPUT}) { int port_idx_by_type = 0; for (auto pin : site.getPins()) { @@ -1900,9 +1930,9 @@ struct ArchReader { port.name = vtr::strdup(str(pin.getName()).c_str()); - port_name_to_wire_name[std::string(port.name)] = str(pins_to_wires[idx++]); - sub_tile.sub_tile_to_tile_pin_indices.push_back(type.num_pins + port_idx); + + port_name_to_sub_tile_idx[str(pin.getName())] = port_idx; port.index = port_idx++; port.absolute_first_pin_index = abs_first_pin_idx++; @@ -1921,9 +1951,9 @@ struct ArchReader { } auto pins_size = site.getPins().size(); - fill_sub_tile(type, sub_tile, pins_size, icount, ocount); + fill_sub_tile(type, sub_tile, pins_size, icount, ocount, &port_name_to_sub_tile_idx, &site_in_tile); - type.sub_tiles.push_back(sub_tile); + type.sub_tiles.emplace_back(sub_tile); } } From b7745fd03c25d9dca46ef8172e5476f2a611c276 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Thu, 10 Feb 2022 09:31:57 +0100 Subject: [PATCH 17/38] interchange: fix alternative sites Also fixes RR Graph generation with alternative site types Signed-off-by: Maciej Dudek --- .../src/fpga_interchange_arch_utils.cpp | 6 +- .../src/read_fpga_interchange_arch.cpp | 101 ++++++++++++------ vpr/src/route/rr_graph_fpga_interchange.cpp | 38 ++++--- 3 files changed, 96 insertions(+), 49 deletions(-) diff --git a/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp b/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp index ccc404a7172..402dd20c108 100644 --- a/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp +++ b/libs/libarchfpga/src/fpga_interchange_arch_utils.cpp @@ -135,8 +135,8 @@ void process_cell_bel_mappings(DeviceResources::Device::Reader ar_, 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(); + if (cell_name != primitive.getName()) continue; + if (str(primitive.getLib()) != std::string("primitives")) continue; bool has_inout = false; for (auto port_idx : primitive.getPorts()) { @@ -148,7 +148,7 @@ void process_cell_bel_mappings(DeviceResources::Device::Reader ar_, } } - if (is_prim && is_cell && !has_inout) { + if (!has_inout) { found_valid_prim = true; break; } diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index f3e4a7d129f..3ab2c603557 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -168,7 +168,7 @@ static t_pin_to_pin_annotation get_pack_pattern(std::string pp_name, std::string return pp; } -void add_segment_with_default_values(t_segment_inf& seg, std::string name) { +static 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; @@ -381,6 +381,25 @@ struct ArchReader { return pad_bels_.count(name) != 0; } + void add_ltype(std::function map, + const char* name, + t_sub_tile& sub_tile, + t_physical_tile_type& type) { + + vtr::bimap directs_map; + auto ltype = get_type_by_name(name, ltypes_); + + for (int npin = 0; npin < ltype->pb_type->num_ports; npin++) { + t_physical_pin physical_pin(map(npin)); + t_logical_pin logical_pin(npin); + + directs_map.insert(logical_pin, physical_pin); + } + + type.tile_block_pin_directs_map[ltype->index][sub_tile.index] = directs_map; + sub_tile.equivalent_sites.push_back(ltype); + } + /** @brief Utility function to fill in all the necessary information for the sub_tile * * Given a physical tile type and a corresponding sub tile with additional information on the IO pin count @@ -421,47 +440,36 @@ struct ArchReader { } } - vtr::bimap directs_map; - - for (int npin = 0; npin < num_pins; npin++) { - t_physical_pin physical_pin(npin); - t_logical_pin logical_pin(npin); - - directs_map.insert(logical_pin, physical_pin); - } - - auto ltype = get_type_by_name(sub_tile.name, ltypes_); - - type.tile_block_pin_directs_map[ltype->index][sub_tile.index] = directs_map; - sub_tile.equivalent_sites.push_back(ltype); - if (site_in_tile) { auto site_types = ar_.getSiteTypeList(); auto site_type = site_types[site_in_tile->getPrimaryType()]; auto alt_sites_pins = site_in_tile->getAltPinsToPrimaryPins(); - for (int i = 0; i < site_type.getAltSiteTypes().size(); ++i) { + if (take_sites_.count(site_type.getName()) != 0) { + std::function map = [](int x){ return x; }; + add_ltype(map, sub_tile.name, sub_tile, type); + } + + for (int i = 0; i < (int)site_type.getAltSiteTypes().size(); ++i) { auto alt_site = site_types[site_type.getAltSiteTypes()[i]]; + if (take_sites_.count(alt_site.getName()) == 0) continue; - auto ltype = get_type_by_name(str(alt_site.getName()).c_str(), ltypes_); - sub_tile.equivalent_sites.push_back(ltype); - - vtr::bimap directs_map; auto pin_map = alt_sites_pins[i]; - for (int npin = 0; npin < ltype->pb_type->num_ports; npin++) { - auto pin = site_type.getPins()[pin_map.getPins()[npin]]; - auto idx = (*port_name_to_sub_tile_idx)[str(pin.getName())]; - t_physical_pin physical_pin(idx); - t_logical_pin logical_pin(npin); + std::function map = [pin_map, site_type, port_name_to_sub_tile_idx, this](int x){ + auto pin = site_type.getPins()[pin_map.getPins()[x]]; + return (*port_name_to_sub_tile_idx)[str(pin.getName())]; + }; - directs_map.insert(logical_pin, physical_pin); - } - type.tile_block_pin_directs_map[ltype->index][sub_tile.index] = directs_map; + add_ltype(map, str(alt_site.getName()).c_str(), sub_tile, type); } + } else { + std::function map = [](int x){ return x; }; + add_ltype(map, sub_tile.name, sub_tile, type); } + // Assign FC specs int iblk_pin = 0; for (const auto& port : sub_tile.ports) { @@ -735,7 +743,22 @@ struct ArchReader { if (found) take_sites_.insert(site_type.getName()); - // TODO: Enable also alternative site types handling + for(auto alt_site_idx : site_type.getAltSiteTypes()) { + auto alt_site = site_types[alt_site_idx]; + found = false; + for (auto bel : alt_site.getBels()) { + auto bel_name = bel.getName(); + bool res = bel_cell_mappings_.find(bel_name) != bel_cell_mappings_.end(); + + found |= res; + + if (res || is_pad(str(bel_name))) + take_bels_.insert(bel_name); + } + + if (found) + take_sites_.insert(alt_site.getName()); + } } } } @@ -1865,8 +1888,14 @@ struct ArchReader { bool has_valid_sites = false; - for (auto site_type : tile.getSiteTypes()) - has_valid_sites |= take_sites_.count(siteTypeList[site_type.getPrimaryType()].getName()) != 0; + for (auto site_type : tile.getSiteTypes()) { + auto site_ = siteTypeList[site_type.getPrimaryType()]; + has_valid_sites |= take_sites_.count(site_.getName()) != 0; + for (auto alt_site_idx : site_.getAltSiteTypes()){ + auto alt_site_ = siteTypeList[alt_site_idx]; + has_valid_sites |= take_sites_.count(alt_site_.getName()) != 0; + } + } if (!has_valid_sites) continue; @@ -1899,14 +1928,20 @@ struct ArchReader { } void process_sub_tiles(t_physical_tile_type& type, Device::TileType::Reader& tile) { - // TODO: only one subtile at the moment auto siteTypeList = ar_.getSiteTypeList(); for (auto site_in_tile : tile.getSiteTypes()) { t_sub_tile sub_tile; + bool site_taken = false; + auto site = siteTypeList[site_in_tile.getPrimaryType()]; + site_taken |= take_sites_.count(site.getName()) != 0; + for (auto alt_site_idx : site.getAltSiteTypes()){ + auto alt_site = siteTypeList[alt_site_idx]; + site_taken |= take_sites_.count(alt_site.getName()) != 0; + } - if (take_sites_.count(site.getName()) == 0) + if (!site_taken) continue; sub_tile.index = type.capacity; diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 676c823e65c..2ac8b4e0d33 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -1300,17 +1300,29 @@ struct InterchangeRRGraphBuilder { */ int next_good_site(int first_idx, const Device::Tile::Reader tile) { auto tile_type = ar_.getTileTypeList()[tile.getType()]; + auto site_types = ar_.getSiteTypeList(); 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()]; - + auto site_type = site_types[tile_type.getSiteTypes()[site.getType()].getPrimaryType()]; bool found = false; for (auto bel : site_type.getBels()) found |= bel_cell_mappings_.find(bel.getName()) != bel_cell_mappings_.end(); if (found) break; + for(auto alt_site_idx : site_type.getAltSiteTypes()) { + auto alt_site = site_types[alt_site_idx]; + for (auto bel : alt_site.getBels()) { + auto bel_name = bel.getName(); + bool res = bel_cell_mappings_.find(bel_name) != bel_cell_mappings_.end(); + found |= res; + } + if (found) + break; + } + if (found) + break; } return ans; @@ -1478,7 +1490,7 @@ struct InterchangeRRGraphBuilder { } void pack_tiles() { - for (auto& node_loc : sink_source_loc_map_) { + for (const auto& node_loc : sink_source_loc_map_) { int tile_id = node_loc.first; int x, y; t_location loc = tile_loc_bimap_[tile_id]; @@ -1624,7 +1636,7 @@ struct InterchangeRRGraphBuilder { } void pack_tiles_edges() { - for (auto& i : sink_source_loc_map_) { + for (const auto& i : sink_source_loc_map_) { int tile_id = i.first; int x, y; t_location loc = tile_loc_bimap_[tile_id]; @@ -1638,22 +1650,22 @@ struct InterchangeRRGraphBuilder { if (node_id == -1) continue; - 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 virtual_chan_key = virtual_redirect_[std::make_tuple(node_id, loc)]; 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; - pin_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, ipin)]; - mux_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, mux, ipin)]; - track_id = loc_type_idx_to_rr_idx_[chan_key]; + int pin_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, pin, ipin)]; + int mux_id = loc_type_idx_to_rr_idx_[std::make_tuple(loc, mux, ipin)]; + + int 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; + int sink = input ? pin_id : track_id; + int sink_src = mux_id; + int src = input ? track_id : pin_id; device_ctx_.rr_graph_builder.emplace_back_edge(RRNodeId(src), RRNodeId(sink_src), From f2ae4e5f4dd17f1c22981cca147fe05c130bb9fb Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Mon, 2 May 2022 09:29:40 +0200 Subject: [PATCH 18/38] run make format Signed-off-by: Alessandro Comodi --- libs/libarchfpga/src/read_fpga_interchange_arch.cpp | 13 ++++++------- vpr/src/route/rr_graph_fpga_interchange.cpp | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 3ab2c603557..6ac7de1a0cc 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -385,7 +385,6 @@ struct ArchReader { const char* name, t_sub_tile& sub_tile, t_physical_tile_type& type) { - vtr::bimap directs_map; auto ltype = get_type_by_name(name, ltypes_); @@ -446,7 +445,7 @@ struct ArchReader { auto alt_sites_pins = site_in_tile->getAltPinsToPrimaryPins(); if (take_sites_.count(site_type.getName()) != 0) { - std::function map = [](int x){ return x; }; + std::function map = [](int x) { return x; }; add_ltype(map, sub_tile.name, sub_tile, type); } @@ -458,7 +457,7 @@ struct ArchReader { auto pin_map = alt_sites_pins[i]; - std::function map = [pin_map, site_type, port_name_to_sub_tile_idx, this](int x){ + std::function map = [pin_map, site_type, port_name_to_sub_tile_idx, this](int x) { auto pin = site_type.getPins()[pin_map.getPins()[x]]; return (*port_name_to_sub_tile_idx)[str(pin.getName())]; }; @@ -466,7 +465,7 @@ struct ArchReader { add_ltype(map, str(alt_site.getName()).c_str(), sub_tile, type); } } else { - std::function map = [](int x){ return x; }; + std::function map = [](int x) { return x; }; add_ltype(map, sub_tile.name, sub_tile, type); } @@ -743,7 +742,7 @@ struct ArchReader { if (found) take_sites_.insert(site_type.getName()); - for(auto alt_site_idx : site_type.getAltSiteTypes()) { + for (auto alt_site_idx : site_type.getAltSiteTypes()) { auto alt_site = site_types[alt_site_idx]; found = false; for (auto bel : alt_site.getBels()) { @@ -1891,7 +1890,7 @@ struct ArchReader { for (auto site_type : tile.getSiteTypes()) { auto site_ = siteTypeList[site_type.getPrimaryType()]; has_valid_sites |= take_sites_.count(site_.getName()) != 0; - for (auto alt_site_idx : site_.getAltSiteTypes()){ + for (auto alt_site_idx : site_.getAltSiteTypes()) { auto alt_site_ = siteTypeList[alt_site_idx]; has_valid_sites |= take_sites_.count(alt_site_.getName()) != 0; } @@ -1936,7 +1935,7 @@ struct ArchReader { auto site = siteTypeList[site_in_tile.getPrimaryType()]; site_taken |= take_sites_.count(site.getName()) != 0; - for (auto alt_site_idx : site.getAltSiteTypes()){ + for (auto alt_site_idx : site.getAltSiteTypes()) { auto alt_site = siteTypeList[alt_site_idx]; site_taken |= take_sites_.count(alt_site.getName()) != 0; } diff --git a/vpr/src/route/rr_graph_fpga_interchange.cpp b/vpr/src/route/rr_graph_fpga_interchange.cpp index 2ac8b4e0d33..975421850a2 100644 --- a/vpr/src/route/rr_graph_fpga_interchange.cpp +++ b/vpr/src/route/rr_graph_fpga_interchange.cpp @@ -1311,7 +1311,7 @@ struct InterchangeRRGraphBuilder { if (found) break; - for(auto alt_site_idx : site_type.getAltSiteTypes()) { + for (auto alt_site_idx : site_type.getAltSiteTypes()) { auto alt_site = site_types[alt_site_idx]; for (auto bel : alt_site.getBels()) { auto bel_name = bel.getName(); From 604a1051281161f8c8a4e5f75d8d35b77330dc01 Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Wed, 6 Apr 2022 14:13:35 -0500 Subject: [PATCH 19/38] Added xdc_constraints.cpp --- vpr/src/base/xdc_constraints.cpp | 168 +++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 vpr/src/base/xdc_constraints.cpp diff --git a/vpr/src/base/xdc_constraints.cpp b/vpr/src/base/xdc_constraints.cpp new file mode 100644 index 00000000000..aaa6be4f913 --- /dev/null +++ b/vpr/src/base/xdc_constraints.cpp @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include + +//#include "DeviceResources.capnp.h" +//#include "LogicalNetlist.capnp.h" +#include "vpr_constraints.h" +#include "atom_netlist.h" + +#define OV_TOSTRING(e) virtual operator std::string() { return (e); } + +/* XDC ERRORS */ + +class XDC_eException { +public: + OV_TOSTRING("Unknown error") +}; + +class XDC_eCustomException : XDC_eException { +public: + std::string message; + XDC_eCustomException(std::string&& message) { + this->message = std::move(message); + } + OV_TOSTRING(this->message) +}; + +class XDC_eFailedToInitTcl : XDC_eException { +public: + int error_code; + XDC_eFailedToInitTcl(int error_code) { + this->error_code = error_code; + } + OV_TOSTRING("Can't initialize TCL (code " + std::to_string(error_code) +")") +}; + +class XDC_eErroneousXDC : XDC_eException { +public: + int line, column; + std::string filename; + OV_TOSTRING(this->filename + ":" + std::to_string(this->line) + "," + std::to_string(this->column) + ": Unknown error") +}; + +/* TCL COMMANDS */ + +using TclCommandError = std::string; +enum class e_TclCommandStatus : int { + TCL_CMD_SUCCESS, + TCL_CMD_FAIL +}; +class TclClientData { +public: + e_TclCommandStatus cmd_status; + TclCommandError error; + VprConstraints& constraints; + AtomNetlist& netlist; + + TclClientData(VprConstraints& constraints, AtomNetlist& netlist) : + cmd_status(e_TclCommandStatus::TCL_CMD_SUCCESS), + error("No errors"), + constraints(constraints), + netlist(netlist) + {} + + void set_error(TclCommandError&& error) { + this->error = std::move(error); + } +}; + +enum class e_XDCProperty { + XDC_PROP_PACKAGE_PIN, + XDC_PROP_UNKNOWN +}; + +static e_XDCProperty xdc_prop_from_str(const char* str) { + if (str == "PACKAGE_PIN") + return e_XDCProperty::XDC_PROP_PACKAGE_PIN; + + return e_XDCProperty::XDC_PROP_UNKNOWN; +} + +static int do_set_property_package_pin( + VprConstraints& constraints, + AtomNetlist& netlist, + const char* pin_name, + const char* port_name, + std::function on_error) +{ + AtomPinId pin = netlist.find_pin(pin_name); + if (pin == AtomPinId::INVALID()) { + on_error("set_property: Can't find PIN named `" + std::string(pin_name) + "`"); + return 0; + } + /* TODO: Handle pins W/O ports properly */ + AtomPortId port = netlist.pin_port(pin); + //constraints.add_constrained_atom() + return 0; +} + +static int tcl_set_property_package_pin(TclClientData* cd, Tcl_Interp* tcl_interp, Tcl_Obj* tcl_pin_name, Tcl_Obj* tcl_port) { + int error; + + const char* pin_name = Tcl_GetString(tcl_pin_name); + if (pin_name == nullptr) { + cd->set_error("set_property: pin_name of PACKAGE_PIN should be a string."); + return 0; + } + + const char* port_name = Tcl_GetString(tcl_port); + if (port_name == nullptr) { + cd->set_error("set_property: pin_name of PACKAGE_PIN should be a string."); + return 0; + } + + error = do_set_property_package_pin(cd->constraints, cd->netlist, pin_name, port_name, [&](std::string msg) { cd->set_error(std::move(msg)); }); +} + +static int tcl_set_property(TclClientData* cd, Tcl_Interp* tcl_interp, int objc, Tcl_Obj* const objvp[]) { + if (objc < 2) { + cd->set_error("set_property: Expected at least 2 arguments."); + return 0; + } + + const char* property_name = Tcl_GetString(objvp[0]); + if (property_name == nullptr) { + cd->set_error("set_property: First argument should be a string."); + return 0; + } + const e_XDCProperty property = xdc_prop_from_str(property_name); + + switch (property) { + case e_XDCProperty::XDC_PROP_PACKAGE_PIN: + if (objc != 3) { + cd->set_error("set_property: Property `PACKAGE_PIN` requires one target and one value."); + return 0; + } + return tcl_set_property_package_pin(cd, tcl_interp, objvp[2], objvp[3]); + case e_XDCProperty::XDC_PROP_UNKNOWN: + cd->set_error("set_property: Property `" + std::string(property_name) + "` is not recognized."); + return 0; + default: + cd->set_error("set_property: Property `" + std::string(property_name) + "` is not supported."); + return 0; + } + +} + +/* API */ + +VprConstraints read_xdc_constraints_to_vpr(AtomNetlist& netlist, const char* argv0, std::istream& xdc_stream) { + int error; + + Tcl_FindExecutable(argv0); + + Tcl_Interp* tcl_interp = Tcl_CreateInterp(); + if ((error = Tcl_Init(tcl_interp)) != TCL_OK) { + Tcl_DeleteInterp(tcl_interp); + throw XDC_eFailedToInitTcl(error); + } + + auto constraints = VprConstraints(); + auto client_data = TclClientData(constraints, netlist); /* ! Shares ownership of constraints and netlist */ + + throw XDC_eCustomException("XDC parser is not fully implemented"); +} \ No newline at end of file From 5a890052c3e1c3f49771ea1eab5764866358037f Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Fri, 8 Apr 2022 12:41:58 -0500 Subject: [PATCH 20/38] WIP XDC constraints --- libs/libarchfpga/src/physical_types.h | 4 + .../src/read_fpga_interchange_arch.cpp | 2 + libs/libvtrutil/src/vtr_range.h | 4 +- vpr/CMakeLists.txt | 3 + vpr/src/base/SetupVPR.cpp | 7 + vpr/src/base/globals.h | 1 + vpr/src/base/read_options.cpp | 4 + vpr/src/base/read_options.h | 1 + vpr/src/base/vpr_api.cpp | 18 +- vpr/src/base/vpr_types.h | 1 + vpr/src/base/xdc_constraints.cpp | 459 +++++++++++++----- vpr/src/base/xdc_constraints.h | 75 +++ 12 files changed, 466 insertions(+), 113 deletions(-) create mode 100644 vpr/src/base/xdc_constraints.h diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 8eeb821a5de..d4ead261c88 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -1773,6 +1773,9 @@ struct t_lut_element { } }; +/* Maps Physical PIN name to a name of PAD BEL */ +typedef std::unordered_map t_phys_grid_mapping; + /* Detailed routing architecture */ struct t_arch { mutable vtr::string_internment strings; @@ -1831,6 +1834,7 @@ struct t_arch { std::string ipin_cblock_switch_name; std::vector grid_layouts; //Set of potential device layouts + t_phys_grid_mapping phys_grid_mapping; //Mapping from physical pins to grid PADs. t_clock_arch_spec clock_arch; // Clock related data types }; diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 6ac7de1a0cc..b79ff01b7a1 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -820,6 +820,8 @@ struct ArchReader { pckg_pin.site_name = str(pin.getSite().getSite()); package_pins_.push_back(pckg_pin); + + arch_->phys_grid_mapping[pckg_pin.name] = pckg_pin.bel_name; } } } diff --git a/libs/libvtrutil/src/vtr_range.h b/libs/libvtrutil/src/vtr_range.h index 493a379fbd9..28aa5296eca 100644 --- a/libs/libvtrutil/src/vtr_range.h +++ b/libs/libvtrutil/src/vtr_range.h @@ -53,9 +53,9 @@ class Range { ///@brief Return an iterator to the end of the range (immutable) const T end() const { return end_; } ///@brief Return true if empty - bool empty() { return begin_ == end_; } + bool empty() const { return begin_ == end_; } ///@brief Return the range size - size_t size() { return std::distance(begin_, end_); } + size_t size() const { return std::distance(begin_, end_); } private: T begin_; diff --git a/vpr/CMakeLists.txt b/vpr/CMakeLists.txt index f43da412c74..dabc414dbec 100644 --- a/vpr/CMakeLists.txt +++ b/vpr/CMakeLists.txt @@ -53,6 +53,9 @@ add_library(libvpr STATIC ${LIB_SOURCES} ) +#Find TCL +find_package(TCL REQUIRED) +target_link_libraries(libvpr tcl) target_include_directories(libvpr PUBLIC ${LIB_INCLUDE_DIRS}) diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index c03fef2e119..b76394f7517 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -27,6 +27,8 @@ #include "clock_modeling.h" #include "ShowSetup.h" +#include "xdc_constraints.h" + static void SetupNetlistOpts(const t_options& Options, t_netlist_opts& NetlistOpts); static void SetupPackerOpts(const t_options& Options, t_packer_opts* PackerOpts); @@ -97,6 +99,7 @@ void SetupVPR(const t_options* Options, FileNameOpts->CmosTechFile = Options->CmosTechFile; FileNameOpts->out_file_prefix = Options->out_file_prefix; FileNameOpts->read_vpr_constraints_file = Options->read_vpr_constraints_file; + FileNameOpts->read_xdc_constraints_file = Options->XDCFile; FileNameOpts->write_vpr_constraints_file = Options->write_vpr_constraints_file; FileNameOpts->write_block_usage = Options->write_block_usage; @@ -109,6 +112,9 @@ void SetupVPR(const t_options* Options, SetupAnalysisOpts(*Options, *AnalysisOpts); SetupPowerOpts(*Options, PowerOpts, Arch); + if (Options->XDCFile.value() != "") + xdc_init(); + if (readArchFile == true) { vtr::ScopedStartFinishTimer t("Loading Architecture Description"); switch (Options->arch_format) { @@ -118,6 +124,7 @@ void SetupVPR(const t_options* Options, Arch, device_ctx.physical_tile_types, device_ctx.logical_block_types); + VTR_ASSERT_MSG(Options->XDCFile.value() != "", "XDC constraints are not supported for VTR format"); break; case e_arch_format::FPGAInterchange: VTR_LOG("Use FPGA Interchange device\n"); diff --git a/vpr/src/base/globals.h b/vpr/src/base/globals.h index cbbbe3a75e3..807692913fc 100644 --- a/vpr/src/base/globals.h +++ b/vpr/src/base/globals.h @@ -8,5 +8,6 @@ #include "vpr_context.h" extern VprContext g_vpr_ctx; +extern const char* _argv0; #endif diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index 074e84a2e8c..be46d9d152a 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1517,6 +1517,10 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg file_grp.add_argument(args.SDCFile, "--sdc_file") .help("Path to timing constraints file in SDC format") .show_in(argparse::ShowIn::HELP_ONLY); + + file_grp.add_argument(args.XDCFile, "--xdc_file") + .help("Path to Xilinx's Design Constraints file") + .show_in(argparse::ShowIn::HELP_ONLY); file_grp.add_argument(args.read_rr_graph_file, "--read_rr_graph") .help( diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index d2edecc27ac..8eb053c7e31 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -19,6 +19,7 @@ struct t_options { argparse::ArgValue PowerFile; argparse::ArgValue CmosTechFile; argparse::ArgValue SDCFile; + argparse::ArgValue XDCFile; argparse::ArgValue arch_format; argparse::ArgValue circuit_format; diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index f4d759a862e..f501ec8d791 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -69,6 +69,7 @@ #include "cluster.h" #include "output_clustering.h" #include "vpr_constraints_reader.h" +#include "xdc_constraints.h" #include "place_constraints.h" #include "place_util.h" @@ -100,6 +101,8 @@ std::unique_ptr tbb_scheduler; #endif +const char* _argv0; + /* Local subroutines */ static void free_complex_block_types(); @@ -168,6 +171,8 @@ void vpr_initialize_logging() { * 4. Sanity check all three */ void vpr_init(const int argc, const char** argv, t_options* options, t_vpr_setup* vpr_setup, t_arch* arch) { + _argv0 = argv[0]; + vpr_initialize_logging(); /* Print title message */ @@ -343,8 +348,19 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a //Initialize vpr floorplanning constraints auto& filename_opts = vpr_setup->FileNameOpts; - if (!filename_opts.read_vpr_constraints_file.empty()) { + if (!filename_opts.read_vpr_constraints_file.empty()) load_vpr_constraints_file(filename_opts.read_vpr_constraints_file.c_str()); + + if (!filename_opts.read_xdc_constraints_file.empty()) { + try { + load_xdc_constraints_file(filename_opts.read_xdc_constraints_file.c_str(), *arch, atom_ctx.nlist); + } catch (const XDC_eErroneousXDC& e) { + VTR_LOG_ERROR("Error reading XDC: %s\n", std::string(e).c_str()); + throw e; + } catch (const XDC_eException& e) { + VTR_LOG_ERROR("Error loading XDC: %s\n", std::string(e).c_str()); + throw e; + } } fflush(stdout); diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index f4c72fdcadd..51cb34cf9fe 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -721,6 +721,7 @@ struct t_file_name_opts { std::string CmosTechFile; std::string out_file_prefix; std::string read_vpr_constraints_file; + std::string read_xdc_constraints_file; std::string write_vpr_constraints_file; std::string write_block_usage; bool verify_file_digests; diff --git a/vpr/src/base/xdc_constraints.cpp b/vpr/src/base/xdc_constraints.cpp index aaa6be4f913..219978b40a4 100644 --- a/vpr/src/base/xdc_constraints.cpp +++ b/vpr/src/base/xdc_constraints.cpp @@ -1,74 +1,57 @@ #include #include #include +#include #include #include +#include #include //#include "DeviceResources.capnp.h" //#include "LogicalNetlist.capnp.h" +#include "globals.h" #include "vpr_constraints.h" #include "atom_netlist.h" -#define OV_TOSTRING(e) virtual operator std::string() { return (e); } +#include "xdc_constraints.h" + +#define OV_TOSTRING(c, e) c::operator std::string() const { return (e); } /* XDC ERRORS */ -class XDC_eException { -public: - OV_TOSTRING("Unknown error") -}; +OV_TOSTRING(XDC_eException, "Unknown error") -class XDC_eCustomException : XDC_eException { -public: - std::string message; - XDC_eCustomException(std::string&& message) { - this->message = std::move(message); - } - OV_TOSTRING(this->message) -}; +XDC_eCustomException::XDC_eCustomException(std::string&& message_) : message(std::move(message_)) {} +XDC_eCustomException::operator std::string() const { return (this->message); } -class XDC_eFailedToInitTcl : XDC_eException { -public: - int error_code; - XDC_eFailedToInitTcl(int error_code) { - this->error_code = error_code; - } - OV_TOSTRING("Can't initialize TCL (code " + std::to_string(error_code) +")") -}; +XDC_eFailedToInitTcl::XDC_eFailedToInitTcl(int error_code_) : error_code(error_code_) {} +OV_TOSTRING(XDC_eFailedToInitTcl, "Can't initialize TCL (code " + std::to_string(error_code) +")") + +XDC_eErroneousXDC::XDC_eErroneousXDC( + std::string&& filename_, + int line_, int column_, + std::string&& message_) + : filename(std::move(filename_)), line(line_), column(column_), message(message_) {} +OV_TOSTRING(XDC_eErroneousXDC, this->filename + ":" + std::to_string(this->line) + "," + + std::to_string(this->column) + ": " + this->message) -class XDC_eErroneousXDC : XDC_eException { -public: - int line, column; - std::string filename; - OV_TOSTRING(this->filename + ":" + std::to_string(this->line) + "," + std::to_string(this->column) + ": Unknown error") -}; /* TCL COMMANDS */ +/* Stolen from nextpnr */ +static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) { + char *copy = Tcl_Alloc(s.size() + 1); + std::copy(s.begin(), s.end(), copy); + copy[s.size()] = '\0'; + Tcl_SetResult(interp, copy, TCL_DYNAMIC); +} + using TclCommandError = std::string; enum class e_TclCommandStatus : int { TCL_CMD_SUCCESS, + TCL_CMD_SUCCESS_STRING, TCL_CMD_FAIL }; -class TclClientData { -public: - e_TclCommandStatus cmd_status; - TclCommandError error; - VprConstraints& constraints; - AtomNetlist& netlist; - - TclClientData(VprConstraints& constraints, AtomNetlist& netlist) : - cmd_status(e_TclCommandStatus::TCL_CMD_SUCCESS), - error("No errors"), - constraints(constraints), - netlist(netlist) - {} - - void set_error(TclCommandError&& error) { - this->error = std::move(error); - } -}; enum class e_XDCProperty { XDC_PROP_PACKAGE_PIN, @@ -76,93 +59,349 @@ enum class e_XDCProperty { }; static e_XDCProperty xdc_prop_from_str(const char* str) { - if (str == "PACKAGE_PIN") + if (!strcmp(str, "PACKAGE_PIN")) return e_XDCProperty::XDC_PROP_PACKAGE_PIN; return e_XDCProperty::XDC_PROP_UNKNOWN; } -static int do_set_property_package_pin( - VprConstraints& constraints, - AtomNetlist& netlist, - const char* pin_name, - const char* port_name, - std::function on_error) -{ - AtomPinId pin = netlist.find_pin(pin_name); - if (pin == AtomPinId::INVALID()) { - on_error("set_property: Can't find PIN named `" + std::string(pin_name) + "`"); - return 0; - } - /* TODO: Handle pins W/O ports properly */ - AtomPortId port = netlist.pin_port(pin); - //constraints.add_constrained_atom() - return 0; -} +class XDCTclClient { +public: + const t_arch& arch; + VprConstraints& constraints; + e_TclCommandStatus cmd_status; + AtomNetlist& netlist; + std::string string; -static int tcl_set_property_package_pin(TclClientData* cd, Tcl_Interp* tcl_interp, Tcl_Obj* tcl_pin_name, Tcl_Obj* tcl_port) { - int error; - - const char* pin_name = Tcl_GetString(tcl_pin_name); - if (pin_name == nullptr) { - cd->set_error("set_property: pin_name of PACKAGE_PIN should be a string."); - return 0; + XDCTclClient(const t_arch& arch_, VprConstraints& constraints_, AtomNetlist& netlist_) : + arch(arch_), + constraints(constraints_), + cmd_status(e_TclCommandStatus::TCL_CMD_SUCCESS), + netlist(netlist_), + string("No errors") + {} + + /* TCL functions */ + + int set_property(int objc, Tcl_Obj* const objvp[]) { + if (objc < 3) + return this->_ret_error("set_property: Expected at least 2 arguments, got " + + std::to_string(objc) + "."); + + const char* property_name = Tcl_GetString(objvp[1]); + if (property_name == nullptr) + return this->_ret_error("set_property: First argument should be a string."); + + e_XDCProperty property = xdc_prop_from_str(property_name); + + switch (property) { + case e_XDCProperty::XDC_PROP_PACKAGE_PIN: + if (objc != 4) + return this->_ret_error("set_property: Property `PACKAGE_PIN` " + "requires one target and one value."); + return this->_set_property_package_pin(objvp[2], objvp[3]); + case e_XDCProperty::XDC_PROP_UNKNOWN: + return this->_ret_error("set_property: Property `" + std::string(property_name) + + "` is not recognized."); + default: break; + } + return this->_ret_error("set_property: Property `" + std::string(property_name) + + "` is not supported."); } - const char* port_name = Tcl_GetString(tcl_port); - if (port_name == nullptr) { - cd->set_error("set_property: pin_name of PACKAGE_PIN should be a string."); - return 0; + int get_ports(int objc, Tcl_Obj* const objvp[]) { + if (objc != 2) + return this->_ret_error("get_ports: Expected one argument."); + + const char* pin_name = Tcl_GetString(objvp[1]); + if (pin_name == nullptr) + return this->_ret_error("get_ports: pin_name should be a string."); + + return this->_do_get_ports(pin_name); } - - error = do_set_property_package_pin(cd->constraints, cd->netlist, pin_name, port_name, [&](std::string msg) { cd->set_error(std::move(msg)); }); -} -static int tcl_set_property(TclClientData* cd, Tcl_Interp* tcl_interp, int objc, Tcl_Obj* const objvp[]) { - if (objc < 2) { - cd->set_error("set_property: Expected at least 2 arguments."); - return 0; +protected: + int _set_property_package_pin(Tcl_Obj* tcl_pin_name, Tcl_Obj* tcl_port) { + int error; + + const char* pin_name = Tcl_GetString(tcl_pin_name); + if (pin_name == nullptr) + return this->_ret_error("set_property: pin_name of PACKAGE_PIN should be a string."); + + const char* port_name = Tcl_GetString(tcl_port); + if (port_name == nullptr) + return this->_ret_error("set_property: pin_name of PACKAGE_PIN should be a string."); + + return this->_do_set_property_package_pin(pin_name, port_name); + + return error; } - const char* property_name = Tcl_GetString(objvp[0]); - if (property_name == nullptr) { - cd->set_error("set_property: First argument should be a string."); - return 0; + int _do_set_property_package_pin(const char* pin_name, const char* port_name) { + VTR_LOG("TCL: set_property PACKAGE_PIN %s %s\n", pin_name, port_name); + + const auto& phys_grid_mapping = this->arch.phys_grid_mapping; + + //g_vpr_ctx.placement().physical_pins + std::string pin_str(pin_name); + /* if (!phys_grid_mapping.count(pin_str)) + return this->_ret_error("Can't find physical PIN named `" + + pin_str + "`"); */ + auto it = phys_grid_mapping.find(pin_str); + if (it == phys_grid_mapping.end()) + return this->_ret_error("Can't find physical PIN named `" + + pin_str + "`"); + std::string bel_name = it->second; + + VTR_LOG("XDC: Assigned PACKAGE_PIN %s = %s\n", pin_name, bel_name.c_str()); + + /* TODO: Handle pins W/O ports properly */ + //AtomPortId port = this->netlist.pin_port(pin); + //constraints.add_constrained_atom() + return this->_ret_ok(); } - const e_XDCProperty property = xdc_prop_from_str(property_name); - switch (property) { - case e_XDCProperty::XDC_PROP_PACKAGE_PIN: - if (objc != 3) { - cd->set_error("set_property: Property `PACKAGE_PIN` requires one target and one value."); - return 0; + int _do_get_ports(const char* pin_name) { + VTR_LOG("TCL: get_ports %s\n", pin_name); + + AtomPinId pin = this->netlist.find_pin(pin_name); + if (pin == AtomPinId::INVALID()) { + std::string available_pins; + const auto& pins = this->netlist.pins(); + int pins_left = pins.size(); + for (auto& pin : pins) { + available_pins += this->netlist.pin_name(pin); + if (pins_left != 1) + available_pins += ", "; + pins_left--; + } + + return this->_ret_error("set_property: Can't find PORT named `" + + std::string(pin_name) + "`. Available pins: " + + available_pins); } - return tcl_set_property_package_pin(cd, tcl_interp, objvp[2], objvp[3]); - case e_XDCProperty::XDC_PROP_UNKNOWN: - cd->set_error("set_property: Property `" + std::string(property_name) + "` is not recognized."); - return 0; - default: - cd->set_error("set_property: Property `" + std::string(property_name) + "` is not supported."); - return 0; + + AtomPortId port = this->netlist.pin_port(pin); + std::string port_name = this->netlist.port_name(port); + + VTR_LOG("get_ports: Got port `%s` for pin `%s`\n", port_name.c_str(), pin_name); + + return this->_ret_string(std::move(port_name)); } -} + /* Return methods */ + + inline int _ret_error(TclCommandError&& error) { + this->cmd_status = e_TclCommandStatus::TCL_CMD_FAIL; + this->string = std::move(error); + return TCL_ERROR; + } + + inline int _ret_ok() { + this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS; + return TCL_OK; + } + + inline int _ret_string(std::string&& string_) { + this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS_STRING; + this->string = std::move(string_); + return TCL_OK; + } +}; + +static bool _xdc_initialized = false; /* API */ -VprConstraints read_xdc_constraints_to_vpr(AtomNetlist& netlist, const char* argv0, std::istream& xdc_stream) { - int error; +void xdc_init() { + if (!_xdc_initialized) + Tcl_FindExecutable(_argv0); + _xdc_initialized = true; + + VTR_LOG("Initialized TCL subsystem"); +} + +/** + * \brief Provide TCL runtime for parsing XDC files. + */ +class XDCCtx { +public: + XDCCtx(const t_arch& arch, AtomNetlist& netlist) : + constraints(VprConstraints()), + _netlist(netlist), + _client(arch, this->constraints, this->_netlist) + { + this->_tcl_interp = Tcl_CreateInterp(); +#ifdef DEBUG + this->_init = false; +#endif + VTR_LOG("Created XDC context.\n"); + } + + ~XDCCtx() { + Tcl_DeleteInterp(this->_tcl_interp); + + VTR_LOG("Deleted XDC context.\n"); + } + + void init() { + int error; + + if ((error = Tcl_Init(this->_tcl_interp)) != TCL_OK) + throw XDC_eFailedToInitTcl(error); + + error = this->_fix_port_indexing(); + VTR_ASSERT(error == TCL_OK); + this->_extend_tcl(); + +#ifdef DEBUG + this->_init = true; +#endif + } + + /** + * \brief Extend (VPR) constraints with constraints read from and XDC script + */ + void read_xdc(std::istream& xdc_stream) { + int error; + + this->_debug_init(); + + std::ostringstream os; + xdc_stream >> os.rdbuf(); + std::string xdc = os.str(); + + error = Tcl_Eval(this->_tcl_interp, xdc.c_str()); + /* TODO: More precise error */ + if (error != TCL_OK) { + int error_line = Tcl_GetErrorLine(this->_tcl_interp); + const char* msg = Tcl_GetStringResult(this->_tcl_interp); + throw XDC_eErroneousXDC("", error_line, 0, std::string(msg)); + } + } + + VprConstraints constraints; + +protected: + + /** + * \brief Add TCL commands used in XDC files + */ + void _extend_tcl() { + this->_add_tcl_cmd("get_ports", &XDCTclClient::get_ports); + this->_add_tcl_cmd("set_property", &XDCTclClient::set_property); + } + + /** + * \brief Tcl by default will interpret bus indices as calls to commands + * (eg. in[0] gets interpreted as call to command `i` with argument being a + * result of calling `0`). This overrides this behaviour. + * + * Code taken from nextpnr. + */ + int _fix_port_indexing() { + int error; + error = Tcl_Eval(this->_tcl_interp, "rename unknown _original_unknown"); + if (error != TCL_OK) + return error; + error = Tcl_Eval( + this->_tcl_interp, + "proc unknown args {\n" + " set result [scan [lindex $args 0] \"%d\" value]\n" + " if { $result == 1 && [llength $args] == 1 } {\n" + " return \\[$value\\]\n" + " } else {\n" + " uplevel 1 [list _original_unknown {*}$args]\n" + " }\n" + "}" + ); + return error; + } + + typedef int(XDCTclClient::*TclMethod)(int objc, Tcl_Obj* const objvp[]); + + struct TclMethodDispatch { + TclMethodDispatch(TclMethod method_, XDCCtx& ctx_) : method(method_), ctx(ctx_) {} + + TclMethod method; + XDCCtx& ctx; + }; + + static int _tcl_do_method( + ClientData cd, + Tcl_Interp* tcl_interp, + int objc, + Tcl_Obj* const objvp[] + ) { + TclMethodDispatch* d = static_cast(cd); + auto method = d->method; + auto& client = d->ctx._client; + + (client.*method)(objc, objvp); + + switch (client.cmd_status) { + case e_TclCommandStatus::TCL_CMD_FAIL: + Tcl_SetStringResult(tcl_interp, d->ctx._client.string); + return TCL_ERROR; + case e_TclCommandStatus::TCL_CMD_SUCCESS_STRING: + Tcl_SetStringResult(tcl_interp, d->ctx._client.string); + default: break; + } + return TCL_OK; + } + + void _add_tcl_cmd(const char* name, TclMethod method) { + this->_method_dispatch_list.push_front(TclMethodDispatch(method, *this)); + auto& md = this->_method_dispatch_list.front(); + Tcl_CreateObjCommand(this->_tcl_interp, name, XDCCtx::_tcl_do_method, &md, nullptr); + } - Tcl_FindExecutable(argv0); + AtomNetlist& _netlist; + Tcl_Interp* _tcl_interp; + XDCTclClient _client; + std::list _method_dispatch_list; - Tcl_Interp* tcl_interp = Tcl_CreateInterp(); - if ((error = Tcl_Init(tcl_interp)) != TCL_OK) { - Tcl_DeleteInterp(tcl_interp); - throw XDC_eFailedToInitTcl(error); +private: +#ifdef DEBUG + inline void _debug_init() const { + VTR_ASSERT(this->_init); } - auto constraints = VprConstraints(); - auto client_data = TclClientData(constraints, netlist); /* ! Shares ownership of constraints and netlist */ + bool _init; +#else + inline void _debug_init() const {} +#endif +}; - throw XDC_eCustomException("XDC parser is not fully implemented"); -} \ No newline at end of file +VprConstraints read_xdc_constraints_to_vpr( + const t_arch& arch, + AtomNetlist& netlist, + std::istream& xdc_stream) +{ + VTR_ASSERT(_xdc_initialized); + + XDCCtx ctx(arch, netlist); /* ! Shares ownership of arch and netlist */ + ctx.init(); + ctx.read_xdc(xdc_stream); + return std::move(ctx.constraints); +} + +void load_xdc_constraints_file( + const char* read_xdc_constraints_name, + const t_arch& arch, + AtomNetlist& netlist) +{ + VTR_ASSERT(vtr::check_file_name_extension(read_xdc_constraints_name, ".xdc")); + VTR_LOG("Reading XDC %s...\n", read_xdc_constraints_name); + + std::ifstream file(read_xdc_constraints_name); + FloorplanningContext& floorplanning_ctx = g_vpr_ctx.mutable_floorplanning(); + VprConstraints& constraints = floorplanning_ctx.constraints; + + try { + constraints = read_xdc_constraints_to_vpr(arch, netlist, file); + } catch (XDC_eErroneousXDC& e) { + e.filename = std::string(read_xdc_constraints_name); + throw e; + } +} diff --git a/vpr/src/base/xdc_constraints.h b/vpr/src/base/xdc_constraints.h new file mode 100644 index 00000000000..df9f8a9efe6 --- /dev/null +++ b/vpr/src/base/xdc_constraints.h @@ -0,0 +1,75 @@ +#ifndef XDC_CONSTRAINTS_H +#define XDC_CONSTRAINTS_H + +#include + +#include "atom_netlist.h" +#include "physical_types.h" + +class XDC_eException { +public: + virtual ~XDC_eException() = default; + virtual operator std::string() const; +}; + +class XDC_eCustomException : XDC_eException { +public: + XDC_eCustomException(std::string&& message_); + ~XDC_eCustomException() override = default; + operator std::string() const override; + + std::string message; +}; + +class XDC_eFailedToInitTcl : XDC_eException { +public: + XDC_eFailedToInitTcl(int error_code_); + ~XDC_eFailedToInitTcl() override = default; + operator std::string() const override; + + int error_code; +}; + +class XDC_eErroneousXDC : XDC_eException { +public: + XDC_eErroneousXDC(std::string&& filename_, int line_, int column_, std::string&& message_); + ~XDC_eErroneousXDC() override = default; + operator std::string() const override; + + std::string filename; + int line, column; + std::string message; +}; + +/** + * \brief Init TCL susystem + */ +void xdc_init(); + +/** + * \brief Creates VprConstraints from input stream in XDC format. + * \throws XDC_eException: base class for all exceptions + * XDC_eFailedToInitTcl: Failure during initializations of TCL subsystem + * XDC_eErroneousXDC: Failure when parsing XDC + * XDC_eCustomException: Anything else that doesn't fit he above categories. + */ +VprConstraints read_xdc_constraints_to_vpr( + const t_arch& arch, + AtomNetlist& netlist, + std::istream& xdc_stream +); + +/** + * \brief Parse a file in XDC format and apply it to global FloorplanningContext. + * \throws XDC_eException: base class for all exceptions + * XDC_eFailedToInitTcl: Failure during initializations of TCL subsystem + * XDC_eErroneousXDC: Failure when parsing XDC + * XDC_eCustomException: Anything else that doesn't fit he above categories. + */ +void load_xdc_constraints_file( + const char* read_xdc_constraints_name, + const t_arch& arch, + AtomNetlist& netlist +); + +#endif From 2242ce9136b77153b7aa8916bea2590bcf2dc998 Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Tue, 19 Apr 2022 13:12:05 -0500 Subject: [PATCH 21/38] XDC: improve TCL handling, associate physical pin names with grid locations and bind them to selected ports Signed-off-by: Krzysztof Boronski --- libs/libarchfpga/src/physical_types.h | 11 +- .../src/read_fpga_interchange_arch.cpp | 31 ++- vpr/src/base/vpr_api.cpp | 1 - vpr/src/base/vpr_constraints.cpp | 3 +- vpr/src/base/vpr_constraints.h | 4 +- vpr/src/base/xdc_constraints.cpp | 196 +++++++++++++----- vpr/src/base/xdc_constraints.h | 13 +- 7 files changed, 192 insertions(+), 67 deletions(-) diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index d4ead261c88..1581d99e67c 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -976,7 +976,7 @@ struct t_mode { * input_string: input string verbatim to parse later * output_string: input string output to parse later * annotations: Annotations for delay, power, etc - * num_annotations: Total number of annotations + * num_annotations: Total number opb_typef annotations * infer_annotations: This interconnect is autogenerated, if true, infer pack_patterns * such as carry-chains and forced packs based on interconnect linked to it * parent_mode_index: Mode of parent as int @@ -1773,8 +1773,13 @@ struct t_lut_element { } }; +struct t_phys_map_region { + int x, y, w, h; + int subtile; +}; + /* Maps Physical PIN name to a name of PAD BEL */ -typedef std::unordered_map t_phys_grid_mapping; +typedef std::unordered_map t_phys_grid_mapping; /* Detailed routing architecture */ struct t_arch { @@ -1834,7 +1839,7 @@ struct t_arch { std::string ipin_cblock_switch_name; std::vector grid_layouts; //Set of potential device layouts - t_phys_grid_mapping phys_grid_mapping; //Mapping from physical pins to grid PADs. + t_phys_grid_mapping phys_grid_mapping; //Mapping from physical pins to grid locations t_clock_arch_spec clock_arch; // Clock related data types }; diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index b79ff01b7a1..545b0278e2c 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -806,10 +806,13 @@ struct ArchReader { } void process_package_pins() { + std::unordered_map site_to_pin_map; + for (auto package : ar_.getPackages()) { for (auto pin : package.getPackagePins()) { t_package_pin pckg_pin; pckg_pin.name = str(pin.getPackagePin()); + if (pin.getBel().isBel()) { pckg_pin.bel_name = str(pin.getBel().getBel()); @@ -821,7 +824,33 @@ struct ArchReader { package_pins_.push_back(pckg_pin); - arch_->phys_grid_mapping[pckg_pin.name] = pckg_pin.bel_name; + site_to_pin_map[pckg_pin.site_name] = pckg_pin.name; + } + } + + for (const auto& tile : ar_.getTileList()) { + int site_idx = 0; + for (const auto& site : tile.getSites()) { + int subtile = site_idx++; + + std::string site_name = str(site.getName()); + auto it = site_to_pin_map.find(site_name); + if (it == site_to_pin_map.end()) + continue; + std::string& pin_name = it->second; + + auto tile_type = ar_.getTileTypeList()[tile.getType()]; + + /* TODO: All tiles are currently set to 1x1 size, but this is done in process_tiles method which gets + * called later. We should split this logic in a way that's more suitable for constraining pins, + * because currently the line below makes an assumption about tile size which happens to be true only + * due to process_tiles implementation. + * + * A similar thing could be said about subtile indexing. + */ + t_phys_map_region pin_region { tile.getCol() + 1, tile.getRow() + 1, 1, 1, subtile }; + + arch_->phys_grid_mapping[pin_name] = std::move(pin_region); } } } diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index f501ec8d791..06eb259e9a0 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -350,7 +350,6 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a auto& filename_opts = vpr_setup->FileNameOpts; if (!filename_opts.read_vpr_constraints_file.empty()) load_vpr_constraints_file(filename_opts.read_vpr_constraints_file.c_str()); - if (!filename_opts.read_xdc_constraints_file.empty()) { try { load_xdc_constraints_file(filename_opts.read_xdc_constraints_file.c_str(), *arch, atom_ctx.nlist); diff --git a/vpr/src/base/vpr_constraints.cpp b/vpr/src/base/vpr_constraints.cpp index 95c7e7b7358..927df3e3d4f 100644 --- a/vpr/src/base/vpr_constraints.cpp +++ b/vpr/src/base/vpr_constraints.cpp @@ -28,8 +28,9 @@ PartitionId VprConstraints::get_atom_partition(AtomBlockId blk_id) { } } -void VprConstraints::add_partition(Partition part) { +PartitionId VprConstraints::add_partition(Partition part) { partitions.push_back(part); + return PartitionId(partitions.size() - 1); } Partition VprConstraints::get_partition(PartitionId part_id) { diff --git a/vpr/src/base/vpr_constraints.h b/vpr/src/base/vpr_constraints.h index fd3f64842a4..524c9ae5a54 100644 --- a/vpr/src/base/vpr_constraints.h +++ b/vpr/src/base/vpr_constraints.h @@ -55,11 +55,11 @@ class VprConstraints { PartitionId get_atom_partition(AtomBlockId blk_id); /** - * @brief Store a partition + * @brief Store a partition, return its id * * @param part The partition being stored */ - void add_partition(Partition part); + PartitionId add_partition(Partition part); /** * @brief Return a partition diff --git a/vpr/src/base/xdc_constraints.cpp b/vpr/src/base/xdc_constraints.cpp index 219978b40a4..d7170ecae37 100644 --- a/vpr/src/base/xdc_constraints.cpp +++ b/vpr/src/base/xdc_constraints.cpp @@ -6,6 +6,7 @@ #include #include #include +#include //#include "DeviceResources.capnp.h" //#include "LogicalNetlist.capnp.h" @@ -40,9 +41,8 @@ OV_TOSTRING(XDC_eErroneousXDC, this->filename + ":" + std::to_string(this->line) /* Stolen from nextpnr */ static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) { - char *copy = Tcl_Alloc(s.size() + 1); - std::copy(s.begin(), s.end(), copy); - copy[s.size()] = '\0'; + char *copy = Tcl_Alloc(s.length() + 1); + std::strcpy(copy, s.c_str()); Tcl_SetResult(interp, copy, TCL_DYNAMIC); } @@ -50,6 +50,7 @@ using TclCommandError = std::string; enum class e_TclCommandStatus : int { TCL_CMD_SUCCESS, TCL_CMD_SUCCESS_STRING, + TCL_CMD_SUCCESS_OBJECT, TCL_CMD_FAIL }; @@ -59,26 +60,58 @@ enum class e_XDCProperty { }; static e_XDCProperty xdc_prop_from_str(const char* str) { - if (!strcmp(str, "PACKAGE_PIN")) + if (!std::strcmp(str, "PACKAGE_PIN")) return e_XDCProperty::XDC_PROP_PACKAGE_PIN; return e_XDCProperty::XDC_PROP_UNKNOWN; } +/* TCL Object Types */ + +extern const Tcl_ObjType port_tcl_t; + +template constexpr const Tcl_ObjType* _tcl_type_of(); +template <> +constexpr const Tcl_ObjType* _tcl_type_of() { + return &port_tcl_t; +} + +class XDCTclClient; + +template +static T* _tcl_obj_getptr(Tcl_Obj* obj) { + if (obj->typePtr != _tcl_type_of()) + return nullptr; + return static_cast(obj->internalRep.twoPtrValue.ptr2); +} + +template <> +XDCTclClient* _tcl_obj_getptr(Tcl_Obj* obj) { + return static_cast(obj->internalRep.twoPtrValue.ptr1); +} + +template <> +const XDCTclClient* _tcl_obj_getptr(Tcl_Obj* obj) { + return static_cast(obj->internalRep.twoPtrValue.ptr1); +} + +/* XDCTclClient - XDC commands are implemented here */ + class XDCTclClient { public: - const t_arch& arch; VprConstraints& constraints; e_TclCommandStatus cmd_status; - AtomNetlist& netlist; std::string string; + Tcl_Obj* object; + const t_arch& arch; + const AtomNetlist& netlist; - XDCTclClient(const t_arch& arch_, VprConstraints& constraints_, AtomNetlist& netlist_) : - arch(arch_), + XDCTclClient(VprConstraints& constraints_, const t_arch& arch_, const AtomNetlist& netlist_) : constraints(constraints_), cmd_status(e_TclCommandStatus::TCL_CMD_SUCCESS), - netlist(netlist_), - string("No errors") + string("No errors"), + arch(arch_), + netlist(netlist_) {} /* TCL functions */ @@ -127,43 +160,52 @@ class XDCTclClient { const char* pin_name = Tcl_GetString(tcl_pin_name); if (pin_name == nullptr) return this->_ret_error("set_property: pin_name of PACKAGE_PIN should be a string."); - - const char* port_name = Tcl_GetString(tcl_port); - if (port_name == nullptr) - return this->_ret_error("set_property: pin_name of PACKAGE_PIN should be a string."); - return this->_do_set_property_package_pin(pin_name, port_name); + auto* port = _tcl_obj_getptr(tcl_port); + if (port == nullptr) + return this->_ret_error("set_property: pin_name of PACKAGE_PIN should have a `port` type."); + + return this->_do_set_property_package_pin(pin_name, *port); return error; } - int _do_set_property_package_pin(const char* pin_name, const char* port_name) { - VTR_LOG("TCL: set_property PACKAGE_PIN %s %s\n", pin_name, port_name); - + int _do_set_property_package_pin(const char* pin_name, AtomPortId port) { const auto& phys_grid_mapping = this->arch.phys_grid_mapping; - //g_vpr_ctx.placement().physical_pins + std::string pin_str(pin_name); - /* if (!phys_grid_mapping.count(pin_str)) - return this->_ret_error("Can't find physical PIN named `" + - pin_str + "`"); */ auto it = phys_grid_mapping.find(pin_str); if (it == phys_grid_mapping.end()) return this->_ret_error("Can't find physical PIN named `" + pin_str + "`"); - std::string bel_name = it->second; - - VTR_LOG("XDC: Assigned PACKAGE_PIN %s = %s\n", pin_name, bel_name.c_str()); + const t_phys_map_region& package_pin_region = it->second; - /* TODO: Handle pins W/O ports properly */ - //AtomPortId port = this->netlist.pin_port(pin); - //constraints.add_constrained_atom() + AtomBlockId port_block = this->netlist.port_block(port); + Partition part; + part.set_name(pin_str + ".PART"); + Region r; + r.set_region_rect( + package_pin_region.x, + package_pin_region.y, + package_pin_region.x + package_pin_region.w - 1, + package_pin_region.y + package_pin_region.h - 1 + ); + r.set_sub_tile(package_pin_region.subtile); + PartitionRegion pr; + pr.add_to_part_region(r); + part.set_part_region(pr); + PartitionId part_id = this->constraints.add_partition(part); + this->constraints.add_constrained_atom(port_block, part_id); + + VTR_LOG("XDC: Assigned PACKAGE_PIN %s = [%d, %d, %d, %d].%d\n", + pin_name, package_pin_region.x, package_pin_region.y, + package_pin_region.w, package_pin_region.h, package_pin_region.subtile); + return this->_ret_ok(); } int _do_get_ports(const char* pin_name) { - VTR_LOG("TCL: get_ports %s\n", pin_name); - AtomPinId pin = this->netlist.find_pin(pin_name); if (pin == AtomPinId::INVALID()) { std::string available_pins; @@ -184,9 +226,9 @@ class XDCTclClient { AtomPortId port = this->netlist.pin_port(pin); std::string port_name = this->netlist.port_name(port); - VTR_LOG("get_ports: Got port `%s` for pin `%s`\n", port_name.c_str(), pin_name); + VTR_LOG("get_ports: Got port `%s` (%llu) for pin `%s`\n", port_name.c_str(), (size_t)port, pin_name); - return this->_ret_string(std::move(port_name)); + return this->_ret_obj(port); } /* Return methods */ @@ -207,6 +249,65 @@ class XDCTclClient { this->string = std::move(string_); return TCL_OK; } + + template + inline int _ret_obj(const T& object_data) { + const Tcl_ObjType* type = _tcl_type_of(); + Tcl_Obj* obj = Tcl_NewObj(); + obj->bytes = nullptr; + obj->typePtr = type; + obj->internalRep.twoPtrValue.ptr1 = this; + obj->internalRep.twoPtrValue.ptr2 = Tcl_Alloc(sizeof(T)); + new(obj->internalRep.twoPtrValue.ptr2) T(object_data); + if (type->updateStringProc != nullptr) + type->updateStringProc(obj); + + this->object = obj; + this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS_OBJECT; + + return TCL_OK; + } +}; + +template +static void _tcl_obj_free(Tcl_Obj* obj) { + T* obj_data = _tcl_obj_getptr(obj); + obj_data->~T(); + Tcl_Free(reinterpret_cast(obj_data)); +} + +static void _tcl_obj_dup(Tcl_Obj* src, Tcl_Obj* dst) { + dst->internalRep.twoPtrValue = src->internalRep.twoPtrValue; + dst->bytes = nullptr; + dst->length = 0; +} + +static void _tcl_set_obj_string(Tcl_Obj* obj, const std::string& str) { + if (obj->bytes != nullptr) + Tcl_Free(obj->bytes); + obj->bytes = Tcl_Alloc(str.length() + 1); + obj->length = str.length(); + std::strcpy(obj->bytes, str.c_str()); +} + +static int _tcl_set_from_none(Tcl_Interp *tcl_interp, Tcl_Obj *obj) { + (void)(obj); /* Surpress "unused parameter" macro */ + /* TODO: Better error message */ + Tcl_SetStringResult(tcl_interp, "Attempted an invalid conversion."); + return TCL_ERROR; +} + +const Tcl_ObjType port_tcl_t = (Tcl_ObjType){ + .name = "port", + .freeIntRepProc = _tcl_obj_free, + .dupIntRepProc = _tcl_obj_dup, + .updateStringProc = [](Tcl_Obj* obj) { + const auto* port = _tcl_obj_getptr(obj); + const auto* client = _tcl_obj_getptr(obj); + std::string port_name = client->netlist.port_name(*port); + _tcl_set_obj_string(obj, port_name); + }, + .setFromAnyProc = _tcl_set_from_none }; static bool _xdc_initialized = false; @@ -226,10 +327,9 @@ void xdc_init() { */ class XDCCtx { public: - XDCCtx(const t_arch& arch, AtomNetlist& netlist) : + XDCCtx(const t_arch& arch_, const AtomNetlist& netlist_) : constraints(VprConstraints()), - _netlist(netlist), - _client(arch, this->constraints, this->_netlist) + _client(this->constraints, arch_, netlist_) { this->_tcl_interp = Tcl_CreateInterp(); #ifdef DEBUG @@ -288,6 +388,7 @@ class XDCCtx { * \brief Add TCL commands used in XDC files */ void _extend_tcl() { + this->_add_tcl_type(&port_tcl_t); this->_add_tcl_cmd("get_ports", &XDCTclClient::get_ports); this->_add_tcl_cmd("set_property", &XDCTclClient::set_property); } @@ -345,6 +446,9 @@ class XDCCtx { return TCL_ERROR; case e_TclCommandStatus::TCL_CMD_SUCCESS_STRING: Tcl_SetStringResult(tcl_interp, d->ctx._client.string); + return TCL_OK; + case e_TclCommandStatus::TCL_CMD_SUCCESS_OBJECT: + Tcl_SetObjResult(tcl_interp, d->ctx._client.object); default: break; } return TCL_OK; @@ -356,7 +460,10 @@ class XDCCtx { Tcl_CreateObjCommand(this->_tcl_interp, name, XDCCtx::_tcl_do_method, &md, nullptr); } - AtomNetlist& _netlist; + void _add_tcl_type(const Tcl_ObjType* ty) { + Tcl_RegisterObjType(ty); + } + Tcl_Interp* _tcl_interp; XDCTclClient _client; std::list _method_dispatch_list; @@ -373,33 +480,24 @@ class XDCCtx { #endif }; -VprConstraints read_xdc_constraints_to_vpr( - const t_arch& arch, - AtomNetlist& netlist, - std::istream& xdc_stream) -{ +VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, const AtomNetlist& netlist) { VTR_ASSERT(_xdc_initialized); - XDCCtx ctx(arch, netlist); /* ! Shares ownership of arch and netlist */ + XDCCtx ctx(arch, netlist); ctx.init(); ctx.read_xdc(xdc_stream); return std::move(ctx.constraints); } -void load_xdc_constraints_file( - const char* read_xdc_constraints_name, - const t_arch& arch, - AtomNetlist& netlist) -{ +void load_xdc_constraints_file(const char* read_xdc_constraints_name, const t_arch& arch, const AtomNetlist& netlist) { VTR_ASSERT(vtr::check_file_name_extension(read_xdc_constraints_name, ".xdc")); VTR_LOG("Reading XDC %s...\n", read_xdc_constraints_name); std::ifstream file(read_xdc_constraints_name); FloorplanningContext& floorplanning_ctx = g_vpr_ctx.mutable_floorplanning(); - VprConstraints& constraints = floorplanning_ctx.constraints; try { - constraints = read_xdc_constraints_to_vpr(arch, netlist, file); + floorplanning_ctx.constraints = read_xdc_constraints_to_vpr(file, arch, netlist); } catch (XDC_eErroneousXDC& e) { e.filename = std::string(read_xdc_constraints_name); throw e; diff --git a/vpr/src/base/xdc_constraints.h b/vpr/src/base/xdc_constraints.h index df9f8a9efe6..f0bc450d2e2 100644 --- a/vpr/src/base/xdc_constraints.h +++ b/vpr/src/base/xdc_constraints.h @@ -5,6 +5,7 @@ #include "atom_netlist.h" #include "physical_types.h" +#include "vpr_constraints.h" class XDC_eException { public: @@ -53,11 +54,7 @@ void xdc_init(); * XDC_eErroneousXDC: Failure when parsing XDC * XDC_eCustomException: Anything else that doesn't fit he above categories. */ -VprConstraints read_xdc_constraints_to_vpr( - const t_arch& arch, - AtomNetlist& netlist, - std::istream& xdc_stream -); +VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, const AtomNetlist& netlist); /** * \brief Parse a file in XDC format and apply it to global FloorplanningContext. @@ -66,10 +63,6 @@ VprConstraints read_xdc_constraints_to_vpr( * XDC_eErroneousXDC: Failure when parsing XDC * XDC_eCustomException: Anything else that doesn't fit he above categories. */ -void load_xdc_constraints_file( - const char* read_xdc_constraints_name, - const t_arch& arch, - AtomNetlist& netlist -); +void load_xdc_constraints_file(const char* read_xdc_constraints_name, const t_arch& arch, const AtomNetlist& netlist); #endif From 885153d9cb7dcce380259a2451cf95c12a432d35 Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Wed, 20 Apr 2022 11:55:16 -0500 Subject: [PATCH 22/38] Refactor XDC handling, add comments, separate wrappers for tcl stuff from XDC command implementation Signed-off-by: Krzysztof Boronski --- vpr/src/base/SetupVPR.cpp | 4 +- vpr/src/base/oo_tcl.cpp | 160 ++++++++++++++ vpr/src/base/oo_tcl.h | 322 ++++++++++++++++++++++++++++ vpr/src/base/vpr_api.cpp | 5 +- vpr/src/base/xdc_constraints.cpp | 347 +++---------------------------- vpr/src/base/xdc_constraints.h | 65 ++---- 6 files changed, 535 insertions(+), 368 deletions(-) create mode 100644 vpr/src/base/oo_tcl.cpp create mode 100644 vpr/src/base/oo_tcl.h diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index b76394f7517..e96f6cb095f 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -27,7 +27,7 @@ #include "clock_modeling.h" #include "ShowSetup.h" -#include "xdc_constraints.h" +#include "oo_tcl.h" static void SetupNetlistOpts(const t_options& Options, t_netlist_opts& NetlistOpts); static void SetupPackerOpts(const t_options& Options, @@ -113,7 +113,7 @@ void SetupVPR(const t_options* Options, SetupPowerOpts(*Options, PowerOpts, Arch); if (Options->XDCFile.value() != "") - xdc_init(); + tcl_init(); if (readArchFile == true) { vtr::ScopedStartFinishTimer t("Loading Architecture Description"); diff --git a/vpr/src/base/oo_tcl.cpp b/vpr/src/base/oo_tcl.cpp new file mode 100644 index 00000000000..a505ed2f46d --- /dev/null +++ b/vpr/src/base/oo_tcl.cpp @@ -0,0 +1,160 @@ +#include "oo_tcl.h" + +#include +#include +#include +#include + +#include "globals.h" +#include "vtr_log.h" + +/* TCL ERRORS */ +#define OV_TOSTRING(c, e) c::operator std::string() const { return (e); } + +OV_TOSTRING(TCL_eException, "Unknown error") + +TCL_eCustomException::TCL_eCustomException(std::string&& message_) : message(std::move(message_)) {} +TCL_eCustomException::operator std::string() const { return (this->message); } + +TCL_eFailedToInitTcl::TCL_eFailedToInitTcl(int error_code_) : error_code(error_code_) {} +OV_TOSTRING(TCL_eFailedToInitTcl, "Can't initialize TCL (code " + std::to_string(error_code) +")") + +TCL_eErroneousTCL::TCL_eErroneousTCL( + std::string&& filename_, + int line_, int column_, + std::string&& message_) + : filename(std::move(filename_)), line(line_), column(column_), message(message_) {} +OV_TOSTRING(TCL_eErroneousTCL, this->filename + ":" + std::to_string(this->line) + "," + + std::to_string(this->column) + ": " + this->message) + + +void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) { + char *copy = Tcl_Alloc(s.length() + 1); + std::strcpy(copy, s.c_str()); + Tcl_SetResult(interp, copy, TCL_DYNAMIC); +} + +TclClient::TclClient() : + cmd_status(e_TclCommandStatus::TCL_CMD_SUCCESS), + string("No errors") {} + + +void tcl_obj_dup(Tcl_Obj* src, Tcl_Obj* dst) { + dst->internalRep.twoPtrValue = src->internalRep.twoPtrValue; + dst->bytes = nullptr; + dst->length = 0; +} + +void tcl_set_obj_string(Tcl_Obj* obj, const std::string& str) { + if (obj->bytes != nullptr) + Tcl_Free(obj->bytes); + obj->bytes = Tcl_Alloc(str.length() + 1); + obj->length = str.length(); + std::strcpy(obj->bytes, str.c_str()); +} + +int tcl_set_from_none(Tcl_Interp *tcl_interp, Tcl_Obj *obj) { + (void)(obj); /* Surpress "unused parameter" macro */ + /* TODO: Better error message */ + Tcl_SetStringResult(tcl_interp, "Attempted an invalid conversion."); + return TCL_ERROR; +} + +static bool _tcl_initialized = false; + +void tcl_init() { + if (!_tcl_initialized) + Tcl_FindExecutable(_argv0); + _tcl_initialized = true; + + VTR_LOG("Initialized TCL subsystem"); +} + + +TclCtx::TclCtx() { + this->_tcl_interp = Tcl_CreateInterp(); +#ifdef DEBUG + this->_init = false; +#endif + VTR_LOG("Created XDC context.\n"); +} + +TclCtx::~TclCtx() { + Tcl_DeleteInterp(this->_tcl_interp); + + VTR_LOG("Deleted XDC context.\n"); +} + +void TclCtx::init() { + int error; + + if ((error = Tcl_Init(this->_tcl_interp)) != TCL_OK) + throw TCL_eFailedToInitTcl(error); + + error = this->_fix_port_indexing(); + VTR_ASSERT(error == TCL_OK); + +#ifdef DEBUG + this->_init = true; +#endif +} + +void TclCtx::read_tcl(std::istream& xdc_stream) { + int error; + + this->_debug_init(); + + std::ostringstream os; + xdc_stream >> os.rdbuf(); + std::string xdc = os.str(); + + error = Tcl_Eval(this->_tcl_interp, xdc.c_str()); + /* TODO: More precise error */ + if (error != TCL_OK) { + int error_line = Tcl_GetErrorLine(this->_tcl_interp); + const char* msg = Tcl_GetStringResult(this->_tcl_interp); + throw TCL_eErroneousTCL("", error_line, 0, std::string(msg)); + } +} + +int TclCtx::_fix_port_indexing() { + int error; + error = Tcl_Eval(this->_tcl_interp, "rename unknown _original_unknown"); + if (error != TCL_OK) + return error; + error = Tcl_Eval( + this->_tcl_interp, + "proc unknown args {\n" + " set result [scan [lindex $args 0] \"%d\" value]\n" + " if { $result == 1 && [llength $args] == 1 } {\n" + " return \\[$value\\]\n" + " } else {\n" + " uplevel 1 [list _original_unknown {*}$args]\n" + " }\n" + "}" + ); + return error; +} + +int TclCtx::_tcl_do_method( + ClientData cd, + Tcl_Interp* tcl_interp, + int objc, + Tcl_Obj* const objvp[] +) { + TclMethodDispatchBase* d = static_cast(cd); + d->do_method(objc, objvp); + + switch (d->client.cmd_status) { + case e_TclCommandStatus::TCL_CMD_FAIL: + Tcl_SetStringResult(tcl_interp, d->client.string); + return TCL_ERROR; + case e_TclCommandStatus::TCL_CMD_SUCCESS_STRING: + Tcl_SetStringResult(tcl_interp, d->client.string); + return TCL_OK; + case e_TclCommandStatus::TCL_CMD_SUCCESS_OBJECT: + Tcl_SetObjResult(tcl_interp, d->client.object); + default: break; + } + return TCL_OK; +} \ No newline at end of file diff --git a/vpr/src/base/oo_tcl.h b/vpr/src/base/oo_tcl.h new file mode 100644 index 00000000000..7569123da57 --- /dev/null +++ b/vpr/src/base/oo_tcl.h @@ -0,0 +1,322 @@ +#ifndef OO_TCL_H +#define OO_TCL_H + +#include +#include +#include + +#include + +/** + * \file + * \brief Object-oriented wrappers for TCL interpreter. + * Overview + * ======== + * This file contains function declarations and definitionsas well as classes that allow embedding TCL interpreter in C++ + * context in a way that allows it to be extended with multiple "clients" that provide methods for managing their content. + * + * A standard scenario is: + * - // Create an instance of TclClient-derived class + * MyTclClient client; + * - TclCtx ctx; + * - // Call this before doing anything else with `ctx`. + * ctx.init() + * - // This will register all methods provided by `client` + * ctx.add_client(client) + * - // Read and interpret TCL code + * ctx.read_tcl(stream) + * - // Process results stored by a client + * do_something(client.data) + * + */ + +/** + * \brief Base class for any exception thrown within TCLCtx. + */ +class TCL_eException { +public: + virtual ~TCL_eException() = default; + virtual operator std::string() const; +}; + +/** + * \brief Generic exception thrown within TCLCtx. Features a message string. + */ +class TCL_eCustomException : TCL_eException { +public: + TCL_eCustomException(std::string&& message_); + ~TCL_eCustomException() override = default; + operator std::string() const override; + + std::string message; +}; + +/** + * \brief Exception thrown within TCLCtx if it fails during TCLCtx::init() + */ +class TCL_eFailedToInitTcl : TCL_eException { +public: + TCL_eFailedToInitTcl(int error_code_); + ~TCL_eFailedToInitTcl() override = default; + operator std::string() const override; + + int error_code; +}; + +/** + * \brief Exception thrown within TCLCtx when an error occurs while interpreting TCL. + */ +class TCL_eErroneousTCL : TCL_eException { +public: + TCL_eErroneousTCL(std::string&& filename_, int line_, int column_, std::string&& message_); + ~TCL_eErroneousTCL() override = default; + operator std::string() const override; + + std::string filename; + int line, column; + std::string message; +}; + +void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s); + +template constexpr const Tcl_ObjType* tcl_type_of(); + +/** + * \brief Get pointer to a C++ object managed within TCL-context + * \return Pointer to a C++ object if `T` matches the type of an object. Otherwise nullptr. + */ +template +static T* tcl_obj_getptr(Tcl_Obj* obj) { + if (obj->typePtr != tcl_type_of()) + return nullptr; + return static_cast(obj->internalRep.twoPtrValue.ptr2); +} + +using TclCommandError = std::string; + +/** + * \brief Descibes a after exiting a custom TCL command implementation. + */ +enum class e_TclCommandStatus : int { + TCL_CMD_SUCCESS, /* Command exited with no return value. */ + TCL_CMD_SUCCESS_STRING, /* Command exited returning a string. */ + TCL_CMD_SUCCESS_OBJECT, /* Command exited returning a custom object. */ + TCL_CMD_FAIL /* Command exited with a failure. Will cause a TCL_eErroneousTCL to be thrown. */ +}; + +class TclClient { +public: + TclClient(); + + e_TclCommandStatus cmd_status; + std::string string; + Tcl_Obj* object; + +protected: + + /* Return methods + * Use these when returning from a TCL command implementation. + */ + + /** + * \brief Throw an error from a TCL command. + */ + inline int _ret_error(TclCommandError&& error) { + this->cmd_status = e_TclCommandStatus::TCL_CMD_FAIL; + this->string = std::move(error); + return TCL_ERROR; + } + + /** + * \brief Return from a TCL command with no errors. + */ + inline int _ret_ok() { + this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS; + return TCL_OK; + } + + /** + * \brief Return a string from a TCL command with no errors. + */ + inline int _ret_string(std::string&& string_) { + this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS_STRING; + this->string = std::move(string_); + return TCL_OK; + } + + /** + * \brief Move an object to TCL-managed context and return it in TCL code. + * \param T registered custom TCL type + * \param this_ pointer to the concrete client. Note that it might be different than `this` within context of base class. + * \param object_data object to be moved into TCL-managed context + */ + template + int _ret_obj(void* this_, const T&& object_data) { + const Tcl_ObjType* type = tcl_type_of(); + Tcl_Obj* obj = Tcl_NewObj(); + obj->bytes = nullptr; + obj->typePtr = type; + obj->internalRep.twoPtrValue.ptr1 = this_; + obj->internalRep.twoPtrValue.ptr2 = Tcl_Alloc(sizeof(T)); + new(obj->internalRep.twoPtrValue.ptr2) T(std::move(object_data)); + if (type->updateStringProc != nullptr) + type->updateStringProc(obj); + + this->object = obj; + this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS_OBJECT; + + return TCL_OK; + } +}; + +template +static void tcl_obj_free(Tcl_Obj* obj) { + T* obj_data = tcl_obj_getptr(obj); + obj_data->~T(); + Tcl_Free(reinterpret_cast(obj_data)); +} + +void tcl_obj_dup(Tcl_Obj* src, Tcl_Obj* dst); +void tcl_set_obj_string(Tcl_Obj* obj, const std::string& str); +int tcl_set_from_none(Tcl_Interp *tcl_interp, Tcl_Obj *obj); + +void tcl_init(); + +/** + * \brief Provide TCL runtime for parsing XDC files. + * Allows extending TCL language with new types which are associated with C++ types and new methods provided by objects of TclClient-derived classes. + * + * `TclCtx` binds a TCL context to the lifetime of a C++ object, so once TclCtx::~TclCtx gets called, TCL context will be cleaned-up. + * It is illegal to call any method of `TclCtx` othar than its destructor after destroying a bound client. + */ +class TclCtx { +public: + TclCtx(); + + ~TclCtx(); + + /** + * \brief Initialize a newly created instance of TclCtx. It's illegal to call any other method before calling this one. + */ + void init(); + + /** + * \brief Read and interpret a TCL script within this context. + */ + void read_tcl(std::istream& tcl_stream); + + /** + * \brief Add a client class that provides methods to interface with a context managed by it + */ + template + void add_tcl_client(C& client) { + auto* client_container = new TclClientContainer(client); + auto register_command = [&](const char* name, int(C::*method)(int objc, Tcl_Obj* const objvp[])) { + client_container->methods.push_front(TclMethodDispatch(method, client)); + auto& md = client_container->methods.front(); + Tcl_CreateObjCommand(this->_tcl_interp, name, TclCtx::_tcl_do_method, static_cast(&md), nullptr); + }; + + client.register_methods(register_command); + + this->_tcl_clients.push_front(std::unique_ptr(static_cast(client_container))); + } + + /** + * \brief Add a custom type to TCL. + * + * The type has to be moveable in order to allow passing it between Tcl-managed cotext and C++ code. + * Use REGISTER_TCL_TYPE macro to associate type T with a static struct instance describing the Tcl type. + */ + template + inline void add_tcl_type() { + Tcl_RegisterObjType(tcl_type_of()); + } + +protected: + + /** + * \brief Tcl by default will interpret bus indices as calls to commands + * (eg. in[0] gets interpreted as call to command `i` with argument being a + * result of calling `0`). This overrides this behaviour. + * + * TODO: This is SDC/XDC-specific and should be handled somewhere else. + */ + int _fix_port_indexing(); + + /** + * \brief Provide an interface to call a client method. + */ + struct TclMethodDispatchBase { + inline TclMethodDispatchBase(TclClient& client_) : client(client_) {} + virtual ~TclMethodDispatchBase() {} + virtual int do_method(int objc, Tcl_Obj* const objvp[]) = 0; + TclClient& client; + }; + + /** + * \brief Wrap a call-by-PTM (pointer-to-method) of a concrete TclClient-derived class as a virtual function of TclMethodDispatchBase. + * This allows us to avoid making methods defined within TclClient-derived virtual methods of TclClient. + */ + template + struct TclMethodDispatch : TclMethodDispatchBase { + typedef int(C::*TclMethod)(int objc, Tcl_Obj* const objvp[]); + inline TclMethodDispatch(TclMethod method_, TclClient& client_) : TclMethodDispatchBase(client_), method(method_) {} + virtual ~TclMethodDispatch() = default; + + virtual int do_method(int objc, Tcl_Obj* const objvp[]) { + auto& casted_client = static_cast(this->client); + return (casted_client.*(this->method))(objc, objvp); + } + + TclMethod method; + }; + + struct TclClientContainerBase {}; + + /** + * \brief Store a reference to a client along with method dispatch list associated with that client. + */ + template + struct TclClientContainer : TclClientContainerBase { + inline TclClientContainer(C& client_) : client(client_) {} + C& client; + /* Use list to guarantee the validity of references */ + std::list> methods; + }; + + static int _tcl_do_method( + ClientData cd, + Tcl_Interp* tcl_interp, + int objc, + Tcl_Obj* const objvp[] + ); + + Tcl_Interp* _tcl_interp; + /* Use list to guarantee the validity of references */ + std::list> _tcl_clients; + +private: +#ifdef DEBUG + inline void _debug_init() const { + VTR_ASSERT(this->_init); + } + + bool _init; +#else + inline void _debug_init() const {} +#endif +}; + +/** + * \brief Associate a C++ struct/class with a `Tcl_ObjType`. + * \param cxx_type C++ type name. + * \param tcl_type pointer to statically-allocated Tcl_ObjType. + */ +#define REGISTER_TCL_TYPE(cxx_type, tcl_type) \ + template <> \ + constexpr const Tcl_ObjType* tcl_type_of() { \ + return tcl_type; \ + } + +#endif \ No newline at end of file diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 06eb259e9a0..61ed038383f 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -69,6 +69,7 @@ #include "cluster.h" #include "output_clustering.h" #include "vpr_constraints_reader.h" +#include "oo_tcl.h" #include "xdc_constraints.h" #include "place_constraints.h" #include "place_util.h" @@ -353,10 +354,10 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a if (!filename_opts.read_xdc_constraints_file.empty()) { try { load_xdc_constraints_file(filename_opts.read_xdc_constraints_file.c_str(), *arch, atom_ctx.nlist); - } catch (const XDC_eErroneousXDC& e) { + } catch (const TCL_eErroneousTCL& e) { VTR_LOG_ERROR("Error reading XDC: %s\n", std::string(e).c_str()); throw e; - } catch (const XDC_eException& e) { + } catch (const TCL_eException& e) { VTR_LOG_ERROR("Error loading XDC: %s\n", std::string(e).c_str()); throw e; } diff --git a/vpr/src/base/xdc_constraints.cpp b/vpr/src/base/xdc_constraints.cpp index d7170ecae37..1d61d425bad 100644 --- a/vpr/src/base/xdc_constraints.cpp +++ b/vpr/src/base/xdc_constraints.cpp @@ -4,56 +4,17 @@ #include #include #include -#include #include -#include -//#include "DeviceResources.capnp.h" -//#include "LogicalNetlist.capnp.h" #include "globals.h" #include "vpr_constraints.h" #include "atom_netlist.h" +#include "oo_tcl.h" #include "xdc_constraints.h" -#define OV_TOSTRING(c, e) c::operator std::string() const { return (e); } - -/* XDC ERRORS */ - -OV_TOSTRING(XDC_eException, "Unknown error") - -XDC_eCustomException::XDC_eCustomException(std::string&& message_) : message(std::move(message_)) {} -XDC_eCustomException::operator std::string() const { return (this->message); } - -XDC_eFailedToInitTcl::XDC_eFailedToInitTcl(int error_code_) : error_code(error_code_) {} -OV_TOSTRING(XDC_eFailedToInitTcl, "Can't initialize TCL (code " + std::to_string(error_code) +")") - -XDC_eErroneousXDC::XDC_eErroneousXDC( - std::string&& filename_, - int line_, int column_, - std::string&& message_) - : filename(std::move(filename_)), line(line_), column(column_), message(message_) {} -OV_TOSTRING(XDC_eErroneousXDC, this->filename + ":" + std::to_string(this->line) + "," + - std::to_string(this->column) + ": " + this->message) - - /* TCL COMMANDS */ -/* Stolen from nextpnr */ -static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) { - char *copy = Tcl_Alloc(s.length() + 1); - std::strcpy(copy, s.c_str()); - Tcl_SetResult(interp, copy, TCL_DYNAMIC); -} - -using TclCommandError = std::string; -enum class e_TclCommandStatus : int { - TCL_CMD_SUCCESS, - TCL_CMD_SUCCESS_STRING, - TCL_CMD_SUCCESS_OBJECT, - TCL_CMD_FAIL -}; - enum class e_XDCProperty { XDC_PROP_PACKAGE_PIN, XDC_PROP_UNKNOWN @@ -69,51 +30,33 @@ static e_XDCProperty xdc_prop_from_str(const char* str) { /* TCL Object Types */ extern const Tcl_ObjType port_tcl_t; +REGISTER_TCL_TYPE(AtomPortId, &port_tcl_t); -template constexpr const Tcl_ObjType* _tcl_type_of(); -template <> -constexpr const Tcl_ObjType* _tcl_type_of() { - return &port_tcl_t; -} - -class XDCTclClient; - -template -static T* _tcl_obj_getptr(Tcl_Obj* obj) { - if (obj->typePtr != _tcl_type_of()) - return nullptr; - return static_cast(obj->internalRep.twoPtrValue.ptr2); -} - -template <> -XDCTclClient* _tcl_obj_getptr(Tcl_Obj* obj) { - return static_cast(obj->internalRep.twoPtrValue.ptr1); -} +class TclPhysicalConstraintsClient; template <> -const XDCTclClient* _tcl_obj_getptr(Tcl_Obj* obj) { - return static_cast(obj->internalRep.twoPtrValue.ptr1); +TclPhysicalConstraintsClient* tcl_obj_getptr(Tcl_Obj* obj) { + return static_cast(obj->internalRep.twoPtrValue.ptr1); } -/* XDCTclClient - XDC commands are implemented here */ - -class XDCTclClient { +class TclPhysicalConstraintsClient : public TclClient { public: VprConstraints& constraints; - e_TclCommandStatus cmd_status; - std::string string; - Tcl_Obj* object; const t_arch& arch; const AtomNetlist& netlist; - XDCTclClient(VprConstraints& constraints_, const t_arch& arch_, const AtomNetlist& netlist_) : + TclPhysicalConstraintsClient(VprConstraints& constraints_, const t_arch& arch_, const AtomNetlist& netlist_) : constraints(constraints_), - cmd_status(e_TclCommandStatus::TCL_CMD_SUCCESS), - string("No errors"), arch(arch_), netlist(netlist_) {} + typedef int(TclPhysicalConstraintsClient::*TclMethod)(int objc, Tcl_Obj* const objvp[]); + void register_methods(std::function register_method) { + register_method("get_ports", &TclPhysicalConstraintsClient::get_ports); + register_method("set_property", &TclPhysicalConstraintsClient::set_property); + }; + /* TCL functions */ int set_property(int objc, Tcl_Obj* const objvp[]) { @@ -161,7 +104,7 @@ class XDCTclClient { if (pin_name == nullptr) return this->_ret_error("set_property: pin_name of PACKAGE_PIN should be a string."); - auto* port = _tcl_obj_getptr(tcl_port); + auto* port = tcl_obj_getptr(tcl_port); if (port == nullptr) return this->_ret_error("set_property: pin_name of PACKAGE_PIN should have a `port` type."); @@ -172,7 +115,6 @@ class XDCTclClient { int _do_set_property_package_pin(const char* pin_name, AtomPortId port) { const auto& phys_grid_mapping = this->arch.phys_grid_mapping; - std::string pin_str(pin_name); auto it = phys_grid_mapping.find(pin_str); @@ -228,265 +170,38 @@ class XDCTclClient { VTR_LOG("get_ports: Got port `%s` (%llu) for pin `%s`\n", port_name.c_str(), (size_t)port, pin_name); - return this->_ret_obj(port); - } - - /* Return methods */ - - inline int _ret_error(TclCommandError&& error) { - this->cmd_status = e_TclCommandStatus::TCL_CMD_FAIL; - this->string = std::move(error); - return TCL_ERROR; - } - - inline int _ret_ok() { - this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS; - return TCL_OK; - } - - inline int _ret_string(std::string&& string_) { - this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS_STRING; - this->string = std::move(string_); - return TCL_OK; - } - - template - inline int _ret_obj(const T& object_data) { - const Tcl_ObjType* type = _tcl_type_of(); - Tcl_Obj* obj = Tcl_NewObj(); - obj->bytes = nullptr; - obj->typePtr = type; - obj->internalRep.twoPtrValue.ptr1 = this; - obj->internalRep.twoPtrValue.ptr2 = Tcl_Alloc(sizeof(T)); - new(obj->internalRep.twoPtrValue.ptr2) T(object_data); - if (type->updateStringProc != nullptr) - type->updateStringProc(obj); - - this->object = obj; - this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS_OBJECT; - - return TCL_OK; + return this->_ret_obj(this, std::move(port)); } }; -template -static void _tcl_obj_free(Tcl_Obj* obj) { - T* obj_data = _tcl_obj_getptr(obj); - obj_data->~T(); - Tcl_Free(reinterpret_cast(obj_data)); -} - -static void _tcl_obj_dup(Tcl_Obj* src, Tcl_Obj* dst) { - dst->internalRep.twoPtrValue = src->internalRep.twoPtrValue; - dst->bytes = nullptr; - dst->length = 0; -} - -static void _tcl_set_obj_string(Tcl_Obj* obj, const std::string& str) { - if (obj->bytes != nullptr) - Tcl_Free(obj->bytes); - obj->bytes = Tcl_Alloc(str.length() + 1); - obj->length = str.length(); - std::strcpy(obj->bytes, str.c_str()); -} - -static int _tcl_set_from_none(Tcl_Interp *tcl_interp, Tcl_Obj *obj) { - (void)(obj); /* Surpress "unused parameter" macro */ - /* TODO: Better error message */ - Tcl_SetStringResult(tcl_interp, "Attempted an invalid conversion."); - return TCL_ERROR; -} - const Tcl_ObjType port_tcl_t = (Tcl_ObjType){ .name = "port", - .freeIntRepProc = _tcl_obj_free, - .dupIntRepProc = _tcl_obj_dup, + .freeIntRepProc = tcl_obj_free, + .dupIntRepProc = tcl_obj_dup, .updateStringProc = [](Tcl_Obj* obj) { - const auto* port = _tcl_obj_getptr(obj); - const auto* client = _tcl_obj_getptr(obj); + const auto* port = tcl_obj_getptr(obj); + const auto* client = tcl_obj_getptr(obj); std::string port_name = client->netlist.port_name(*port); - _tcl_set_obj_string(obj, port_name); + tcl_set_obj_string(obj, port_name); }, - .setFromAnyProc = _tcl_set_from_none + .setFromAnyProc = tcl_set_from_none }; -static bool _xdc_initialized = false; - /* API */ -void xdc_init() { - if (!_xdc_initialized) - Tcl_FindExecutable(_argv0); - _xdc_initialized = true; - - VTR_LOG("Initialized TCL subsystem"); -} - -/** - * \brief Provide TCL runtime for parsing XDC files. - */ -class XDCCtx { -public: - XDCCtx(const t_arch& arch_, const AtomNetlist& netlist_) : - constraints(VprConstraints()), - _client(this->constraints, arch_, netlist_) - { - this->_tcl_interp = Tcl_CreateInterp(); -#ifdef DEBUG - this->_init = false; -#endif - VTR_LOG("Created XDC context.\n"); - } - - ~XDCCtx() { - Tcl_DeleteInterp(this->_tcl_interp); - - VTR_LOG("Deleted XDC context.\n"); - } - - void init() { - int error; - - if ((error = Tcl_Init(this->_tcl_interp)) != TCL_OK) - throw XDC_eFailedToInitTcl(error); - - error = this->_fix_port_indexing(); - VTR_ASSERT(error == TCL_OK); - this->_extend_tcl(); - -#ifdef DEBUG - this->_init = true; -#endif - } - - /** - * \brief Extend (VPR) constraints with constraints read from and XDC script - */ - void read_xdc(std::istream& xdc_stream) { - int error; - - this->_debug_init(); - - std::ostringstream os; - xdc_stream >> os.rdbuf(); - std::string xdc = os.str(); - - error = Tcl_Eval(this->_tcl_interp, xdc.c_str()); - /* TODO: More precise error */ - if (error != TCL_OK) { - int error_line = Tcl_GetErrorLine(this->_tcl_interp); - const char* msg = Tcl_GetStringResult(this->_tcl_interp); - throw XDC_eErroneousXDC("", error_line, 0, std::string(msg)); - } - } +VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, const AtomNetlist& netlist) { + //VTR_ASSERT(_xdc_initialized); VprConstraints constraints; -protected: - - /** - * \brief Add TCL commands used in XDC files - */ - void _extend_tcl() { - this->_add_tcl_type(&port_tcl_t); - this->_add_tcl_cmd("get_ports", &XDCTclClient::get_ports); - this->_add_tcl_cmd("set_property", &XDCTclClient::set_property); - } - - /** - * \brief Tcl by default will interpret bus indices as calls to commands - * (eg. in[0] gets interpreted as call to command `i` with argument being a - * result of calling `0`). This overrides this behaviour. - * - * Code taken from nextpnr. - */ - int _fix_port_indexing() { - int error; - error = Tcl_Eval(this->_tcl_interp, "rename unknown _original_unknown"); - if (error != TCL_OK) - return error; - error = Tcl_Eval( - this->_tcl_interp, - "proc unknown args {\n" - " set result [scan [lindex $args 0] \"%d\" value]\n" - " if { $result == 1 && [llength $args] == 1 } {\n" - " return \\[$value\\]\n" - " } else {\n" - " uplevel 1 [list _original_unknown {*}$args]\n" - " }\n" - "}" - ); - return error; - } - - typedef int(XDCTclClient::*TclMethod)(int objc, Tcl_Obj* const objvp[]); - - struct TclMethodDispatch { - TclMethodDispatch(TclMethod method_, XDCCtx& ctx_) : method(method_), ctx(ctx_) {} - - TclMethod method; - XDCCtx& ctx; - }; - - static int _tcl_do_method( - ClientData cd, - Tcl_Interp* tcl_interp, - int objc, - Tcl_Obj* const objvp[] - ) { - TclMethodDispatch* d = static_cast(cd); - auto method = d->method; - auto& client = d->ctx._client; - - (client.*method)(objc, objvp); - - switch (client.cmd_status) { - case e_TclCommandStatus::TCL_CMD_FAIL: - Tcl_SetStringResult(tcl_interp, d->ctx._client.string); - return TCL_ERROR; - case e_TclCommandStatus::TCL_CMD_SUCCESS_STRING: - Tcl_SetStringResult(tcl_interp, d->ctx._client.string); - return TCL_OK; - case e_TclCommandStatus::TCL_CMD_SUCCESS_OBJECT: - Tcl_SetObjResult(tcl_interp, d->ctx._client.object); - default: break; - } - return TCL_OK; - } - - void _add_tcl_cmd(const char* name, TclMethod method) { - this->_method_dispatch_list.push_front(TclMethodDispatch(method, *this)); - auto& md = this->_method_dispatch_list.front(); - Tcl_CreateObjCommand(this->_tcl_interp, name, XDCCtx::_tcl_do_method, &md, nullptr); - } - - void _add_tcl_type(const Tcl_ObjType* ty) { - Tcl_RegisterObjType(ty); - } - - Tcl_Interp* _tcl_interp; - XDCTclClient _client; - std::list _method_dispatch_list; - -private: -#ifdef DEBUG - inline void _debug_init() const { - VTR_ASSERT(this->_init); - } - - bool _init; -#else - inline void _debug_init() const {} -#endif -}; - -VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, const AtomNetlist& netlist) { - VTR_ASSERT(_xdc_initialized); - - XDCCtx ctx(arch, netlist); + TclPhysicalConstraintsClient pc_client(constraints, arch, netlist); + TclCtx ctx; ctx.init(); - ctx.read_xdc(xdc_stream); - return std::move(ctx.constraints); + ctx.add_tcl_type(); + ctx.add_tcl_client(pc_client); + ctx.read_tcl(xdc_stream); + + return std::move(constraints); } void load_xdc_constraints_file(const char* read_xdc_constraints_name, const t_arch& arch, const AtomNetlist& netlist) { @@ -498,7 +213,7 @@ void load_xdc_constraints_file(const char* read_xdc_constraints_name, const t_ar try { floorplanning_ctx.constraints = read_xdc_constraints_to_vpr(file, arch, netlist); - } catch (XDC_eErroneousXDC& e) { + } catch (TCL_eErroneousTCL& e) { e.filename = std::string(read_xdc_constraints_name); throw e; } diff --git a/vpr/src/base/xdc_constraints.h b/vpr/src/base/xdc_constraints.h index f0bc450d2e2..b2e62f5d106 100644 --- a/vpr/src/base/xdc_constraints.h +++ b/vpr/src/base/xdc_constraints.h @@ -7,61 +7,30 @@ #include "physical_types.h" #include "vpr_constraints.h" -class XDC_eException { -public: - virtual ~XDC_eException() = default; - virtual operator std::string() const; -}; - -class XDC_eCustomException : XDC_eException { -public: - XDC_eCustomException(std::string&& message_); - ~XDC_eCustomException() override = default; - operator std::string() const override; - - std::string message; -}; - -class XDC_eFailedToInitTcl : XDC_eException { -public: - XDC_eFailedToInitTcl(int error_code_); - ~XDC_eFailedToInitTcl() override = default; - operator std::string() const override; - - int error_code; -}; - -class XDC_eErroneousXDC : XDC_eException { -public: - XDC_eErroneousXDC(std::string&& filename_, int line_, int column_, std::string&& message_); - ~XDC_eErroneousXDC() override = default; - operator std::string() const override; - - std::string filename; - int line, column; - std::string message; -}; - -/** - * \brief Init TCL susystem - */ -void xdc_init(); - /** * \brief Creates VprConstraints from input stream in XDC format. - * \throws XDC_eException: base class for all exceptions - * XDC_eFailedToInitTcl: Failure during initializations of TCL subsystem - * XDC_eErroneousXDC: Failure when parsing XDC - * XDC_eCustomException: Anything else that doesn't fit he above categories. + * \param xdc_stream XDC script + * \param arch reference to loaded device architecture + * \param netlist loaded AtomNetlist + * \throws TCL_eException: base class for all exceptions + * TCL_eFailedToInitTcl: Failure during initializations of TCL subsystem + * TCL_eErroneousTCL: Failure when parsing XDC + * TCL_eCustomException: Anything else that doesn't fit he above categories. + * \return VprConstraints created from an XDC script. */ VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, const AtomNetlist& netlist); /** * \brief Parse a file in XDC format and apply it to global FloorplanningContext. - * \throws XDC_eException: base class for all exceptions - * XDC_eFailedToInitTcl: Failure during initializations of TCL subsystem - * XDC_eErroneousXDC: Failure when parsing XDC - * XDC_eCustomException: Anything else that doesn't fit he above categories. + * \param read_xdc_constraints_name path to an XDC file with constraints + * \param arch reference to loaded device architecture + * \param netlist loaded AtomNetlist + * \throws TCL_eException: base class for all exceptions + * TCL_eFailedToInitTcl: Failure during initializations of TCL subsystem + * TCL_eErroneousTCL: Failure when parsing XDC + * TCL_eCustomException: Anything else that doesn't fit he above categories. + * + * This function overwrites global floorplanning constraints g_vpr.constraints_.constraints */ void load_xdc_constraints_file(const char* read_xdc_constraints_name, const t_arch& arch, const AtomNetlist& netlist); From 15a9d7e77b6baf775c2a3a51d9ae14b99d86ddd6 Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Wed, 20 Apr 2022 12:32:40 -0500 Subject: [PATCH 23/38] USe atom block names when getting ports Signed-off-by: Krzysztof Boronski --- vpr/src/base/xdc_constraints.cpp | 46 +++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/vpr/src/base/xdc_constraints.cpp b/vpr/src/base/xdc_constraints.cpp index 1d61d425bad..07b0d4ff083 100644 --- a/vpr/src/base/xdc_constraints.cpp +++ b/vpr/src/base/xdc_constraints.cpp @@ -13,7 +13,6 @@ #include "oo_tcl.h" #include "xdc_constraints.h" -/* TCL COMMANDS */ enum class e_XDCProperty { XDC_PROP_PACKAGE_PIN, @@ -27,8 +26,6 @@ static e_XDCProperty xdc_prop_from_str(const char* str) { return e_XDCProperty::XDC_PROP_UNKNOWN; } -/* TCL Object Types */ - extern const Tcl_ObjType port_tcl_t; REGISTER_TCL_TYPE(AtomPortId, &port_tcl_t); @@ -55,7 +52,7 @@ class TclPhysicalConstraintsClient : public TclClient { void register_methods(std::function register_method) { register_method("get_ports", &TclPhysicalConstraintsClient::get_ports); register_method("set_property", &TclPhysicalConstraintsClient::set_property); - }; + } /* TCL functions */ @@ -148,25 +145,38 @@ class TclPhysicalConstraintsClient : public TclClient { } int _do_get_ports(const char* pin_name) { - AtomPinId pin = this->netlist.find_pin(pin_name); - if (pin == AtomPinId::INVALID()) { - std::string available_pins; - const auto& pins = this->netlist.pins(); + AtomBlockId pin_block = this->netlist.find_block(pin_name); + + if (pin_block == AtomBlockId::INVALID()) { + std::string available_ports; + const auto& pins = this->netlist.blocks(); int pins_left = pins.size(); - for (auto& pin : pins) { - available_pins += this->netlist.pin_name(pin); - if (pins_left != 1) - available_pins += ", "; + for (auto& pin_block_ : pins) { + auto ports = this->netlist.block_ports(pin_block_); + auto port_it = this->netlist.block_ports(pin_block_).begin(); + if (port_it != ports.end()) { + available_ports += this->netlist.block_name(pin_block_); + if (pins_left != 1) + available_ports += ", "; + } pins_left--; } - return this->_ret_error("set_property: Can't find PORT named `" + - std::string(pin_name) + "`. Available pins: " + - available_pins); + return this->_ret_error("set_property: Can't find ports for `" + + std::string(pin_name) + "`. Available blocks: " + + available_ports); } - AtomPortId port = this->netlist.pin_port(pin); - std::string port_name = this->netlist.port_name(port); + auto ports = this->netlist.block_ports(pin_block); + auto port_it = this->netlist.block_ports(pin_block).begin(); + if (port_it == ports.end()) + return this->_ret_error("set_property: '" + std::string(pin_name) + "' - no ports found."); + + /* TODO: this assumes there's only one port, which just happens to be true in case of fpga interchange. + * If we want to handle it better, we should return a list of port ids instead. + */ + AtomPortId port = *this->netlist.block_ports(pin_block).begin(); + std::string port_name = this->netlist.block_name(pin_block); VTR_LOG("get_ports: Got port `%s` (%llu) for pin `%s`\n", port_name.c_str(), (size_t)port, pin_name); @@ -187,8 +197,6 @@ const Tcl_ObjType port_tcl_t = (Tcl_ObjType){ .setFromAnyProc = tcl_set_from_none }; -/* API */ - VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, const AtomNetlist& netlist) { //VTR_ASSERT(_xdc_initialized); From 2f1d44ef62081cbb5a7069dc279588fd8ede3109 Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Thu, 21 Apr 2022 07:20:23 -0500 Subject: [PATCH 24/38] WIP set_propertty IOSTANDARD, a bit of refactoring Signed-off-by: Krzysztof Boronski --- .../src/read_fpga_interchange_arch.cpp | 1 + vpr/src/base/oo_tcl.h | 38 +++++++-- vpr/src/base/xdc_constraints.cpp | 78 +++++++++++-------- vpr/src/base/xdc_constraints.h | 9 ++- 4 files changed, 86 insertions(+), 40 deletions(-) diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 545b0278e2c..f919ec3200e 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -828,6 +828,7 @@ struct ArchReader { } } + /* Populate phys_grid_mapping - associate pin names with grid locations */ for (const auto& tile : ar_.getTileList()) { int site_idx = 0; for (const auto& site : tile.getSites()) { diff --git a/vpr/src/base/oo_tcl.h b/vpr/src/base/oo_tcl.h index 7569123da57..06fa2cd0e62 100644 --- a/vpr/src/base/oo_tcl.h +++ b/vpr/src/base/oo_tcl.h @@ -92,6 +92,20 @@ static T* tcl_obj_getptr(Tcl_Obj* obj) { return static_cast(obj->internalRep.twoPtrValue.ptr2); } +/** + * \brief Get pointer to an extra referenced C++ object. + * \return Pointer to a C++ object. + * + * This can be used to access context associated with a client, but when doing so + * the user must ensure that whatever client is bound there, gets handled correctly. + * Ie. when passing an object between two different clients, this should point to a + * common interface. + */ +template +static T* tcl_obj_get_ctx_ptr(Tcl_Obj* obj) { + return static_cast(obj->internalRep.twoPtrValue.ptr1); +} + using TclCommandError = std::string; /** @@ -169,13 +183,13 @@ class TclClient { } }; +/* Default implementations for handling TCL-managed C++ objects */ template static void tcl_obj_free(Tcl_Obj* obj) { T* obj_data = tcl_obj_getptr(obj); obj_data->~T(); Tcl_Free(reinterpret_cast(obj_data)); } - void tcl_obj_dup(Tcl_Obj* src, Tcl_Obj* dst); void tcl_set_obj_string(Tcl_Obj* obj, const std::string& str); int tcl_set_from_none(Tcl_Interp *tcl_interp, Tcl_Obj *obj); @@ -308,15 +322,27 @@ class TclCtx { #endif }; +#define DECLARE_TCL_TYPE(cxx_type, tcl_type) \ + extern const Tcl_ObjType tcl_type; \ + template <> \ + constexpr const Tcl_ObjType* tcl_type_of() { \ + return &tcl_type; \ + } \ + /** * \brief Associate a C++ struct/class with a `Tcl_ObjType`. * \param cxx_type C++ type name. * \param tcl_type pointer to statically-allocated Tcl_ObjType. */ -#define REGISTER_TCL_TYPE(cxx_type, tcl_type) \ - template <> \ - constexpr const Tcl_ObjType* tcl_type_of() { \ - return tcl_type; \ - } +#define REGISTER_TCL_TYPE_W_STR_UPDATE(cxx_type, tcl_type) \ + const Tcl_ObjType tcl_type = (Tcl_ObjType){ \ + .name = #cxx_type, \ + .freeIntRepProc = tcl_obj_free, \ + .dupIntRepProc = tcl_obj_dup, \ + .updateStringProc = [](Tcl_Obj* obj) + +#define END_REGISTER_TCL_TYPE , \ + .setFromAnyProc = tcl_set_from_none \ +} #endif \ No newline at end of file diff --git a/vpr/src/base/xdc_constraints.cpp b/vpr/src/base/xdc_constraints.cpp index 07b0d4ff083..dea4531be9a 100644 --- a/vpr/src/base/xdc_constraints.cpp +++ b/vpr/src/base/xdc_constraints.cpp @@ -16,33 +16,28 @@ enum class e_XDCProperty { XDC_PROP_PACKAGE_PIN, + XDC_PROP_IOSTANDARD, XDC_PROP_UNKNOWN }; static e_XDCProperty xdc_prop_from_str(const char* str) { if (!std::strcmp(str, "PACKAGE_PIN")) return e_XDCProperty::XDC_PROP_PACKAGE_PIN; + if (!std::strcmp(str, "IOSTANDARD")) + return e_XDCProperty::XDC_PROP_IOSTANDARD; return e_XDCProperty::XDC_PROP_UNKNOWN; } -extern const Tcl_ObjType port_tcl_t; -REGISTER_TCL_TYPE(AtomPortId, &port_tcl_t); - -class TclPhysicalConstraintsClient; - -template <> -TclPhysicalConstraintsClient* tcl_obj_getptr(Tcl_Obj* obj) { - return static_cast(obj->internalRep.twoPtrValue.ptr1); -} +DECLARE_TCL_TYPE(AtomPortId, port_tcl_t) class TclPhysicalConstraintsClient : public TclClient { public: VprConstraints& constraints; const t_arch& arch; - const AtomNetlist& netlist; + AtomNetlist& netlist; - TclPhysicalConstraintsClient(VprConstraints& constraints_, const t_arch& arch_, const AtomNetlist& netlist_) : + TclPhysicalConstraintsClient(VprConstraints& constraints_, const t_arch& arch_, AtomNetlist& netlist_) : constraints(constraints_), arch(arch_), netlist(netlist_) @@ -73,6 +68,11 @@ class TclPhysicalConstraintsClient : public TclClient { return this->_ret_error("set_property: Property `PACKAGE_PIN` " "requires one target and one value."); return this->_set_property_package_pin(objvp[2], objvp[3]); + case e_XDCProperty::XDC_PROP_IOSTANDARD: + if (objc != 4) + return this->_ret_error("set_property: Property `IOSTANDARD` " + "requires one value and one target."); + return this->_set_property_iostandard(objvp[2], objvp[3]); case e_XDCProperty::XDC_PROP_UNKNOWN: return this->_ret_error("set_property: Property `" + std::string(property_name) + "` is not recognized."); @@ -95,22 +95,20 @@ class TclPhysicalConstraintsClient : public TclClient { protected: int _set_property_package_pin(Tcl_Obj* tcl_pin_name, Tcl_Obj* tcl_port) { - int error; - const char* pin_name = Tcl_GetString(tcl_pin_name); if (pin_name == nullptr) return this->_ret_error("set_property: pin_name of PACKAGE_PIN should be a string."); auto* port = tcl_obj_getptr(tcl_port); if (port == nullptr) - return this->_ret_error("set_property: pin_name of PACKAGE_PIN should have a `port` type."); + return this->_ret_error("set_property: port__name of PACKAGE_PIN should have a `port` type."); return this->_do_set_property_package_pin(pin_name, *port); - - return error; } int _do_set_property_package_pin(const char* pin_name, AtomPortId port) { + /* Find associated AtomBlock */ + const auto& phys_grid_mapping = this->arch.phys_grid_mapping; std::string pin_str(pin_name); @@ -121,6 +119,9 @@ class TclPhysicalConstraintsClient : public TclClient { const t_phys_map_region& package_pin_region = it->second; AtomBlockId port_block = this->netlist.port_block(port); + + /* Create a 1x1 VprConstraints Partition to contrain the block */ + Partition part; part.set_name(pin_str + ".PART"); Region r; @@ -135,6 +136,9 @@ class TclPhysicalConstraintsClient : public TclClient { pr.add_to_part_region(r); part.set_part_region(pr); PartitionId part_id = this->constraints.add_partition(part); + + /* Constrain the AtomBlock */ + this->constraints.add_constrained_atom(port_block, part_id); VTR_LOG("XDC: Assigned PACKAGE_PIN %s = [%d, %d, %d, %d].%d\n", @@ -144,6 +148,24 @@ class TclPhysicalConstraintsClient : public TclClient { return this->_ret_ok(); } + int _set_property_iostandard(Tcl_Obj* tcl_io_standard, Tcl_Obj* tcl_port) { + const char* io_standard = Tcl_GetString(tcl_io_standard); + if (tcl_io_standard == nullptr) + return this->_ret_error("set_property: iostandard should be a string."); + + auto* port = tcl_obj_getptr(tcl_port); + if (port == nullptr) + return this->_ret_error("set_property: port_name of IOSTANDARD should have a `port` type."); + + return this->_do_set_property_iostandard(io_standard, *port); + } + + int _do_set_property_iostandard(const char* io_standard, AtomPortId port) { + AtomBlockId block = this->netlist.port_block(port); + this->netlist.set_block_param(block, "IOSTANDARD", io_standard); + VTR_LOG("XDC: Assigned IOSTANDARD %s = %s\n", this->netlist.block_name(block).c_str(), io_standard); + } + int _do_get_ports(const char* pin_name) { AtomBlockId pin_block = this->netlist.find_block(pin_name); @@ -184,20 +206,14 @@ class TclPhysicalConstraintsClient : public TclClient { } }; -const Tcl_ObjType port_tcl_t = (Tcl_ObjType){ - .name = "port", - .freeIntRepProc = tcl_obj_free, - .dupIntRepProc = tcl_obj_dup, - .updateStringProc = [](Tcl_Obj* obj) { - const auto* port = tcl_obj_getptr(obj); - const auto* client = tcl_obj_getptr(obj); - std::string port_name = client->netlist.port_name(*port); - tcl_set_obj_string(obj, port_name); - }, - .setFromAnyProc = tcl_set_from_none -}; +REGISTER_TCL_TYPE_W_STR_UPDATE(AtomPortId, port_tcl_t) { + const auto* port = tcl_obj_getptr(obj); + const auto* client = tcl_obj_get_ctx_ptr(obj); + std::string port_name = client->netlist.port_name(*port); + tcl_set_obj_string(obj, port_name); +} END_REGISTER_TCL_TYPE; -VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, const AtomNetlist& netlist) { +VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, AtomNetlist& netlist) { //VTR_ASSERT(_xdc_initialized); VprConstraints constraints; @@ -209,10 +225,10 @@ VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arc ctx.add_tcl_client(pc_client); ctx.read_tcl(xdc_stream); - return std::move(constraints); + return constraints; } -void load_xdc_constraints_file(const char* read_xdc_constraints_name, const t_arch& arch, const AtomNetlist& netlist) { +void load_xdc_constraints_file(const char* read_xdc_constraints_name, const t_arch& arch, AtomNetlist& netlist) { VTR_ASSERT(vtr::check_file_name_extension(read_xdc_constraints_name, ".xdc")); VTR_LOG("Reading XDC %s...\n", read_xdc_constraints_name); diff --git a/vpr/src/base/xdc_constraints.h b/vpr/src/base/xdc_constraints.h index b2e62f5d106..38b47bcf7a4 100644 --- a/vpr/src/base/xdc_constraints.h +++ b/vpr/src/base/xdc_constraints.h @@ -17,8 +17,10 @@ * TCL_eErroneousTCL: Failure when parsing XDC * TCL_eCustomException: Anything else that doesn't fit he above categories. * \return VprConstraints created from an XDC script. + * + * This function may modify the netlist by changing its blocks' properties. */ -VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, const AtomNetlist& netlist); +VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, AtomNetlist& netlist); /** * \brief Parse a file in XDC format and apply it to global FloorplanningContext. @@ -30,8 +32,9 @@ VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arc * TCL_eErroneousTCL: Failure when parsing XDC * TCL_eCustomException: Anything else that doesn't fit he above categories. * - * This function overwrites global floorplanning constraints g_vpr.constraints_.constraints + * This function overwrites global floorplanning constraints (g_vpr.constraints_.constraints)\ + * This function may modify the netlist by changing its blocks' properties. */ -void load_xdc_constraints_file(const char* read_xdc_constraints_name, const t_arch& arch, const AtomNetlist& netlist); +void load_xdc_constraints_file(const char* read_xdc_constraints_name, const t_arch& arch, AtomNetlist& netlist); #endif From f28745bd2f6774a7725d495b3eb87ca5deb878ad Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Thu, 21 Apr 2022 08:34:15 -0500 Subject: [PATCH 25/38] Further cleanup/refactor Signed-off-by: Krzysztof Boronski --- vpr/src/base/oo_tcl.cpp | 36 +++---------- vpr/src/base/oo_tcl.h | 93 ++++++++++++++++++-------------- vpr/src/base/xdc_constraints.cpp | 49 ++++++++++++----- vpr/src/base/xdc_constraints.h | 22 ++++---- 4 files changed, 109 insertions(+), 91 deletions(-) diff --git a/vpr/src/base/oo_tcl.cpp b/vpr/src/base/oo_tcl.cpp index a505ed2f46d..e769b97a757 100644 --- a/vpr/src/base/oo_tcl.cpp +++ b/vpr/src/base/oo_tcl.cpp @@ -76,22 +76,21 @@ TclCtx::TclCtx() { #ifdef DEBUG this->_init = false; #endif - VTR_LOG("Created XDC context.\n"); + VTR_LOG("Created TCL context.\n"); } TclCtx::~TclCtx() { Tcl_DeleteInterp(this->_tcl_interp); - VTR_LOG("Deleted XDC context.\n"); + VTR_LOG("Deleted TCL context.\n"); } -void TclCtx::init() { +void TclCtx::_init() { int error; if ((error = Tcl_Init(this->_tcl_interp)) != TCL_OK) throw TCL_eFailedToInitTcl(error); - - error = this->_fix_port_indexing(); + VTR_ASSERT(error == TCL_OK); #ifdef DEBUG @@ -99,16 +98,16 @@ void TclCtx::init() { #endif } -void TclCtx::read_tcl(std::istream& xdc_stream) { +void TclCtx::read_tcl(std::istream& tcl_stream) { int error; this->_debug_init(); std::ostringstream os; - xdc_stream >> os.rdbuf(); - std::string xdc = os.str(); + tcl_stream >> os.rdbuf(); + std::string tcl = os.str(); - error = Tcl_Eval(this->_tcl_interp, xdc.c_str()); + error = Tcl_Eval(this->_tcl_interp, tcl.c_str()); /* TODO: More precise error */ if (error != TCL_OK) { int error_line = Tcl_GetErrorLine(this->_tcl_interp); @@ -117,25 +116,6 @@ void TclCtx::read_tcl(std::istream& xdc_stream) { } } -int TclCtx::_fix_port_indexing() { - int error; - error = Tcl_Eval(this->_tcl_interp, "rename unknown _original_unknown"); - if (error != TCL_OK) - return error; - error = Tcl_Eval( - this->_tcl_interp, - "proc unknown args {\n" - " set result [scan [lindex $args 0] \"%d\" value]\n" - " if { $result == 1 && [llength $args] == 1 } {\n" - " return \\[$value\\]\n" - " } else {\n" - " uplevel 1 [list _original_unknown {*}$args]\n" - " }\n" - "}" - ); - return error; -} - int TclCtx::_tcl_do_method( ClientData cd, Tcl_Interp* tcl_interp, diff --git a/vpr/src/base/oo_tcl.h b/vpr/src/base/oo_tcl.h index 06fa2cd0e62..a441bd3717e 100644 --- a/vpr/src/base/oo_tcl.h +++ b/vpr/src/base/oo_tcl.h @@ -4,12 +4,13 @@ #include #include #include +#include #include /** - * \file - * \brief Object-oriented wrappers for TCL interpreter. + * @file + * @brief Object-oriented wrappers for TCL interpreter. * Overview * ======== * This file contains function declarations and definitionsas well as classes that allow embedding TCL interpreter in C++ @@ -18,20 +19,22 @@ * A standard scenario is: * - // Create an instance of TclClient-derived class * MyTclClient client; - * - TclCtx ctx; - * - // Call this before doing anything else with `ctx`. - * ctx.init() - * - // This will register all methods provided by `client` - * ctx.add_client(client) - * - // Read and interpret TCL code - * ctx.read_tcl(stream) + * - // Create a TclCtx context and run code within its lifetime. + * TclCtx::with_ctx([&](TclCtx& ctx) {; + * - // Call this before doing anything else with `ctx`. + * ctx.init() + * - // This will register all methods provided by `client` + * ctx.add_client(client) + * - // Read and interpret TCL code + * ctx.read_tcl(stream) + * - // TclCtx gets destroyd at the end of this call + * }); * - // Process results stored by a client * do_something(client.data) - * */ /** - * \brief Base class for any exception thrown within TCLCtx. + * @brief Base class for any exception thrown within TCLCtx. */ class TCL_eException { public: @@ -40,7 +43,7 @@ class TCL_eException { }; /** - * \brief Generic exception thrown within TCLCtx. Features a message string. + * @brief Generic exception thrown within TCLCtx. Features a message string. */ class TCL_eCustomException : TCL_eException { public: @@ -52,7 +55,7 @@ class TCL_eCustomException : TCL_eException { }; /** - * \brief Exception thrown within TCLCtx if it fails during TCLCtx::init() + * @brief Exception thrown within TCLCtx if it fails during TCLCtx::init() */ class TCL_eFailedToInitTcl : TCL_eException { public: @@ -64,7 +67,7 @@ class TCL_eFailedToInitTcl : TCL_eException { }; /** - * \brief Exception thrown within TCLCtx when an error occurs while interpreting TCL. + * @brief Exception thrown within TCLCtx when an error occurs while interpreting TCL. */ class TCL_eErroneousTCL : TCL_eException { public: @@ -82,8 +85,8 @@ void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s); template constexpr const Tcl_ObjType* tcl_type_of(); /** - * \brief Get pointer to a C++ object managed within TCL-context - * \return Pointer to a C++ object if `T` matches the type of an object. Otherwise nullptr. + * @brief Get pointer to a C++ object managed within TCL-context + * @return Pointer to a C++ object if `T` matches the type of an object. Otherwise nullptr. */ template static T* tcl_obj_getptr(Tcl_Obj* obj) { @@ -93,8 +96,8 @@ static T* tcl_obj_getptr(Tcl_Obj* obj) { } /** - * \brief Get pointer to an extra referenced C++ object. - * \return Pointer to a C++ object. + * @brief Get pointer to an extra referenced C++ object. + * @return Pointer to a C++ object. * * This can be used to access context associated with a client, but when doing so * the user must ensure that whatever client is bound there, gets handled correctly. @@ -133,7 +136,7 @@ class TclClient { */ /** - * \brief Throw an error from a TCL command. + * @brief Throw an error from a TCL command. */ inline int _ret_error(TclCommandError&& error) { this->cmd_status = e_TclCommandStatus::TCL_CMD_FAIL; @@ -142,7 +145,7 @@ class TclClient { } /** - * \brief Return from a TCL command with no errors. + * @brief Return from a TCL command with no errors. */ inline int _ret_ok() { this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS; @@ -150,7 +153,7 @@ class TclClient { } /** - * \brief Return a string from a TCL command with no errors. + * @brief Return a string from a TCL command with no errors. */ inline int _ret_string(std::string&& string_) { this->cmd_status = e_TclCommandStatus::TCL_CMD_SUCCESS_STRING; @@ -159,10 +162,10 @@ class TclClient { } /** - * \brief Move an object to TCL-managed context and return it in TCL code. - * \param T registered custom TCL type - * \param this_ pointer to the concrete client. Note that it might be different than `this` within context of base class. - * \param object_data object to be moved into TCL-managed context + * @brief Move an object to TCL-managed context and return it in TCL code. + * @param T registered custom TCL type + * @param this_ pointer to the concrete client. Note that it might be different than `this` within context of base class. + * @param object_data object to be moved into TCL-managed context */ template int _ret_obj(void* this_, const T&& object_data) { @@ -197,7 +200,7 @@ int tcl_set_from_none(Tcl_Interp *tcl_interp, Tcl_Obj *obj); void tcl_init(); /** - * \brief Provide TCL runtime for parsing XDC files. + * @brief Provide a TCL runtime. * Allows extending TCL language with new types which are associated with C++ types and new methods provided by objects of TclClient-derived classes. * * `TclCtx` binds a TCL context to the lifetime of a C++ object, so once TclCtx::~TclCtx gets called, TCL context will be cleaned-up. @@ -205,14 +208,17 @@ void tcl_init(); */ class TclCtx { public: - TclCtx(); - - ~TclCtx(); - /** - * \brief Initialize a newly created instance of TclCtx. It's illegal to call any other method before calling this one. + /** + * @brief Create and initialize context, then run a closure with it. + * This is done to limit TclCtx lifetime. */ - void init(); + template + static T with_ctx(std::function fun) { + TclCtx ctx; + ctx._init(); + fun(ctx); + } /** * \brief Read and interpret a TCL script within this context. @@ -220,7 +226,7 @@ class TclCtx { void read_tcl(std::istream& tcl_stream); /** - * \brief Add a client class that provides methods to interface with a context managed by it + * @brief Add a client class that provides methods to interface with a context managed by it */ template void add_tcl_client(C& client) { @@ -237,7 +243,7 @@ class TclCtx { } /** - * \brief Add a custom type to TCL. + * @brief Add a custom type to TCL. * * The type has to be moveable in order to allow passing it between Tcl-managed cotext and C++ code. * Use REGISTER_TCL_TYPE macro to associate type T with a static struct instance describing the Tcl type. @@ -249,14 +255,23 @@ class TclCtx { protected: + TclCtx(); + + ~TclCtx(); + + /** + * @brief Initialize a newly created instance of TclCtx. It's illegal to call any other method before calling this one. + */ + void _init(); + /** - * \brief Tcl by default will interpret bus indices as calls to commands + * @brief Tcl by default will interpret bus indices as calls to commands * (eg. in[0] gets interpreted as call to command `i` with argument being a * result of calling `0`). This overrides this behaviour. * * TODO: This is SDC/XDC-specific and should be handled somewhere else. */ - int _fix_port_indexing(); + /* int _fix_port_indexing(); */ /** * \brief Provide an interface to call a client method. @@ -330,9 +345,9 @@ class TclCtx { } \ /** - * \brief Associate a C++ struct/class with a `Tcl_ObjType`. - * \param cxx_type C++ type name. - * \param tcl_type pointer to statically-allocated Tcl_ObjType. + * @brief Associate a C++ struct/class with a `Tcl_ObjType`. + * @param cxx_type C++ type name. + * @param tcl_type pointer to statically-allocated Tcl_ObjType. */ #define REGISTER_TCL_TYPE_W_STR_UPDATE(cxx_type, tcl_type) \ const Tcl_ObjType tcl_type = (Tcl_ObjType){ \ diff --git a/vpr/src/base/xdc_constraints.cpp b/vpr/src/base/xdc_constraints.cpp index dea4531be9a..f3412bc681d 100644 --- a/vpr/src/base/xdc_constraints.cpp +++ b/vpr/src/base/xdc_constraints.cpp @@ -101,7 +101,7 @@ class TclPhysicalConstraintsClient : public TclClient { auto* port = tcl_obj_getptr(tcl_port); if (port == nullptr) - return this->_ret_error("set_property: port__name of PACKAGE_PIN should have a `port` type."); + return this->_ret_error("set_property: port_name of PACKAGE_PIN should have a `AtomPortId` type."); return this->_do_set_property_package_pin(pin_name, *port); } @@ -155,7 +155,7 @@ class TclPhysicalConstraintsClient : public TclClient { auto* port = tcl_obj_getptr(tcl_port); if (port == nullptr) - return this->_ret_error("set_property: port_name of IOSTANDARD should have a `port` type."); + return this->_ret_error("set_property: port_name of IOSTANDARD should have a `AtomPortId` type."); return this->_do_set_property_iostandard(io_standard, *port); } @@ -184,7 +184,7 @@ class TclPhysicalConstraintsClient : public TclClient { pins_left--; } - return this->_ret_error("set_property: Can't find ports for `" + + return this->_ret_error("get_ports: Can't find ports for `" + std::string(pin_name) + "`. Available blocks: " + available_ports); } @@ -192,12 +192,13 @@ class TclPhysicalConstraintsClient : public TclClient { auto ports = this->netlist.block_ports(pin_block); auto port_it = this->netlist.block_ports(pin_block).begin(); if (port_it == ports.end()) - return this->_ret_error("set_property: '" + std::string(pin_name) + "' - no ports found."); + return this->_ret_error("get_ports: '" + std::string(pin_name) + "' - no ports found."); - /* TODO: this assumes there's only one port, which just happens to be true in case of fpga interchange. - * If we want to handle it better, we should return a list of port ids instead. + /* We assume that there's only one port, which just happens to be true in case of fpga interchange. + * If we ever want to handle it better, we should return a list of port ids instead. */ - AtomPortId port = *this->netlist.block_ports(pin_block).begin(); + VTR_ASSERT(ports.size() == 1); + AtomPortId port = *ports.begin(); std::string port_name = this->netlist.block_name(pin_block); VTR_LOG("get_ports: Got port `%s` (%llu) for pin `%s`\n", port_name.c_str(), (size_t)port, pin_name); @@ -217,14 +218,36 @@ VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arc //VTR_ASSERT(_xdc_initialized); VprConstraints constraints; - TclPhysicalConstraintsClient pc_client(constraints, arch, netlist); - TclCtx ctx; - ctx.init(); - ctx.add_tcl_type(); - ctx.add_tcl_client(pc_client); - ctx.read_tcl(xdc_stream); + TclCtx::with_ctx([&](TclCtx& ctx) { + /* + * Tcl by default will interpret bus indices as calls to commands + * (eg. in[0] gets interpreted as call to command `i` with argument being a + * result of calling `0`). This overrides this behaviour. + */ + std::istringstream fix_port_indexing( + "rename unknown _original_unknown\n" + "proc unknown args {\n" + " set result [scan [lindex $args 0] \"%d\" value]\n" + " if { $result == 1 && [llength $args] == 1 } {\n" + " return \\[$value\\]\n" + " } else {\n" + " uplevel 1 [list _original_unknown {*}$args]\n" + " }\n" + "}" + ); + ctx.read_tcl(fix_port_indexing); + + /* Add types and commands to handle XDC files */ + ctx.add_tcl_type(); + ctx.add_tcl_client(pc_client); + + /* Read and interpret XDC */ + ctx.read_tcl(xdc_stream); + }); + + /* At this point `pc_client` has written the contraints */ return constraints; } diff --git a/vpr/src/base/xdc_constraints.h b/vpr/src/base/xdc_constraints.h index 38b47bcf7a4..f7b8473b6d5 100644 --- a/vpr/src/base/xdc_constraints.h +++ b/vpr/src/base/xdc_constraints.h @@ -8,26 +8,26 @@ #include "vpr_constraints.h" /** - * \brief Creates VprConstraints from input stream in XDC format. - * \param xdc_stream XDC script - * \param arch reference to loaded device architecture - * \param netlist loaded AtomNetlist - * \throws TCL_eException: base class for all exceptions + * @brief Creates VprConstraints from input stream in XDC format. + * @param xdc_stream XDC script + * @param arch reference to loaded device architecture + * @param netlist loaded AtomNetlist + * @throws TCL_eException: base class for all exceptions * TCL_eFailedToInitTcl: Failure during initializations of TCL subsystem * TCL_eErroneousTCL: Failure when parsing XDC * TCL_eCustomException: Anything else that doesn't fit he above categories. - * \return VprConstraints created from an XDC script. + * @return VprConstraints created from an XDC script. * * This function may modify the netlist by changing its blocks' properties. */ VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, AtomNetlist& netlist); /** - * \brief Parse a file in XDC format and apply it to global FloorplanningContext. - * \param read_xdc_constraints_name path to an XDC file with constraints - * \param arch reference to loaded device architecture - * \param netlist loaded AtomNetlist - * \throws TCL_eException: base class for all exceptions + * @brief Parse a file in XDC format and apply it to global FloorplanningContext. + * @param read_xdc_constraints_name path to an XDC file with constraints + * @param arch reference to loaded device architecture + * @param netlist loaded AtomNetlist + * @throws TCL_eException: base class for all exceptions * TCL_eFailedToInitTcl: Failure during initializations of TCL subsystem * TCL_eErroneousTCL: Failure when parsing XDC * TCL_eCustomException: Anything else that doesn't fit he above categories. From 09a9c6a555fa72d81bcbc7f82035fcf5777e3cd9 Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Fri, 22 Apr 2022 11:04:14 -0500 Subject: [PATCH 26/38] Fixed set_property IOSTANDARD no-return Signed-off-by: Krzysztof Boronski --- vpr/src/base/xdc_constraints.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vpr/src/base/xdc_constraints.cpp b/vpr/src/base/xdc_constraints.cpp index f3412bc681d..e0914004a9a 100644 --- a/vpr/src/base/xdc_constraints.cpp +++ b/vpr/src/base/xdc_constraints.cpp @@ -43,8 +43,8 @@ class TclPhysicalConstraintsClient : public TclClient { netlist(netlist_) {} - typedef int(TclPhysicalConstraintsClient::*TclMethod)(int objc, Tcl_Obj* const objvp[]); - void register_methods(std::function register_method) { + template + void register_methods(F register_method) { register_method("get_ports", &TclPhysicalConstraintsClient::get_ports); register_method("set_property", &TclPhysicalConstraintsClient::set_property); } @@ -164,6 +164,7 @@ class TclPhysicalConstraintsClient : public TclClient { AtomBlockId block = this->netlist.port_block(port); this->netlist.set_block_param(block, "IOSTANDARD", io_standard); VTR_LOG("XDC: Assigned IOSTANDARD %s = %s\n", this->netlist.block_name(block).c_str(), io_standard); + return this->_ret_ok(); } int _do_get_ports(const char* pin_name) { @@ -215,7 +216,6 @@ REGISTER_TCL_TYPE_W_STR_UPDATE(AtomPortId, port_tcl_t) { } END_REGISTER_TCL_TYPE; VprConstraints read_xdc_constraints_to_vpr(std::istream& xdc_stream, const t_arch& arch, AtomNetlist& netlist) { - //VTR_ASSERT(_xdc_initialized); VprConstraints constraints; TclPhysicalConstraintsClient pc_client(constraints, arch, netlist); From 61ed2c2dae7918a18c75a28422dabaa8365d59da Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Fri, 22 Apr 2022 11:06:09 -0500 Subject: [PATCH 27/38] Moved TCL C++ abstraction into libtclcpp Signed-off-by: Krzysztof Boronski --- CMakeLists.txt | 1 + libs/CMakeLists.txt | 1 + libs/libtclcpp/CMakeLists.txt | 21 ++++ .../libtclcpp/src/tclcpp.cpp | 21 +--- .../oo_tcl.h => libs/libtclcpp/src/tclcpp.h | 102 ++++++++++++++---- vpr/CMakeLists.txt | 1 + vpr/src/base/SetupVPR.cpp | 5 - vpr/src/base/vpr_api.cpp | 2 +- vpr/src/base/xdc_constraints.cpp | 6 +- 9 files changed, 109 insertions(+), 51 deletions(-) create mode 100644 libs/libtclcpp/CMakeLists.txt rename vpr/src/base/oo_tcl.cpp => libs/libtclcpp/src/tclcpp.cpp (90%) rename vpr/src/base/oo_tcl.h => libs/libtclcpp/src/tclcpp.h (76%) diff --git a/CMakeLists.txt b/CMakeLists.txt index a69ecf55017..0a104810b2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -439,6 +439,7 @@ list(APPEND DIRS_TO_FORMAT_CPP "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil") list(APPEND DIRS_TO_FORMAT_CPP "${CMAKE_CURRENT_SOURCE_DIR}/libs/libpugiutil") list(APPEND DIRS_TO_FORMAT_CPP "${CMAKE_CURRENT_SOURCE_DIR}/libs/liblog") list(APPEND DIRS_TO_FORMAT_CPP "${CMAKE_CURRENT_SOURCE_DIR}/libs/librtlnumber") +list(APPEND DIRS_TO_FORMAT_CPP "${CMAKE_CURRENT_SOURCE_DIR}/libs/libtclcpp") list(APPEND DIRS_TO_FORMAT_CPP "${CMAKE_CURRENT_SOURCE_DIR}/ODIN_II") include(AutoClangFormat) diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 152eafec323..c2dc51a973f 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -18,3 +18,4 @@ if(${VTR_ENABLE_CAPNPROTO}) add_subdirectory(libvtrcapnproto) endif() add_subdirectory(librrgraph) +add_subdirectory(libtclcpp) \ No newline at end of file diff --git a/libs/libtclcpp/CMakeLists.txt b/libs/libtclcpp/CMakeLists.txt new file mode 100644 index 00000000000..7694b477c17 --- /dev/null +++ b/libs/libtclcpp/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.9) + +project("libtclcpp") + +file(GLOB_RECURSE LIB_SOURCES src/*.cpp) +file(GLOB_RECURSE LIB_HEADERS src/*.h) +files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS) + +add_library(libtclcpp STATIC + ${LIB_HEADERS} + ${LIB_SOURCES} +) + +target_include_directories(libtclcpp PUBLIC ${LIB_INCLUDE_DIRS}) + +set_target_properties(libtclcpp PROPERTIES PREFIX "") + +find_package(TCL REQUIRED) +target_link_libraries(libtclcpp tcl) + +install(TARGETS libtclcpp DESTINATION bin) diff --git a/vpr/src/base/oo_tcl.cpp b/libs/libtclcpp/src/tclcpp.cpp similarity index 90% rename from vpr/src/base/oo_tcl.cpp rename to libs/libtclcpp/src/tclcpp.cpp index e769b97a757..9f94eca60fd 100644 --- a/vpr/src/base/oo_tcl.cpp +++ b/libs/libtclcpp/src/tclcpp.cpp @@ -1,12 +1,10 @@ -#include "oo_tcl.h" +#include "tclcpp.h" #include #include #include #include - -#include "globals.h" -#include "vtr_log.h" +#include /* TCL ERRORS */ #define OV_TOSTRING(c, e) c::operator std::string() const { return (e); } @@ -60,29 +58,16 @@ int tcl_set_from_none(Tcl_Interp *tcl_interp, Tcl_Obj *obj) { return TCL_ERROR; } -static bool _tcl_initialized = false; - -void tcl_init() { - if (!_tcl_initialized) - Tcl_FindExecutable(_argv0); - _tcl_initialized = true; - - VTR_LOG("Initialized TCL subsystem"); -} - TclCtx::TclCtx() { this->_tcl_interp = Tcl_CreateInterp(); #ifdef DEBUG this->_init = false; #endif - VTR_LOG("Created TCL context.\n"); } TclCtx::~TclCtx() { Tcl_DeleteInterp(this->_tcl_interp); - - VTR_LOG("Deleted TCL context.\n"); } void TclCtx::_init() { @@ -90,8 +75,6 @@ void TclCtx::_init() { if ((error = Tcl_Init(this->_tcl_interp)) != TCL_OK) throw TCL_eFailedToInitTcl(error); - - VTR_ASSERT(error == TCL_OK); #ifdef DEBUG this->_init = true; diff --git a/vpr/src/base/oo_tcl.h b/libs/libtclcpp/src/tclcpp.h similarity index 76% rename from vpr/src/base/oo_tcl.h rename to libs/libtclcpp/src/tclcpp.h index a441bd3717e..54d5f3c6e62 100644 --- a/vpr/src/base/oo_tcl.h +++ b/libs/libtclcpp/src/tclcpp.h @@ -1,5 +1,5 @@ -#ifndef OO_TCL_H -#define OO_TCL_H +#ifndef TCLCPP_H +#define TCLCPP_H #include #include @@ -112,7 +112,7 @@ static T* tcl_obj_get_ctx_ptr(Tcl_Obj* obj) { using TclCommandError = std::string; /** - * \brief Descibes a after exiting a custom TCL command implementation. + * @brief Descibes a after exiting a custom TCL command implementation. */ enum class e_TclCommandStatus : int { TCL_CMD_SUCCESS, /* Command exited with no return value. */ @@ -121,15 +121,61 @@ enum class e_TclCommandStatus : int { TCL_CMD_FAIL /* Command exited with a failure. Will cause a TCL_eErroneousTCL to be thrown. */ }; + +/** + * @brief Provide a C++ state with TCL interface. + * + * This class should be a base for a Client class to implement a TCL interface. + * + * Commands need to be registered within + * template + * void register_methods(F register_method) + * method template. + * + * Commands are implemented as methods of TclClient-derived class: + * int(DerivedClient::*)(int objc, Tcl_Obj* const objvp[])) + * + * Use TclClient::_ret_* methods to return from a command implementation. + */ class TclClient { public: - TclClient(); e_TclCommandStatus cmd_status; std::string string; Tcl_Obj* object; + /** + * Define this method template in a derived TclClient: + * + * template + * void register_methods(F register_method) + * + * F should be a functor of acting as the following type: + * void(*)(const char* name, int(DerivedClient::*)(int objc, Tcl_Obj* const objvp[])) + * + * (alternatively you can define a method taking std::function) + * + * Eg.: + * template + * void DerivedClient::register_methods(F register_method) { + * register_method("my_custom_command", DerivedClient::my_custom_command); + * register_method("my_other_custom_command", DerivedClient::my_other_custom_command); + * } + * + * In the future if C++20 is going to be adopted as a standard for VPR, this should be + * handled using a concept. + */ + protected: + /* TclClient must be derived */ + TclClient(); + + /* TclClient must be immovable to prevent invalidating any references to it stored within TCL objects */ + TclClient(TclClient&& other) = delete; + /* TclClient shouldn't be copied, because that would require duplicating this->object and binding it to + * the new copy of a client. This might be an unwanted behaviour. */ + TclClient(const TclClient& other) = delete; + TclClient& operator=(const TclClient& other) = delete; /* Return methods * Use these when returning from a TCL command implementation. @@ -194,10 +240,12 @@ static void tcl_obj_free(Tcl_Obj* obj) { Tcl_Free(reinterpret_cast(obj_data)); } void tcl_obj_dup(Tcl_Obj* src, Tcl_Obj* dst); -void tcl_set_obj_string(Tcl_Obj* obj, const std::string& str); int tcl_set_from_none(Tcl_Interp *tcl_interp, Tcl_Obj *obj); -void tcl_init(); +/** + * @brief set TCL object's string representation + */ +void tcl_set_obj_string(Tcl_Obj* obj, const std::string& str); /** * @brief Provide a TCL runtime. @@ -221,7 +269,7 @@ class TclCtx { } /** - * \brief Read and interpret a TCL script within this context. + * @brief Read and interpret a TCL script within this context. */ void read_tcl(std::istream& tcl_stream); @@ -230,8 +278,8 @@ class TclCtx { */ template void add_tcl_client(C& client) { - auto* client_container = new TclClientContainer(client); - auto register_command = [&](const char* name, int(C::*method)(int objc, Tcl_Obj* const objvp[])) { + auto* client_container = new TclClientContainer(client); + auto register_command = [&](const char* name, int(C::*method)(int objc, Tcl_Obj* const objvp[])) { client_container->methods.push_front(TclMethodDispatch(method, client)); auto& md = client_container->methods.front(); Tcl_CreateObjCommand(this->_tcl_interp, name, TclCtx::_tcl_do_method, static_cast(&md), nullptr); @@ -274,7 +322,7 @@ class TclCtx { /* int _fix_port_indexing(); */ /** - * \brief Provide an interface to call a client method. + * @brief Provide an interface to call a client method. */ struct TclMethodDispatchBase { inline TclMethodDispatchBase(TclClient& client_) : client(client_) {} @@ -284,7 +332,7 @@ class TclCtx { }; /** - * \brief Wrap a call-by-PTM (pointer-to-method) of a concrete TclClient-derived class as a virtual function of TclMethodDispatchBase. + * @brief Wrap a call-by-PTM (pointer-to-method) of a concrete TclClient-derived class as a virtual function of TclMethodDispatchBase. * This allows us to avoid making methods defined within TclClient-derived virtual methods of TclClient. */ template @@ -304,7 +352,7 @@ class TclCtx { struct TclClientContainerBase {}; /** - * \brief Store a reference to a client along with method dispatch list associated with that client. + * @brief Store a reference to a client along with method dispatch list associated with that client. */ template struct TclClientContainer : TclClientContainerBase { @@ -337,27 +385,35 @@ class TclCtx { #endif }; -#define DECLARE_TCL_TYPE(cxx_type, tcl_type) \ - extern const Tcl_ObjType tcl_type; \ +#define TCL_TYPE_OF(cxx_type) cxx_type##_tcl_t + +/** + * @brief Declare Tcl type associated with a C++ type + * @param cxx_type C++ type name. + */ +#define DECLARE_TCL_TYPE(cxx_type) \ + extern const Tcl_ObjType TCL_TYPE_OF(cxx_type); \ template <> \ constexpr const Tcl_ObjType* tcl_type_of() { \ - return &tcl_type; \ + return &TCL_TYPE_OF(cxx_type); \ } \ /** * @brief Associate a C++ struct/class with a `Tcl_ObjType`. * @param cxx_type C++ type name. - * @param tcl_type pointer to statically-allocated Tcl_ObjType. + * + * Foollow it with a body of updateStringProc and close it with END_REGISTER_TCL_TYPE: + * REGISTER_TCL_TYPE_W_STR_UPDATE(CppType)(Tcl_Obj* obj) { ...body... } END_REGISTER_TCL_TYPE; */ -#define REGISTER_TCL_TYPE_W_STR_UPDATE(cxx_type, tcl_type) \ - const Tcl_ObjType tcl_type = (Tcl_ObjType){ \ - .name = #cxx_type, \ - .freeIntRepProc = tcl_obj_free, \ - .dupIntRepProc = tcl_obj_dup, \ - .updateStringProc = [](Tcl_Obj* obj) +#define REGISTER_TCL_TYPE_W_STR_UPDATE(cxx_type ) \ + const Tcl_ObjType TCL_TYPE_OF(cxx_type) { \ + /* .name = */ #cxx_type, \ + /* .freeIntRepProc = */ tcl_obj_free, \ + /* .dupIntRepProc = */ tcl_obj_dup, \ + /* .updateStringProc = */ [] #define END_REGISTER_TCL_TYPE , \ - .setFromAnyProc = tcl_set_from_none \ + /* .setFromAnyProc = */ tcl_set_from_none \ } #endif \ No newline at end of file diff --git a/vpr/CMakeLists.txt b/vpr/CMakeLists.txt index dabc414dbec..fef8150246d 100644 --- a/vpr/CMakeLists.txt +++ b/vpr/CMakeLists.txt @@ -87,6 +87,7 @@ target_link_libraries(libvpr libargparse libpugixml librrgraph + libtclcpp ) #link graphics library only when graphics set to on diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index e96f6cb095f..a7bcdb19627 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -27,8 +27,6 @@ #include "clock_modeling.h" #include "ShowSetup.h" -#include "oo_tcl.h" - static void SetupNetlistOpts(const t_options& Options, t_netlist_opts& NetlistOpts); static void SetupPackerOpts(const t_options& Options, t_packer_opts* PackerOpts); @@ -112,9 +110,6 @@ void SetupVPR(const t_options* Options, SetupAnalysisOpts(*Options, *AnalysisOpts); SetupPowerOpts(*Options, PowerOpts, Arch); - if (Options->XDCFile.value() != "") - tcl_init(); - if (readArchFile == true) { vtr::ScopedStartFinishTimer t("Loading Architecture Description"); switch (Options->arch_format) { diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 61ed038383f..2de509771a5 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -69,7 +69,7 @@ #include "cluster.h" #include "output_clustering.h" #include "vpr_constraints_reader.h" -#include "oo_tcl.h" +#include "tclcpp.h" #include "xdc_constraints.h" #include "place_constraints.h" #include "place_util.h" diff --git a/vpr/src/base/xdc_constraints.cpp b/vpr/src/base/xdc_constraints.cpp index e0914004a9a..aea3bb9f969 100644 --- a/vpr/src/base/xdc_constraints.cpp +++ b/vpr/src/base/xdc_constraints.cpp @@ -10,7 +10,7 @@ #include "vpr_constraints.h" #include "atom_netlist.h" -#include "oo_tcl.h" +#include "tclcpp.h" #include "xdc_constraints.h" @@ -29,7 +29,7 @@ static e_XDCProperty xdc_prop_from_str(const char* str) { return e_XDCProperty::XDC_PROP_UNKNOWN; } -DECLARE_TCL_TYPE(AtomPortId, port_tcl_t) +DECLARE_TCL_TYPE(AtomPortId) class TclPhysicalConstraintsClient : public TclClient { public: @@ -208,7 +208,7 @@ class TclPhysicalConstraintsClient : public TclClient { } }; -REGISTER_TCL_TYPE_W_STR_UPDATE(AtomPortId, port_tcl_t) { +REGISTER_TCL_TYPE_W_STR_UPDATE(AtomPortId) (Tcl_Obj* obj) { const auto* port = tcl_obj_getptr(obj); const auto* client = tcl_obj_get_ctx_ptr(obj); std::string port_name = client->netlist.port_name(*port); From e9013da592a7354c32a8a74721f8627dc4ed447e Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Fri, 22 Apr 2022 11:29:50 -0500 Subject: [PATCH 28/38] Use vpr_throw for reporting XDC errors Signed-off-by: Krzysztof Boronski --- vpr/src/base/vpr_api.cpp | 9 +++++---- vpr/src/util/vpr_error.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 2de509771a5..379889bf527 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -355,11 +355,9 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a try { load_xdc_constraints_file(filename_opts.read_xdc_constraints_file.c_str(), *arch, atom_ctx.nlist); } catch (const TCL_eErroneousTCL& e) { - VTR_LOG_ERROR("Error reading XDC: %s\n", std::string(e).c_str()); - throw e; + vpr_throw(VPR_ERROR_XDC, e.filename.c_str(), e.line, e.message.c_str()); } catch (const TCL_eException& e) { - VTR_LOG_ERROR("Error loading XDC: %s\n", std::string(e).c_str()); - throw e; + VPR_THROW(VPR_ERROR_OTHER, "Error loading XDC: %s\n", std::string(e).c_str()); } } @@ -1408,6 +1406,9 @@ void vpr_print_error(const VprError& vpr_error) { case VPR_ERROR_SDC: error_type = "SDC file"; break; + case VPR_ERROR_XDC: + error_type = "XDC file"; + break; case VPR_ERROR_NET_F: error_type = "Netlist file"; break; diff --git a/vpr/src/util/vpr_error.h b/vpr/src/util/vpr_error.h index 0c3d5a1cbf8..0c1076100e9 100644 --- a/vpr/src/util/vpr_error.h +++ b/vpr/src/util/vpr_error.h @@ -18,6 +18,7 @@ enum e_vpr_error { VPR_ERROR_TIMING, VPR_ERROR_POWER, VPR_ERROR_SDC, + VPR_ERROR_XDC, // File parsing errors VPR_ERROR_NET_F, // Error while parsing the packed netlist file From 0379c1d4a7681f1fa08f37252be19012b1c65cd9 Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Fri, 22 Apr 2022 11:38:18 -0500 Subject: [PATCH 29/38] Remove _argv0 global Signed-off-by: Krzysztof Boronski --- vpr/src/base/globals.h | 1 - vpr/src/base/vpr_api.cpp | 3 --- 2 files changed, 4 deletions(-) diff --git a/vpr/src/base/globals.h b/vpr/src/base/globals.h index 807692913fc..cbbbe3a75e3 100644 --- a/vpr/src/base/globals.h +++ b/vpr/src/base/globals.h @@ -8,6 +8,5 @@ #include "vpr_context.h" extern VprContext g_vpr_ctx; -extern const char* _argv0; #endif diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 379889bf527..524eeed0f95 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -102,8 +102,6 @@ std::unique_ptr tbb_scheduler; #endif -const char* _argv0; - /* Local subroutines */ static void free_complex_block_types(); @@ -172,7 +170,6 @@ void vpr_initialize_logging() { * 4. Sanity check all three */ void vpr_init(const int argc, const char** argv, t_options* options, t_vpr_setup* vpr_setup, t_arch* arch) { - _argv0 = argv[0]; vpr_initialize_logging(); From bb44f69c96802e9bd470c485c51a666244faa0d8 Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Mon, 25 Apr 2022 15:01:39 -0500 Subject: [PATCH 30/38] libtclcpp: TCL List wrapper. get_ports: return list of ports. set_property: handle lists of ports. Signed-off-by: Krzysztof Boronski --- libs/libtclcpp/src/tclcpp.cpp | 10 +- libs/libtclcpp/src/tclcpp.h | 206 +++++++++++++++++++++++++++++-- vpr/src/base/xdc_constraints.cpp | 75 +++++++---- 3 files changed, 260 insertions(+), 31 deletions(-) diff --git a/libs/libtclcpp/src/tclcpp.cpp b/libs/libtclcpp/src/tclcpp.cpp index 9f94eca60fd..b8c94906d7f 100644 --- a/libs/libtclcpp/src/tclcpp.cpp +++ b/libs/libtclcpp/src/tclcpp.cpp @@ -1,6 +1,7 @@ #include "tclcpp.h" #include +#include #include #include #include @@ -39,6 +40,7 @@ TclClient::TclClient() : void tcl_obj_dup(Tcl_Obj* src, Tcl_Obj* dst) { dst->internalRep.twoPtrValue = src->internalRep.twoPtrValue; + dst->typePtr = src->typePtr; dst->bytes = nullptr; dst->length = 0; } @@ -116,8 +118,14 @@ int TclCtx::_tcl_do_method( Tcl_SetStringResult(tcl_interp, d->client.string); return TCL_OK; case e_TclCommandStatus::TCL_CMD_SUCCESS_OBJECT: + case e_TclCommandStatus::TCL_CMD_SUCCESS_LIST: Tcl_SetObjResult(tcl_interp, d->client.object); default: break; } return TCL_OK; -} \ No newline at end of file +} + +/* std::vector Tcl_GetList(Tcl_Obj* obj) { + obj->typePtr +} + */ \ No newline at end of file diff --git a/libs/libtclcpp/src/tclcpp.h b/libs/libtclcpp/src/tclcpp.h index 54d5f3c6e62..aaba95a5e06 100644 --- a/libs/libtclcpp/src/tclcpp.h +++ b/libs/libtclcpp/src/tclcpp.h @@ -3,8 +3,11 @@ #include #include +#include #include #include +#include +#include #include @@ -118,9 +121,174 @@ enum class e_TclCommandStatus : int { TCL_CMD_SUCCESS, /* Command exited with no return value. */ TCL_CMD_SUCCESS_STRING, /* Command exited returning a string. */ TCL_CMD_SUCCESS_OBJECT, /* Command exited returning a custom object. */ + TCL_CMD_SUCCESS_LIST, /* Command exited returning a list. */ TCL_CMD_FAIL /* Command exited with a failure. Will cause a TCL_eErroneousTCL to be thrown. */ }; +template +Tcl_Obj* Tcl_MoveCppObject(void* ctx, T object_data) { + const Tcl_ObjType* type = tcl_type_of(); + Tcl_Obj* obj = Tcl_NewObj(); + obj->bytes = nullptr; + obj->typePtr = type; + obj->internalRep.twoPtrValue.ptr1 = ctx; + obj->internalRep.twoPtrValue.ptr2 = Tcl_Alloc(sizeof(T)); + new(obj->internalRep.twoPtrValue.ptr2) T(std::move(object_data)); + if (type->updateStringProc != nullptr) + type->updateStringProc(obj); + + return obj; +} + +template class Template> +struct is_specialization : std::false_type {}; + +template