diff --git a/doc/src/vpr/command_line_usage.rst b/doc/src/vpr/command_line_usage.rst index 41ace87adb2..692098afadc 100644 --- a/doc/src/vpr/command_line_usage.rst +++ b/doc/src/vpr/command_line_usage.rst @@ -350,7 +350,7 @@ Use the options below to override this default naming behaviour. .. option:: --read_rr_graph - Reads in the routing resource graph named loads it for use during the placement and routing stages. Expects a file extension of either ``.xml`` and ``.bin``. + Reads in the routing resource graph named loads it for use during the placement and routing stages. Expects a file extension of either ``.xml`` or ``.bin``. The routing resource graph overthrows all the architecture definitions regarding switches, nodes, and edges. Other information such as grid information, block types, and segment information are matched with the architecture file to ensure accuracy. @@ -368,19 +368,22 @@ Use the options below to override this default naming behaviour. .. option:: --read_router_lookahead - Reads the lookahead data from the specified file instead of computing it. + Reads the lookahead data from the specified file instead of computing it. Expects a file extension of either ``.capnp`` or ``.bin``. .. option:: --write_router_lookahead - Writes the lookahead data to the specified file. + Writes the lookahead data to the specified file. Accepted file extensions are ``.capnp``, ``.bin``, and ``.csv``. .. option:: --read_placement_delay_lookup - Reads the placement delay lookup from the specified file instead of computing it. + Reads the placement delay lookup from the specified file instead of computing it. Expects a file extension of either ``.capnp`` or ``.bin``. .. option:: --write_placement_delay_lookup - Writes the placement delay lookup to the specified file. + Writes the placement delay lookup to the specified file. Expects a file extension of either ``.capnp`` or ``.bin``. +.. option:: --write_initial_place_file + + Writes out the the placement chosen by the initial placement algorithm to the specified file. .. option:: --outfile_prefix @@ -769,6 +772,19 @@ If any of init_t, exit_t or alpha_t is specified, the user schedule, with a fixe **Default:** ``criticality_timing`` +.. option:: --place_bounding_box_mode {auto_bb | cube_bb | per_layer_bb} + + Specifies the type of the wirelength estimator used during placement. For single layer architectures, cube_bb (a 3D bounding box) is always used (and is the same as per_layer_bb). + For 3D architectures, cube_bb is appropriate if you can cross between layers at switch blocks, while if you can only cross between layers at output pins per_layer_bb (one bouding box per layer) is more accurate and appropriate. + + ``auto_bb``: The bounding box type is determined automatically based on the cross-layer connections. + + ``cube_bb``: ``cube_bb`` bounding box is used to estimate the wirelength. + + ``per_layer_bb``: ``per_layer_bb`` bounding box is used to estimate the wirelength + + **Default:** ``auto_bb`` + .. option:: --place_chan_width Tells VPR how many tracks a channel of relative width 1 is expected to need to complete routing of this circuit. @@ -784,7 +800,7 @@ If any of init_t, exit_t or alpha_t is specified, the user schedule, with a fixe **Default:** ``0.0`` .. _dusty_sa_options: -Setting any of the following options selects `Dusty's annealing schedule `_. +Setting any of the following 5 options selects :ref:`Dusty's annealing schedule ` . .. option:: --alpha_min @@ -879,6 +895,38 @@ Setting any of the following options selects `Dusty's annealing schedule + + .. note:: This option is likely only of interest to developers debugging the placement algorithm + + Controls which block the placer produces detailed debug information for. + + If the block being moved has the same ID as the number assigned to this parameter, the placer will print debugging information about it. + + * For values >= 0, the value is the block ID for which detailed placer debug information should be produced. + * For value == -1, detailed placer debug information is produced for all blocks. + * For values < -1, no placer debug output is produced. + + .. warning:: VPR must have been compiled with `VTR_ENABLE_DEBUG_LOGGING` on to get any debug output from this option. + + **Default:** ``-2`` + +.. option:: --placer_debug_net + + .. note:: This option is likely only of interest to developers debugging the placement algorithm + + Controls which net the placer produces detailed debug information for. + + If a net with the same ID assigned to this parameter is connected to the block that is being moved, the placer will print debugging information about it. + + * For values >= 0, the value is the net ID for which detailed placer debug information should be produced. + * For value == -1, detailed placer debug information is produced for all nets. + * For values < -1, no placer debug output is produced. + + .. warning:: VPR must have been compiled with `VTR_ENABLE_DEBUG_LOGGING` on to get any debug output from this option. + + **Default:** ``-2`` + .. _timing_driven_placer_options: @@ -906,6 +954,13 @@ The following options are only valid when the placement engine is in timing-driv **Default:** ``0`` +.. option:: --quench_recompute_divider + + Controls how many times the placer performs a timing analysis to update its criticality estimates during a quench. + If unspecified, uses the value from --inner_loop_recompute_divider. + + **Default:** ``0`` + .. option:: --td_place_exp_first Controls how critical a connection is considered as a function of its slack, at the start of the anneal. @@ -1065,6 +1120,14 @@ VPR uses a negotiated congestion algorithm (based on Pathfinder) to perform rout .. seealso:: :ref:`timing_driven_router_options` +.. option:: --flat_routing {on | off} + + If this option is enabled, the *run-flat* router is used instead of the *two-stage* router. + This means that during the routing stage, all nets, both intra- and inter-cluster, are routed directly from one primitive pin to another primitive pin. + This increases routing time but can improve routing quality by re-arranging LUT inputs and exposing additional optimization opportunities in architectures with local intra-cluster routing that is not a full crossbar. + + **Default:** ``OFF` + .. option:: --max_router_iterations The number of iterations of a Pathfinder-based router that will be executed before a circuit is declared unrouteable (if it hasn’t routed successfully yet) at a given channel width. diff --git a/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp b/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp index 9383257cecb..4f35b27b7a5 100644 --- a/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp +++ b/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp @@ -202,7 +202,7 @@ namespace Catch { FatalConditionHandler::FatalConditionHandler() { assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); if (altStackSize == 0) { - altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); + altStackSize = std::max(static_cast(/*SIGSTKSZ*/32768), minStackSizeForErrors); } altStackMem = new char[altStackSize](); } diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp index 609b0c0b03e..69c9ba07a83 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp @@ -99,6 +99,15 @@ void TimingReporter::report_timing_setup(std::ostream& os, report_timing(os, paths); } +void TimingReporter::report_timing_setup(std::vector& paths, + std::ostream& os, + const SetupTimingAnalyzer& setup_analyzer, + size_t npaths) const { + paths = path_collector_.collect_worst_setup_timing_paths(timing_graph_, setup_analyzer, npaths); + + report_timing(os, paths); +} + void TimingReporter::report_timing_hold(std::string filename, const HoldTimingAnalyzer& hold_analyzer, size_t npaths) const { @@ -114,6 +123,15 @@ void TimingReporter::report_timing_hold(std::ostream& os, report_timing(os, paths); } +void TimingReporter::report_timing_hold(std::vector& paths, + std::ostream& os, + const HoldTimingAnalyzer& hold_analyzer, + size_t npaths) const { + paths = path_collector_.collect_worst_hold_timing_paths(timing_graph_, hold_analyzer, npaths); + + report_timing(os, paths); +} + void TimingReporter::report_skew_setup(std::string filename, const SetupTimingAnalyzer& setup_analyzer, size_t nworst) const { diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp index 1569aa6d704..590e3bca690 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp @@ -64,9 +64,11 @@ class TimingReporter { public: void report_timing_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_setup(std::vector& paths, std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_hold(std::string filename, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_hold(std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_hold(std::vector& paths, std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_skew_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; void report_skew_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index c00d65bfb41..2999b3357ce 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -166,8 +166,11 @@ enum e_pin_type { enum e_interconnect { COMPLETE_INTERC = 1, DIRECT_INTERC = 2, - MUX_INTERC = 3 + MUX_INTERC = 3, + NUM_INTERC_TYPES /* Invalid type */ }; +/* String version of interconnect types. Use for debugging messages */ +constexpr std::array INTERCONNECT_TYPE_STRING = {{"unknown", "complete", "direct", "mux"}}; /* Orientations. */ enum e_side : unsigned char { @@ -1501,7 +1504,7 @@ enum e_directionality { BI_DIRECTIONAL }; /* X_AXIS: Data that describes an x-directed wire segment (CHANX) * - * Y_AXIS: Data that describes an y-directed wire segment (CHANY) * + * Y_AXIS: Data that describes an y-directed wire segment (CHANY) * * BOTH_AXIS: Data that can be applied to both x-directed and y-directed wire segment */ enum e_parallel_axis { X_AXIS, @@ -1554,7 +1557,7 @@ enum e_Fc_type { * Cmetal: Capacitance of a routing track, per unit logic block length. * * Rmetal: Resistance of a routing track, per unit logic block length. * * (UDSD by AY) drivers: How do signals driving a routing track connect to * - * the track? + * the track? * seg_index: The index of the segment as stored in the appropriate Segs list* * Upon loading the architecture, we use this field to keep track * * the segment's index in the unified segment_inf vector. This is * @@ -1611,12 +1614,12 @@ constexpr std::array SWITCH_T /* Constant/Reserved names for switches in architecture XML * Delayless switch: - * The zero-delay switch created by VPR internally + * The zero-delay switch created by VPR internally * This is a special switch just to ease CAD algorithms * It is mainly used in - * - the edges between SOURCE and SINK nodes in routing resource graphs + * - the edges between SOURCE and SINK nodes in routing resource graphs * - the edges in CLB-to-CLB connections (defined by in arch XML) - * + * */ constexpr const char* VPR_DELAYLESS_SWITCH_NAME = "__vpr_delayless_switch__"; @@ -1958,12 +1961,19 @@ struct t_arch { char* architecture_id; //Secure hash digest of the architecture file to uniquely identify this architecture + /* Xifan Tang: options for tileable routing architectures */ + bool tileable; + bool shrink_boundary; + bool through_channel; + t_chan_width_dist Chans; enum e_switch_block_type SBType; + enum e_switch_block_type SBSubType; std::vector switchblocks; float R_minW_nmos; float R_minW_pmos; int Fs; + int subFs; float grid_logic_tile_area; std::vector Segments; t_arch_switch_inf* Switches = nullptr; diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index fa605911698..978865ec6de 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -120,6 +120,11 @@ static std::pair ProcessPinString(pugi::xml_node Locations, T type, const char* pin_loc_string, const pugiutil::loc_data& loc_data); +template +static std::pair ProcessInstanceString(pugi::xml_node Locations, + T type, + const char* pin_loc_string, + const pugiutil::loc_data& loc_data); /* Process XML hierarchy */ static void ProcessTiles(pugi::xml_node Node, @@ -312,10 +317,10 @@ void XmlReadArch(const char* ArchFile, pugi::xml_node Next; ReqOpt POWER_REQD, SWITCHBLOCKLIST_REQD; - if (vtr::check_file_name_extension(ArchFile, ".xml") == false) { + if ((vtr::check_file_name_extension(ArchFile, ".xml") == false) && (vtr::check_file_name_extension(ArchFile, ".xml") == false)) { VTR_LOG_WARN( "Architecture file '%s' may be in incorrect format. " - "Expecting .xml format for architecture files.\n", + "Expecting .xml or .xmle format for architecture files.\n", ArchFile); } @@ -627,10 +632,15 @@ static void LoadPinLoc(pugi::xml_node Locations, &sub_tile, token.c_str(), loc_data); - + /* Get the offset in the capacity range */ + auto capacity_range = ProcessInstanceString(Locations, + &sub_tile, + token.c_str(), + loc_data); + VTR_ASSERT(0 <= capacity_range.first && capacity_range.second < sub_tile_capacity); for (int pin_num = pin_range.first; pin_num < pin_range.second; ++pin_num) { VTR_ASSERT(pin_num < (int)sub_tile.sub_tile_to_tile_pin_indices.size() / sub_tile_capacity); - for (int capacity = 0; capacity < sub_tile_capacity; ++capacity) { + for (int capacity = capacity_range.first; capacity <= capacity_range.second; ++capacity) { int sub_tile_pin_index = pin_num + capacity * sub_tile.num_phy_pins / sub_tile_capacity; int physical_pin_index = sub_tile.sub_tile_to_tile_pin_indices[sub_tile_pin_index]; type->pinloc[width][height][side][physical_pin_index] = true; @@ -660,6 +670,99 @@ static void LoadPinLoc(pugi::xml_node Locations, } } +/* Parse the string to extract instance range, e.g., io[4:7] -> (4, 7) + * If no instance range is explicitly defined, we assume the range of type capacity, i.e., (0, capacity - 1) */ +template +static std::pair ProcessInstanceString(pugi::xml_node Locations, + T type, + const char* pin_loc_string, + const pugiutil::loc_data& loc_data) { + int num_tokens; + auto tokens = GetTokensFromString(pin_loc_string, &num_tokens); + + int token_index = 0; + auto token = tokens[token_index]; + + if (token.type != TOKEN_STRING || 0 != strcmp(token.data, type->name)) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Wrong physical type name of the port: %s\n", pin_loc_string); + } + + token_index++; + token = tokens[token_index]; + + int first_inst = 0; + int last_inst = type->capacity.total() - 1; + + /* If there is a dot, such as io.input[0:3], it indicates the full range of the capacity, the default value should be returned */ + if (token.type == TOKEN_DOT) { + freeTokens(tokens, num_tokens); + return std::make_pair(first_inst, last_inst); + } + + /* If the string contains index for capacity range, e.g., io[3:3].in[0:5], we skip the capacity range here. */ + if (token.type != TOKEN_OPEN_SQUARE_BRACKET) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No open square bracket present: %s\n", pin_loc_string); + } + + token_index++; + token = tokens[token_index]; + + if (token.type != TOKEN_INT) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No integer to indicate least significant instance index: %s\n", pin_loc_string); + } + + first_inst = vtr::atoi(token.data); + + token_index++; + token = tokens[token_index]; + + // Single pin is specified + if (token.type != TOKEN_COLON) { + if (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No closing bracket: %s\n", pin_loc_string); + } + + token_index++; + + if (token_index != num_tokens) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "instance of pin location should be completed, but more tokens are present: %s\n", pin_loc_string); + } + + freeTokens(tokens, num_tokens); + return std::make_pair(first_inst, first_inst); + } + + token_index++; + token = tokens[token_index]; + + if (token.type != TOKEN_INT) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No integer to indicate most significant instance index: %s\n", pin_loc_string); + } + + last_inst = vtr::atoi(token.data); + + token_index++; + token = tokens[token_index]; + + if (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No closed square bracket: %s\n", pin_loc_string); + } + + if (first_inst > last_inst) { + std::swap(first_inst, last_inst); + } + + freeTokens(tokens, num_tokens); + return std::make_pair(first_inst, last_inst); +} + template static std::pair ProcessPinString(pugi::xml_node Locations, T type, @@ -679,6 +782,20 @@ static std::pair ProcessPinString(pugi::xml_node Locations, token_index++; token = tokens[token_index]; + /* If the string contains index for capacity range, e.g., io[3:3].in[0:5], we skip the capacity range here. */ + if (token.type == TOKEN_OPEN_SQUARE_BRACKET) { + while (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { + token_index++; + token = tokens[token_index]; + if (token_index == num_tokens) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Found an open '[' but miss close ']' of the port: %s\n", pin_loc_string); + } + } + token_index++; + token = tokens[token_index]; + } + if (token.type != TOKEN_DOT) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No dot is present to separate type name and port name: %s\n", pin_loc_string); @@ -2403,7 +2520,11 @@ static void ProcessLayout(pugi::xml_node layout_tag, t_arch* arch, const pugiuti VTR_ASSERT(layout_tag.name() == std::string("layout")); //Expect no attributes on - expect_only_attributes(layout_tag, {}, loc_data); + //expect_only_attributes(layout_tag, {}, loc_data); + + arch->tileable = get_attribute(layout_tag, "tileable", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->shrink_boundary = get_attribute(layout_tag, "shrink_boundary", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->through_channel = get_attribute(layout_tag, "through_channel", loc_data, ReqOpt::OPTIONAL).as_bool(false); //Count the number of or tags size_t auto_layout_cnt = 0; @@ -2811,8 +2932,9 @@ static void ProcessDevice(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& // tag Cur = get_single_child(Node, "switch_block", loc_data); - expect_only_attributes(Cur, {"type", "fs"}, loc_data); + expect_only_attributes(Cur, {"type", "fs", "sub_type", "sub_fs"}, loc_data); Prop = get_attribute(Cur, "type", loc_data).value(); + /* Parse attribute 'type', representing the major connectivity pattern for switch blocks */ if (strcmp(Prop, "wilton") == 0) { arch->SBType = WILTON; } else if (strcmp(Prop, "universal") == 0) { @@ -2826,9 +2948,31 @@ static void ProcessDevice(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Unknown property %s for switch block type x\n", Prop); } + /* Parse attribute 'sub_type', representing the minor connectivity pattern for switch blocks + * If not specified, the 'sub_type' is the same as major type + * This option is only valid for tileable routing resource graph builder + * Note that sub_type does not support custom switch block pattern!!! + * If 'sub_type' is specified, the custom switch block for 'type' is not allowed! + */ + std::string sub_type_str = get_attribute(Cur, "sub_type", loc_data, BoolToReqOpt(false)).as_string(""); + if (!sub_type_str.empty()) { + if (sub_type_str == std::string("wilton")) { + arch->SBSubType = WILTON; + } else if (sub_type_str == std::string("universal")) { + arch->SBSubType = UNIVERSAL; + } else if (sub_type_str == std::string("subset")) { + arch->SBSubType = SUBSET; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), + "Unknown property %s for switch block subtype x\n", sub_type_str.c_str()); + } + } else { + arch->SBSubType = arch->SBType; + } ReqOpt CUSTOM_SWITCHBLOCK_REQD = BoolToReqOpt(!custom_switch_block); arch->Fs = get_attribute(Cur, "fs", loc_data, CUSTOM_SWITCHBLOCK_REQD).as_int(3); + arch->subFs = get_attribute(Cur, "sub_fs", loc_data, BoolToReqOpt(false)).as_int(arch->Fs); Cur = get_single_child(Node, "default_fc", loc_data, ReqOpt::OPTIONAL); if (Cur) { @@ -3393,8 +3537,8 @@ static void ProcessPinLocations(pugi::xml_node Locations, //Verify that all top-level pins have had their locations specified - //Record all the specified pins - std::map> port_pins_with_specified_locations; + //Record all the specified pins, (capacity, port_name, index) + std::map>> port_pins_with_specified_locations; for (int l = 0; l < num_of_avail_layer; ++l) { for (int w = 0; w < PhysicalTileType->width; ++w) { for (int h = 0; h < PhysicalTileType->height; ++h) { @@ -3403,10 +3547,22 @@ static void ProcessPinLocations(pugi::xml_node Locations, InstPort inst_port(token.c_str()); //A pin specification should contain only the block name, and not any instance count information + //A pin specification may contain instance count, but should be in the range of capacity + int inst_lsb = 0; + int inst_msb = SubTile->capacity.total() - 1; if (inst_port.instance_low_index() != InstPort::UNSPECIFIED || inst_port.instance_high_index() != InstPort::UNSPECIFIED) { - archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), - "Pin location specification '%s' should not contain an instance range (should only be the block name)", - token.c_str()); + /* Extract range numbers */ + inst_lsb = inst_port.instance_low_index(); + inst_msb = inst_port.instance_high_index(); + if (inst_lsb > inst_msb) { + std::swap(inst_lsb, inst_msb); + } + /* Check if we have a valid range */ + if (inst_lsb < 0 || inst_msb > SubTile->capacity.total() - 1) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Pin location specification '%s' contain an out-of-range instance. Expect [%d:%d]", + token.c_str(), 0, SubTile->capacity.total() - 1); + } } //Check that the block name matches @@ -3443,9 +3599,11 @@ static void ProcessPinLocations(pugi::xml_node Locations, VTR_ASSERT(pin_low_idx >= 0); VTR_ASSERT(pin_high_idx >= 0); - for (int ipin = pin_low_idx; ipin <= pin_high_idx; ++ipin) { - //Record that the pin has it's location specified - port_pins_with_specified_locations[inst_port.port_name()].insert(ipin); + for (int iinst = inst_lsb + SubTile->capacity.low; iinst <= inst_msb + SubTile->capacity.low; ++iinst) { + for (int ipin = pin_low_idx; ipin <= pin_high_idx; ++ipin) { + //Record that the pin has it's location specified + port_pins_with_specified_locations[iinst][inst_port.port_name()].insert(ipin); + } } } } @@ -3454,13 +3612,15 @@ static void ProcessPinLocations(pugi::xml_node Locations, } //Check for any pins missing location specs - for (const auto& port : SubTile->ports) { - for (int ipin = 0; ipin < port.num_pins; ++ipin) { - if (!port_pins_with_specified_locations[port.name].count(ipin)) { - //Missing - archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), - "Pin '%s.%s[%d]' has no pin location specified (a location is required for pattern=\"custom\")", - SubTile->name, port.name, ipin); + for (int iinst = SubTile->capacity.low; iinst < SubTile->capacity.high; ++iinst) { + for (const auto& port : SubTile->ports) { + for (int ipin = 0; ipin < port.num_pins; ++ipin) { + if (!port_pins_with_specified_locations[iinst][port.name].count(ipin)) { + //Missing + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Pin '%s[%d].%s[%d]' has no pin location specificed (a location is required for pattern=\"custom\")", + SubTile->name, iinst, port.name, ipin); + } } } } diff --git a/libs/libdecrypt/ README.md b/libs/libdecrypt/ README.md new file mode 100644 index 00000000000..e6a4193bf43 --- /dev/null +++ b/libs/libdecrypt/ README.md @@ -0,0 +1,98 @@ + # libdecrypt + +libdecrypt is a C++ library for decrypting encrypted files. It provides a simple interface to decrypt files using OpenSSL for encryption and decryption operations and pugixml for XML parsing. + +## Features + +- Decrypts encrypted files using RSA encryption algorithm. +- Supports loading private key from PEM string. +- Retrieves encrypted data and session key from XML file. +- Decrypts session key using RSA private key. +- Decrypts XML string using the decrypted session key. +- Outputs the decrypted content as a string. + +## Installation + +1. Build the library using CMake + + cd libdecrypt + mkdir build + cd build + cmake .. + make +2. Install the library (optional): + + make install +## Dependencies + +Make sure you have the following dependencies installed on your system: + +- **OpenSSL**: The library depends on OpenSSL for encryption and decryption operations. Make sure you have OpenSSL installed on your system. + +- **pugixml**: The library uses pugixml for XML parsing. It is included as a submodule in the project. + +## Usage + +To use the `libdecrypt` library in your C++ projects, follow the steps below: + +1. Include the `decryption.h` header file in your source code: + + ```cpp + #include "decryption.h" + +2. Create a `Decryption` object with the path to the encrypted file: + + std::string encryptedFile = "path/to/encrypted/file"; + Decryption decryption(encryptedFile); + +3. Decrypt the contents of the encrypted file: + + decryption.decryptFile(); + +4. Retrieve the decrypted content as a string: + + std::string decryptedContent = decryption.getDecryptedContent(); + +5. Optional: Write the decrypted content to a file: + + std::ofstream outputFile("output.txt"); + outputFile << decryptedContent; + outputFile.close(); + +## Configuration + +To configure `libdecrypt` for your specific use case, follow these steps: + +### Private Key: + +- **Option 1: Define `PRIVATE_KEY` in the code:** + - Locate the `decryption.h` file in the project. + - Uncomment the `#define PRIVATE_KEY` line. + - Replace the dummy private key string with your actual private key in PEM format. + +- **Option 2: Provide a `private_key.pem` file:** + - Place your private key file in the project directory. + - Make sure the file is named `private_key.pem`. + - `libdecrypt` will automatically load the private key from this file. + +### Passphrase: + +- **Option 1: Define `PASSPHRASE` in the code:** + - Locate the `decryption.h` file in the project. + - Uncomment the `#define PASSPHRASE` line. + - Replace the empty string with your actual passphrase. + +- **Option 2: Provide a `config.txt` file:** + - Create a plain text file named `config.txt` in the project directory. + - Write your passphrase in the file. + - `libdecrypt` will read the passphrase from this file. + +Make sure to configure the private key and passphrase according to your specific requirements before using the `libdecrypt` library. + +## License + +This project is licensed under the MIT License + +## Contributing + +Contributions are welcome! If you find any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request. We appreciate your contributions to make this project better. diff --git a/libs/libdecrypt/CMakeLists.txt b/libs/libdecrypt/CMakeLists.txt new file mode 100644 index 00000000000..3eaeab363c6 --- /dev/null +++ b/libs/libdecrypt/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.16) +project(libdecrypt) + +file(READ "private_key.pem" PRIVATE_KEY_CONTENTS) +file(READ "config.txt" PASSPHRASE_CONTENTS) + +string(REPLACE "\n" "\\n" PRIVATE_KEY_CONTENTS "${PRIVATE_KEY_CONTENTS}") +string(REPLACE "\n" "\\n" PASSPHRASE_CONTENTS "${PASSPHRASE_CONTENTS}") +set(PRIVATE_KEY "${PRIVATE_KEY_CONTENTS}") +set(PASS_PHRASE "${PASSPHRASE_CONTENTS}") +add_compile_definitions(PASS_PHRASE="${PASS_PHRASE}") +add_compile_definitions(PRIVATE_KEY="${PRIVATE_KEY}") +find_package(PkgConfig REQUIRED) +pkg_search_module(OPENSSL REQUIRED openssl) + +if( OPENSSL_FOUND ) + include_directories(${OPENSSL_INCLUDE_DIRS}) + message(STATUS "Using OpenSSL ${OPENSSL_VERSION}") +else() + message("SSL not found") + # Error; with REQUIRED, pkg_search_module() will throw an error by it's own +endif() +# Source files and library +file(GLOB_RECURSE LIB_SOURCES src/*.cpp) +file(GLOB_RECURSE LIB_HEADERS src/*.hpp src/*.h) +set(LIB_INCLUDE_DIRS "") +foreach (_headerFile ${LIB_HEADERS}) + get_filename_component(_dir ${_headerFile} PATH) + list(APPEND LIB_INCLUDE_DIRS ${_dir}) +endforeach () +list(REMOVE_DUPLICATES LIB_INCLUDE_DIRS) + + + +# Create the library +add_library(libdecrypt STATIC + ${LIB_HEADERS} + ${LIB_SOURCES}) +target_include_directories(libdecrypt PUBLIC ${LIB_INCLUDE_DIRS}) +set_target_properties(libdecrypt PROPERTIES PREFIX "") # Avoid extra 'lib' prefix + +target_link_libraries(libdecrypt + libpugixml + ${OPENSSL_LIBRARIES} +) +install(TARGETS libdecrypt DESTINATION bin) +install(FILES ${LIB_HEADERS} DESTINATION include/libdecrypt) \ No newline at end of file diff --git a/libs/libdecrypt/config.txt b/libs/libdecrypt/config.txt new file mode 100644 index 00000000000..85df50785d6 --- /dev/null +++ b/libs/libdecrypt/config.txt @@ -0,0 +1 @@ +abcd \ No newline at end of file diff --git a/libs/libdecrypt/private_key.pem b/libs/libdecrypt/private_key.pem new file mode 100644 index 00000000000..f93621227a2 --- /dev/null +++ b/libs/libdecrypt/private_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIxiWq9/gKEI4CAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCM12wmpHSRMA8no6WSnyiRBIIE +0HWEpZCWG230mIezxiEQGtR/W0HliJIU/fkJukt7hyqt6ECwcr9te80KIcqftUsH +64d8GBwCAfIpP5gDtojsm2XwM6acdGyFO4ZFyF2yO+kO9kdUVPQbe6Um2wYFGpXq +wMeMclkL+ZRxhttu6j9RsB9iQKqh13/Nw2x7y2xCDK0lkbHKprdO466C92Vqygvb +6Yz7VoawwJT9Wpn7Z6//vSQ1QI7ePf2P1CrZp+z0Nr+kJqxCoSuQ7wavnsEgF+pA +qqx9WRU6twtG97LHeOeYoZZmSN5i3KAFiwWIlxka96VTl86lREPzDJYa4pHR2FNm +186rrKjAIlDlF9UC6zZMIGUtNdo5miuhpjR6BUFQlDIMW6y2nubyj3awBVvM7F45 +QDh0Mzr4x0Ohqaw5+CphkcUKozRHLXA5/31bJLNIGYB6J3uRdJAVVr1R4ouNeI2U ++2Z60WhMIDROVIXn4AwdoukiUy2uBHNPJp8HPyX+aD7I6FgmcLtuZWrjCnOYU44I +8yBVDGbFb/dsjaJbNDNfyl4db6RcPlOjIhIv7kPF3T4AAY2VRZAGQJBNPZ5XO0U9 +nK5q5wgyagsiIWnIhtx7k9texnnVJpOmaF2mS3Wh/PyxJ01wav8yd5TYu8V8vbUV +7dcM/qIUkkYr1Q6llXrSOeA6kGdOfbRSDqgu+9n3NfH4T5msKQQN6u6b1Z/0ayev +pJgFjaxZITh+7T20BEgULuuv3+sCNYPpEmf+fi7V8AKjjdB3iol2XIFKtKYzInwP +Jb/sageAS/u0MHYt4s9XMi69998CAnlt+qZD2wekI6/AOXyxXxkx9WLaxP6Gb6L+ +e/EEui/gKw+AjKlwHrQHFTo2byJy0dOMfJDaCdb8TXouGLrw0iDcoVm/mxN/66WH +ncFSeVqiYOl7lc2y9cfmMNffdw8E92ab6k1bxy4wdmcskVqXsUskveQ4IbAxdaOp +R6B/FZuRvUpjIHlKb0wpmLxvaY1i1bWDXfE5hbIv6zDSMAdgpLeE9Om1xdhOvm2p +dndJguGR6Nk8tYGvJFXREwLxdEU5z3/Cdv/hTV2kmdz1I/JKcEB2cmgqLV1jBpIl +Mzebwl7Dwtxc2VYIDgrij3bU2ekRw1s6dV+RlJdbEX+j5/pxi/sh67szP0JZ4Lfk +87Pk1pmUksqKc6ybU+KCqHmTvlKu1fMrAj4DVG2qLMKl3nVXLnYCm8J7jh5CNXZe +/Qptee/KaT86bP4POQ6w8fKwF2EMT0HrvL5tUoHt6cwH8Hn6pHmrYRI8dbaG9PH2 +wtOvgzRzCalkCh0hAJDrYehnmDCMTFxDC0y8O3l5Ngz/0vhXEsiMUoEk4J9ZbkuZ +yjLl8DSeA8tcCh5XmQ99QMrhdtPCcak+LmbmbEVPWV91FYowcvTTegC9qaJvE5HK +T5X/JkJtw2ZzC/P0CJuAnfLD+mDBayb71/44PqlzOnuPB/6qqldktw4utH0Yjmdx +kri2neh2tJswe/d8pm2g6RJXquxgmszIBh7O6AZ38hjQVVAiQbxuUviB8K4Q7w3B +bTEB2xM7/4uW/QJbPShAA9pd6iMVm8dWMqXJ3zLg+P+qUGQbI9XKx2Ramsy91Xk6 +o6hYZncsAZoVO6HnCoH7WhBozoyFKiZlA9WqqMGL83lU +-----END ENCRYPTED PRIVATE KEY----- diff --git a/libs/libdecrypt/src/config.h b/libs/libdecrypt/src/config.h new file mode 100644 index 00000000000..c01a0dce535 --- /dev/null +++ b/libs/libdecrypt/src/config.h @@ -0,0 +1,9 @@ + +#ifndef CONFIG_H +#define CONFIG_H + +#define PRIVATE_KEY "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIxiWq9/gKEI4CAggA\nMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCM12wmpHSRMA8no6WSnyiRBIIE\n0HWEpZCWG230mIezxiEQGtR/W0HliJIU/fkJukt7hyqt6ECwcr9te80KIcqftUsH\n64d8GBwCAfIpP5gDtojsm2XwM6acdGyFO4ZFyF2yO+kO9kdUVPQbe6Um2wYFGpXq\nwMeMclkL+ZRxhttu6j9RsB9iQKqh13/Nw2x7y2xCDK0lkbHKprdO466C92Vqygvb\n6Yz7VoawwJT9Wpn7Z6//vSQ1QI7ePf2P1CrZp+z0Nr+kJqxCoSuQ7wavnsEgF+pA\nqqx9WRU6twtG97LHeOeYoZZmSN5i3KAFiwWIlxka96VTl86lREPzDJYa4pHR2FNm\n186rrKjAIlDlF9UC6zZMIGUtNdo5miuhpjR6BUFQlDIMW6y2nubyj3awBVvM7F45\nQDh0Mzr4x0Ohqaw5+CphkcUKozRHLXA5/31bJLNIGYB6J3uRdJAVVr1R4ouNeI2U\n+2Z60WhMIDROVIXn4AwdoukiUy2uBHNPJp8HPyX+aD7I6FgmcLtuZWrjCnOYU44I\n8yBVDGbFb/dsjaJbNDNfyl4db6RcPlOjIhIv7kPF3T4AAY2VRZAGQJBNPZ5XO0U9\nnK5q5wgyagsiIWnIhtx7k9texnnVJpOmaF2mS3Wh/PyxJ01wav8yd5TYu8V8vbUV\n7dcM/qIUkkYr1Q6llXrSOeA6kGdOfbRSDqgu+9n3NfH4T5msKQQN6u6b1Z/0ayev\npJgFjaxZITh+7T20BEgULuuv3+sCNYPpEmf+fi7V8AKjjdB3iol2XIFKtKYzInwP\nJb/sageAS/u0MHYt4s9XMi69998CAnlt+qZD2wekI6/AOXyxXxkx9WLaxP6Gb6L+\ne/EEui/gKw+AjKlwHrQHFTo2byJy0dOMfJDaCdb8TXouGLrw0iDcoVm/mxN/66WH\nncFSeVqiYOl7lc2y9cfmMNffdw8E92ab6k1bxy4wdmcskVqXsUskveQ4IbAxdaOp\nR6B/FZuRvUpjIHlKb0wpmLxvaY1i1bWDXfE5hbIv6zDSMAdgpLeE9Om1xdhOvm2p\ndndJguGR6Nk8tYGvJFXREwLxdEU5z3/Cdv/hTV2kmdz1I/JKcEB2cmgqLV1jBpIl\nMzebwl7Dwtxc2VYIDgrij3bU2ekRw1s6dV+RlJdbEX+j5/pxi/sh67szP0JZ4Lfk\n87Pk1pmUksqKc6ybU+KCqHmTvlKu1fMrAj4DVG2qLMKl3nVXLnYCm8J7jh5CNXZe\n/Qptee/KaT86bP4POQ6w8fKwF2EMT0HrvL5tUoHt6cwH8Hn6pHmrYRI8dbaG9PH2\nwtOvgzRzCalkCh0hAJDrYehnmDCMTFxDC0y8O3l5Ngz/0vhXEsiMUoEk4J9ZbkuZ\nyjLl8DSeA8tcCh5XmQ99QMrhdtPCcak+LmbmbEVPWV91FYowcvTTegC9qaJvE5HK\nT5X/JkJtw2ZzC/P0CJuAnfLD+mDBayb71/44PqlzOnuPB/6qqldktw4utH0Yjmdx\nkri2neh2tJswe/d8pm2g6RJXquxgmszIBh7O6AZ38hjQVVAiQbxuUviB8K4Q7w3B\nbTEB2xM7/4uW/QJbPShAA9pd6iMVm8dWMqXJ3zLg+P+qUGQbI9XKx2Ramsy91Xk6\no6hYZncsAZoVO6HnCoH7WhBozoyFKiZlA9WqqMGL83lU\n-----END ENCRYPTED PRIVATE KEY-----\n" +#define PASSPHRASE "abcd" +// Add more configuration variables as needed + +#endif // CONFIG_H diff --git a/libs/libdecrypt/src/decryption.cpp b/libs/libdecrypt/src/decryption.cpp new file mode 100644 index 00000000000..2a745cfd5bf --- /dev/null +++ b/libs/libdecrypt/src/decryption.cpp @@ -0,0 +1,185 @@ +#include "decryption.h" + +#ifdef PASS_PHRASE +std::string passphrase = PASS_PHRASE; +#else +std::string passphrase = ""; // Set your PEM pass phrase here +#endif + +/** + * @brief Constructs a Decryption object with the specified encrypted file. + * + * @param encryptedFile The path to the encrypted file. + */ +Decryption::Decryption(const std::string& encryptedFile) + : encryptedFile_(encryptedFile) { + decryptFile(); +} + +/** + * @brief Decrypts the contents of the encrypted file. + * + * This function performs the decryption process, including loading the private key, + * retrieving the encrypted data and session key from the XML file, decrypting the + * session key, and decrypting the XML string. + */ +void Decryption::decryptFile() { +#ifdef PRIVATE_KEY + const char* privateKeyString = AY_OBFUSCATE(PRIVATE_KEY); +#else + const char* privateKeyString = AY_OBFUSCATE( + "-----BEGIN RSA PRIVATE KEY-----\n" + "dummykey\n" + "-----END RSA PRIVATE KEY-----\n"); // Replace with your private key string +#endif + RSA* privateKey = loadPrivateKey(privateKeyString); + if (!privateKey) { + return; + } + + // Load encrypted data and session key from XML file + pugi::xml_document encryptedDocLoaded; + pugi::xml_parse_result result = encryptedDocLoaded.load_file(encryptedFile_.c_str()); + if (!result) { + std::cerr << "XML parse error: " << result.description() << std::endl; + RSA_free(privateKey); + return; + } + + pugi::xml_node root = encryptedDocLoaded.child("EncryptedData"); + std::string base64EncryptedSessionKeyLoaded = root.child_value("SessionKey"); + std::string base64EncryptedLoaded = root.child_value("Data"); + + // Base64 decode encrypted session key and data + std::string encryptedSessionKeyLoaded = base64_decode(base64EncryptedSessionKeyLoaded); + std::string encryptedLoaded = base64_decode(base64EncryptedLoaded); + + // Decrypt session key + std::string decryptedSessionKey = decryptSessionKey(encryptedSessionKeyLoaded, privateKey); + + // Decrypt XML string + std::string decrypted = decrypt(encryptedLoaded, privateKey); + + // Write the decrypted data to a file + // std::ofstream decryptedFile("decrypted.xml"); + // decryptedFile << decrypted; + // decryptedFile.close(); + + decryptedContent_ = decrypted; + RSA_free(privateKey); +} + +/** + * @brief Retrieves the decrypted content. + * + * @return The decrypted content as a string. + */ +std::string Decryption::getDecryptedContent() const { + return decryptedContent_; +} + +/** + * @brief Decrypts the given ciphertext using the provided RSA key. + * + * @param ciphertext The ciphertext to decrypt. + * @param key The RSA key for decryption. + * @return The decrypted plaintext. + */ +std::string Decryption::decrypt(const std::string& ciphertext, RSA* key) { + int rsaLen = RSA_size(key); + int len = ciphertext.size(); + std::string plaintext; + + for (int i = 0; i < len; i += rsaLen) { + std::vector buffer(rsaLen); + std::string substr = ciphertext.substr(i, rsaLen); + + int result = RSA_private_decrypt(substr.size(), reinterpret_cast(substr.data()), buffer.data(), key, RSA_PKCS1_OAEP_PADDING); + if (result == -1) { + std::cerr << "Decryption error: " << ERR_error_string(ERR_get_error(), NULL) << std::endl; + return ""; + } + + plaintext.append(reinterpret_cast(buffer.data()), result); + } + + return plaintext; +} + +/** + * @brief Decodes the given base64-encoded string. + * + * @param input The base64-encoded input string. + * @return The decoded output string. + */ +std::string Decryption::base64_decode(const std::string& input) { + BIO* b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + BIO* bmem = BIO_new_mem_buf(input.data(), input.size()); + b64 = BIO_push(b64, bmem); + + std::string output; + output.resize(input.size()); + + int decoded_size = BIO_read(b64, &output[0], input.size()); + output.resize(decoded_size); + BIO_free_all(b64); + + return output; +} + +/** + * @brief Loads the private key from the given PEM string. + * + * @param privateKeyString The PEM string representing the private key. + * @return The loaded RSA private key. + */ +RSA* Decryption::loadPrivateKey(const std::string& privateKeyString) { + RSA* key = nullptr; + BIO* privateKeyBio = BIO_new_mem_buf(privateKeyString.data(), privateKeyString.size()); + + if (!privateKeyBio) { + std::cerr << "Error creating BIO for private key" << std::endl; + return nullptr; + } + + if (!PEM_read_bio_RSAPrivateKey(privateKeyBio, &key, NULL, (void*)passphrase.c_str())) { + std::cerr << "Error reading private key" << std::endl; + BIO_free(privateKeyBio); + return nullptr; + } + + BIO_free(privateKeyBio); + return key; +} + +/** + * @brief Decrypts the given encrypted session key using the provided RSA key. + * + * @param encryptedSessionKey The encrypted session key. + * @param key The RSA key for decryption. + * @return The decrypted session key. + */ +std::string Decryption::decryptSessionKey(const std::string& encryptedSessionKey, RSA* key) { + std::vector decryptedSessionKey(RSA_size(key)); + if (RSA_private_decrypt(encryptedSessionKey.size(), reinterpret_cast(encryptedSessionKey.data()), decryptedSessionKey.data(), key, RSA_PKCS1_OAEP_PADDING) == -1) { + std::cerr << "Session key decryption error: " << ERR_error_string(ERR_get_error(), NULL) << std::endl; + return ""; + } + + return std::string(reinterpret_cast(decryptedSessionKey.data()), decryptedSessionKey.size()); +} + +// int main(int argc, char* argv[]) { +// if (argc < 2) { +// std::cerr << "Usage: " << argv[0] << " \n"; +// return -1; +// } + +// std::string encryptedFile = argv[1]; + +// Decryption decryption(encryptedFile); + +// return 0; +// } diff --git a/libs/libdecrypt/src/decryption.h b/libs/libdecrypt/src/decryption.h new file mode 100644 index 00000000000..52d3023dd93 --- /dev/null +++ b/libs/libdecrypt/src/decryption.h @@ -0,0 +1,86 @@ +#ifndef DECRYPTION_H +#define DECRYPTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pugixml.hpp" +#include "obfuscate.h" + +/** + * @brief The Decryption class for decrypting encrypted files. + */ +class Decryption +{ +public: + /** + * @brief Constructs a Decryption object with the specified encrypted file. + * + * @param encryptedFile The path to the encrypted file. + */ + Decryption(const std::string &encryptedFile); + + /** + * @brief Decrypts the contents of the encrypted file. + * + * This function performs the decryption process, including loading the private key, + * retrieving the encrypted data and session key from the XML file, decrypting the + * session key, and decrypting the XML string. + */ + void decryptFile(); + + /** + * @brief Retrieves the decrypted content. + * + * @return The decrypted content as a string. + */ + std::string getDecryptedContent() const; + +private: + std::string encryptedFile_; /**< The path to the encrypted file. */ + std::string decryptedContent_; /**< The decrypted content of the file. */ + + /** + * @brief Decrypts the given ciphertext using the provided RSA key. + * + * @param ciphertext The ciphertext to decrypt. + * @param key The RSA key for decryption. + * @return The decrypted plaintext. + */ + static std::string decrypt(const std::string &ciphertext, RSA *key); + + /** + * @brief Decodes the given base64-encoded string. + * + * @param input The base64-encoded input string. + * @return The decoded output string. + */ + static std::string base64_decode(const std::string &input); + + /** + * @brief Loads the private key from the given PEM string. + * + * @param privateKeyString The PEM string representing the private key. + * @return The loaded RSA private key. + */ + static RSA *loadPrivateKey(const std::string &privateKeyString); + + /** + * @brief Decrypts the given encrypted session key using the provided RSA key. + * + * @param encryptedSessionKey The encrypted session key. + * @param key The RSA key for decryption. + * @return The decrypted session key. + */ + static std::string decryptSessionKey(const std::string &encryptedSessionKey, RSA *key); +}; + +#endif // DECRYPTION_H diff --git a/libs/libdecrypt/src/obfuscate.h b/libs/libdecrypt/src/obfuscate.h new file mode 100644 index 00000000000..5450eb44f01 --- /dev/null +++ b/libs/libdecrypt/src/obfuscate.h @@ -0,0 +1,185 @@ +#ifndef OBFUSCATE_H +#define OBFUSCATE_H + +#ifdef _MSC_VER + #define AY_CAT(X,Y) AY_CAT2(X,Y) + #define AY_CAT2(X,Y) X##Y + #define AY_LINE int(AY_CAT(__LINE__,U)) +#else + #define AY_LINE __LINE__ +#endif + +#ifndef AY_OBFUSCATE_DEFAULT_KEY + // The default 64 bit key to obfuscate strings with. + // This can be user specified by defining AY_OBFUSCATE_DEFAULT_KEY before + // including obfuscate.h + #define AY_OBFUSCATE_DEFAULT_KEY ay::generate_key(AY_LINE) +#endif + +namespace ay +{ + using size_type = unsigned long long; + using key_type = unsigned long long; + + // Generate a pseudo-random key that spans all 8 bytes + constexpr key_type generate_key(key_type seed) + { + // Use the MurmurHash3 64-bit finalizer to hash our seed + key_type key = seed; + key ^= (key >> 33); + key *= 0xff51afd7ed558ccd; + key ^= (key >> 33); + key *= 0xc4ceb9fe1a85ec53; + key ^= (key >> 33); + + // Make sure that a bit in each byte is set + key |= 0x0101010101010101ull; + + return key; + } + + // Obfuscates or deobfuscates data with key + constexpr void cipher(char* data, size_type size, key_type key) + { + // Obfuscate with a simple XOR cipher based on key + for (size_type i = 0; i < size; i++) + { + data[i] ^= char(key >> ((i % 8) * 8)); + } + } + + // Obfuscates a string at compile time + template + class obfuscator + { + public: + // Obfuscates the string 'data' on construction + constexpr obfuscator(const char* data) + { + // Copy data + for (size_type i = 0; i < N; i++) + { + m_data[i] = data[i]; + } + + // On construction each of the characters in the string is + // obfuscated with an XOR cipher based on key + cipher(m_data, N, KEY); + } + + constexpr const char* data() const + { + return &m_data[0]; + } + + constexpr size_type size() const + { + return N; + } + + constexpr key_type key() const + { + return KEY; + } + + private: + + char m_data[N]{}; + }; + + // Handles decryption and re-encryption of an encrypted string at runtime + template + class obfuscated_data + { + public: + obfuscated_data(const obfuscator& obfuscator) + { + // Copy obfuscated data + for (size_type i = 0; i < N; i++) + { + m_data[i] = obfuscator.data()[i]; + } + } + + ~obfuscated_data() + { + // Zero m_data to remove it from memory + for (size_type i = 0; i < N; i++) + { + m_data[i] = 0; + } + } + + // Returns a pointer to the plain text string, decrypting it if + // necessary + operator char*() + { + decrypt(); + return m_data; + } + + // Manually decrypt the string + void decrypt() + { + if (m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = false; + } + } + + // Manually re-encrypt the string + void encrypt() + { + if (!m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = true; + } + } + + // Returns true if this string is currently encrypted, false otherwise. + bool is_encrypted() const + { + return m_encrypted; + } + + private: + + // Local storage for the string. Call is_encrypted() to check whether or + // not the string is currently obfuscated. + char m_data[N]; + + // Whether data is currently encrypted + bool m_encrypted{ true }; + }; + + // This function exists purely to extract the number of elements 'N' in the + // array 'data' + template + constexpr auto make_obfuscator(const char(&data)[N]) + { + return obfuscator(data); + } +} + +// Obfuscates the string 'data' at compile-time and returns a reference to a +// ay::obfuscated_data object with global lifetime that has functions for +// decrypting the string and is also implicitly convertable to a char* +#define AY_OBFUSCATE(data) AY_OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY) + +// Obfuscates the string 'data' with 'key' at compile-time and returns a +// reference to a ay::obfuscated_data object with global lifetime that has +// functions for decrypting the string and is also implicitly convertable to a +// char* +#define AY_OBFUSCATE_KEY(data, key) \ + []() -> ay::obfuscated_data& { \ + static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \ + static_assert((key) >= (1ull << 56), " must span all 8 bytes"); \ + constexpr auto n = sizeof(data)/sizeof(data[0]); \ + constexpr auto obfuscator = ay::make_obfuscator(data); \ + static auto obfuscated_data = ay::obfuscated_data(obfuscator); \ + return obfuscated_data; \ + }() + +#endif diff --git a/libs/libencrypt/CMakeLists.txt b/libs/libencrypt/CMakeLists.txt new file mode 100644 index 00000000000..23a589415ac --- /dev/null +++ b/libs/libencrypt/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.16) +project(libencrypt) + +find_package(PkgConfig REQUIRED) +pkg_search_module(OPENSSL REQUIRED openssl) + +file(READ "config.txt" PASSPHRASE_CONTENTS) +string(REPLACE "\n" "\\n" PASSPHRASE_CONTENTS "${PASSPHRASE_CONTENTS}") +set(PASS_PHRASE "${PASSPHRASE_CONTENTS}") +add_compile_definitions(PASS_PHRASE="${PASS_PHRASE}") + +if(OPENSSL_FOUND) + include_directories(${OPENSSL_INCLUDE_DIRS}) + message(STATUS "Using OpenSSL ${OPENSSL_VERSION}") +else() + message(FATAL_ERROR "OpenSSL not found") +endif() + +# Source files for the library +file(GLOB_RECURSE LIB_SOURCES src/*.cpp ) +file(GLOB_RECURSE LIB_HEADERS src/*.hpp src/*.h) +set(LIB_INCLUDE_DIRS "") +foreach(_headerFile ${LIB_HEADERS}) + get_filename_component(_dir ${_headerFile} PATH) + list(APPEND LIB_INCLUDE_DIRS ${_dir}) +endforeach() +list(REMOVE_DUPLICATES LIB_INCLUDE_DIRS) + + + +# Add executable +add_executable(encrypt main.cpp ${LIB_HEADERS} ${LIB_SOURCES} ) + +target_include_directories(encrypt PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src +) +target_link_libraries(encrypt PRIVATE ${OPENSSL_LIBRARIES} libpugixml) + +#install(TARGETS libencrypt DESTINATION bin) +#install(FILES ${LIB_HEADERS} DESTINATION include/libencrypt) diff --git a/libs/libencrypt/README.md b/libs/libencrypt/README.md new file mode 100644 index 00000000000..e61004cffc1 --- /dev/null +++ b/libs/libencrypt/README.md @@ -0,0 +1,71 @@ + # libencrypt (XML Encryption) + +libencrypt is a C++ library that provides XML encryption functionality using the RSA encryption algorithm. It allows you to encrypt XML data and files using a public key, perform encryption-related operations, and save the encrypted data in an XML format. + +## Prerequisites + +Before using the libencrypt library, make sure you have the following prerequisites installed: + +- C++ compiler with C++17 support +- OpenSSL library (version 1.1 or higher) + +## Installation + +To use the libencrypt library in your project, follow these steps: + +1. Clone the libencrypt repository or download the source code. + +2. Configure the library by replacing the contents of the `public_key.pem` file with your own RSA public key. Also, update the `config.txt` file with your desired passphrase. + +3. Build the library using CMake: + + ```bash + cmake . + make + +## Usage + +To use the libencrypt library in your code, follow these steps: + +1. Include the `encryption.h` header in your source file. + +2. Use the provided functions to perform XML encryption operations: + + - `loadPublicKey`: Loads a public key from a file. + - `encryptSessionKey`: Encrypts a session key using the provided public key. + - `encrypt`: Encrypts XML data using the provided public key. + - `base64_encode`: Base64 encodes a string. + - `encryptFile`: Encrypts an XML file using the provided public key. + +3. Customize the encryption process as needed based on your XML structure and encryption requirements. + +## Example + +Here's an example of how to use the libencrypt library to encrypt an XML file: + +```cpp +#include "encryption.h" + +int main() { + std::string filePath = "data.xml"; + + // Encrypt the XML file using the provided public key + bool success = Encryption::encryptFile(filePath); + + if (success) { + std::cout << "XML file encryption successful!" << std::endl; + } else { + std::cerr << "XML file encryption failed." << std::endl; + } + + return 0; + } + + +##License + +This project is licensed under the MIT License + +##Contributing + +Contributions are welcome! If you find any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request. We appreciate your contributions to make this project better. \ No newline at end of file diff --git a/libs/libencrypt/config.txt b/libs/libencrypt/config.txt new file mode 100644 index 00000000000..85df50785d6 --- /dev/null +++ b/libs/libencrypt/config.txt @@ -0,0 +1 @@ +abcd \ No newline at end of file diff --git a/libs/libencrypt/main.cpp b/libs/libencrypt/main.cpp new file mode 100644 index 00000000000..9e798b00613 --- /dev/null +++ b/libs/libencrypt/main.cpp @@ -0,0 +1,22 @@ +#include +#include "encryption.h" + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return -1; + } + + + std::string publicKeyFile = argv[1]; + std::string filePath = argv[2]; + + if (Encryption::encryptFile(publicKeyFile,filePath)) { + std::cout << "Encryption completed successfully." << std::endl; + return 0; + } else { + std::cerr << "Encryption failed." << std::endl; + return -1; + } +} + diff --git a/libs/libencrypt/public_key.pem b/libs/libencrypt/public_key.pem new file mode 100644 index 00000000000..a166d5329f9 --- /dev/null +++ b/libs/libencrypt/public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZGiAzN24Hao44eCxVgv +EBxsR8OAI7hNp7uqzPF85QNafQKCJqeGKtY7NM/w1vkaxm0uHTBCMsjdi48v0Eof +m18v3n9OBczDeTJVjwGhRz8ePurp68gLmXLBK2hFgEZUY4+08cb+K63i+BqRP7iJ +wP5iVofHY9HDpIHk+jwFSTWIArgR2+4RM95qF3i+/6zl+4uIN6oI9q9v/TqmiLTY +bzIynGeDpYhh0kM33nfxBz9xKz78kGS885HMT0ZoGrFiJmJ8AkqsNUSWF0DinA7b +MHCBrurqhmBsWFa3Rk0V74F7ofQFIK7jD/tNpXVhEqeqr0gHXzJ27xNILJ8b+rGF +LwIDAQAB +-----END PUBLIC KEY----- diff --git a/libs/libencrypt/src/encryption.cpp b/libs/libencrypt/src/encryption.cpp new file mode 100644 index 00000000000..07bdae5c220 --- /dev/null +++ b/libs/libencrypt/src/encryption.cpp @@ -0,0 +1,177 @@ +#include "encryption.h" +#ifdef PASSPHRASE +std::string passphrase_enc = PASSPHRASE; +#else +std::string passphrase_enc = ""; // Set your PEM pass phrase here +#endif +/** + * @brief Loads a public key from a file. + * + * @param filename The name of the file containing the public key. + * @return RSA* A pointer to the loaded public key. + * Returns nullptr if the key file cannot be opened or there is an error reading the key. + */ +RSA* Encryption::loadPublicKey(const std::string& filename) { + + RSA* key = nullptr; + FILE* keyFile = fopen(filename.c_str(), "r"); + if (!keyFile) { + std::cerr << "Unable to open key file: " << filename << std::endl; + return nullptr; + } + + if (!PEM_read_RSA_PUBKEY(keyFile, &key, NULL, (void*)passphrase_enc.c_str())) { + std::cerr << "Error reading public key from file: " << filename << std::endl; + fclose(keyFile); + return nullptr; + } + + fclose(keyFile); + return key; +} + +/** + * @brief Encrypts a session key using the provided public key. + * + * @param key The public key used for encryption. + * @return std::string The encrypted session key. + * Returns an empty string if there is an error generating or encrypting the session key. + */ +std::string Encryption::encryptSessionKey(RSA* key) { + unsigned char sessionKey[128]; + if (!RAND_bytes(sessionKey, sizeof(sessionKey))) { + std::cerr << "Error generating session key" << std::endl; + return ""; + } + + std::vector encryptedSessionKey(RSA_size(key)); + if (RSA_public_encrypt(sizeof(sessionKey), sessionKey, encryptedSessionKey.data(), key, RSA_PKCS1_OAEP_PADDING) == -1) { + std::cerr << "Session key encryption error: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return ""; + } + + return std::string(reinterpret_cast(encryptedSessionKey.data()), encryptedSessionKey.size()); +} + +/** + * @brief Encrypts the given plaintext using the provided public key. + * + * @param plaintext The plaintext to be encrypted. + * @param key The public key used for encryption. + * @return std::string The encrypted ciphertext. + * Returns an empty string if there is an error during encryption. + */ +std::string Encryption::encrypt(const std::string& plaintext, RSA* key) { + int rsaLen = RSA_size(key); + int blockLen = rsaLen - 42; // PKCS1_OAEP_PADDING reduces max data length + int len = plaintext.size(); + std::string ciphertext; + + for (int i = 0; i < len; i += blockLen) { + std::vector buffer(rsaLen); + std::string substr = plaintext.substr(i, blockLen); + + if (RSA_public_encrypt(substr.size(), reinterpret_cast(substr.data()), buffer.data(), key, RSA_PKCS1_OAEP_PADDING) == -1) { + std::cerr << "Encryption error: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return ""; + } + + ciphertext.append(reinterpret_cast(buffer.data()), rsaLen); + } + + return ciphertext; +} + +/** + * @brief Base64 encodes the given input string. + * + * @param input The input string to be encoded. + * @return std::string The base64-encoded string. + */ +std::string Encryption::base64_encode(const std::string& input) { + BIO* b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + BIO* bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); + + BIO_write(b64, input.data(), input.size()); + BIO_flush(b64); + + BUF_MEM* bptr; + BIO_get_mem_ptr(b64, &bptr); + + std::string output(bptr->data, bptr->length); + BIO_free_all(b64); + + return output; +} + +/** + * @brief Encrypts a file using the provided public key. + * + * @param filePath The path to the file to be encrypted. + * @param publicKeyFile The path to the file containing the public key. + * @return bool True if the file encryption is successful, false otherwise. + */ +bool Encryption::encryptFile(const std::string& publicKeyFile, std::string& filePath ) { + + // Load public key + RSA* publicKey = loadPublicKey(publicKeyFile); + if (!publicKey) { + return false; + } + + // Read file contents + std::ifstream file(filePath, std::ios::binary); + if (!file) { + std::cerr << "Unable to open file: " << filePath << std::endl; + RSA_free(publicKey); + return false; + } + + std::string plaintext((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + // Encrypt session key + std::string encryptedSessionKey = encryptSessionKey(publicKey); + if (encryptedSessionKey.empty()) { + RSA_free(publicKey); + return false; + } + + // Base64 encode session key + std::string base64EncryptedSessionKey = base64_encode(encryptedSessionKey); + + // Encrypt file contents + std::string encrypted = encrypt(plaintext, publicKey); + RSA_free(publicKey); + + // Base64 encode encrypted data + std::string base64Encrypted = base64_encode(encrypted); + + // Create an XML document for the encrypted data and session key + pugi::xml_document encryptedDoc; + auto root = encryptedDoc.append_child("EncryptedData"); + auto sessionKeyNode = root.append_child("SessionKey"); + sessionKeyNode.append_child(pugi::node_pcdata).set_value(base64EncryptedSessionKey.c_str()); + auto dataNode = root.append_child("Data"); + dataNode.append_child(pugi::node_pcdata).set_value(base64Encrypted.c_str()); + + + size_t lastDotPos = filePath.find_last_of('.'); + + if (lastDotPos != std::string::npos && filePath.substr(lastDotPos) == ".xml") { + // Remove the .xml extension + filePath.erase(lastDotPos); + // Append the .xmle extension + filePath.append(".xmle"); + } + // Save the encrypted data to a new file + std::string encryptedFilePath = filePath; + encryptedDoc.save_file(encryptedFilePath.c_str(), " "); + + std::cout << "File encrypted successfully. Encrypted file saved as: " << encryptedFilePath << std::endl; + + return true; +} diff --git a/libs/libencrypt/src/encryption.h b/libs/libencrypt/src/encryption.h new file mode 100644 index 00000000000..daedcdbcba2 --- /dev/null +++ b/libs/libencrypt/src/encryption.h @@ -0,0 +1,70 @@ +#ifndef ENCRYPTION_H +#define ENCRYPTION_H + +#include +#include +#include +#include +#include +#include +#include "pugixml.hpp" +#include +#include +#include +#include +#include +#include "obfuscate.h" +//#include "config.h" +/** + * @class Encryption + * @brief Provides encryption and decryption functionality using RSA encryption algorithm. + */ +class Encryption { +public: + /** + * @brief Loads a public key from a file. + * + * @param filename The name of the file containing the public key. + * @return RSA* A pointer to the loaded public key. + * Returns nullptr if the key file cannot be opened or there is an error reading the key. + */ + static RSA *loadPublicKey(const std::string &filename); + + /** + * @brief Encrypts a session key using the provided public key. + * + * @param key The public key used for encryption. + * @return std::string The encrypted session key. + * Returns an empty string if there is an error generating or encrypting the session key. + */ + static std::string encryptSessionKey(RSA *key); + + /** + * @brief Encrypts the given plaintext using the provided public key. + * + * @param plaintext The plaintext to be encrypted. + * @param key The public key used for encryption. + * @return std::string The encrypted ciphertext. + * Returns an empty string if there is an error during encryption. + */ + static std::string encrypt(const std::string &plaintext, RSA *key); + + /** + * @brief Base64 encodes the given input string. + * + * @param input The input string to be encoded. + * @return std::string The base64-encoded string. + */ + static std::string base64_encode(const std::string &input); + + /** + * @brief Encrypts a file using the provided public key. + * + * @param filePath The path to the file to be encrypted. + * @param publicKeyFile The path to the file containing the public key. + * @return bool True if the file encryption is successful, false otherwise. + */ + static bool encryptFile(const std::string& publicKeyFile, std::string &filePath); +}; + +#endif // ENCRYPTION_H diff --git a/libs/libencrypt/src/obfuscate.h b/libs/libencrypt/src/obfuscate.h new file mode 100644 index 00000000000..5450eb44f01 --- /dev/null +++ b/libs/libencrypt/src/obfuscate.h @@ -0,0 +1,185 @@ +#ifndef OBFUSCATE_H +#define OBFUSCATE_H + +#ifdef _MSC_VER + #define AY_CAT(X,Y) AY_CAT2(X,Y) + #define AY_CAT2(X,Y) X##Y + #define AY_LINE int(AY_CAT(__LINE__,U)) +#else + #define AY_LINE __LINE__ +#endif + +#ifndef AY_OBFUSCATE_DEFAULT_KEY + // The default 64 bit key to obfuscate strings with. + // This can be user specified by defining AY_OBFUSCATE_DEFAULT_KEY before + // including obfuscate.h + #define AY_OBFUSCATE_DEFAULT_KEY ay::generate_key(AY_LINE) +#endif + +namespace ay +{ + using size_type = unsigned long long; + using key_type = unsigned long long; + + // Generate a pseudo-random key that spans all 8 bytes + constexpr key_type generate_key(key_type seed) + { + // Use the MurmurHash3 64-bit finalizer to hash our seed + key_type key = seed; + key ^= (key >> 33); + key *= 0xff51afd7ed558ccd; + key ^= (key >> 33); + key *= 0xc4ceb9fe1a85ec53; + key ^= (key >> 33); + + // Make sure that a bit in each byte is set + key |= 0x0101010101010101ull; + + return key; + } + + // Obfuscates or deobfuscates data with key + constexpr void cipher(char* data, size_type size, key_type key) + { + // Obfuscate with a simple XOR cipher based on key + for (size_type i = 0; i < size; i++) + { + data[i] ^= char(key >> ((i % 8) * 8)); + } + } + + // Obfuscates a string at compile time + template + class obfuscator + { + public: + // Obfuscates the string 'data' on construction + constexpr obfuscator(const char* data) + { + // Copy data + for (size_type i = 0; i < N; i++) + { + m_data[i] = data[i]; + } + + // On construction each of the characters in the string is + // obfuscated with an XOR cipher based on key + cipher(m_data, N, KEY); + } + + constexpr const char* data() const + { + return &m_data[0]; + } + + constexpr size_type size() const + { + return N; + } + + constexpr key_type key() const + { + return KEY; + } + + private: + + char m_data[N]{}; + }; + + // Handles decryption and re-encryption of an encrypted string at runtime + template + class obfuscated_data + { + public: + obfuscated_data(const obfuscator& obfuscator) + { + // Copy obfuscated data + for (size_type i = 0; i < N; i++) + { + m_data[i] = obfuscator.data()[i]; + } + } + + ~obfuscated_data() + { + // Zero m_data to remove it from memory + for (size_type i = 0; i < N; i++) + { + m_data[i] = 0; + } + } + + // Returns a pointer to the plain text string, decrypting it if + // necessary + operator char*() + { + decrypt(); + return m_data; + } + + // Manually decrypt the string + void decrypt() + { + if (m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = false; + } + } + + // Manually re-encrypt the string + void encrypt() + { + if (!m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = true; + } + } + + // Returns true if this string is currently encrypted, false otherwise. + bool is_encrypted() const + { + return m_encrypted; + } + + private: + + // Local storage for the string. Call is_encrypted() to check whether or + // not the string is currently obfuscated. + char m_data[N]; + + // Whether data is currently encrypted + bool m_encrypted{ true }; + }; + + // This function exists purely to extract the number of elements 'N' in the + // array 'data' + template + constexpr auto make_obfuscator(const char(&data)[N]) + { + return obfuscator(data); + } +} + +// Obfuscates the string 'data' at compile-time and returns a reference to a +// ay::obfuscated_data object with global lifetime that has functions for +// decrypting the string and is also implicitly convertable to a char* +#define AY_OBFUSCATE(data) AY_OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY) + +// Obfuscates the string 'data' with 'key' at compile-time and returns a +// reference to a ay::obfuscated_data object with global lifetime that has +// functions for decrypting the string and is also implicitly convertable to a +// char* +#define AY_OBFUSCATE_KEY(data, key) \ + []() -> ay::obfuscated_data& { \ + static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \ + static_assert((key) >= (1ull << 56), " must span all 8 bytes"); \ + constexpr auto n = sizeof(data)/sizeof(data[0]); \ + constexpr auto obfuscator = ay::make_obfuscator(data); \ + static auto obfuscated_data = ay::obfuscated_data(obfuscator); \ + return obfuscated_data; \ + }() + +#endif diff --git a/libs/libpugiutil/CMakeLists.txt b/libs/libpugiutil/CMakeLists.txt index b7c2640c549..183afc81dad 100644 --- a/libs/libpugiutil/CMakeLists.txt +++ b/libs/libpugiutil/CMakeLists.txt @@ -17,7 +17,8 @@ target_include_directories(libpugiutil PUBLIC ${LIB_INCLUDE_DIRS}) set_target_properties(libpugiutil PROPERTIES PREFIX "") #Avoid extra 'lib' prefix target_link_libraries(libpugiutil - libpugixml) + libpugixml + libdecrypt) install(TARGETS libpugiutil DESTINATION bin) install(FILES ${LIB_HEADERS} DESTINATION include/libpugiutil) diff --git a/libs/libpugiutil/src/pugixml_loc.cpp b/libs/libpugiutil/src/pugixml_loc.cpp index b773b410b53..5a86d3f3409 100644 --- a/libs/libpugiutil/src/pugixml_loc.cpp +++ b/libs/libpugiutil/src/pugixml_loc.cpp @@ -46,4 +46,16 @@ void loc_data::build_loc_data() { fclose(f); } +void loc_data::build_loc_data_from_string(char* filename_val, size_t buffersize) { + std::ptrdiff_t offset = 0; + char buffer[1024]; + std::size_t size; + for (std::size_t i = 0; i < buffersize; ++i) { + if (filename_val[i] == '\0') + break; + else if (filename_val[i] == '\n') { + offsets_.push_back(i); + } + } +} } // namespace pugiutil diff --git a/libs/libpugiutil/src/pugixml_loc.hpp b/libs/libpugiutil/src/pugixml_loc.hpp index 0f597a593b1..b8e11fded7d 100644 --- a/libs/libpugiutil/src/pugixml_loc.hpp +++ b/libs/libpugiutil/src/pugixml_loc.hpp @@ -7,7 +7,8 @@ #include #include "pugixml.hpp" - +#include "decryption.h" +#include namespace pugiutil { //pugi offset to line/col data based on: https://stackoverflow.com/questions/21003471/convert-pugixmls-result-offset-to-column-line @@ -20,6 +21,10 @@ class loc_data { build_loc_data(); } + loc_data(char* filename_val, size_t buffersize) { + build_loc_data_from_string(filename_val, buffersize); + } + //The filename this location data is for const std::string& filename() const { return filename_; } const char* filename_c_str() const { return filename_.c_str(); } @@ -42,7 +47,7 @@ class loc_data { private: void build_loc_data(); - + void build_loc_data_from_string(char* filename_val, size_t buffersize); std::string filename_; std::vector offsets_; }; diff --git a/libs/libpugiutil/src/pugixml_util.cpp b/libs/libpugiutil/src/pugixml_util.cpp index d4d2a398246..8002ce620ff 100644 --- a/libs/libpugiutil/src/pugixml_util.cpp +++ b/libs/libpugiutil/src/pugixml_util.cpp @@ -8,21 +8,43 @@ namespace pugiutil { //Returns loc_data look-up for xml node line numbers loc_data load_xml(pugi::xml_document& doc, //Document object to be loaded with file contents const std::string filename) { //Filename to load from - auto location_data = loc_data(filename); - - auto load_result = doc.load_file(filename.c_str()); - if (!load_result) { - std::string msg = load_result.description(); - auto line = location_data.line(load_result.offset); - auto col = location_data.col(load_result.offset); - throw XmlError("Unable to load XML file '" + filename + "', " + msg - + " (line: " + std::to_string(line) + " col: " + std::to_string(col) + ")", - filename.c_str(), line); + //store the position of last '.' in the file name + int position = filename.find_last_of("."); + //store the characters after the '.' from the file_name string + std::string result = filename.substr(position); + if (result == ".xmle") { + Decryption E1(filename); + std::string fn_file = E1.getDecryptedContent(); + size_t buffersize = fn_file.size(); + char* final = new char[buffersize]; + strcpy(final, fn_file.c_str()); + auto location_data = loc_data(final, buffersize); + auto load_result = doc.load_buffer(final, buffersize); + if (!load_result) { + std::string msg = load_result.description(); + auto line = location_data.line(load_result.offset); + auto col = location_data.col(load_result.offset); + throw XmlError("Unable to load XML file '" + filename + "', " + msg + + " (line: " + std::to_string(line) + " col: " + std::to_string(col) + ")", + filename.c_str(), line); + } + delete (final); + return location_data; + } else { + //auto location_data = loc_data(end_result_fname); + auto location_data = loc_data(filename); + auto load_result = doc.load_file(filename.c_str()); + if (!load_result) { + std::string msg = load_result.description(); + auto line = location_data.line(load_result.offset); + auto col = location_data.col(load_result.offset); + throw XmlError("Unable to load XML file '" + filename + "', " + msg + + " (line: " + std::to_string(line) + " col: " + std::to_string(col) + ")", + filename.c_str(), line); + } + return location_data; } - - return location_data; } - //Gets the first child element of the given name and returns it. // // node - The parent xml node diff --git a/libs/librrgraph/src/base/get_parallel_segs.cpp b/libs/librrgraph/src/base/get_parallel_segs.cpp index d20b3066992..adc3bce4f64 100644 --- a/libs/librrgraph/src/base/get_parallel_segs.cpp +++ b/libs/librrgraph/src/base/get_parallel_segs.cpp @@ -4,14 +4,17 @@ *Segments that have BOTH_AXIS attribute value are always included in the returned vector.*/ std::vector get_parallel_segs(const std::vector& segment_inf, t_unified_to_parallel_seg_index& seg_index_map, - enum e_parallel_axis parallel_axis) { + enum e_parallel_axis parallel_axis, + bool keep_original_index) { std::vector result; for (size_t i = 0; i < segment_inf.size(); ++i) { if (segment_inf[i].parallel_axis == parallel_axis || segment_inf[i].parallel_axis == BOTH_AXIS) { result.push_back(segment_inf[i]); - result[result.size() - 1].seg_index = i; + if (!keep_original_index) { + result[result.size() - 1].seg_index = i; + } seg_index_map.insert(std::make_pair(i, std::make_pair(result.size() - 1, parallel_axis))); } } return result; -} \ No newline at end of file +} diff --git a/libs/librrgraph/src/base/get_parallel_segs.h b/libs/librrgraph/src/base/get_parallel_segs.h index c88b2c88701..70539863dda 100644 --- a/libs/librrgraph/src/base/get_parallel_segs.h +++ b/libs/librrgraph/src/base/get_parallel_segs.h @@ -6,6 +6,7 @@ std::vector get_parallel_segs(const std::vector& segment_inf, t_unified_to_parallel_seg_index& seg_index_map, - enum e_parallel_axis parallel_axis); + enum e_parallel_axis parallel_axis, + bool keep_original_index = false); -#endif \ No newline at end of file +#endif diff --git a/libs/librrgraph/src/base/rr_graph_builder.cpp b/libs/librrgraph/src/base/rr_graph_builder.cpp index ca73a5a3811..ec7d66cab18 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.cpp +++ b/libs/librrgraph/src/base/rr_graph_builder.cpp @@ -1,3 +1,4 @@ +#include "vtr_assert.h" #include "vtr_log.h" #include "rr_graph_builder.h" #include "vtr_time.h" @@ -7,7 +8,10 @@ //#include "globals.h" -RRGraphBuilder::RRGraphBuilder() {} +RRGraphBuilder::RRGraphBuilder() { + is_edge_dirty_ = true; + is_incoming_edge_dirty_ = true; +} t_rr_graph_storage& RRGraphBuilder::rr_nodes() { return node_storage_; @@ -25,6 +29,14 @@ MetadataStorage>& RRGraphBuilder::rr_edge_metadata() return rr_edge_metadata_; } +vtr::vector>& RRGraphBuilder::node_in_edge_storage() { + return node_in_edges_; +} + +vtr::vector>& RRGraphBuilder::node_ptc_storage() { + return node_ptc_nums_; +} + void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { t_rr_type node_type = node_storage_.node_type(node); short node_ptc_num = node_storage_.node_ptc_num(node); @@ -60,6 +72,32 @@ void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { } } +RRNodeId RRGraphBuilder::create_node(int layer, int x, int y, t_rr_type type, int ptc, e_side side) { + e_side node_side = SIDES[0]; + /* Only OPIN and IPIN nodes have sides, otherwise force to use a default side */ + if (OPIN == type || IPIN == type) { + node_side = side; + } + node_storage_.emplace_back(); + node_ptc_nums_.emplace_back(); + RRNodeId new_node = RRNodeId(node_storage_.size() - 1); + node_storage_.set_node_layer(new_node, layer); + node_storage_.set_node_type(new_node, type); + node_storage_.set_node_coordinates(new_node, x, y, x, y); + node_storage_.set_node_ptc_num(new_node, ptc); + if (OPIN == type || IPIN == type) { + node_storage_.add_node_side(new_node, node_side); + } + /* Special for CHANX, being consistent with the rule in find_node() */ + if (CHANX == type) { + node_lookup_.add_node(new_node, layer, y, x, type, ptc, node_side); + } else { + node_lookup_.add_node(new_node, layer, x, y, type, ptc, node_side); + } + + return new_node; +} + void RRGraphBuilder::init_edge_remap(bool val) { node_storage_.init_edge_remap(val); } @@ -71,10 +109,15 @@ void RRGraphBuilder::clear_temp_storage() { void RRGraphBuilder::clear() { node_lookup_.clear(); node_storage_.clear(); + node_in_edges_.clear(); + node_ptc_nums_.clear(); rr_node_metadata_.clear(); rr_edge_metadata_.clear(); rr_segments_.clear(); rr_switch_inf_.clear(); + edges_to_build_.clear(); + is_edge_dirty_ = true; + is_incoming_edge_dirty_ = true; } void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_nodes_algorithm, @@ -146,3 +189,96 @@ void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_ std::get<2>(edge)); }); } + +void RRGraphBuilder::create_edge(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped) { + edges_to_build_.emplace_back(src, dest, size_t(edge_switch), remapped); + is_edge_dirty_ = true; /* Adding a new edge revokes the flag */ + is_incoming_edge_dirty_ = true; +} + +void RRGraphBuilder::build_edges(const bool& uniquify) { + if (uniquify) { + std::sort(edges_to_build_.begin(), edges_to_build_.end()); + edges_to_build_.erase(std::unique(edges_to_build_.begin(), edges_to_build_.end()), edges_to_build_.end()); + } + alloc_and_load_edges(&edges_to_build_); + edges_to_build_.clear(); + is_edge_dirty_ = false; +} + +void RRGraphBuilder::build_in_edges() { + VTR_ASSERT(validate()); + node_in_edges_.clear(); + node_in_edges_.resize(node_storage_.size()); + + for (RRNodeId src_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + for (auto iedge : node_storage_.edges(src_node)) { + VTR_ASSERT(src_node == node_storage_.edge_source_node(node_storage_.edge_id(src_node, iedge))); + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, iedge)); + node_in_edges_[des_node].push_back(node_storage_.edge_id(src_node, iedge)); + } + } + is_incoming_edge_dirty_ = false; +} + +std::vector RRGraphBuilder::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (is_incoming_edge_dirty_) { + VTR_LOG_ERROR("Incoming edges are not built yet in routing resource graph. Please call build_in_edges()."); + return std::vector(); + } + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +void RRGraphBuilder::add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id) { + VTR_ASSERT(size_t(node) < node_storage_.size()); + VTR_ASSERT(size_t(node) < node_ptc_nums_.size()); + VTR_ASSERT_MSG(node_storage_.node_type(node) == CHANX || node_storage_.node_type(node) == CHANY, "Track number valid only for CHANX/CHANY RR nodes"); + + size_t node_length = std::abs(node_storage_.node_xhigh(node) - node_storage_.node_xlow(node)) + + std::abs(node_storage_.node_yhigh(node) - node_storage_.node_ylow(node)); + if (node_length + 1 != node_ptc_nums_[node].size()) { + node_ptc_nums_[node].resize(node_length + 1); + } + + size_t offset = node_offset.x() - node_storage_.node_xlow(node) + node_offset.y() - node_storage_.node_ylow(node); + VTR_ASSERT(offset < node_ptc_nums_[node].size()); + + node_ptc_nums_[node][offset] = track_id; +} + +void RRGraphBuilder::add_track_node_to_lookup(RRNodeId node) { + VTR_ASSERT_MSG(node_storage_.node_type(node) == CHANX || node_storage_.node_type(node) == CHANY, "Update track node look-up is only valid to CHANX/CHANY nodes"); + + /* Compute the track id based on the (x, y) coordinate */ + size_t x_start = std::min(node_storage_.node_xlow(node), node_storage_.node_xhigh(node)); + size_t y_start = std::min(node_storage_.node_ylow(node), node_storage_.node_yhigh(node)); + std::vector node_x(std::abs(node_storage_.node_xlow(node) - node_storage_.node_xhigh(node)) + 1); + std::vector node_y(std::abs(node_storage_.node_ylow(node) - node_storage_.node_yhigh(node)) + 1); + + std::iota(node_x.begin(), node_x.end(), x_start); + std::iota(node_y.begin(), node_y.end(), y_start); + + VTR_ASSERT(size_t(std::max(node_storage_.node_xlow(node), node_storage_.node_xhigh(node))) == node_x.back()); + VTR_ASSERT(size_t(std::max(node_storage_.node_ylow(node), node_storage_.node_yhigh(node))) == node_y.back()); + + for (const size_t& x : node_x) { + for (const size_t& y : node_y) { + size_t ptc = node_storage_.node_ptc_num(node); + /* Routing channel nodes may have different ptc num + * Find the track ids using the x/y offset + * FIXME: Special case on assigning CHANX (x,y) should be changed to a natural way! + */ + if (CHANX == node_storage_.node_type(node)) { + ptc = node_ptc_nums_[node][x - node_storage_.node_xlow(node)]; + node_lookup_.add_node(node, node_storage_.node_layer(node), y, x, CHANX, ptc); + } else if (CHANY == node_storage_.node_type(node)) { + ptc = node_ptc_nums_[node][y - node_storage_.node_ylow(node)]; + node_lookup_.add_node(node, node_storage_.node_layer(node), x, y, CHANY, ptc); + } + } + } +} diff --git a/libs/librrgraph/src/base/rr_graph_builder.h b/libs/librrgraph/src/base/rr_graph_builder.h index cf82d64a24a..e36b70449e0 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.h +++ b/libs/librrgraph/src/base/rr_graph_builder.h @@ -16,6 +16,7 @@ #include "rr_graph_storage.h" #include "rr_spatial_lookup.h" #include "metadata_storage.h" +#include "rr_edge.h" class RRGraphBuilder { /* -- Constructors -- */ @@ -44,6 +45,10 @@ class RRGraphBuilder { MetadataStorage& rr_node_metadata(); /** @brief Return a writable object for the meta data on the edge */ MetadataStorage>& rr_edge_metadata(); + /** @brief Return a writable object fo the incoming edge storage */ + vtr::vector>& node_in_edge_storage(); + /** @brief Return a writable object of the node ptc storage (for tileable routing resource graph) */ + vtr::vector>& node_ptc_storage(); /** @brief Return the size for rr_node_metadata */ inline size_t rr_node_metadata_size() const { @@ -114,6 +119,10 @@ class RRGraphBuilder { inline void set_node_type(RRNodeId id, t_rr_type type) { node_storage_.set_node_type(id, type); } + /** @brief Create a new rr_node in the node storage and register it to the node look-up. + * Return a valid node id if succeed. Otherwise, return an invalid id. + */ + RRNodeId create_node(int layer, int x, int y, t_rr_type type, int ptc, e_side side = NUM_SIDES); /** * @brief Add an existing rr_node in the node storage to the node look-up * @@ -188,11 +197,6 @@ class RRGraphBuilder { node_storage_.set_node_ptc_num(id, new_ptc_num); } - /** @brief set the layer number at which RRNodeId is located at */ - inline void set_node_layer(RRNodeId id, int layer){ - node_storage_.set_node_layer(id, layer); - } - /** @brief set_node_pin_num() is designed for logic blocks, which are IPIN and OPIN nodes */ inline void set_node_pin_num(RRNodeId id, int new_pin_num) { node_storage_.set_node_pin_num(id, new_pin_num); @@ -203,7 +207,16 @@ class RRGraphBuilder { node_storage_.set_node_track_num(id, new_track_num); } - /** @brief set_ node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ + /** @brief Add a track id for a given node base on the offset in coordinate, applicable only to CHANX and CHANY nodes. + * This API is used by tileable routing resource graph generator, which requires each routing track has a different + * track id depending their location in FPGA fabric. + */ + void add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id); + + /** @brief Update the node_lookup for a track node. This is applicable to tileable routing graph */ + void add_track_node_to_lookup(RRNodeId node); + + /** @brief set_node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ inline void set_node_class_num(RRNodeId id, int new_class_num) { node_storage_.set_node_class_num(id, new_class_num); } @@ -213,6 +226,24 @@ class RRGraphBuilder { node_storage_.set_node_direction(id, new_direction); } + /** @brief Add a new edge to the cache of edges to be built + * .. note:: This will not add an edge to storage! You need to call build_edges() after all the edges are cached! */ + void create_edge(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped); + + /** @brief Allocate and build actual edges in storage. + * Once called, the cached edges will be uniquified and added to routing resource nodes, + * while the cache will be empty once build-up is accomplished */ + void build_edges(const bool& uniquify = true); + + /** @brief Allocate and build incoming edges for each node. + * By default, no incoming edges are kept in storage, to be memory efficient */ + void build_in_edges(); + + /** @brief Return incoming edges for a given routing resource node + * Require build_in_edges() to be called first + */ + std::vector node_in_edges(RRNodeId node) const; + /** @brief Reserve the lists of edges to be memory efficient. * This function is mainly used to reserve memory space inside RRGraph, * when adding a large number of edges in order to avoid memory fragements */ @@ -277,6 +308,9 @@ class RRGraphBuilder { return node_storage_.count_rr_switches(arch_switch_inf, arch_switch_fanins); } + /* Unlock storage; required to modify an routing resource graph after edge is read */ + inline void unlock_storage() { node_storage_.edges_read_ = false; node_storage_.partitioned_ = false; node_storage_.clear_node_first_edge();} + /** @brief Reserve the lists of nodes, edges, switches etc. to be memory efficient. * This function is mainly used to reserve memory space inside RRGraph, * when adding a large number of nodes/edge/switches/segments, @@ -300,13 +334,13 @@ class RRGraphBuilder { rr_switch_inf_.resize(size); } - /** @brief Validate that edge data is partitioned correctly + /** @brief Validate that edge data is partitioned correctly. Also there are no edges left to be built! * @note This function is used to validate the correctness of the routing resource graph in terms * of graph attributes. Strongly recommend to call it when you finish the building a routing resource * graph. If you need more advance checks, which are related to architecture features, you should * consider to use the check_rr_graph() function or build your own check_rr_graph() function. */ inline bool validate() const { - return node_storage_.validate(rr_switch_inf_); + return node_storage_.validate(rr_switch_inf_) && edges_to_build_.empty(); } /** @brief Sorts edge data such that configurable edges appears before @@ -353,6 +387,14 @@ class RRGraphBuilder { /* Fast look-up for rr nodes */ RRSpatialLookup node_lookup_; + /* A cache for edge-related information, required to build edges for routing resource nodes. + * It is used when building a routing resource graph by considering memory efficiency. + * It will be clear up after calling build_edges(). + * + * .. warning:: This is a temporary data which is used to collect edges to be built for nodes + */ + t_rr_edge_info_set edges_to_build_; + /** Wire segment types in RR graph * - Each rr_segment contains the detailed information of a routing track, which is denoted by a node in CHANX or CHANY type. * - We use a fly-weight data structure here, in the same philosophy as the rr_indexed_data. See detailed explanation in the t_segment_inf data structure @@ -366,6 +408,29 @@ class RRGraphBuilder { /* Detailed information about the switches, which are used in the RRGraph */ vtr::vector rr_switch_inf_; + /** A list of incoming edges for each routing resource node. This can be built optionally, as required by applications. + * By default, it is empty! Call build_in_edges() to construct it!!! */ + vtr::vector> node_in_edges_; + + /* Extra ptc number for each routing resource node. This is required by tileable routing resource graph. + * In a tileable routing architecture, routing tracks, e.g., CHANX and CHANY, follows a staggered organization. + * Hence, a routing track may appear in different routing channels, representing different ptc/track id. + * Here is an illustrative example of a X-direction routing track (CHANX) in INC direction, which is organized in staggered way. + * + * Coord(x,y) (1,0) (2,0) (3,0) (4,0) Another track (node) + * ptc=0 ------> ------> + * \ / + * ptc=1 ------> / + * \ / + * ptc=2 ------> / + * \ / + * ptc=3 -------> + * ^ ^ + * | | + * starting point ending point + */ + vtr::vector> node_ptc_nums_; + /** .. warning:: The Metadata should stay as an independent data structure than rest of the internal data, * e.g., node_lookup! */ /* Metadata is an extra data on rr-nodes and edges, respectively, that is not used by vpr @@ -390,6 +455,11 @@ class RRGraphBuilder { * value: map of */ MetadataStorage> rr_edge_metadata_; + + /** @brief a flag to mark the status of edge storage + * dirty means that the edge storage is not complete, should call related APIs to build */ + bool is_edge_dirty_; + bool is_incoming_edge_dirty_; }; #endif diff --git a/libs/librrgraph/src/base/rr_graph_fwd.h b/libs/librrgraph/src/base/rr_graph_fwd.h index 41d0b8f3d58..2f685e4c15a 100644 --- a/libs/librrgraph/src/base/rr_graph_fwd.h +++ b/libs/librrgraph/src/base/rr_graph_fwd.h @@ -1,8 +1,7 @@ -#ifndef RR_GRAPH_OBJ_FWD_H -#define RR_GRAPH_OBJ_FWD_H +#ifndef RR_GRAPH_FWD_H +#define RR_GRAPH_FWD_H #include - #include "vtr_strong_id.h" /*************************************************************** @@ -14,8 +13,6 @@ //Forward declaration class t_rr_graph_storage; -class RRGraph; - struct rr_node_id_tag; struct rr_edge_id_tag; struct rr_indexed_data_id_tag; diff --git a/libs/librrgraph/src/base/rr_graph_storage.cpp b/libs/librrgraph/src/base/rr_graph_storage.cpp index ef85d779a4c..2930c29d5a6 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.cpp +++ b/libs/librrgraph/src/base/rr_graph_storage.cpp @@ -380,7 +380,10 @@ void t_rr_graph_storage::assign_first_edges() { bool t_rr_graph_storage::verify_first_edges() const { size_t num_edges = edge_src_node_.size(); - VTR_ASSERT(node_first_edge_[RRNodeId(node_storage_.size())] == RREdgeId(num_edges)); + if (node_first_edge_[RRNodeId(node_storage_.size())] != RREdgeId(num_edges)) { + VTR_LOG("node first edge is '%lu' while expected edge id is '%lu'\n", size_t(node_first_edge_[RRNodeId(node_storage_.size())]), num_edges); + VTR_ASSERT(node_first_edge_[RRNodeId(node_storage_.size())] == RREdgeId(num_edges)); + } // Each edge should belong with the edge range defined by // [node_first_edge_[src_node], node_first_edge_[src_node+1]). @@ -558,6 +561,11 @@ t_edge_size t_rr_graph_storage::num_non_configurable_edges(RRNodeId node, const return num_edges(node) - num_configurable_edges(node, rr_switches); } +bool t_rr_graph_storage::edge_is_configurable(RREdgeId edge, const vtr::vector& rr_switches) const { + auto iswitch = edge_switch(edge); + return rr_switches[RRSwitchId(iswitch)].configurable(); +} + bool t_rr_graph_storage::edge_is_configurable(RRNodeId id, t_edge_size iedge, const vtr::vector& rr_switches) const { auto iswitch = edge_switch(id, iedge); return rr_switches[RRSwitchId(iswitch)].configurable(); @@ -627,21 +635,21 @@ void t_rr_graph_storage::set_node_ptc_num(RRNodeId id, int new_ptc_num) { } void t_rr_graph_storage::set_node_pin_num(RRNodeId id, int new_pin_num) { if (node_type(id) != IPIN && node_type(id) != OPIN) { - VTR_LOG_ERROR("Attempted to set RR node 'pin_num' for non-IPIN/OPIN type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'pin_num' for non-IPIN/OPIN type '%s'\n", node_type_string(id)); } node_ptc_[id].ptc_.pin_num = new_pin_num; } void t_rr_graph_storage::set_node_track_num(RRNodeId id, int new_track_num) { if (node_type(id) != CHANX && node_type(id) != CHANY) { - VTR_LOG_ERROR("Attempted to set RR node 'track_num' for non-CHANX/CHANY type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'track_num' for non-CHANX/CHANY type '%s'\n", node_type_string(id)); } node_ptc_[id].ptc_.track_num = new_track_num; } void t_rr_graph_storage::set_node_class_num(RRNodeId id, int new_class_num) { if (node_type(id) != SOURCE && node_type(id) != SINK) { - VTR_LOG_ERROR("Attempted to set RR node 'class_num' for non-SOURCE/SINK type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'class_num' for non-SOURCE/SINK type '%s'\n", node_type_string(id)); } node_ptc_[id].ptc_.class_num = new_class_num; } @@ -656,7 +664,7 @@ static int get_node_pin_num( RRNodeId id) { auto node_type = node_storage[id].type_; if (node_type != IPIN && node_type != OPIN) { - VTR_LOG_ERROR("Attempted to access RR node 'pin_num' for non-IPIN/OPIN type '%s'", rr_node_typename[node_type]); + VTR_LOG_ERROR("Attempted to access RR node 'pin_num' for non-IPIN/OPIN type '%s'\n", rr_node_typename[node_type]); } return node_ptc[id].ptc_.pin_num; } @@ -667,7 +675,7 @@ static int get_node_track_num( RRNodeId id) { auto node_type = node_storage[id].type_; if (node_type != CHANX && node_type != CHANY) { - VTR_LOG_ERROR("Attempted to access RR node 'track_num' for non-CHANX/CHANY type '%s'", rr_node_typename[node_type]); + VTR_LOG_ERROR("Attempted to access RR node 'track_num' for non-CHANX/CHANY type '%s'\n", rr_node_typename[node_type]); } return node_ptc[id].ptc_.track_num; } @@ -678,7 +686,7 @@ static int get_node_class_num( RRNodeId id) { auto node_type = node_storage[id].type_; if (node_type != SOURCE && node_type != SINK) { - VTR_LOG_ERROR("Attempted to access RR node 'class_num' for non-SOURCE/SINK type '%s'", rr_node_typename[node_type]); + VTR_LOG_ERROR("Attempted to access RR node 'class_num' for non-SOURCE/SINK type '%s'\n", rr_node_typename[node_type]); } return node_ptc[id].ptc_.class_num; } @@ -728,7 +736,7 @@ void t_rr_graph_storage::set_node_coordinates(RRNodeId id, short x1, short y1, s void t_rr_graph_storage::set_node_cost_index(RRNodeId id, RRIndexedDataId new_cost_index) { auto& node = node_storage_[id]; if ((size_t)new_cost_index >= std::numeric_limits::max()) { - VTR_LOG_ERROR("Attempted to set cost_index_ %zu above cost_index storage max value.", + VTR_LOG_ERROR("Attempted to set cost_index_ %zu above cost_index storage max value.\n", new_cost_index); } node.cost_index_ = (size_t)new_cost_index; @@ -745,19 +753,19 @@ void t_rr_graph_storage::set_node_capacity(RRNodeId id, short new_capacity) { void t_rr_graph_storage::set_node_direction(RRNodeId id, Direction new_direction) { if (node_type(id) != CHANX && node_type(id) != CHANY) { - VTR_LOG_ERROR("Attempted to set RR node 'direction' for non-channel type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'direction' for non-channel type '%s'\n", node_type_string(id)); } node_storage_[id].dir_side_.direction = new_direction; } void t_rr_graph_storage::add_node_side(RRNodeId id, e_side new_side) { if (node_type(id) != IPIN && node_type(id) != OPIN) { - VTR_LOG_ERROR("Attempted to set RR node 'side' for non-channel type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'side' for non-channel type '%s'\n", node_type_string(id)); } std::bitset side_bits = node_storage_[id].dir_side_.sides; side_bits[size_t(new_side)] = true; if (side_bits.to_ulong() > CHAR_MAX) { - VTR_LOG_ERROR("Invalid side '%s' to be added to rr node %u", SIDE_STRING[new_side], size_t(id)); + VTR_LOG_ERROR("Invalid side '%s' to be added to rr node %u\n", SIDE_STRING[new_side], size_t(id)); } node_storage_[id].dir_side_.sides = static_cast(side_bits.to_ulong()); } diff --git a/libs/librrgraph/src/base/rr_graph_storage.h b/libs/librrgraph/src/base/rr_graph_storage.h index 09d80264645..60cafacf1cd 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.h +++ b/libs/librrgraph/src/base/rr_graph_storage.h @@ -63,13 +63,13 @@ struct alignas(16) t_rr_node_data { t_rr_type type_ = NUM_RR_TYPES; /* The character is a hex number which is a 4-bit truth table for node sides - * The 4-bits in serial represent 4 sides on which a node could appear - * It follows a fixed sequence, which is (LEFT, BOTTOM, RIGHT, TOP) whose indices are (3, 2, 1, 0) + * The 4-bits in serial represent 4 sides on which a node could appear + * It follows a fixed sequence, which is (LEFT, BOTTOM, RIGHT, TOP) whose indices are (3, 2, 1, 0) * - When a node appears on a given side, it is set to "1" * - When a node does not appear on a given side, it is set to "0" * For example, - * - '1' means '0001' in hex number, which means the node appears on TOP - * - 'A' means '1100' in hex number, which means the node appears on LEFT and BOTTOM sides, + * - '1' means '0001' in hex number, which means the node appears on TOP + * - 'A' means '1100' in hex number, which means the node appears on LEFT and BOTTOM sides, */ union { Direction direction; //Valid only for CHANX/CHANY @@ -261,6 +261,7 @@ class t_rr_graph_storage { * - num_non_configurable_edges(RRNodeId) * - edge_id(RRNodeId, t_edge_size) * - edge_sink_node(RRNodeId, t_edge_size) + * - edge_source_node(RRNodeId, t_edge_size) * - edge_switch(RRNodeId, t_edge_size) * * Only call these methods after partition_edges has been invoked. */ @@ -278,6 +279,7 @@ class t_rr_graph_storage { t_edge_size num_edges(const RRNodeId& id) const { return size_t(last_edge(id)) - size_t(first_edge(id)); } + bool edge_is_configurable(RREdgeId edge, const vtr::vector& rr_switches) const; bool edge_is_configurable(RRNodeId id, t_edge_size iedge, const vtr::vector& rr_switches) const; t_edge_size num_configurable_edges(RRNodeId node, const vtr::vector& rr_switches) const; t_edge_size num_non_configurable_edges(RRNodeId node, const vtr::vector& rr_switches) const; @@ -325,6 +327,11 @@ class t_rr_graph_storage { return edge_dest_node_[edge]; } + // Get the source node for the specified edge. + RRNodeId edge_source_node(const RREdgeId& edge) const { + return edge_src_node_[edge]; + } + // Call the `apply` function with the edge id, source, and sink nodes of every edge. void for_each_edge(std::function apply) const { for (size_t i = 0; i < edge_dest_node_.size(); i++) { @@ -341,6 +348,11 @@ class t_rr_graph_storage { return edge_sink_node(edge_id(id, iedge)); } + // Get the source node for the iedge'th edge from specified RRNodeId. + RRNodeId edge_source_node(const RRNodeId& id, t_edge_size iedge) const { + return edge_source_node(edge_id(id, iedge)); + } + // Get the switch used for the specified edge. short edge_switch(const RREdgeId& edge) const { return edge_switch_[edge]; @@ -654,6 +666,7 @@ class t_rr_graph_storage { return side_tt[size_t(side)]; } + public: inline void clear_node_first_edge() { node_first_edge_.clear(); } diff --git a/libs/librrgraph/src/base/rr_graph_utils.h b/libs/librrgraph/src/base/rr_graph_utils.h index 0725bcd0cf9..1aa58acc0b4 100644 --- a/libs/librrgraph/src/base/rr_graph_utils.h +++ b/libs/librrgraph/src/base/rr_graph_utils.h @@ -5,8 +5,8 @@ * the function declaration */ #include -#include "rr_graph_fwd.h" #include "rr_node_types.h" +#include "rr_graph_obj.h" #include "rr_graph_view.h" struct t_pin_chain_node { @@ -48,4 +48,4 @@ vtr::vector> get_fan_in_list(const RRGraphView& int seg_index_of_cblock(const RRGraphView& rr_graph, t_rr_type from_rr_type, int to_node); int seg_index_of_sblock(const RRGraphView& rr_graph, int from_node, int to_node); -#endif \ No newline at end of file +#endif diff --git a/libs/librrgraph/src/base/rr_graph_view.cpp b/libs/librrgraph/src/base/rr_graph_view.cpp index 0ddc5445d42..225af722e5b 100644 --- a/libs/librrgraph/src/base/rr_graph_view.cpp +++ b/libs/librrgraph/src/base/rr_graph_view.cpp @@ -1,3 +1,4 @@ +#include #include "rr_graph_view.h" #include "rr_node.h" #include "physical_types.h" @@ -9,7 +10,9 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, const vtr::vector& rr_indexed_data, const std::vector& rr_rc_data, const vtr::vector& rr_segments, - const vtr::vector& rr_switch_inf) + const vtr::vector& rr_switch_inf, + const vtr::vector>& node_in_edges, + const vtr::vector>& node_ptc_nums) : node_storage_(node_storage) , node_lookup_(node_lookup) , rr_node_metadata_(rr_node_metadata) @@ -17,5 +20,117 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, , rr_indexed_data_(rr_indexed_data) , rr_rc_data_(rr_rc_data) , rr_segments_(rr_segments) - , rr_switch_inf_(rr_switch_inf) { + , rr_switch_inf_(rr_switch_inf) + , node_in_edges_(node_in_edges) + , node_ptc_nums_(node_ptc_nums) { } + +std::vector RRGraphView::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +std::vector RRGraphView::node_configurable_in_edges(RRNodeId node) const { + /* Note: This is not efficient in runtime, should sort edges by configurability when allocating the array! */ + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (rr_switch_inf_[edge_switch(edge)].configurable()) { + ret_edges.push_back(edge); + } + } + return ret_edges; +} + +std::vector RRGraphView::node_non_configurable_in_edges(RRNodeId node) const { + /* Note: This is not efficient in runtime, should sort edges by configurability when allocating the array! */ + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (!rr_switch_inf_[edge_switch(edge)].configurable()) { + ret_edges.push_back(edge); + } + } + return ret_edges; +} + +std::vector RRGraphView::find_edges(const RRNodeId& src_node, const RRNodeId& des_node) const { + std::vector edge_list; + for (auto iedge : node_out_edges(src_node)) { + if (edge_sink_node(RREdgeId(iedge)) == des_node) { + edge_list.push_back(RREdgeId(iedge)); + } + } + return edge_list; +} + +RRSegmentId RRGraphView::node_segment(RRNodeId node) const { + RRIndexedDataId cost_index = node_cost_index(node); + return RRSegmentId(rr_indexed_data_[cost_index].seg_index); +} + +size_t RRGraphView::in_edges_count() const { + size_t edge_count = 0; + for (auto edge_list : node_in_edges_) { + edge_count += edge_list.size(); + } + return edge_count; +} + +bool RRGraphView::validate_in_edges() const { + size_t num_err = 0; + /* For each edge, validate that + * - The source node is in the fan-in edge list of the destination node + * - The sink node is in the fan-out edge list of the source node + */ + for (RRNodeId curr_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + /* curr_node ---> des_node + * <-?- check if the incoming edge is correct or not + */ + for (auto iedge : node_storage_.edges(curr_node)) { + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(curr_node, iedge)); + std::vector des_fanin_nodes; + for (auto next_edge : node_in_edges(des_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_source_node(next_edge); + des_fanin_nodes.push_back(prev_edge_des_node); + } + if (des_fanin_nodes.end() == std::find(des_fanin_nodes.begin(), des_fanin_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-in edges of Node '%s', while does drive it in its fan-out list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(des_node).c_str()); + num_err++; + } + } + /* src_node -?-> curr_node + * <--- check if the fan-out edge is correct or not + */ + for (auto iedge : node_in_edges(curr_node)) { + RRNodeId src_node = node_storage_.edge_source_node(iedge); + std::vector src_fanout_nodes; + for (auto prev_edge : node_storage_.edges(src_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, prev_edge)); + src_fanout_nodes.push_back(prev_edge_des_node); + } + if (src_fanout_nodes.end() == std::find(src_fanout_nodes.begin(), src_fanout_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-out edges of Node '%s', while does exist in its fan-in list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(src_node).c_str()); + num_err++; + } + } + } + if (num_err) { + VTR_LOG_ERROR("Found %ld errors when validating incoming edges for routing resource graph\n", num_err); + return false; + } + return true; +} + + diff --git a/libs/librrgraph/src/base/rr_graph_view.h b/libs/librrgraph/src/base/rr_graph_view.h index 3d808b23c71..92fac9f0a6c 100644 --- a/libs/librrgraph/src/base/rr_graph_view.h +++ b/libs/librrgraph/src/base/rr_graph_view.h @@ -25,9 +25,9 @@ * - This avoids massive changes for each client on using the APIs * as each frame view provides adhoc APIs for each client * - * TODO: more compact frame views will be created, e.g., + * TODO: more compact frame views will be created, e.g., * - a mini frame view: contains only node and edges, representing the connectivity of the graph - * - a geometry frame view: an extended mini frame view with node-level attributes, + * - a geometry frame view: an extended mini frame view with node-level attributes, * in particular geometry information (type, x, y etc). * */ @@ -42,32 +42,34 @@ class RRGraphView { const vtr::vector& rr_indexed_data, const std::vector& rr_rc_data, const vtr::vector& rr_segments, - const vtr::vector& rr_switch_inf); + const vtr::vector& rr_switch_inf, + const vtr::vector>& node_in_edges, + const vtr::vector>& node_ptc_nums); /* Disable copy constructors and copy assignment operator - * This is to avoid accidental copy because it could be an expensive operation considering that the + * This is to avoid accidental copy because it could be an expensive operation considering that the * memory footprint of the data structure could ~ Gb - * Using the following syntax, we prohibit accidental 'pass-by-value' which can be immediately caught + * Using the following syntax, we prohibit accidental 'pass-by-value' which can be immediately caught * by compiler */ RRGraphView(const RRGraphView&) = delete; void operator=(const RRGraphView&) = delete; /* -- Accessors -- */ - /* TODO: The accessors may be turned into private later if they are replacable by 'questionin' + /* TODO: The accessors may be turned into private later if they are replacable by 'questionin' * kind of accessors */ public: /* Aggregates: create range-based loops for nodes - * To iterate over the nodes in a RRGraph, - * using a range-based loop is suggested. - * ----------------------------------------------------------------- - * Example: iterate over all the nodes - * // Strongly suggest to use a read-only rr_graph object - * const RRGraph& rr_graph; - * for (const RRNodeId& node : rr_graph.nodes()) { - * // Do something with each node - * } + * To iterate over the nodes in a RRGraph, + * using a range-based loop is suggested. + * ----------------------------------------------------------------- + * Example: iterate over all the nodes + * // Strongly suggest to use a read-only rr_graph object + * const RRGraph& rr_graph; + * for (const RRNodeId& node : rr_graph.nodes()) { + * // Do something with each node + * } */ inline vtr::StrongIdRange nodes() const { return vtr::StrongIdRange(RRNodeId(0), RRNodeId(num_nodes())); @@ -193,7 +195,7 @@ class RRGraphView { && (node_xhigh(node) == -1) && (node_yhigh(node) == -1)); } - /** @brief Check if two routing resource nodes are adjacent (must be a CHANX and a CHANY). + /** @brief Check if two routing resource nodes are adjacent (must be a CHANX and a CHANY). * This function is used for error checking; it checks if two nodes are physically adjacent (could be connected) based on their geometry. * It does not check the routing edges to see if they are, in fact, possible to connect in the current routing graph. * This function is inlined for runtime optimization. */ @@ -208,7 +210,7 @@ class RRGraphView { return true; } - /** @brief Check if node is within bounding box. + /** @brief Check if node is within bounding box. * To return true, the RRNode must be completely contained within the specified bounding box, with the edges of the bounding box being inclusive. *This function is inlined for runtime optimization. */ inline bool node_is_inside_bounding_box(RRNodeId node, vtr::Rect bounding_box) const { @@ -305,17 +307,39 @@ class RRGraphView { inline short edge_switch(RRNodeId id, t_edge_size iedge) const { return node_storage_.edge_switch(id, iedge); } + inline RRSwitchId edge_switch(RREdgeId edge) const { + return RRSwitchId(node_storage_.edge_switch(edge)); + } + /** @brief Get the source node for the iedge'th edge from specified RRNodeId. + * This method should generally not be used, and instead first_edge and + * last_edge should be used.*/ + inline RRNodeId edge_src_node(RREdgeId edge) const { + return node_storage_.edge_source_node(edge); + } /** @brief Get the destination node for the iedge'th edge from specified RRNodeId. * This method should generally not be used, and instead first_edge and * last_edge should be used.*/ inline RRNodeId edge_sink_node(RRNodeId id, t_edge_size iedge) const { return node_storage_.edge_sink_node(id, iedge); } + inline RRNodeId edge_sink_node(RREdgeId edge) const { + return node_storage_.edge_sink_node(edge); + } + + /** @brief Get the source node for the iedge'th edge from specified RRNodeId. + * This method should generally not be used, and instead first_edge and + * last_edge should be used.*/ + inline RRNodeId edge_source_node(RRNodeId id, t_edge_size iedge) const { + return node_storage_.edge_source_node(id, iedge); + } /** @brief Detect if the edge is a configurable edge (controlled by a programmable routing multipler or a tri-state switch). */ inline bool edge_is_configurable(RRNodeId id, t_edge_size iedge) const { return node_storage_.edge_is_configurable(id, iedge, rr_switch_inf_); } + inline bool edge_is_configurable(RREdgeId edge) const { + return node_storage_.edge_is_configurable(edge, rr_switch_inf_); + } /** @brief Get the number of configurable edges. This function is inlined for runtime optimization. */ inline t_edge_size num_configurable_edges(RRNodeId node) const { @@ -327,22 +351,28 @@ class RRGraphView { return node_storage_.num_non_configurable_edges(node, rr_switch_inf_); } - /** @brief A configurable edge represents a programmable switch between routing resources, which could be + /** @brief A configurable edge represents a programmable switch between routing resources, which could be * a multiplexer * a tri-state buffer - * a pass gate + * a pass gate * This API gets ID range for configurable edges. This function is inlined for runtime optimization. */ inline edge_idx_range configurable_edges(RRNodeId node) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(node_storage_.num_edges(node) - num_non_configurable_edges(node))); } + inline edge_idx_range node_configurable_out_edges(RRNodeId node) const { + return configurable_edges(node); + } - /** @brief A non-configurable edge represents a hard-wired connection between routing resources, which could be + /** @brief A non-configurable edge represents a hard-wired connection between routing resources, which could be * a non-configurable buffer that can not be turned off * a short metal connection that can not be turned off * This API gets ID range for non-configurable edges. This function is inlined for runtime optimization. */ inline edge_idx_range non_configurable_edges(RRNodeId node) const { return vtr::make_range(edge_idx_iterator(node_storage_.num_edges(node) - num_non_configurable_edges(node)), edge_idx_iterator(num_edges(node))); } + inline edge_idx_range node_non_configurable_out_edges(RRNodeId node) const { + return non_configurable_edges(node); + } /** @brief Get outgoing edges for a node. * This API is designed to enable range-based loop to walk through the outgoing edges of a node @@ -356,32 +386,38 @@ class RRGraphView { inline edge_idx_range edges(const RRNodeId& id) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); } + inline edge_idx_range node_out_edges(const RRNodeId& id) const { + return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); + } + + /** @brief find the edges between two nodes */ + std::vector find_edges(const RRNodeId& src_node, const RRNodeId& des_node) const; /** @brief Get the number of edges. This function is inlined for runtime optimization. */ inline t_edge_size num_edges(RRNodeId node) const { return node_storage_.num_edges(node); } - /** @brief The ptc_num carries different meanings for different node types - * (true in VPR RRG that is currently supported, may not be true in customized RRG) - * CHANX or CHANY: the track id in routing channels - * OPIN or IPIN: the index of pins in the logic block data structure - * SOURCE and SINK: the class id of a pin (indicating logic equivalence of pins) in the logic block data structure - * @note - * This API is very powerful and developers should not use it unless it is necessary, - * e.g the node type is unknown. If the node type is known, the more specific routines, `node_pin_num()`, + /** @brief The ptc_num carries different meanings for different node types + * (true in VPR RRG that is currently supported, may not be true in customized RRG) + * CHANX or CHANY: the track id in routing channels + * OPIN or IPIN: the index of pins in the logic block data structure + * SOURCE and SINK: the class id of a pin (indicating logic equivalence of pins) in the logic block data structure + * @note + * This API is very powerful and developers should not use it unless it is necessary, + * e.g the node type is unknown. If the node type is known, the more specific routines, `node_pin_num()`, * `node_track_num()`and `node_class_num()`, for different types of nodes should be used.*/ inline int node_ptc_num(RRNodeId node) const { return node_storage_.node_ptc_num(node); } - /** @brief Get the pin num of a routing resource node. This is designed for logic blocks, + /** @brief Get the pin num of a routing resource node. This is designed for logic blocks, * which are IPIN and OPIN nodes. This function is inlined for runtime optimization. */ inline int node_pin_num(RRNodeId node) const { return node_storage_.node_pin_num(node); } - /** @brief Get the track num of a routing resource node. This is designed for routing tracks, + /** @brief Get the track num of a routing resource node. This is designed for routing tracks, * which are CHANX and CHANY nodes. This function is inlined for runtime optimization. */ inline int node_track_num(RRNodeId node) const { return node_storage_.node_track_num(node); @@ -397,6 +433,17 @@ class RRGraphView { RRIndexedDataId node_cost_index(RRNodeId node) const { return node_storage_.node_cost_index(node); } + + /** @brief Get the segment id which a routing resource node represents. Only applicable to nodes whose type is CHANX or CHANY */ + RRSegmentId node_segment(RRNodeId node) const; + + /** @brief Return incoming edges for a given routing resource node + * Require build_in_edges() to be called first + */ + std::vector node_in_edges(RRNodeId node) const; + std::vector node_configurable_in_edges(RRNodeId node) const; + std::vector node_non_configurable_in_edges(RRNodeId node) const; + /** @brief Return detailed routing segment information with a given id* @note The routing segments here may not be exactly same as those defined in architecture file. They have been * adapted to fit the context of routing resource graphs. */ @@ -460,7 +507,7 @@ class RRGraphView { } public: /* Validators */ - /** brief Validate that edge data is partitioned correctly + /** @brief Validate that edge data is partitioned correctly * @note This function is used to validate the correctness of the routing resource graph in terms * of graph attributes. Strongly recommend to call it when you finish the building a routing resource * graph. If you need more advance checks, which are related to architecture features, you should @@ -469,6 +516,21 @@ class RRGraphView { return node_storage_.validate_node(node_id, rr_switch_inf_); } + /** @brief Check if the node id is a valid one in storage */ + inline bool valid_node(RRNodeId node_id) const { + return size_t(node_id) < node_storage_.size(); + } + + /** @brief Check if the switch is a valid one in storage */ + inline bool valid_switch(RRSwitchId switch_id) const { + return (size_t(switch_id) < rr_switch_inf_.size()); + } + + /** @brief Validate if all the fan-in edge lists are valid */ + bool validate_in_edges() const; + /** @brief Count the number of incoming edges for all the nodes */ + size_t in_edges_count() const; + /* -- Internal data storage -- */ /* Note: only read-only object or data structures are allowed!!! */ private: @@ -503,13 +565,20 @@ class RRGraphView { /* rr_indexed_data_ and rr_segments_ are needed to lookup the segment information in node_coordinate_to_string() */ const vtr::vector& rr_indexed_data_; - /* RC data for nodes. This is a flyweight data */ + /* RC data for nodes. This is a flyweight data */ const std::vector& rr_rc_data_; /* Segment info for rr nodes */ const vtr::vector& rr_segments_; /* switch info for rr nodes */ const vtr::vector& rr_switch_inf_; + + /** A list of incoming edges for each routing resource node. This can be built optionally, as required by applications. + * By default, it is empty! Call build_in_edges() to construct it!!! */ + const vtr::vector>& node_in_edges_; + + /** A list of extra ptc numbers for each routing resource node. See details in RRGraphBuilder class */ + const vtr::vector>& node_ptc_nums_; }; #endif diff --git a/libs/librrgraph/src/base/rr_spatial_lookup.cpp b/libs/librrgraph/src/base/rr_spatial_lookup.cpp index 5b76b3418af..37b4c314470 100644 --- a/libs/librrgraph/src/base/rr_spatial_lookup.cpp +++ b/libs/librrgraph/src/base/rr_spatial_lookup.cpp @@ -1,4 +1,5 @@ #include "vtr_assert.h" +#include "vtr_log.h" #include "rr_spatial_lookup.h" RRSpatialLookup::RRSpatialLookup() { diff --git a/libs/libvtrcapnproto/CMakeLists.txt b/libs/libvtrcapnproto/CMakeLists.txt index cd97b0ec4f1..0f66a10856a 100644 --- a/libs/libvtrcapnproto/CMakeLists.txt +++ b/libs/libvtrcapnproto/CMakeLists.txt @@ -32,7 +32,7 @@ capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS ) if (VPR_ENABLE_INTERCHANGE) - set(IC_DIR ${CMAKE_SOURCE_DIR}/libs/EXTERNAL/libinterchange/interchange) + set(IC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../EXTERNAL/libinterchange/interchange) set(CAPNPC_SRC_PREFIX ${IC_DIR}) find_program(WGET wget REQUIRED) diff --git a/libs/libvtrutil/CMakeLists.txt b/libs/libvtrutil/CMakeLists.txt index 12217151ed2..73240389622 100644 --- a/libs/libvtrutil/CMakeLists.txt +++ b/libs/libvtrutil/CMakeLists.txt @@ -61,24 +61,28 @@ endif() # 2) The custom command depends on the touched version input file and generates the processed # version file, with updated values. The custom command uses the configure_version.cmake # script to generate the up-to-date vtr_version.cpp -add_custom_target(version ALL - COMMAND ${CMAKE_COMMAND} -E touch ${VTR_VERSION_FILE_IN}) - -add_custom_command(OUTPUT ${VTR_VERSION_FILE_OUT} - COMMAND ${CMAKE_COMMAND} - -D IN_FILE=${VTR_VERSION_FILE_IN} - -D OUT_FILE=${VTR_VERSION_FILE_OUT} - -D VTR_VERSION_MAJOR=${VTR_VERSION_MAJOR} - -D VTR_VERSION_MINOR=${VTR_VERSION_MINOR} - -D VTR_VERSION_PATCH=${VTR_VERSION_PATCH} - -D VTR_VERSION_PRERELEASE=${VTR_VERSION_PRERELEASE} - -D VTR_COMPILER_INFO=${VTR_COMPILER_INFO} - -D VTR_BUILD_INFO=${VTR_BUILD_INFO} - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/configure_version.cmake - MAIN_DEPENDENCY ${VTR_VERSION_FILE_IN} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - VERBATIM) - +if (VTR_ENABLE_VERSION) + add_custom_target(version ALL + COMMAND ${CMAKE_COMMAND} -E touch ${VTR_VERSION_FILE_IN}) + + add_custom_command(OUTPUT ${VTR_VERSION_FILE_OUT} + COMMAND ${CMAKE_COMMAND} + -D IN_FILE=${VTR_VERSION_FILE_IN} + -D OUT_FILE=${VTR_VERSION_FILE_OUT} + -D VTR_VERSION_MAJOR=${VTR_VERSION_MAJOR} + -D VTR_VERSION_MINOR=${VTR_VERSION_MINOR} + -D VTR_VERSION_PATCH=${VTR_VERSION_PATCH} + -D VTR_VERSION_PRERELEASE=${VTR_VERSION_PRERELEASE} + -D VTR_COMPILER_INFO=${VTR_COMPILER_INFO} + -D VTR_BUILD_INFO=${VTR_BUILD_INFO} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/configure_version.cmake + MAIN_DEPENDENCY ${VTR_VERSION_FILE_IN} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM) +else() +# Just copy the input file to output file with version number + configure_file(${VTR_VERSION_FILE_IN} ${VTR_VERSION_FILE_OUT}) +endif() # # Source files and library @@ -98,7 +102,9 @@ target_include_directories(libvtrutil PUBLIC ${LIB_INCLUDE_DIRS}) set_target_properties(libvtrutil PROPERTIES PREFIX "") #Avoid extra 'lib' prefix #Ensure version is always up to date by requiring version to be run first -add_dependencies(libvtrutil version) +if (VTR_ENABLE_VERSION) + add_dependencies(libvtrutil version) +endif() #Specify link-time dependancies target_link_libraries(libvtrutil diff --git a/libs/libvtrutil/src/vtr_geometry.h b/libs/libvtrutil/src/vtr_geometry.h index 3685c308653..1e11f87e1d7 100644 --- a/libs/libvtrutil/src/vtr_geometry.h +++ b/libs/libvtrutil/src/vtr_geometry.h @@ -61,6 +61,9 @@ bool operator!=(const RectUnion& lhs, const RectUnion& rhs); template class Point { public: //Constructors + // below is to create a no argument constructor for libopenfpga/libopenfpgautil/src/openfpga_pb_parser.cpp + // need to figure out a better solution to avoid change this in libs from vtr + Point(); Point(T x_val, T y_val) noexcept; public: //Accessors diff --git a/libs/libvtrutil/src/vtr_geometry.tpp b/libs/libvtrutil/src/vtr_geometry.tpp index 2010700fc50..c3e25dcf8a9 100644 --- a/libs/libvtrutil/src/vtr_geometry.tpp +++ b/libs/libvtrutil/src/vtr_geometry.tpp @@ -3,6 +3,11 @@ namespace vtr { * Point */ +template +Point::Point() { + //pass +} + template Point::Point(T x_val, T y_val) noexcept : x_(x_val) diff --git a/libs/libvtrutil/src/vtr_logic.h b/libs/libvtrutil/src/vtr_logic.h index 30d44c4a6b9..b43ee49291e 100644 --- a/libs/libvtrutil/src/vtr_logic.h +++ b/libs/libvtrutil/src/vtr_logic.h @@ -16,6 +16,8 @@ constexpr int FALSE = 0; constexpr int TRUE = 1; +#include + namespace vtr { /** @@ -25,9 +27,12 @@ enum class LogicValue { FALSE = 0, TRUE = 1, DONT_CARE = 2, - UNKOWN = 3 + UNKOWN = 3, + NUM_LOGIC_VALUE_TYPES }; +constexpr std::array LOGIC_VALUE_STRING = {{"false", "true", "don't care", "unknown"}}; + } // namespace vtr #endif diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 18306db87a7..ffecd4aa9f6 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -38,6 +38,8 @@ static void SetupAnnealSched(const t_options& Options, static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts); static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts); +static void SetupServerOpts(const t_options& Options, + t_server_opts* ServerOpts); static void SetupRoutingArch(const t_arch& Arch, t_det_routing_arch* RoutingArch); static void SetupTiming(const t_options& Options, const bool TimingEnabled, t_timing_inf* Timing); static void SetupSwitches(const t_arch& Arch, @@ -99,6 +101,7 @@ void SetupVPR(const t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraphs, std::vector& Segments, @@ -144,6 +147,7 @@ void SetupVPR(const t_options* Options, SetupAnalysisOpts(*Options, *AnalysisOpts); SetupPowerOpts(*Options, PowerOpts, Arch); SetupNocOpts(*Options, NocOpts); + SetupServerOpts(*Options, ServerOpts); if (readArchFile == true) { vtr::ScopedStartFinishTimer t("Loading Architecture Description"); @@ -395,9 +399,11 @@ static void SetupSwitches(const t_arch& Arch, static void SetupRoutingArch(const t_arch& Arch, t_det_routing_arch* RoutingArch) { RoutingArch->switch_block_type = Arch.SBType; + RoutingArch->switch_block_subtype = Arch.SBSubType; RoutingArch->R_minW_nmos = Arch.R_minW_nmos; RoutingArch->R_minW_pmos = Arch.R_minW_pmos; RoutingArch->Fs = Arch.Fs; + RoutingArch->subFs = Arch.subFs; RoutingArch->directionality = BI_DIRECTIONAL; if (Arch.Segments.size()) { RoutingArch->directionality = Arch.Segments[0].directionality; @@ -405,6 +411,11 @@ static void SetupRoutingArch(const t_arch& Arch, /* copy over the switch block information */ RoutingArch->switchblocks = Arch.switchblocks; + + /* Copy the tileable routing setting */ + RoutingArch->tileable = Arch.tileable; + RoutingArch->shrink_boundary = Arch.shrink_boundary; + RoutingArch->through_channel = Arch.through_channel; } static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) { @@ -432,6 +443,11 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->router_algorithm = Options.RouterAlgorithm; RouterOpts->fixed_channel_width = Options.RouteChanWidth; RouterOpts->min_channel_width_hint = Options.min_route_chan_width_hint; + + //TODO document these? + RouterOpts->trim_empty_channels = false; /* DEFAULT */ + RouterOpts->trim_obs_channels = false; /* DEFAULT */ + RouterOpts->read_rr_edge_metadata = Options.read_rr_edge_metadata; RouterOpts->reorder_rr_graph_nodes_algorithm = Options.reorder_rr_graph_nodes_algorithm; RouterOpts->reorder_rr_graph_nodes_threshold = Options.reorder_rr_graph_nodes_threshold; @@ -697,6 +713,8 @@ static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysi analysis_opts.timing_update_type = Options.timing_update_type; analysis_opts.write_timing_summary = Options.write_timing_summary; + + analysis_opts.skip_sync_clustering_and_routing_results = Options.skip_sync_clustering_and_routing_results; } static void SetupPowerOpts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch) { @@ -736,6 +754,11 @@ static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts) { return; } +static void SetupServerOpts(const t_options& Options, t_server_opts* ServerOpts) { + ServerOpts->is_server_mode_enabled = Options.is_server_mode_enabled; + ServerOpts->port_num = Options.server_port_num; +} + static void find_ipin_cblock_switch_index(const t_arch& Arch, int& wire_to_arch_ipin_switch, int& wire_to_arch_ipin_switch_between_dice) { for (auto cb_switch_name_index = 0; cb_switch_name_index < (int)Arch.ipin_cblock_switch_name.size(); cb_switch_name_index++) { int ipin_cblock_switch_index = UNDEFINED; diff --git a/vpr/src/base/SetupVPR.h b/vpr/src/base/SetupVPR.h index 7f7bb7105ea..97499eb8614 100644 --- a/vpr/src/base/SetupVPR.h +++ b/vpr/src/base/SetupVPR.h @@ -20,6 +20,7 @@ void SetupVPR(const t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraphs, std::vector& Segments, diff --git a/vpr/src/base/ShowSetup.cpp b/vpr/src/base/ShowSetup.cpp index 61f1bf772c3..189e90fb890 100644 --- a/vpr/src/base/ShowSetup.cpp +++ b/vpr/src/base/ShowSetup.cpp @@ -319,6 +319,8 @@ static void ShowRouterOpts(const t_router_opts& RouterOpts) { VPR_FATAL_ERROR(VPR_ERROR_UNKNOWN, "Unknown check_route value\n"); } + VTR_LOG("RouterOpts.trim_empty_chan: %s\n", (RouterOpts.trim_empty_channels ? "true" : "false")); + VTR_LOG("RouterOpts.trim_obs_chan: %s\n", (RouterOpts.trim_obs_channels ? "true" : "false")); VTR_LOG("RouterOpts.acc_fac: %f\n", RouterOpts.acc_fac); VTR_LOG("RouterOpts.bb_factor: %d\n", RouterOpts.bb_factor); VTR_LOG("RouterOpts.bend_cost: %f\n", RouterOpts.bend_cost); diff --git a/vpr/src/base/gen/vpr_constraints_uxsdcxx.h b/vpr/src/base/gen/vpr_constraints_uxsdcxx.h index 3248c338281..166a5629425 100644 --- a/vpr/src/base/gen/vpr_constraints_uxsdcxx.h +++ b/vpr/src/base/gen/vpr_constraints_uxsdcxx.h @@ -4,9 +4,9 @@ * https://github.com/duck2/uxsdcxx * Modify only if your build process doesn't involve regenerating this file. * - * Cmdline: uxsdcxx.py vpr_constraints.xsd - * Input file: /home/khalid88/Documents/uxsdcxx/vpr_constraints.xsd - * md5sum of input file: 6b6011a6e6446347b234da82e517422e + * Cmdline: uxsdcxx.py /home/tao/works/dev/clock/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * Input file: /home/tao/works/dev/clock/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * md5sum of input file: 2d6f442d8044f76e8f1b1d276b7358da */ #include @@ -25,8 +25,6 @@ #include "pugixml.hpp" #include "vpr_constraints_uxsdcxx_interface.h" -#include "region.h" - /* All uxsdcxx functions and structs live in this namespace. */ namespace uxsd { @@ -52,6 +50,10 @@ inline void load_partition(const pugi::xml_node& root, T& out, Context& context, template inline void load_partition_list(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); template +inline void load_set_global_signal(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); +template +inline void load_global_route_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); +template inline void load_vpr_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); /* Declarations for internal write functions for the complex types. */ @@ -60,6 +62,8 @@ inline void write_partition(T& in, std::ostream& os, const void* data, void* ite template inline void write_partition_list(T& in, std::ostream& os, const void* data, void* iter); template +inline void write_global_route_constraints(T& in, std::ostream& os, const void* data, void* iter); +template inline void write_vpr_constraints(T& in, std::ostream& os, const void* data, void* iter); /* Load function for the root element. */ @@ -142,8 +146,17 @@ constexpr const char* atok_lookup_t_partition[] = {"name"}; enum class gtok_t_partition_list { PARTITION }; constexpr const char* gtok_lookup_t_partition_list[] = {"partition"}; -enum class gtok_t_vpr_constraints { PARTITION_LIST }; -constexpr const char* gtok_lookup_t_vpr_constraints[] = {"partition_list"}; + +enum class atok_t_set_global_signal { NAME, + ROUTE_MODEL, + TYPE }; +constexpr const char* atok_lookup_t_set_global_signal[] = {"name", "route_model", "type"}; + +enum class gtok_t_global_route_constraints { SET_GLOBAL_SIGNAL }; +constexpr const char* gtok_lookup_t_global_route_constraints[] = {"set_global_signal"}; +enum class gtok_t_vpr_constraints { PARTITION_LIST, + GLOBAL_ROUTE_CONSTRAINTS }; +constexpr const char* gtok_lookup_t_vpr_constraints[] = {"partition_list", "global_route_constraints"}; enum class atok_t_vpr_constraints { TOOL_NAME }; constexpr const char* atok_lookup_t_vpr_constraints[] = {"tool_name"}; @@ -348,6 +361,84 @@ inline gtok_t_partition_list lex_node_t_partition_list(const char* in, const std noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); } +inline atok_t_set_global_signal lex_attr_t_set_global_signal(const char* in, const std::function* report_error) { + unsigned int len = strlen(in); + switch (len) { + case 4: + switch (*((triehash_uu32*)&in[0])) { + case onechar('n', 0, 32) | onechar('a', 8, 32) | onechar('m', 16, 32) | onechar('e', 24, 32): + return atok_t_set_global_signal::NAME; + break; + case onechar('t', 0, 32) | onechar('y', 8, 32) | onechar('p', 16, 32) | onechar('e', 24, 32): + return atok_t_set_global_signal::TYPE; + break; + default: + break; + } + break; + case 11: + switch (*((triehash_uu64*)&in[0])) { + case onechar('r', 0, 64) | onechar('o', 8, 64) | onechar('u', 16, 64) | onechar('t', 24, 64) | onechar('e', 32, 64) | onechar('_', 40, 64) | onechar('m', 48, 64) | onechar('o', 56, 64): + switch (in[8]) { + case onechar('d', 0, 8): + switch (in[9]) { + case onechar('e', 0, 8): + switch (in[10]) { + case onechar('l', 0, 8): + return atok_t_set_global_signal::ROUTE_MODEL; + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); +} + +inline gtok_t_global_route_constraints lex_node_t_global_route_constraints(const char* in, const std::function* report_error) { + unsigned int len = strlen(in); + switch (len) { + case 17: + switch (*((triehash_uu64*)&in[0])) { + case onechar('s', 0, 64) | onechar('e', 8, 64) | onechar('t', 16, 64) | onechar('_', 24, 64) | onechar('g', 32, 64) | onechar('l', 40, 64) | onechar('o', 48, 64) | onechar('b', 56, 64): + switch (*((triehash_uu64*)&in[8])) { + case onechar('a', 0, 64) | onechar('l', 8, 64) | onechar('_', 16, 64) | onechar('s', 24, 64) | onechar('i', 32, 64) | onechar('g', 40, 64) | onechar('n', 48, 64) | onechar('a', 56, 64): + switch (in[16]) { + case onechar('l', 0, 8): + return gtok_t_global_route_constraints::SET_GLOBAL_SIGNAL; + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); +} + inline gtok_t_vpr_constraints lex_node_t_vpr_constraints(const char* in, const std::function* report_error) { unsigned int len = strlen(in); switch (len) { @@ -378,6 +469,27 @@ inline gtok_t_vpr_constraints lex_node_t_vpr_constraints(const char* in, const s break; } break; + case 24: + switch (*((triehash_uu64*)&in[0])) { + case onechar('g', 0, 64) | onechar('l', 8, 64) | onechar('o', 16, 64) | onechar('b', 24, 64) | onechar('a', 32, 64) | onechar('l', 40, 64) | onechar('_', 48, 64) | onechar('r', 56, 64): + switch (*((triehash_uu64*)&in[8])) { + case onechar('o', 0, 64) | onechar('u', 8, 64) | onechar('t', 16, 64) | onechar('e', 24, 64) | onechar('_', 32, 64) | onechar('c', 40, 64) | onechar('o', 48, 64) | onechar('n', 56, 64): + switch (*((triehash_uu64*)&in[16])) { + case onechar('s', 0, 64) | onechar('t', 8, 64) | onechar('r', 16, 64) | onechar('a', 24, 64) | onechar('i', 32, 64) | onechar('n', 40, 64) | onechar('t', 48, 64) | onechar('s', 56, 64): + return gtok_t_vpr_constraints::GLOBAL_ROUTE_CONSTRAINTS; + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + break; default: break; } @@ -412,12 +524,6 @@ inline atok_t_vpr_constraints lex_attr_t_vpr_constraints(const char* in, const s */ [[noreturn]] inline void dfa_error(const char* wrong, const int* states, const char* const* lookup, int len, const std::function* report_error); -/** - * Internal error function for xs:all validators. - */ -template -[[noreturn]] inline void all_error(std::bitset gstate, const char* const* lookup, const std::function* report_error); - /** * Internal error function for attribute validators. */ @@ -427,8 +533,6 @@ template /* Internal loading functions, which validate and load a PugiXML DOM tree into memory. */ inline int load_int(const char* in, const std::function* report_error) { int out; - // global variable, must set to 0 before using it to avoid changed by other errors - errno = 0; out = std::strtol(in, NULL, 10); if (errno != 0) noreturn_report(report_error, ("Invalid value `" + std::string(in) + "` when loading into a int.").c_str()); @@ -674,6 +778,102 @@ inline void load_partition_list(const pugi::xml_node& root, T& out, Context& con if (state != 0) dfa_error("end of input", gstate_t_partition_list[state], gtok_lookup_t_partition_list, 1, report_error); } +template +inline void load_set_global_signal(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + for (pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()) { + atok_t_set_global_signal in = lex_attr_t_set_global_signal(attr.name(), report_error); + switch (in) { + case atok_t_set_global_signal::NAME: + out.set_set_global_signal_name(attr.value(), context); + break; + case atok_t_set_global_signal::ROUTE_MODEL: + out.set_set_global_signal_route_model(attr.value(), context); + break; + case atok_t_set_global_signal::TYPE: + out.set_set_global_signal_type(attr.value(), context); + break; + default: + break; /* Not possible. */ + } + } + + if (root.first_child().type() == pugi::node_element) + noreturn_report(report_error, "Unexpected child element in ."); +} + +constexpr int NUM_T_GLOBAL_ROUTE_CONSTRAINTS_STATES = 2; +constexpr const int NUM_T_GLOBAL_ROUTE_CONSTRAINTS_INPUTS = 1; +constexpr int gstate_t_global_route_constraints[NUM_T_GLOBAL_ROUTE_CONSTRAINTS_STATES][NUM_T_GLOBAL_ROUTE_CONSTRAINTS_INPUTS] = { + {0}, + {0}, +}; +template +inline void load_global_route_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + if (root.first_attribute()) + noreturn_report(report_error, "Unexpected attribute in ."); + + // Preallocate arrays by counting child nodes (if any) + size_t set_global_signal_count = 0; + { + int next, state = 1; + for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_global_route_constraints in = lex_node_t_global_route_constraints(node.name(), report_error); + next = gstate_t_global_route_constraints[state][(int)in]; + if (next == -1) + dfa_error(gtok_lookup_t_global_route_constraints[(int)in], gstate_t_global_route_constraints[state], gtok_lookup_t_global_route_constraints, 1, report_error); + state = next; + switch (in) { + case gtok_t_global_route_constraints::SET_GLOBAL_SIGNAL: + set_global_signal_count += 1; + break; + default: + break; /* Not possible. */ + } + } + + out.preallocate_global_route_constraints_set_global_signal(context, set_global_signal_count); + } + int next, state = 1; + for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_global_route_constraints in = lex_node_t_global_route_constraints(node.name(), report_error); + next = gstate_t_global_route_constraints[state][(int)in]; + if (next == -1) + dfa_error(gtok_lookup_t_global_route_constraints[(int)in], gstate_t_global_route_constraints[state], gtok_lookup_t_global_route_constraints, 1, report_error); + state = next; + switch (in) { + case gtok_t_global_route_constraints::SET_GLOBAL_SIGNAL: { + auto child_context = out.add_global_route_constraints_set_global_signal(context); + load_set_global_signal(node, out, child_context, report_error, offset_debug); + out.finish_global_route_constraints_set_global_signal(child_context); + } break; + default: + break; /* Not possible. */ + } + } + if (state != 0) dfa_error("end of input", gstate_t_global_route_constraints[state], gtok_lookup_t_global_route_constraints, 1, report_error); +} + +constexpr int NUM_T_VPR_CONSTRAINTS_STATES = 1; +constexpr const int NUM_T_VPR_CONSTRAINTS_INPUTS = 2; +constexpr int gstate_t_vpr_constraints[NUM_T_VPR_CONSTRAINTS_STATES][NUM_T_VPR_CONSTRAINTS_INPUTS] = { + {0, 0}, +}; template inline void load_vpr_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { (void)root; @@ -694,26 +894,57 @@ inline void load_vpr_constraints(const pugi::xml_node& root, T& out, Context& co } } - std::bitset<1> gstate = 0; + // Preallocate arrays by counting child nodes (if any) + size_t partition_list_count = 0; + size_t global_route_constraints_count = 0; + { + int next, state = 0; + for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_vpr_constraints in = lex_node_t_vpr_constraints(node.name(), report_error); + next = gstate_t_vpr_constraints[state][(int)in]; + if (next == -1) + dfa_error(gtok_lookup_t_vpr_constraints[(int)in], gstate_t_vpr_constraints[state], gtok_lookup_t_vpr_constraints, 2, report_error); + state = next; + switch (in) { + case gtok_t_vpr_constraints::PARTITION_LIST: + partition_list_count += 1; + break; + case gtok_t_vpr_constraints::GLOBAL_ROUTE_CONSTRAINTS: + global_route_constraints_count += 1; + break; + default: + break; /* Not possible. */ + } + } + + out.preallocate_vpr_constraints_partition_list(context, partition_list_count); + out.preallocate_vpr_constraints_global_route_constraints(context, global_route_constraints_count); + } + int next, state = 0; for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { *offset_debug = node.offset_debug(); gtok_t_vpr_constraints in = lex_node_t_vpr_constraints(node.name(), report_error); - if (gstate[(int)in] == 0) - gstate[(int)in] = 1; - else - noreturn_report(report_error, ("Duplicate element " + std::string(node.name()) + " in .").c_str()); + next = gstate_t_vpr_constraints[state][(int)in]; + if (next == -1) + dfa_error(gtok_lookup_t_vpr_constraints[(int)in], gstate_t_vpr_constraints[state], gtok_lookup_t_vpr_constraints, 2, report_error); + state = next; switch (in) { case gtok_t_vpr_constraints::PARTITION_LIST: { - auto child_context = out.init_vpr_constraints_partition_list(context); + auto child_context = out.add_vpr_constraints_partition_list(context); load_partition_list(node, out, child_context, report_error, offset_debug); out.finish_vpr_constraints_partition_list(child_context); } break; + case gtok_t_vpr_constraints::GLOBAL_ROUTE_CONSTRAINTS: { + auto child_context = out.add_vpr_constraints_global_route_constraints(context); + load_global_route_constraints(node, out, child_context, report_error, offset_debug); + out.finish_vpr_constraints_global_route_constraints(child_context); + } break; default: break; /* Not possible. */ } } - std::bitset<1> test_gstate = gstate | std::bitset<1>(0b0); - if (!test_gstate.all()) all_error(test_gstate, gtok_lookup_t_vpr_constraints, report_error); + if (state != 0) dfa_error("end of input", gstate_t_vpr_constraints[state], gtok_lookup_t_vpr_constraints, 2, report_error); } /* Internal writing functions, which uxsdcxx uses to write out a class. */ @@ -734,7 +965,7 @@ inline void write_partition(T& in, std::ostream& os, Context& context) { for (size_t i = 0, n = in.num_partition_add_region(context); i < n; i++) { auto child_context = in.get_partition_add_region(i, context); os << " +inline void write_global_route_constraints(T& in, std::ostream& os, Context& context) { + (void)in; + (void)os; + (void)context; + { + for (size_t i = 0, n = in.num_global_route_constraints_set_global_signal(context); i < n; i++) { + auto child_context = in.get_global_route_constraints_set_global_signal(i, context); + os << "\n"; + } + } +} + template inline void write_vpr_constraints(T& in, std::ostream& os, Context& context) { (void)in; (void)os; (void)context; { - auto child_context = in.get_vpr_constraints_partition_list(context); - os << "\n"; - write_partition_list(in, os, child_context); - os << "\n"; + for (size_t i = 0, n = in.num_vpr_constraints_partition_list(context); i < n; i++) { + auto child_context = in.get_vpr_constraints_partition_list(i, context); + os << "\n"; + write_partition_list(in, os, child_context); + os << "\n"; + } + } + { + for (size_t i = 0, n = in.num_vpr_constraints_global_route_constraints(context); i < n; i++) { + auto child_context = in.get_vpr_constraints_global_route_constraints(i, context); + os << "\n"; + write_global_route_constraints(in, os, child_context); + os << "\n"; + } } } @@ -788,20 +1046,6 @@ inline void dfa_error(const char* wrong, const int* states, const char* const* l noreturn_report(report_error, ("Expected " + expected_or + ", found " + std::string(wrong)).c_str()); } -template -inline void all_error(std::bitset gstate, const char* const* lookup, const std::function* report_error) { - std::vector missing; - for (unsigned int i = 0; i < N; i++) { - if (gstate[i] == 0) missing.push_back(lookup[i]); - } - - std::string missing_and = missing[0]; - for (unsigned int i = 1; i < missing.size(); i++) - missing_and += std::string(", ") + missing[i]; - - noreturn_report(report_error, ("Didn't find required elements " + missing_and + ".").c_str()); -} - template inline void attr_error(std::bitset astate, const char* const* lookup, const std::function* report_error) { std::vector missing; @@ -820,7 +1064,7 @@ inline void get_line_number(const char* filename, std::ptrdiff_t target_offset, std::unique_ptr f(fopen(filename, "rb"), fclose); if (!f) { - throw std::runtime_error(std::string("Failed to open file") + filename); + throw std::runtime_error(std::string("Failed to open file ") + filename); } int current_line = 1; diff --git a/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h b/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h index 8939778861e..a3ec52085fe 100644 --- a/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h +++ b/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h @@ -4,9 +4,9 @@ * https://github.com/duck2/uxsdcxx * Modify only if your build process doesn't involve regenerating this file. * - * Cmdline: uxsdcxx.py vpr_constraints.xsd - * Input file: /home/khalid88/Documents/uxsdcxx/vpr_constraints.xsd - * md5sum of input file: 6b6011a6e6446347b234da82e517422e + * Cmdline: uxsdcxx.py /home/tao/works/dev/clock/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * Input file: /home/tao/works/dev/clock/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * md5sum of input file: 2d6f442d8044f76e8f1b1d276b7358da */ #include @@ -24,11 +24,15 @@ struct DefaultVprConstraintsContextTypes { using AddRegionReadContext = void*; using PartitionReadContext = void*; using PartitionListReadContext = void*; + using SetGlobalSignalReadContext = void*; + using GlobalRouteConstraintsReadContext = void*; using VprConstraintsReadContext = void*; using AddAtomWriteContext = void*; using AddRegionWriteContext = void*; using PartitionWriteContext = void*; using PartitionListWriteContext = void*; + using SetGlobalSignalWriteContext = void*; + using GlobalRouteConstraintsWriteContext = void*; using VprConstraintsWriteContext = void*; }; @@ -93,7 +97,7 @@ class VprConstraintsBase { /** Generated for complex type "partition_list": * * - * + * * * */ @@ -103,19 +107,54 @@ class VprConstraintsBase { virtual inline size_t num_partition_list_partition(typename ContextTypes::PartitionListReadContext& ctx) = 0; virtual inline typename ContextTypes::PartitionReadContext get_partition_list_partition(int n, typename ContextTypes::PartitionListReadContext& ctx) = 0; + /** Generated for complex type "set_global_signal": + * + * + * + * + * + */ + virtual inline const char* get_set_global_signal_name(typename ContextTypes::SetGlobalSignalReadContext& ctx) = 0; + virtual inline void set_set_global_signal_name(const char* name, typename ContextTypes::SetGlobalSignalWriteContext& ctx) = 0; + virtual inline const char* get_set_global_signal_route_model(typename ContextTypes::SetGlobalSignalReadContext& ctx) = 0; + virtual inline void set_set_global_signal_route_model(const char* route_model, typename ContextTypes::SetGlobalSignalWriteContext& ctx) = 0; + virtual inline const char* get_set_global_signal_type(typename ContextTypes::SetGlobalSignalReadContext& ctx) = 0; + virtual inline void set_set_global_signal_type(const char* type, typename ContextTypes::SetGlobalSignalWriteContext& ctx) = 0; + + /** Generated for complex type "global_route_constraints": + * + * + * + * + * + */ + virtual inline void preallocate_global_route_constraints_set_global_signal(typename ContextTypes::GlobalRouteConstraintsWriteContext& ctx, size_t size) = 0; + virtual inline typename ContextTypes::SetGlobalSignalWriteContext add_global_route_constraints_set_global_signal(typename ContextTypes::GlobalRouteConstraintsWriteContext& ctx) = 0; + virtual inline void finish_global_route_constraints_set_global_signal(typename ContextTypes::SetGlobalSignalWriteContext& ctx) = 0; + virtual inline size_t num_global_route_constraints_set_global_signal(typename ContextTypes::GlobalRouteConstraintsReadContext& ctx) = 0; + virtual inline typename ContextTypes::SetGlobalSignalReadContext get_global_route_constraints_set_global_signal(int n, typename ContextTypes::GlobalRouteConstraintsReadContext& ctx) = 0; + /** Generated for complex type "vpr_constraints": * - * + * * - * + * + * * * */ virtual inline const char* get_vpr_constraints_tool_name(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; virtual inline void set_vpr_constraints_tool_name(const char* tool_name, typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; - virtual inline typename ContextTypes::PartitionListWriteContext init_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; + virtual inline void preallocate_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsWriteContext& ctx, size_t size) = 0; + virtual inline typename ContextTypes::PartitionListWriteContext add_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; virtual inline void finish_vpr_constraints_partition_list(typename ContextTypes::PartitionListWriteContext& ctx) = 0; - virtual inline typename ContextTypes::PartitionListReadContext get_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; + virtual inline size_t num_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; + virtual inline typename ContextTypes::PartitionListReadContext get_vpr_constraints_partition_list(int n, typename ContextTypes::VprConstraintsReadContext& ctx) = 0; + virtual inline void preallocate_vpr_constraints_global_route_constraints(typename ContextTypes::VprConstraintsWriteContext& ctx, size_t size) = 0; + virtual inline typename ContextTypes::GlobalRouteConstraintsWriteContext add_vpr_constraints_global_route_constraints(typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; + virtual inline void finish_vpr_constraints_global_route_constraints(typename ContextTypes::GlobalRouteConstraintsWriteContext& ctx) = 0; + virtual inline size_t num_vpr_constraints_global_route_constraints(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; + virtual inline typename ContextTypes::GlobalRouteConstraintsReadContext get_vpr_constraints_global_route_constraints(int n, typename ContextTypes::VprConstraintsReadContext& ctx) = 0; }; } /* namespace uxsd */ diff --git a/vpr/src/base/place_and_route.cpp b/vpr/src/base/place_and_route.cpp index c34cd9cfbae..b4cd431ce7d 100644 --- a/vpr/src/base/place_and_route.cpp +++ b/vpr/src/base/place_and_route.cpp @@ -96,6 +96,10 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, graph_directionality = GRAPH_BIDIR; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch->directionality == UNI_DIRECTIONAL && det_routing_arch->tileable) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } diff --git a/vpr/src/base/read_blif.cpp b/vpr/src/base/read_blif.cpp index 2425a18d239..326216896f4 100644 --- a/vpr/src/base/read_blif.cpp +++ b/vpr/src/base/read_blif.cpp @@ -719,18 +719,15 @@ bool is_binary_param(const std::string& param) { } bool is_real_param(const std::string& param) { - const std::string chars = "012345678."; - /* Must be non-empty */ if (param.empty()) { return false; } - /* The string mustn't contain any other chars that the expected ones */ - for (size_t i = 0; i < param.length(); ++i) { - if (chars.find(param[i]) == std::string::npos) { - return false; - } + /* The string must match the regular expression */ + static const std::regex real_number_expr("[+-]?([0-9]*\\.[0-9]+)|([0-9]+\\.[0-9]*)"); + if (!std::regex_match(param, real_number_expr)) { + return false; } /* This is a real number param */ diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index 35fd191c488..36308fe3720 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1280,6 +1280,16 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .action(argparse::Action::STORE_TRUE) .default_value("off"); + stage_grp.add_argument(args.is_server_mode_enabled, "--server") + .help("Run in server mode") + .action(argparse::Action::STORE_TRUE) + .default_value("off"); + + stage_grp.add_argument(args.server_port_num, "--port") + .help("Server port number") + .default_value("60555") + .show_in(argparse::ShowIn::HELP_ONLY); + stage_grp.epilog( "If none of the stage options are specified, all stages are run.\n" "Analysis is always run after routing, unless the implementation\n" @@ -2677,6 +2687,13 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .help("Writes implemented design final timing summary to the specified JSON, XML or TXT file.") .show_in(argparse::ShowIn::HELP_ONLY); + analysis_grp.add_argument(args.skip_sync_clustering_and_routing_results, "--skip_sync_clustering_and_routing_results") + .help( + "Select to skip the synchronization on clustering results based on routing optimization results." + "Note that when this sync-up is disabled, clustering results may be wrong (leading to incorrect bitstreams)!") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + auto& power_grp = parser.add_argument_group("power analysis options"); power_grp.add_argument(args.do_power, "--power") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 90f82ac07fb..fc4992c9261 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -74,6 +74,10 @@ struct t_options { argparse::ArgValue allow_dangling_combinational_nodes; argparse::ArgValue terminate_if_timing_fails; + /* Server options */ + argparse::ArgValue is_server_mode_enabled; + argparse::ArgValue server_port_num; + /* Atom netlist options */ argparse::ArgValue absorb_buffer_luts; argparse::ArgValue const_gen_inference; @@ -233,6 +237,7 @@ struct t_options { argparse::ArgValue post_synth_netlist_unconn_input_handling; argparse::ArgValue post_synth_netlist_unconn_output_handling; argparse::ArgValue write_timing_summary; + argparse::ArgValue skip_sync_clustering_and_routing_results; }; argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& args); diff --git a/vpr/src/base/route_constraint.cpp b/vpr/src/base/route_constraint.cpp new file mode 100644 index 00000000000..4ed76deb108 --- /dev/null +++ b/vpr/src/base/route_constraint.cpp @@ -0,0 +1,43 @@ +#include "route_constraint.h" + +RouteConstraint::RouteConstraint() { + net_name_ = std::string(""); + net_type_ = std::string(""); + route_method_ = std::string(""); + is_valid_ = false; +} + +void RouteConstraint::set_net_name(std::string name) { + net_name_ = name; + return; +} + +std::string RouteConstraint::net_name() const { + return net_name_; +} + +void RouteConstraint::set_net_type(std::string type) { + net_type_ = type; + return; +} + +std::string RouteConstraint::net_type() const { + return net_type_; +} + +void RouteConstraint::set_route_model(std::string route_method) { + route_method_ = route_method; + return; +} + +std::string RouteConstraint::route_model() const { + return route_method_; +} + +void RouteConstraint::set_is_valid(bool value) { + is_valid_ = value; +} + +bool RouteConstraint::is_valid() const { + return is_valid_; +} diff --git a/vpr/src/base/route_constraint.h b/vpr/src/base/route_constraint.h new file mode 100644 index 00000000000..58b2839d060 --- /dev/null +++ b/vpr/src/base/route_constraint.h @@ -0,0 +1,65 @@ +#ifndef ROUTE_CONSTRAINT_H +#define ROUTE_CONSTRAINT_H + +#include "vpr_types.h" + +/** + * @file + * @brief This file defines the RouteConstraint class. + */ + +class RouteConstraint { + public: + /** + * @brief Constructor for the RouteConstraint class, sets member variables to invalid values + */ + RouteConstraint(); + + /** + * @brief get net name + */ + std::string net_name() const; + + /** + * @brief set net name + */ + void set_net_name(std::string); + + /** + * @brief get net type + */ + std::string net_type() const; + + /** + * @brief set net type + */ + void set_net_type(std::string); + + /** + * @brief get route model + */ + std::string route_model() const; + + /** + * @brief set route model + */ + void set_route_model(std::string); + + /** + * @brief set is valid + */ + void set_is_valid(bool); + + /** + * @brief get is valid + */ + bool is_valid() const; + + private: + std::string net_name_; + std::string net_type_; + std::string route_method_; + bool is_valid_; +}; + +#endif /* ROUTE_CONSTRAINT_H */ diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index c87d2bec7fc..c9f3ef62966 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -290,6 +290,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a &vpr_setup->RouterOpts, &vpr_setup->AnalysisOpts, &vpr_setup->NocOpts, + &vpr_setup->ServerOpts, &vpr_setup->RoutingArch, &vpr_setup->PackerRRGraph, vpr_setup->Segments, @@ -346,10 +347,15 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a } } - //Initialize vpr floorplanning constraints + //Initialize vpr floorplanning and routing constraints auto& filename_opts = vpr_setup->FileNameOpts; if (!filename_opts.read_vpr_constraints_file.empty()) { - load_vpr_constraints_file(filename_opts.read_vpr_constraints_file.c_str()); + load_vpr_constraints_files(filename_opts.read_vpr_constraints_file.c_str()); + + // give a notificaiton on routing constraints overiding clock modeling + if (g_vpr_ctx.routing().constraints.get_route_constraint_num() && options->clock_modeling.provenance() == argparse::Provenance::SPECIFIED) { + VTR_LOG_WARN("Route constraint(s) detected and will override clock modeling setting.\n"); + } } fflush(stdout); @@ -399,6 +405,9 @@ bool vpr_flow(t_vpr_setup& vpr_setup, t_arch& arch) { vpr_analysis_flow(router_net_list, vpr_setup, arch, route_status, is_flat); } + // write out constratins + write_vpr_constraints(vpr_setup); + //close the graphics vpr_close_graphics(vpr_setup); @@ -785,6 +794,11 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, //Assume successful route_status = RouteStatus(true, -1); } else { //Do or load + + // apply route constraints + // use mutable routing context here to apply change on the constraints when binding the constraint to real net + apply_route_constraints(g_vpr_ctx.mutable_routing().constraints); + int chan_width = router_opts.fixed_channel_width; NetPinsMatrix net_delay; @@ -1007,6 +1021,9 @@ void vpr_create_rr_graph(t_vpr_setup& vpr_setup, const t_arch& arch, int chan_wi } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + if ((UNI_DIRECTIONAL == det_routing_arch->directionality) && (true == det_routing_arch->tileable)) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } } t_chan_width chan_width = init_chan(chan_width_fac, arch.Chans, graph_directionality); @@ -1035,7 +1052,7 @@ void vpr_init_graphics(const t_vpr_setup& vpr_setup, const t_arch& arch, bool is /* Startup X graphics */ init_graphics_state(vpr_setup.ShowGraphics, vpr_setup.GraphPause, vpr_setup.RouterOpts.route_type, vpr_setup.SaveGraphics, - vpr_setup.GraphicsCommands, is_flat); + vpr_setup.GraphicsCommands, is_flat, vpr_setup.ServerOpts.is_server_mode_enabled, vpr_setup.ServerOpts.port_num); if (vpr_setup.ShowGraphics || vpr_setup.SaveGraphics || !vpr_setup.GraphicsCommands.empty()) alloc_draw_structs(&arch); } @@ -1250,6 +1267,7 @@ void vpr_setup_vpr(t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraph, std::vector& Segments, @@ -1274,6 +1292,7 @@ void vpr_setup_vpr(t_options* Options, RouterOpts, AnalysisOpts, NocOpts, + ServerOpts, RoutingArch, PackerRRGraph, Segments, @@ -1329,7 +1348,8 @@ bool vpr_analysis_flow(const Netlist<>& net_list, VTR_LOG("*****************************************************************************************\n"); } - /* If routing is successful, apply post-routing annotations + /* If routing is successful and users do not force to skip the sync-up, + * - apply post-routing annotations * - apply logic block pin fix-up * * Note: @@ -1338,20 +1358,24 @@ bool vpr_analysis_flow(const Netlist<>& net_list, */ if (!is_flat) { if (route_status.success()) { - sync_netlists_to_routing(net_list, - g_vpr_ctx.device(), - g_vpr_ctx.mutable_atom(), - g_vpr_ctx.atom().lookup, - g_vpr_ctx.mutable_clustering(), - g_vpr_ctx.placement(), - g_vpr_ctx.routing(), - vpr_setup.PackerOpts.pack_verbosity > 2, - is_flat); - - std::string post_routing_packing_output_file_name = vpr_setup.PackerOpts.output_file + ".post_routing"; - write_packing_results_to_xml(vpr_setup.PackerOpts.global_clocks, - Arch.architecture_id, - post_routing_packing_output_file_name.c_str()); + if (!analysis_opts.skip_sync_clustering_and_routing_results) { + sync_netlists_to_routing(net_list, + g_vpr_ctx.device(), + g_vpr_ctx.mutable_atom(), + g_vpr_ctx.atom().lookup, + g_vpr_ctx.mutable_clustering(), + g_vpr_ctx.placement(), + g_vpr_ctx.routing(), + vpr_setup.PackerOpts.pack_verbosity > 2, + is_flat); + + std::string post_routing_packing_output_file_name = vpr_setup.PackerOpts.output_file + ".post_routing"; + write_packing_results_to_xml(vpr_setup.PackerOpts.global_clocks, + Arch.architecture_id, + post_routing_packing_output_file_name.c_str()); + } else { + VTR_LOG_WARN("Sychronization between packing and routing results is not applied due to users select to skip it\n"); + } } else { VTR_LOG_WARN("Sychronization between packing and routing results is not applied due to illegal circuit implementation\n"); } diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index 15509be1115..fbdd0f052f8 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -177,6 +177,7 @@ void vpr_setup_vpr(t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraph, std::vector& Segments, diff --git a/vpr/src/base/vpr_constraints.cpp b/vpr/src/base/vpr_constraints.cpp index 95c7e7b7358..64dcfd310c8 100644 --- a/vpr/src/base/vpr_constraints.cpp +++ b/vpr/src/base/vpr_constraints.cpp @@ -1,5 +1,7 @@ #include "vpr_constraints.h" #include "partition.h" +#include "route_constraint.h" +#include void VprConstraints::add_constrained_atom(const AtomBlockId blk_id, const PartitionId part_id) { auto got = constrained_atoms.find(blk_id); @@ -58,6 +60,68 @@ PartitionRegion VprConstraints::get_partition_pr(PartitionId part_id) { return pr; } +void VprConstraints::add_route_constraint(RouteConstraint rc) { + route_constraints_.insert({rc.net_name(), rc}); + return; +} + +const RouteConstraint VprConstraints::get_route_constraint_by_net_name(std::string net_name) { + RouteConstraint rc; + auto const& rc_itr = route_constraints_.find(net_name); + if (rc_itr == route_constraints_.end()) { + // try regexp + bool found_thru_regex = false; + for (auto constraint : route_constraints_) { + if (std::regex_match(net_name, std::regex(constraint.first))) { + rc = constraint.second; + + // mark as invalid so write constraint function will not write constraint + // of regexpr name + // instead a matched constraint is inserted in + constraint.second.set_is_valid(false); + rc.set_net_name(net_name); + rc.set_is_valid(true); + route_constraints_.insert({net_name, rc}); + + found_thru_regex = true; + break; + } + } + if (!found_thru_regex) { + rc.set_net_name("INVALID"); + rc.set_net_type("INVALID"); + rc.set_route_model("INVALID"); + rc.set_is_valid(false); + } + } else { + rc = rc_itr->second; + } + return rc; +} + +const RouteConstraint VprConstraints::get_route_constraint_by_idx(std::size_t idx) const { + RouteConstraint rc; + if ((route_constraints_.size() == 0) || (idx > route_constraints_.size() - 1)) { + rc.set_net_name("INVALID"); + rc.set_net_type("INVALID"); + rc.set_route_model("INVALID"); + rc.set_is_valid(false); + } else { + std::size_t i = 0; + for (auto const& rc_itr : route_constraints_) { + if (i == idx) { + rc = rc_itr.second; + break; + } + } + } + return rc; +} + +int VprConstraints::get_route_constraint_num(void) const { + return route_constraints_.size(); +} + void print_constraints(FILE* fp, VprConstraints constraints) { Partition temp_part; std::vector atoms; diff --git a/vpr/src/base/vpr_constraints.h b/vpr/src/base/vpr_constraints.h index fd3f64842a4..f9343267a9e 100644 --- a/vpr/src/base/vpr_constraints.h +++ b/vpr/src/base/vpr_constraints.h @@ -5,6 +5,7 @@ #include "vpr_utils.h" #include "partition.h" #include "partition_region.h" +#include "route_constraint.h" /** * @file @@ -87,6 +88,34 @@ class VprConstraints { */ PartitionRegion get_partition_pr(PartitionId part_id); + /** + * @brief add route constraint + * + * @param net_name the route constraint + */ + void add_route_constraint(RouteConstraint rc); + + /** + * @brief returns route constraint by index + * + * @param index the constraint index + */ + const RouteConstraint get_route_constraint_by_idx(std::size_t index) const; + + /** + * @brief returns route constraint of a specific net + * + * @param net_name the net name + */ + const RouteConstraint get_route_constraint_by_net_name(std::string net_name); + + /** + * @brief returns number of route constraints + * + * @param void + */ + int get_route_constraint_num(void) const; + private: /** * Store all constrained atoms @@ -97,6 +126,11 @@ class VprConstraints { * Store all partitions */ vtr::vector partitions; + + /** + * store all route constraints + */ + std::unordered_map route_constraints_; }; ///@brief used to print floorplanning constraints data from a VprConstraints object diff --git a/vpr/src/base/vpr_constraints.xsd b/vpr/src/base/vpr_constraints.xsd index 406e2abcda4..2ed93b77347 100644 --- a/vpr/src/base/vpr_constraints.xsd +++ b/vpr/src/base/vpr_constraints.xsd @@ -59,16 +59,36 @@ + + + + + + + + + + + + + + + - + - - + + diff --git a/vpr/src/base/vpr_constraints_reader.cpp b/vpr/src/base/vpr_constraints_reader.cpp index 8e69b7b42b4..1140e5269c6 100644 --- a/vpr/src/base/vpr_constraints_reader.cpp +++ b/vpr/src/base/vpr_constraints_reader.cpp @@ -10,31 +10,60 @@ #include #include "vpr_constraints_reader.h" +#include "vtr_token.h" -void load_vpr_constraints_file(const char* read_vpr_constraints_name) { - vtr::ScopedStartFinishTimer timer("Loading VPR constraints file"); +void load_vpr_constraints_files(const char* read_vpr_constraints_name) { + vtr::ScopedStartFinishTimer timer("Loading VPR constraints file(s)"); VprConstraintsSerializer reader; - if (vtr::check_file_name_extension(read_vpr_constraints_name, ".xml")) { - try { - std::ifstream file(read_vpr_constraints_name); - void* context; - uxsd::load_vpr_constraints_xml(reader, context, read_vpr_constraints_name, file); - } catch (pugiutil::XmlError& e) { - vpr_throw(VPR_ERROR_ROUTE, read_vpr_constraints_name, e.line(), "%s", e.what()); + // file name from arguments could be a serial of files, seperated by colon ":" + // at this point, caller has already checked that the required constraint file name is not emtpy + int num_tokens = 0, num_file_read = 0; + bool found_file = false; + t_token* tokens = GetTokensFromString(read_vpr_constraints_name, &num_tokens); + std::string in_tokens(""); + for (int i = 0; i < num_tokens; i++) { + if ((tokens[i].type == TOKEN_COLON)) { // end of one input file + found_file = true; + } else if (i == num_tokens - 1) { // end of inputs, append token anyway + in_tokens += std::string(tokens[i].data); + found_file = true; + } else { + in_tokens += std::string(tokens[i].data); + } + if (found_file) { + const char* file_name = in_tokens.c_str(); + if (vtr::check_file_name_extension(file_name, ".xml")) { + try { + std::ifstream file(file_name); + void* context; + uxsd::load_vpr_constraints_xml(reader, context, file_name, file); + } catch (pugiutil::XmlError& e) { + vpr_throw(VPR_ERROR_ROUTE, file_name, e.line(), "%s", e.what()); + } + } else { + VTR_LOG_WARN( + "VPR constraints file '%s' may be in incorrect format. " + "Expecting .xml format. Not reading file.\n", + file_name); + } + in_tokens.clear(); + num_file_read++; + found_file = false; } - } else { - VTR_LOG_WARN( - "VPR constraints file '%s' may be in incorrect format. " - "Expecting .xml format. Not reading file.\n", - read_vpr_constraints_name); } + VTR_LOG("Read in '%d' constraint file(s) successfully.\n", num_file_read); + freeTokens(tokens, num_tokens); //Update the floorplanning constraints in the floorplanning constraints context auto& floorplanning_ctx = g_vpr_ctx.mutable_floorplanning(); floorplanning_ctx.constraints = reader.constraints_; + // update vpr constraints for routing + auto& routing_ctx = g_vpr_ctx.mutable_routing(); + routing_ctx.constraints = reader.constraints_; + VprConstraints ctx_constraints = floorplanning_ctx.constraints; if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_VPR_CONSTRAINTS)) { diff --git a/vpr/src/base/vpr_constraints_reader.h b/vpr/src/base/vpr_constraints_reader.h index 95fcc988607..60aefc25b5b 100644 --- a/vpr/src/base/vpr_constraints_reader.h +++ b/vpr/src/base/vpr_constraints_reader.h @@ -5,6 +5,6 @@ #ifndef VPR_CONSTRAINTS_READER_H_ #define VPR_CONSTRAINTS_READER_H_ -void load_vpr_constraints_file(const char* read_vpr_constraints_name); +void load_vpr_constraints_files(const char* read_vpr_constraints_name); #endif /* VPR_CONSTRAINTS_READER_H_ */ diff --git a/vpr/src/base/vpr_constraints_serializer.h b/vpr/src/base/vpr_constraints_serializer.h index 5405eb0e21a..13ceca72896 100644 --- a/vpr/src/base/vpr_constraints_serializer.h +++ b/vpr/src/base/vpr_constraints_serializer.h @@ -2,6 +2,7 @@ #define VPR_CONSTRAINTS_SERIALIZER_H_ #include "region.h" +#include "route_constraint.h" #include "vpr_constraints.h" #include "partition.h" #include "partition_region.h" @@ -69,11 +70,15 @@ struct VprConstraintsContextTypes : public uxsd::DefaultVprConstraintsContextTyp using AddRegionReadContext = Region; using PartitionReadContext = partition_info; using PartitionListReadContext = void*; + using SetGlobalSignalReadContext = RouteConstraint; + using GlobalRouteConstraintsReadContext = void*; using VprConstraintsReadContext = void*; using AddAtomWriteContext = void*; using AddRegionWriteContext = void*; using PartitionWriteContext = void*; using PartitionListWriteContext = void*; + using SetGlobalSignalWriteContext = void*; + using GlobalRouteConstraintsWriteContext = void*; using VprConstraintsWriteContext = void*; }; @@ -305,11 +310,65 @@ class VprConstraintsSerializer final : public uxsd::VprConstraintsBase + * + * + * + * + */ + virtual inline const char* get_set_global_signal_name(RouteConstraint& rc) final { + temp_name_string_ = rc.net_name(); + return temp_name_string_.c_str(); + } + virtual inline void set_set_global_signal_name(const char* name, void*& /*ctx*/) final { + std::string net_name = std::string(name); + loaded_route_constraint.set_net_name(net_name); + return; + } + virtual inline const char* get_set_global_signal_route_model(RouteConstraint& rc) final { + temp_name_string_ = rc.route_model(); + return temp_name_string_.c_str(); + } + virtual inline void set_set_global_signal_route_model(const char* route_model, void*& /*ctx*/) final { + loaded_route_constraint.set_route_model(std::string(route_model)); + loaded_route_constraint.set_is_valid(true); + } + virtual inline const char* get_set_global_signal_type(RouteConstraint& rc) final { + temp_name_string_ = rc.net_type(); + return temp_name_string_.c_str(); + } + virtual inline void set_set_global_signal_type(const char* type, void*& /*ctx*/) final { + loaded_route_constraint.set_net_type(std::string(type)); + } + + /** Generated for complex type "global_route_constraints": + * + * + * + * + * + */ + virtual inline void preallocate_global_route_constraints_set_global_signal(void*& /*ctx*/, size_t /*size*/) final {} + virtual inline void* add_global_route_constraints_set_global_signal(void*& /*ctx*/) final { + return nullptr; + } + virtual inline void finish_global_route_constraints_set_global_signal(void*& /*ctx*/) final { + constraints_.add_route_constraint(loaded_route_constraint); + } + virtual inline size_t num_global_route_constraints_set_global_signal(void*& /*ctx*/) final { + return constraints_.get_route_constraint_num(); + } + virtual inline RouteConstraint get_global_route_constraints_set_global_signal(int n, void*& /*ctx*/) final { + return constraints_.get_route_constraint_by_idx((std::size_t)n); + } + /** Generated for complex type "vpr_constraints": * - * + * * - * + * + * * * */ @@ -317,30 +376,64 @@ class VprConstraintsSerializer final : public uxsd::VprConstraintsBase::max_digits10); + void* context; + uxsd::write_vpr_constraints_xml(writer, context, fp); + } else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Unknown extension on output %s", + file_name.c_str()); + } + } + return; +} + void write_vpr_floorplan_constraints(const char* file_name, int expand, bool subtile, int horizontal_partitions, int vertical_partitions) { VprConstraints constraints; diff --git a/vpr/src/base/vpr_constraints_writer.h b/vpr/src/base/vpr_constraints_writer.h index 955542be637..131c842328a 100644 --- a/vpr/src/base/vpr_constraints_writer.h +++ b/vpr/src/base/vpr_constraints_writer.h @@ -25,6 +25,14 @@ #ifndef VPR_SRC_BASE_VPR_CONSTRAINTS_WRITER_H_ #define VPR_SRC_BASE_VPR_CONSTRAINTS_WRITER_H_ +/** + * @brief Write out vpr constratins to an XML file based on current placement and + * route constraint settings + * + * @param vpr_setup VPR setup information + */ +void write_vpr_constraints(t_vpr_setup& vpr_setup); + /** * @brief Write out floorplan constraints to an XML file based on current placement * diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 9e1c9241346..fd54a2219da 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -32,6 +32,9 @@ #include "noc_storage.h" #include "noc_traffic_flows.h" #include "noc_routing.h" +#include "gateio.h" +#include "taskresolver.h" +#include "tatum/report/TimingPath.hpp" /** * @brief A Context is collection of state relating to a particular part of VPR @@ -208,7 +211,13 @@ struct DeviceContext : public Context { /* A read-only view of routing resource graph to be the ONLY database * for client functions: GUI, placer, router, timing analyzer etc. */ - RRGraphView rr_graph{rr_graph_builder.rr_nodes(), rr_graph_builder.node_lookup(), rr_graph_builder.rr_node_metadata(), rr_graph_builder.rr_edge_metadata(), rr_indexed_data, rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch()}; + RRGraphView rr_graph{rr_graph_builder.rr_nodes(), rr_graph_builder.node_lookup(), rr_graph_builder.rr_node_metadata(), rr_graph_builder.rr_edge_metadata(), rr_indexed_data, rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch(), rr_graph_builder.node_in_edge_storage(), rr_graph_builder.node_ptc_storage()}; + + /* Track ids for each rr_node in the rr_graph. + * This is used by drawer for tileable routing resource graph + */ + std::map> rr_node_track_ids; + std::vector arch_switch_inf; // [0..(num_arch_switches-1)] std::map all_sw_inf; @@ -472,6 +481,11 @@ struct RoutingContext : public Context { vtr::Cache>, RouterLookahead> cached_router_lookahead_; + + /** + * @brief Routing constraints, read only + */ + VprConstraints constraints; }; /** @@ -545,6 +559,69 @@ struct NocContext : public Context { NocRouting* noc_flows_router; }; +/** + * @brief State relating to server mode + * + * This should contain only data structures that + * related to server state. + */ +class ServerContext : public Context { + public: + const server::GateIO& gateIO() const { return gate_io_; } + server::GateIO& mutable_gateIO() { return gate_io_; } + + const server::TaskResolver& task_resolver() const { return task_resolver_; } + server::TaskResolver& mutable_task_resolver() { return task_resolver_; } + + void set_crit_paths(const std::vector& crit_paths) { crit_paths_ = crit_paths; } + const std::vector& crit_paths() const { return crit_paths_; } + + void set_critical_path_num(int critical_path_num) { critical_path_num_ = critical_path_num; } + int critical_path_num() const { return critical_path_num_; } + + void set_path_type(const std::string& path_type) { path_type_ = path_type; } + const std::string& path_type() const { return path_type_; } + + void set_crit_path_index(int crit_path_index) { crit_path_index_ = crit_path_index; } + int crit_path_index() const { return crit_path_index_; } + + private: + server::GateIO gate_io_; + server::TaskResolver task_resolver_; + + /** + * @brief Stores the critical path items. + * + * This value is used when rendering the critical path by the selected index. + * Once calculated upon request, it provides the value for a specific critical path + * to be rendered upon user request. + */ + std::vector crit_paths_; + + /** + * @brief Stores the number of critical paths items. + * + * This value is used to generate a critical path report with a certain number of items, + * which will be sent back to the client upon request. + */ + int critical_path_num_ = 1; + + /** + * @brief Stores the critical path type. + * + * This value is used to generate a specific type of critical path report and send + * it back to the client upon request. + */ + std::string path_type_ = "setup"; + + /** + * @brief Stores the last selected critical path index. + * + * This value is used to render the selected critical path upon client request. + */ + int crit_path_index_ = 0; +}; + /** * @brief This object encapsulates VPR's state. * @@ -628,6 +705,9 @@ class VprContext : public Context { const PackingMultithreadingContext& packing_multithreading() const { return packing_multithreading_; } PackingMultithreadingContext& mutable_packing_multithreading() { return packing_multithreading_; } + const ServerContext& server() const { return server_; } + ServerContext& mutable_server() { return server_; } + private: DeviceContext device_; @@ -644,6 +724,8 @@ class VprContext : public Context { FloorplanningContext constraints_; NocContext noc_; + ServerContext server_; + PackingMultithreadingContext packing_multithreading_; }; diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 5d03e194f8a..158ae4b7fce 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1338,6 +1338,8 @@ struct t_router_opts { enum e_route_type route_type; int fixed_channel_width; int min_channel_width_hint; /// switchblocks; + /* Xifan Tang: subtype of switch blocks. + * Sub type and Fs are applied to pass tracks + */ + int subFs; + enum e_switch_block_type switch_block_subtype; + + /* Xifan Tang: tileable routing */ + bool tileable; + bool shrink_boundary; + bool through_channel; + short global_route_switch; short delayless_switch; int wire_to_arch_ipin_switch; @@ -1762,6 +1776,12 @@ struct t_TokenPair { struct t_lb_type_rr_node; /* Defined in pack_types.h */ +/// @brief Stores settings for VPR server mode +struct t_server_opts { + bool is_server_mode_enabled = false; + int port_num = -1; +}; + ///@brief Store settings for VPR struct t_vpr_setup { bool TimingEnabled; ///* PackerRRGraph; std::vector Segments; ///graphics_commands = graphics_commands; draw_state->is_flat = is_flat; + if (enable_server) { + /* Set up a server and its callback to be triggered at 100ms intervals by the timer's timeout event. */ + server::GateIO& gate_io = g_vpr_ctx.mutable_server().mutable_gateIO(); + if (!gate_io.isRunning()) { + gate_io.start(port_num); + g_timeout_add(/*interval_ms*/ 100, server::update, &application); + } + } #else //Suppress unused parameter warnings (void)show_graphics_val; diff --git a/vpr/src/draw/draw.h b/vpr/src/draw/draw.h index 145f05800aa..9c68fd80ac5 100644 --- a/vpr/src/draw/draw.h +++ b/vpr/src/draw/draw.h @@ -56,7 +56,9 @@ void init_graphics_state(bool show_graphics_val, enum e_route_type route_type, bool save_graphics, std::string graphics_commands, - bool is_flat); + bool is_flat, + bool server, + int port_num); /* Allocates the structures needed to draw the placement and routing.*/ void alloc_draw_structs(const t_arch* arch); diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 0eb49a02034..c80f844d4a1 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -932,17 +932,10 @@ void draw_routing_util(ezgl::renderer* g) { draw_state->color_map = std::move(cmap); } -/* Draws the critical path if Crit. Path (in the GUI) is selected. Each stage between primitive - * pins is shown in a different colour. - * User can toggle between two different visualizations: - * a) during placement, critical path only shown as flylines - * b) during routing, critical path is shown by both flylines and routed net connections. +/* Draws the critical path when selected in the GUI or requested by the client in server mode. */ void draw_crit_path(ezgl::renderer* g) { - tatum::TimingPathCollector path_collector; - t_draw_state* draw_state = get_draw_state_vars(); - auto& timing_ctx = g_vpr_ctx.timing(); if (draw_state->show_crit_path == DRAW_NO_CRIT_PATH) { return; @@ -952,11 +945,47 @@ void draw_crit_path(ezgl::renderer* g) { return; //No timing to draw } - //Get the worst timing path - auto paths = path_collector.collect_worst_setup_timing_paths( - *timing_ctx.graph, - *(draw_state->setup_timing_info->setup_analyzer()), 1); - tatum::TimingPath path = paths[0]; + if (g_vpr_ctx.server().gateIO().isRunning()) { + const ServerContext& server_ctx = g_vpr_ctx.server(); // shortcut + const auto& paths = server_ctx.crit_paths(); + + const int index = server_ctx.crit_path_index(); // shortcut + + /* check critical path index bounds and render critical path, + * otherwise, resets selected critical path index with value -1. + */ + if ((index >= 0) && (index < static_cast(paths.size()))) { + tatum::TimingPath path = paths[index]; + draw_concrete_crit_path(path, g); + } else { + if (index != -1) { + g_vpr_ctx.mutable_server().set_crit_path_index(-1); + } + } + } else { + tatum::TimingPathCollector path_collector; + + auto& timing_ctx = g_vpr_ctx.timing(); + + auto paths = path_collector.collect_worst_setup_timing_paths( + *timing_ctx.graph, + *(draw_state->setup_timing_info->setup_analyzer()), 1); + + if (paths.size() > 0) { + auto path = paths[0]; + draw_concrete_crit_path(path, g); + } + } +} + +/* Draws the concrete critical path helper function. + * Each stage between primitive pins is shown in a different colour. + * User can toggle between two different visualizations: + * a) during placement, critical path only shown as flylines + * b) during routing, critical path is shown by both flylines and routed net connections. + */ +void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g) { + t_draw_state* draw_state = get_draw_state_vars(); //Walk through the timing path drawing each edge tatum::NodeId prev_node; diff --git a/vpr/src/draw/draw_basic.h b/vpr/src/draw/draw_basic.h index 4b2fad749c2..31f9342d9ae 100644 --- a/vpr/src/draw/draw_basic.h +++ b/vpr/src/draw/draw_basic.h @@ -78,13 +78,17 @@ void draw_partial_route(const std::vector& rr_nodes_to_draw, * channels, while darker colours (e.g. blue) correspond to lower utilization.*/ void draw_routing_util(ezgl::renderer* g); -/* Draws the critical path if Crit. Path (in the GUI) is selected. Each stage between primitive - * pins is shown in a different colour. +/* Draws the critical path when selected in the GUI or requested by the client in server mode. + */ +void draw_crit_path(ezgl::renderer* g); + +/* Draws the concrete critical path helper function. + * Each stage between primitive pins is shown in a different colour. * User can toggle between two different visualizations: * a) during placement, critical path only shown as flylines * b) during routing, critical path is shown by both flylines and routed net connections. */ -void draw_crit_path(ezgl::renderer* g); +void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g); /* Draws critical path shown as flylines. Takes in start and end coordinates, time delay, & renderer.*/ void draw_flyline_timing_edge(ezgl::point2d start, ezgl::point2d end, float incr_delay, ezgl::renderer* g); diff --git a/vpr/src/route/route_common.cpp b/vpr/src/route/route_common.cpp index 881dbfd46aa..26f44e47afb 100644 --- a/vpr/src/route/route_common.cpp +++ b/vpr/src/route/route_common.cpp @@ -167,6 +167,10 @@ void try_graph(int width_fac, graph_directionality = GRAPH_BIDIR; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch->directionality == UNI_DIRECTIONAL && det_routing_arch->tileable) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } @@ -221,6 +225,10 @@ bool try_route(const Netlist<>& net_list, graph_directionality = GRAPH_BIDIR; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch->directionality == UNI_DIRECTIONAL && det_routing_arch->tileable) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } diff --git a/vpr/src/route/router_delay_profiling.cpp b/vpr/src/route/router_delay_profiling.cpp index 4e2274c406f..63c35dc8f35 100644 --- a/vpr/src/route/router_delay_profiling.cpp +++ b/vpr/src/route/router_delay_profiling.cpp @@ -217,6 +217,9 @@ void alloc_routing_structs(t_chan_width chan_width, graph_type = GRAPH_GLOBAL; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + if ((UNI_DIRECTIONAL == det_routing_arch->directionality) && (true == det_routing_arch->tileable)) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } } create_rr_graph(graph_type, diff --git a/vpr/src/route/router_lookahead_map.cpp b/vpr/src/route/router_lookahead_map.cpp index 33b181a8be7..0a7004691cb 100644 --- a/vpr/src/route/router_lookahead_map.cpp +++ b/vpr/src/route/router_lookahead_map.cpp @@ -1431,7 +1431,9 @@ static void compute_tile_lookahead(std::unordered_map>& switch_fanin_remap, - const std::map arch_sw_inf, - const float R_minW_nmos, - const float R_minW_pmos, - const int wire_to_arch_ipin_switch, - int* wire_to_rr_ipin_switch); - static void remap_rr_node_switch_indices(RRGraphBuilder& rr_graph_builder, const t_arch_switch_fanin& switch_fanin); @@ -509,27 +492,9 @@ static void alloc_rr_switch_inf(RRGraphBuilder& rr_graph_builder, t_arch_switch_fanin& arch_switch_fanins, const std::map& arch_sw_map); -static void rr_graph_externals(const std::vector& segment_inf, - const std::vector& segment_inf_x, - const std::vector& segment_inf_y, - int wire_to_rr_ipin_switch, - enum e_base_cost_type base_cost_type); - -static t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, const int delayless_switch); - static t_seg_details* alloc_and_load_global_route_seg_details(const int global_route_switch, int* num_seg_details = nullptr); -static std::vector> alloc_and_load_actual_fc(const std::vector& types, - const int max_pins, - const std::vector& segment_inf, - const int* sets_per_seg_type, - const t_chan_width* nodes_per_chan, - const e_fc_type fc_type, - const enum e_directionality directionality, - bool* Fc_clipped, - bool is_flat); - static RRNodeId pick_best_direct_connect_target_rr_node(const RRGraphView& rr_graph, RRNodeId from_rr, const std::vector& candidate_rr_nodes); @@ -716,26 +681,50 @@ void create_rr_graph(const t_graph_type graph_type, } } else { free_rr_graph(); - build_rr_graph(graph_type, - block_types, - grid, - nodes_per_chan, - det_routing_arch->switch_block_type, - det_routing_arch->Fs, - det_routing_arch->switchblocks, - segment_inf, - det_routing_arch->global_route_switch, - det_routing_arch->wire_to_arch_ipin_switch, - det_routing_arch->wire_to_arch_ipin_switch_between_dice, - det_routing_arch->delayless_switch, - det_routing_arch->R_minW_nmos, - det_routing_arch->R_minW_pmos, - router_opts.base_cost_type, - router_opts.clock_modeling, - directs, num_directs, - &det_routing_arch->wire_to_rr_ipin_switch, - is_flat, - Warnings); + if (GRAPH_UNIDIR_TILEABLE != graph_type) { + build_rr_graph(graph_type, + block_types, + grid, + nodes_per_chan, + det_routing_arch->switch_block_type, + det_routing_arch->Fs, + det_routing_arch->switchblocks, + segment_inf, + det_routing_arch->global_route_switch, + det_routing_arch->wire_to_arch_ipin_switch, + det_routing_arch->wire_to_arch_ipin_switch_between_dice, + det_routing_arch->delayless_switch, + det_routing_arch->R_minW_nmos, + det_routing_arch->R_minW_pmos, + router_opts.base_cost_type, + router_opts.clock_modeling, + directs, num_directs, + &det_routing_arch->wire_to_rr_ipin_switch, + is_flat, + Warnings); + } else { + /* We do not support dedicated network for clocks in tileable rr_graph generation */ + VTR_LOG_WARN("Tileable routing resource graph does not support clock modeling yet! Related options are ignored...\n"); + build_tileable_unidir_rr_graph(block_types, + grid, + nodes_per_chan, + det_routing_arch->switch_block_type, + det_routing_arch->Fs, + det_routing_arch->switch_block_subtype, + det_routing_arch->subFs, + segment_inf, + det_routing_arch->delayless_switch, + det_routing_arch->wire_to_arch_ipin_switch, + det_routing_arch->R_minW_nmos, + det_routing_arch->R_minW_pmos, + router_opts.base_cost_type, + directs, num_directs, + &det_routing_arch->wire_to_rr_ipin_switch, + det_routing_arch->shrink_boundary, /* Shrink to the smallest boundary, no routing wires for empty zone */ + router_opts.trim_obs_channels || det_routing_arch->through_channel, /* Allow/Prohibit through tracks across multi-height and multi-width grids */ + false, /* Do not allow passing tracks to be wired to the same routing channels */ + Warnings); + } } } @@ -751,6 +740,7 @@ void create_rr_graph(const t_graph_type graph_type, is_flat, load_rr_graph); + /* Reorder nodes upon needs in algorithms and router options */ if (router_opts.reorder_rr_graph_nodes_algorithm != DONT_REORDER) { mutable_device_ctx.rr_graph_builder.reorder_nodes(router_opts.reorder_rr_graph_nodes_algorithm, router_opts.reorder_rr_graph_nodes_threshold, @@ -1548,13 +1538,13 @@ void build_tile_rr_graph(RRGraphBuilder& rr_graph_builder, * and count how many different fan-ins exist for each arch switch. * Then we create these rr switches and update the switch indices * of rr_nodes to index into the rr_switch_inf array. */ -static void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, - std::vector>& switch_fanin_remap, - const std::map arch_sw_inf, - const float R_minW_nmos, - const float R_minW_pmos, - const int wire_to_arch_ipin_switch, - int* wire_to_rr_ipin_switch) { +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch) { /* we will potentially be creating a couple of versions of each arch switch where * each version corresponds to a different fan-in. We will need to fill device_ctx.rr_switch_inf * with this expanded list of switches. @@ -1736,11 +1726,11 @@ static void remap_rr_node_switch_indices(RRGraphBuilder& rr_graph_builder, rr_graph_builder.remap_rr_node_switch_indices(switch_fanin); } -static void rr_graph_externals(const std::vector& segment_inf, - const std::vector& segment_inf_x, - const std::vector& segment_inf_y, - int wire_to_rr_ipin_switch, - enum e_base_cost_type base_cost_type) { +void rr_graph_externals(const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + int wire_to_rr_ipin_switch, + enum e_base_cost_type base_cost_type) { auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; const auto& grid = device_ctx.grid; @@ -1838,15 +1828,15 @@ static t_seg_details* alloc_and_load_global_route_seg_details(const int global_r } /* Calculates the number of track connections from each block pin to each segment type */ -static std::vector> alloc_and_load_actual_fc(const std::vector& types, - const int max_pins, - const std::vector& segment_inf, - const int* sets_per_seg_type, - const t_chan_width* nodes_per_chan, - const e_fc_type fc_type, - const enum e_directionality directionality, - bool* Fc_clipped, - bool is_flat) { +std::vector> alloc_and_load_actual_fc(const std::vector& types, + const int max_pins, + const std::vector& segment_inf, + const int* sets_per_seg_type, + const t_chan_width* nodes_per_chan, + const e_fc_type fc_type, + const enum e_directionality directionality, + bool* Fc_clipped, + bool is_flat) { //Initialize Fc of all blocks to zero auto zeros = vtr::Matrix({size_t(max_pins), segment_inf.size()}, 0); std::vector> Fc(types.size(), zeros); @@ -2603,6 +2593,8 @@ void free_rr_graph() { device_ctx.rr_graph_builder.clear(); + device_ctx.rr_node_track_ids.clear(); + device_ctx.rr_indexed_data.clear(); device_ctx.switch_fanin_remap.clear(); @@ -4106,7 +4098,7 @@ static void build_unidir_rr_opins(RRGraphBuilder& rr_graph_builder, * This data structure supplements the the info in the "directs" data structure * TODO: The function that does this parsing in placement is poorly done because it lacks generality on heterogeniety, should replace with this one */ -static t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, int delayless_switch) { +t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, int delayless_switch) { int i; t_clb_to_clb_directs* clb_to_clb_directs; char *tile_name, *port_name; diff --git a/vpr/src/route/rr_graph.h b/vpr/src/route/rr_graph.h index af06257d98e..14d2bf903b6 100644 --- a/vpr/src/route/rr_graph.h +++ b/vpr/src/route/rr_graph.h @@ -10,6 +10,7 @@ #include "vpr_types.h" #include "rr_graph_type.h" #include "describe_rr_node.h" +#include "clb2clb_directs.h" /* Warnings about the routing graph that can be returned. * This is to avoid output messages during a value sweep */ @@ -47,6 +48,33 @@ void free_rr_graph(); t_rr_switch_inf create_rr_switch_from_arch_switch(const t_arch_switch_inf& arch_sw_inf, const float R_minW_nmos, const float R_minW_pmos); + +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch); + +void rr_graph_externals(const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + int wire_to_rr_ipin_switch, + enum e_base_cost_type base_cost_type); + +t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, int delayless_switch); + +std::vector> alloc_and_load_actual_fc(const std::vector& types, + const int max_pins, + const std::vector& segment_inf, + const int* sets_per_seg_type, + const t_chan_width* nodes_per_chan, + const e_fc_type fc_type, + const enum e_directionality directionality, + bool* Fc_clipped, + bool is_flat); + // Sets the spec for the rr_switch based on the arch switch void load_rr_switch_from_arch_switch(RRGraphBuilder& rr_graph_builder, const std::map& arch_sw_inf, diff --git a/vpr/src/server/commconstants.h b/vpr/src/server/commconstants.h new file mode 100644 index 00000000000..3462e345e54 --- /dev/null +++ b/vpr/src/server/commconstants.h @@ -0,0 +1,36 @@ +#ifndef COMMCONSTS_H +#define COMMCONSTS_H + +namespace comm { + +constexpr const char* KEY_JOB_ID = "JOB_ID"; +constexpr const char* KEY_CMD = "CMD"; +constexpr const char* KEY_OPTIONS = "OPTIONS"; +constexpr const char* KEY_DATA = "DATA"; +constexpr const char* KEY_STATUS = "STATUS"; +constexpr const unsigned char TELEGRAM_FRAME_DELIMETER{0x17}; // 0x17 - End of Transmission Block + +constexpr const char* OPTION_PATH_NUM = "path_num"; +constexpr const char* OPTION_PATH_TYPE = "path_type"; +constexpr const char* OPTION_DETAILS_LEVEL = "details_level"; +constexpr const char* OPTION_IS_FLOAT_ROUTING = "is_flat_routing"; +constexpr const char* OPTION_PATH_INDEX = "path_index"; +constexpr const char* OPTION_PATH_ELEMENTS = "path_elements"; +constexpr const char* OPTION_HIGHTLIGHT_MODE = "hight_light_mode"; +constexpr const char* OPTION_DRAW_PATH_CONTOUR = "draw_path_contour"; + +constexpr const char* CRITICAL_PATH_ITEMS_SELECTION_NONE = "none"; + +// please don't change values as they are involved in socket communication +constexpr const char* KEY_SETUP_PATH_LIST = "setup"; +constexpr const char* KEY_HOLD_PATH_LIST = "hold"; +// + +enum CMD { + CMD_GET_PATH_LIST_ID = 0, + CMD_DRAW_PATH_ID +}; + +} // namespace comm + +#endif diff --git a/vpr/src/server/convertutils.cpp b/vpr/src/server/convertutils.cpp new file mode 100644 index 00000000000..691aedca1bf --- /dev/null +++ b/vpr/src/server/convertutils.cpp @@ -0,0 +1,17 @@ +#include "convertutils.h" +#include + +std::optional tryConvertToInt(const std::string& str) { + std::optional result; + + std::istringstream iss(str); + int intValue; + if (iss >> intValue) { + // Check if there are no any characters left in the stream + char remaining; + if (!(iss >> remaining)) { + result = intValue; + } + } + return result; +} diff --git a/vpr/src/server/convertutils.h b/vpr/src/server/convertutils.h new file mode 100644 index 00000000000..0647449c6a8 --- /dev/null +++ b/vpr/src/server/convertutils.h @@ -0,0 +1,9 @@ +#ifndef CONVERTUTILS_H +#define CONVERTUTILS_H + +#include +#include + +std::optional tryConvertToInt(const std::string&); + +#endif // CONVERTUTILS_H diff --git a/vpr/src/server/gateio.cpp b/vpr/src/server/gateio.cpp new file mode 100644 index 00000000000..2c309d3d5e5 --- /dev/null +++ b/vpr/src/server/gateio.cpp @@ -0,0 +1,197 @@ +#include "gateio.h" +#include "telegramparser.h" + +#include +#include +#include +#include +#include +#include + +namespace server { + +GateIO::GateIO() { + m_isRunning.store(false); +} + +GateIO::~GateIO() { + stop(); +} + +void GateIO::start(int portNum) { + if (!m_isRunning.load()) { + m_portNum = portNum; + std::cout << "th=" << std::this_thread::get_id() << " starting server" << std::endl; + m_isRunning.store(true); + m_thread = std::thread(&GateIO::startListening, this); + } +} + +void GateIO::stop() { + if (m_isRunning.load()) { + m_isRunning.store(false); + if (m_thread.joinable()) { + m_thread.join(); + } + } +} + +void GateIO::takeRecievedTasks(std::vector& tasks) { + tasks.clear(); + std::unique_lock lock(m_receivedTasksMutex); + if (m_receivedTasks.size() > 0) { + std::cout << "take " << m_receivedTasks.size() << " num of received tasks" << std::endl; + } + std::swap(tasks, m_receivedTasks); +} + +void GateIO::addSendTasks(const std::vector& tasks) { + std::unique_lock lock(m_sendTasksMutex); + for (const Task& task : tasks) { + std::cout << "addSendTasks id=" << task.jobId() << std::endl; + m_sendTasks.push_back(task); + } +} + +void GateIO::startListening() { + int server_socket; + int client_socket = -1; + struct sockaddr_in address; + int opt = 1; + int addrlen = sizeof(address); + + // Creating socket file descriptor + if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + perror("socket failed"); + exit(EXIT_FAILURE); + } + + // Forcefully attaching socket to the port + if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { + perror("setsockopt"); + exit(EXIT_FAILURE); + } + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(m_portNum); + + // Bind the socket to the network address and port + if (bind(server_socket, (struct sockaddr*)&address, sizeof(address)) < 0) { + perror("bind failed"); + exit(EXIT_FAILURE); + } else { + std::cout << "th=" << std::this_thread::get_id() << " start listening port: " << m_portNum << std::endl; + } + + // Put the server socket in a passive mode, where it waits for the client to approach the server to make a connection + if (listen(server_socket, 3) < 0) { + perror("listen"); + exit(EXIT_FAILURE); + } + + // Set the server socket to non-blocking mode + if (fcntl(server_socket, F_SETFL, fcntl(server_socket, F_GETFL, 0) | O_NONBLOCK) < 0) { + perror("Error setting socket to non-blocking"); + exit(EXIT_FAILURE); + } + + bool connectionProblemDetected = false; + + // Event loop + while (m_isRunning.load()) { + if (connectionProblemDetected || client_socket < 0) { + int flags = fcntl(client_socket, F_GETFL, 0); + fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); + client_socket = accept(server_socket, (struct sockaddr*)&address, (socklen_t*)&addrlen); + if (client_socket > 0) { + std::cout << "accept client" << std::endl; + } + } + + if (client_socket >= 0) { + connectionProblemDetected = false; + // Handle sending + { + std::unique_lock lock(m_sendTasksMutex); + for (const Task& task : m_sendTasks) { + std::string response = task.toJsonStr(); + response += static_cast(comm::TELEGRAM_FRAME_DELIMETER); + std::cout << "sending" << response << "to client" << std::endl; + send(client_socket, response.c_str(), response.length(), 0); + } + m_sendTasks.clear(); + } + + //////////////////// + // Handle receiving + fd_set readfds; + struct timeval tv; + + // Set a timeout + tv.tv_sec = 1; + tv.tv_usec = 0; + + // Clear the set ahead of time + FD_ZERO(&readfds); + + // Add our descriptor to the set + FD_SET(client_socket, &readfds); + + // Wait until data is available or timeout + int selectResult = select(client_socket + 1, &readfds, NULL, NULL, &tv); + + if (selectResult == -1) { + std::cerr << "Error in select()\n"; + } else if (selectResult == 0) { + //std::cout << "Timeout occurred! No data available.\n"; + } else { + if (FD_ISSET(client_socket, &readfds)) { + // Data is available; proceed with recv + char data[2048]; + std::memset(data, 0, sizeof(data)); + ssize_t bytes_received = recv(client_socket, data, sizeof(data), 0); + if (bytes_received > 0) { + m_telegramBuff.append(comm::ByteArray{data, static_cast(bytes_received)}); + } else if (bytes_received == 0) { + std::cout << "Connection closed\n"; + connectionProblemDetected = true; + } else { + std::cerr << "Error in recv()\n"; + connectionProblemDetected = true; + } + } + } + + auto frames = m_telegramBuff.takeFrames(); + for (const comm::ByteArray& frame : frames) { + // Process received data + std::string message{frame.to_string()}; + std::cout << "Received: " << message << std::endl; + std::optional jobIdOpt = comm::TelegramParser::tryExtractFieldJobId(message); + std::optional cmdOpt = comm::TelegramParser::tryExtractFieldCmd(message); + std::optional optionsOpt = comm::TelegramParser::tryExtractFieldOptions(message); + if (jobIdOpt && cmdOpt && optionsOpt) { + std::cout << "server: jobId=" << jobIdOpt.value() << ", cmd=" << cmdOpt.value() << ", options=" << optionsOpt.value() << std::endl; + std::unique_lock lock(m_receivedTasksMutex); + m_receivedTasks.emplace_back(jobIdOpt.value(), cmdOpt.value(), optionsOpt.value()); + } + } + + if (connectionProblemDetected && (client_socket >= 0)) { + std::cout << "close client connection" << std::endl; + close(client_socket); + m_telegramBuff.clear(); + } + } + } + + if (client_socket >= 0) { + std::cout << "close client socket" << std::endl; + close(client_socket); + } + + close(server_socket); +} + +} // namespace server diff --git a/vpr/src/server/gateio.h b/vpr/src/server/gateio.h new file mode 100644 index 00000000000..fee9591eec8 --- /dev/null +++ b/vpr/src/server/gateio.h @@ -0,0 +1,64 @@ +#ifndef GATEIO_H +#define GATEIO_H + +#include "task.h" +#include "telegrambuffer.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace server { + +/** + * @brief Implements the socket communication layer with the outside world. + * It begins listening on the specified port number for incoming client requests, + * collects and encapsulates them into tasks. + * The incoming tasks are extracted and handled by the top-level logic (TaskResolver). + * Once the tasks are resolved by the TaskResolver, they are returned + * to be sent back to the client as a response. + * + * Note: + * - gateio is not started automatically upon creation; you have to use the 'start' method with the port number. + * - The gateio runs in a separate thread to ensure smooth IO behavior. + * - The socket is initialized in a non-blocking mode to function properly in a multithreaded environment. +*/ +class GateIO { + public: + explicit GateIO(); + ~GateIO(); + + bool isRunning() const { return m_isRunning.load(); } + + void takeRecievedTasks(std::vector&); + void addSendTasks(const std::vector&); + + void start(int portNum); + void stop(); + + private: + int m_portNum = -1; + + std::atomic m_isRunning; // is true when started + + std::thread m_thread; // thread to execute socket IO work + + std::vector m_receivedTasks; // tasks from clinet (requests) + std::mutex m_receivedTasksMutex; + + std::vector m_sendTasks; // task to client (reponses) + std::mutex m_sendTasksMutex; + + comm::TelegramBuffer m_telegramBuff; + + void startListening(); // thread worker function +}; + +} // namespace server + +#endif // GATEIO_H diff --git a/vpr/src/server/gtkcomboboxhelper.cpp b/vpr/src/server/gtkcomboboxhelper.cpp new file mode 100644 index 00000000000..faeec50fc89 --- /dev/null +++ b/vpr/src/server/gtkcomboboxhelper.cpp @@ -0,0 +1,50 @@ +#include "gtkcomboboxhelper.h" +#include + +namespace { + +/** + * @brief Helper function to retrieve the count of items in a GTK combobox. + */ +gint get_items_count(gpointer combo_box) { + GtkComboBoxText* combo = GTK_COMBO_BOX_TEXT(combo_box); + + // Get the model of the combo box + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + + // Get the number of items (indexes) in the combo box + gint count = gtk_tree_model_iter_n_children(model, NULL); + return count; +} + +} // namespace + +/** + * @brief Helper function to retrieve the index of an item by its text. + * Returns -1 if the item with the specified text is absent. + */ +gint get_item_index_by_text(gpointer combo_box, const gchar* target_item) { + gint result_index = -1; + GtkComboBoxText* combo = GTK_COMBO_BOX_TEXT(combo_box); + + // Get the model of the combo box + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + + gchar* current_item_text = nullptr; + + for (gint index = 0; index < get_items_count(combo_box); ++index) { + GtkTreeIter iter; + + // Check if the index is within bounds + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, index)) { + gtk_tree_model_get(model, &iter, 0, ¤t_item_text, -1); + if (g_ascii_strcasecmp(target_item, current_item_text) == 0) { + result_index = index; + break; + } + } + } + + g_free(current_item_text); + return result_index; +} diff --git a/vpr/src/server/gtkcomboboxhelper.h b/vpr/src/server/gtkcomboboxhelper.h new file mode 100644 index 00000000000..24da5c66b7d --- /dev/null +++ b/vpr/src/server/gtkcomboboxhelper.h @@ -0,0 +1,12 @@ +#ifndef GTKCOMBOBOXHELPER_H +#define GTKCOMBOBOXHELPER_H + +#include + +/** + * @brief Helper function to retrieve the index of an item by its text. + * Returns -1 if the item with the specified text is absent. + */ +gint get_item_index_by_text(gpointer combo_box, const gchar* target_item); + +#endif // GTKCOMBOBOXHELPER_H diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp new file mode 100644 index 00000000000..594a1a7067d --- /dev/null +++ b/vpr/src/server/pathhelper.cpp @@ -0,0 +1,95 @@ +#include "pathhelper.h" +#include "globals.h" +#include "vpr_net_pins_matrix.h" +#include "VprTimingGraphResolver.h" +#include "tatum/TimingReporter.hpp" + +#include "draw_types.h" +#include "draw_global.h" +#include "net_delay.h" +#include "concrete_timing_info.h" + +#include "timing_info_fwd.h" +#include "AnalysisDelayCalculator.h" +#include "vpr_types.h" + +#include +#include + +namespace server { + +namespace { + +/** + * @brief helper function to calculate the setup critical path with specified parameters. + */ +CritPathsResult generate_setup_timing_report(const SetupTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { + auto& timing_ctx = g_vpr_ctx.timing(); + auto& atom_ctx = g_vpr_ctx.atom(); + + VprTimingGraphResolver resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, delay_calc, is_flat); + resolver.set_detail_level(analysis_opts.timing_report_detail); + + tatum::TimingReporter timing_reporter(resolver, *timing_ctx.graph, *timing_ctx.constraints); + + std::vector paths; + std::stringstream ss; + timing_reporter.report_timing_setup(paths, ss, *timing_info.setup_analyzer(), analysis_opts.timing_report_npaths); + return CritPathsResult{paths, ss.str()}; +} + +/** + * @brief helper function to calculate the hold critical path with specified parameters. + */ +CritPathsResult generate_hold_timing_report(const HoldTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { + auto& timing_ctx = g_vpr_ctx.timing(); + auto& atom_ctx = g_vpr_ctx.atom(); + + VprTimingGraphResolver resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, delay_calc, is_flat); + resolver.set_detail_level(analysis_opts.timing_report_detail); + + tatum::TimingReporter timing_reporter(resolver, *timing_ctx.graph, *timing_ctx.constraints); + + std::vector paths; + std::stringstream ss; + timing_reporter.report_timing_hold(paths, ss, *timing_info.hold_analyzer(), analysis_opts.timing_report_npaths); + return CritPathsResult{paths, ss.str()}; +} + +} // namespace + +/** + * @brief Unified helper function to calculate the critical path with specified parameters. + */ +CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing) { + // shortcuts + auto& atom_ctx = g_vpr_ctx.atom(); + + //Load the net delays + const Netlist<>& router_net_list = is_flat_routing ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; + const Netlist<>& net_list = router_net_list; + + NetPinsMatrix net_delay = make_net_pins_matrix(net_list); + load_net_delay_from_routing(net_list, + net_delay); + + //Do final timing analysis + auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, is_flat_routing); + + e_timing_update_type timing_update_type = e_timing_update_type::AUTO; // FULL, INCREMENTAL, AUTO + auto timing_info = make_setup_hold_timing_info(analysis_delay_calc, timing_update_type); + timing_info->update(); + + t_analysis_opts analysis_opt; + analysis_opt.timing_report_detail = detailsLevel; + analysis_opt.timing_report_npaths = critPathNum; + + if (type == "setup") { + return generate_setup_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing); + } else if (type == "hold") { + return generate_hold_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing); + } + return CritPathsResult{std::vector(), ""}; +} + +} // namespace server diff --git a/vpr/src/server/pathhelper.h b/vpr/src/server/pathhelper.h new file mode 100644 index 00000000000..61c86295634 --- /dev/null +++ b/vpr/src/server/pathhelper.h @@ -0,0 +1,31 @@ +#ifndef PATHHELPER_H +#define PATHHELPER_H + +#include +#include +#include + +#include "tatum/report/TimingPath.hpp" +#include "vpr_types.h" + +namespace server { + +/** + * @brief Structure to retain the calculation result of the critical path. + * + * It contains the critical path list and the generated report as a string. +*/ +struct CritPathsResult { + bool isValid() const { return !report.empty(); } + std::vector paths; + std::string report; +}; + +/** + * @brief Unified helper function to calculate the critical path with specified parameters. + */ +CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing); + +} // namespace server + +#endif // PATHHELPER_H diff --git a/vpr/src/server/serverupdate.cpp b/vpr/src/server/serverupdate.cpp new file mode 100644 index 00000000000..9aab36b2d1e --- /dev/null +++ b/vpr/src/server/serverupdate.cpp @@ -0,0 +1,43 @@ +#include "serverupdate.h" +#include "gateio.h" +#include "taskresolver.h" +#include "globals.h" +#include "ezgl/application.hpp" + +#ifndef NO_GRAPHICS + +namespace server { + +gboolean update(gpointer data) { + bool isRunning = g_vpr_ctx.server().gateIO().isRunning(); + if (isRunning) { + // shortcuts + ezgl::application* app = static_cast(data); + GateIO& gate_io = g_vpr_ctx.mutable_server().mutable_gateIO(); + TaskResolver& task_resolver = g_vpr_ctx.mutable_server().mutable_task_resolver(); + + std::vector tasksBuff; + + gate_io.takeRecievedTasks(tasksBuff); + task_resolver.addTasks(tasksBuff); + + bool process_task = task_resolver.update(app); + + tasksBuff.clear(); + task_resolver.takeFinished(tasksBuff); + + gate_io.addSendTasks(tasksBuff); + + // Call the redraw method of the application if any of task was processed + if (process_task) { + app->refresh_drawing(); + } + } + + // Return TRUE to keep the timer running, or FALSE to stop it + return isRunning; +} + +} // namespace server + +#endif // NO_GRAPHICS \ No newline at end of file diff --git a/vpr/src/server/serverupdate.h b/vpr/src/server/serverupdate.h new file mode 100644 index 00000000000..17301988b45 --- /dev/null +++ b/vpr/src/server/serverupdate.h @@ -0,0 +1,23 @@ +#ifndef SERVERUPDATE_H +#define SERVERUPDATE_H + +#include + +#ifndef NO_GRAPHICS + +namespace server { + +/** + * @brief Main server update callback. + * + * This function is a periodic callback invoked at a fixed interval to manage and handle incoming client requests. + * It acts as the central control point for processing client interactions and orchestrating server-side operations + * within the specified time intervals. + */ +gboolean update(gpointer); + +} // namespace server + +#endif // NO_GRAPHICS + +#endif // SERVERUPDATE_H diff --git a/vpr/src/server/task.h b/vpr/src/server/task.h new file mode 100644 index 00000000000..1576b518c66 --- /dev/null +++ b/vpr/src/server/task.h @@ -0,0 +1,80 @@ +#ifndef TASK_H +#define TASK_H + +#include +#include +#include + +#include "commconstants.h" + +namespace server { + +/** + * @brief Implements the server task. + * + * This structure aids in encapsulating the client request, request result, and result status. + * It generates a JSON data structure to be sent back to the client as a response. + */ +class Task { + public: + Task(int jobId, int cmd, const std::string& options = "") + : m_jobId(jobId) + , m_cmd(cmd) + , m_options(options) {} + + int jobId() const { return m_jobId; } + int cmd() const { return m_cmd; } + const std::string& options() const { return m_options; } + const std::string& result() const { return m_result; } + + std::string info() const { + std::stringstream result; + result << "id=" << std::to_string(m_jobId) << ","; + result << "cmd=" << std::to_string(m_cmd) << ","; + result << "opt=" << m_options; + return result.str(); + } + + bool isFinished() const { return m_isFinished; } + bool hasError() const { return m_hasError; } + + void fail(const std::string& error) { + std::cout << "task " << info() << " finished with error " << error << std::endl; + m_result = error; + m_isFinished = true; + m_hasError = true; + } + void success(const std::string& result = "") { + std::cout << "task " << info() << " finished with success " << result << std::endl; + m_result = result; + m_isFinished = true; + } + + std::string toJsonStr() const { + std::stringstream ss; + ss << "{"; + + ss << "\"" << comm::KEY_JOB_ID << "\":\"" << m_jobId << "\","; + ss << "\"" << comm::KEY_CMD << "\":\"" << m_cmd << "\","; + ss << "\"" << comm::KEY_OPTIONS << "\":\"" << m_options << "\","; + ss << "\"" << comm::KEY_DATA << "\":\"" << m_result << "\","; + int status = m_hasError ? 0 : 1; + ss << "\"" << comm::KEY_STATUS << "\":\"" << status << "\""; + + ss << "}"; + + return ss.str(); + } + + private: + int m_jobId = -1; + int m_cmd = -1; + std::string m_options; + std::string m_result; + bool m_isFinished = false; + bool m_hasError = false; +}; + +} // namespace server + +#endif // TASK_H diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp new file mode 100644 index 00000000000..cf5c98a21b6 --- /dev/null +++ b/vpr/src/server/taskresolver.cpp @@ -0,0 +1,167 @@ +#include "taskresolver.h" + +#include "commconstants.h" +#include "globals.h" +#include "pathhelper.h" +#include "telegramoptions.h" +#include "telegramparser.h" +#include "gtkcomboboxhelper.h" + +#include + +namespace server { + +void TaskResolver::addTask(Task task) { + // pre-process task before adding, where we could quickly detect failure scenario + for (auto& t : m_tasks) { + if (t.cmd() == task.cmd()) { + if (t.options() == task.options()) { + std::string msg = "similar task is already in execution, reject new task: " + t.info() + " and waiting for old task: " + task.info() + " execution"; + task.fail(msg); + } else { + // case when task has same jobId but different options + if (task.jobId() > t.jobId()) { + std::string msg = "old task: " + t.info() + " is overriden by a new task: " + task.info(); + t.fail(msg); + } + } + } + } + + // add task + m_tasks.push_back(std::move(task)); +} + +void TaskResolver::addTasks(const std::vector& tasks) { + for (const Task& task : tasks) { + addTask(task); + } +} + +void TaskResolver::takeFinished(std::vector& result) { + for (auto it = m_tasks.begin(); it != m_tasks.end();) { + Task task = *it; + if (task.isFinished()) { + result.push_back(std::move(task)); + it = m_tasks.erase(it); + } else { + ++it; + } + } +} + +e_timing_report_detail TaskResolver::getDetailsLevelEnum(const std::string& pathDetailsLevelStr) const { + e_timing_report_detail detailesLevel = e_timing_report_detail::NETLIST; + if (pathDetailsLevelStr == "netlist") { + detailesLevel = e_timing_report_detail::NETLIST; + } else if (pathDetailsLevelStr == "aggregated") { + detailesLevel = e_timing_report_detail::AGGREGATED; + } else if (pathDetailsLevelStr == "detailed") { + detailesLevel = e_timing_report_detail::DETAILED_ROUTING; + } else if (pathDetailsLevelStr == "debug") { + detailesLevel = e_timing_report_detail::DEBUG; + } else { + std::cerr << "unhandled option" << pathDetailsLevelStr << std::endl; + } + return detailesLevel; +} + +bool TaskResolver::update(ezgl::application* app) { + bool has_processed_task = false; + for (auto& task : m_tasks) { + if (!task.isFinished()) { + switch (task.cmd()) { + case comm::CMD_GET_PATH_LIST_ID: { + processGetPathListTask(app, task); + has_processed_task = true; + break; + } + case comm::CMD_DRAW_PATH_ID: { + processDrawCriticalPathTask(app, task); + has_processed_task = true; + break; + } + default: + break; + } + } + } + + return has_processed_task; +} + +void TaskResolver::processGetPathListTask(ezgl::application*, Task& task) { + TelegramOptions options{task.options(), {comm::OPTION_PATH_NUM, comm::OPTION_PATH_TYPE, comm::OPTION_DETAILS_LEVEL, comm::OPTION_IS_FLOAT_ROUTING}}; + if (!options.hasErrors()) { + ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut + + server_ctx.set_crit_path_index(-1); // reset selection if path list options has changed + + // read options + const int nCriticalPathNum = options.getInt(comm::OPTION_PATH_NUM, 1); + const std::string pathType = options.getString(comm::OPTION_PATH_TYPE); + const std::string detailsLevel = options.getString(comm::OPTION_DETAILS_LEVEL); + const bool isFlat = options.getBool(comm::OPTION_IS_FLOAT_ROUTING, false); + + // calculate critical path depending on options and store result in server context + CritPathsResult crit_paths_result = calcCriticalPath(pathType, nCriticalPathNum, getDetailsLevelEnum(detailsLevel), isFlat); + + // setup context + server_ctx.set_path_type(pathType); + server_ctx.set_critical_path_num(nCriticalPathNum); + server_ctx.set_crit_paths(crit_paths_result.paths); + + if (crit_paths_result.isValid()) { + std::string msg{crit_paths_result.report}; + task.success(msg); + } else { + std::string msg{"Critical paths report is empty"}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"options errors in get crit path list telegram: " + options.errorsStr()}; + std::cerr << msg << std::endl; + task.fail(msg); + } +} + +void TaskResolver::processDrawCriticalPathTask(ezgl::application* app, Task& task) { + TelegramOptions options{task.options(), {comm::OPTION_PATH_INDEX, comm::OPTION_HIGHTLIGHT_MODE}}; + if (!options.hasErrors()) { + ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut + + const int pathIndex = options.getInt(comm::OPTION_PATH_INDEX, -1); + const std::string highLightMode = options.getString(comm::OPTION_HIGHTLIGHT_MODE); + + if (pathIndex == -1) { + server_ctx.set_crit_path_index(-1); // clear selection + task.success(); + } else if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx.crit_paths().size()))) { + // set critical path index for rendering + server_ctx.set_crit_path_index(pathIndex); + + // update gtk UI + GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); + gint highLightModeIndex = get_item_index_by_text(toggle_crit_path, highLightMode.c_str()); + if (highLightModeIndex != -1) { + gtk_combo_box_set_active(toggle_crit_path, highLightModeIndex); + task.success(); + } else { + std::string msg{"cannot find ToggleCritPath qcombobox index for item " + highLightMode}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx.crit_paths().size()) - 1) + "]"}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"options errors in highlight crit path telegram: " + options.errorsStr()}; + std::cerr << msg << std::endl; + task.fail(msg); + } +} + +} // namespace server diff --git a/vpr/src/server/taskresolver.h b/vpr/src/server/taskresolver.h new file mode 100644 index 00000000000..50e7e3a5ed6 --- /dev/null +++ b/vpr/src/server/taskresolver.h @@ -0,0 +1,51 @@ +#ifndef TASKRESOLVER_H +#define TASKRESOLVER_H + +#include "task.h" +#include "vpr_types.h" + +#include + +namespace ezgl { +class application; +} + +namespace server { + +/** + * @brief Resolve server task. + * + * Process and resolve server task, store result and status for processed task. +*/ + +class TaskResolver { + public: + TaskResolver() = default; + ~TaskResolver() = default; + + int tasksNum() const { return m_tasks.size(); } + + /* add tasks to process */ + void addTask(Task); + void addTasks(const std::vector&); + + /* process tasks */ + bool update(ezgl::application*); + + /* extract finished tasks */ + void takeFinished(std::vector&); + + const std::vector& tasks() const { return m_tasks; } + + private: + std::vector m_tasks; + + void processGetPathListTask(ezgl::application*, Task&); + void processDrawCriticalPathTask(ezgl::application*, Task&); + + e_timing_report_detail getDetailsLevelEnum(const std::string& pathDetailsLevelStr) const; +}; + +} // namespace server + +#endif // TASKRESOLVER_H diff --git a/vpr/src/server/telegrambuffer.cpp b/vpr/src/server/telegrambuffer.cpp new file mode 100644 index 00000000000..8068beafef3 --- /dev/null +++ b/vpr/src/server/telegrambuffer.cpp @@ -0,0 +1,27 @@ +#include "telegrambuffer.h" +#include "commconstants.h" + +namespace comm { + +void TelegramBuffer::append(const ByteArray& bytes) { + m_rawBuffer.append(bytes); +} + +std::vector TelegramBuffer::takeFrames() { + std::vector result; + ByteArray candidate; + for (unsigned char b : m_rawBuffer) { + if (b == TELEGRAM_FRAME_DELIMETER) { + if (!candidate.empty()) { + result.push_back(candidate); + } + candidate = ByteArray(); + } else { + candidate.append(b); + } + } + std::swap(m_rawBuffer, candidate); + return result; +} + +} // namespace comm diff --git a/vpr/src/server/telegrambuffer.h b/vpr/src/server/telegrambuffer.h new file mode 100644 index 00000000000..9547b7efc8a --- /dev/null +++ b/vpr/src/server/telegrambuffer.h @@ -0,0 +1,69 @@ +#ifndef TELEGRAMBUFFER_H +#define TELEGRAMBUFFER_H + +#include +#include +#include + +namespace comm { + +/** + * @brief ByteArray as a simple wrapper over std::vector +*/ +class ByteArray : public std::vector { + public: + static const std::size_t DEFAULT_SIZE_HINT = 1024; + + ByteArray(const char* data) + : std::vector(reinterpret_cast(data), + reinterpret_cast(data + std::strlen(data))) {} + + ByteArray(const char* data, std::size_t size) + : std::vector(reinterpret_cast(data), + reinterpret_cast(data + size)) {} + ByteArray(std::size_t sizeHint = DEFAULT_SIZE_HINT) { + this->reserve(sizeHint); + } + + void append(const ByteArray& appendix) { + for (unsigned char b : appendix) { + this->push_back(b); + } + } + + void append(unsigned char b) { + this->push_back(b); + } + + std::string to_string() const { + return std::string(reinterpret_cast(this->data()), this->size()); + } +}; + +/** + * @brief Implements Telegram Buffer as a wrapper over BytesArray + * + * It aggregates received bytes and return only well filled frames, separated by telegram delimerer byte. +*/ +class TelegramBuffer { + static const std::size_t DEFAULT_SIZE_HINT = 1024; + + public: + TelegramBuffer(std::size_t sizeHint = DEFAULT_SIZE_HINT) + : m_rawBuffer(sizeHint) {} + ~TelegramBuffer() = default; + + void clear() { m_rawBuffer.clear(); } + + void append(const ByteArray&); + std::vector takeFrames(); + + const ByteArray& data() const { return m_rawBuffer; } + + private: + ByteArray m_rawBuffer; +}; + +} // namespace comm + +#endif // TELEGRAMBUFFER_H diff --git a/vpr/src/server/telegramoptions.h b/vpr/src/server/telegramoptions.h new file mode 100644 index 00000000000..67d9b5e12ec --- /dev/null +++ b/vpr/src/server/telegramoptions.h @@ -0,0 +1,160 @@ +#ifndef TELEGRAMOPTIONS_H +#define TELEGRAMOPTIONS_H + +#include + +#include +#include +#include +#include +#include + +namespace server { + +/** + * @brief Option class Parser + * + * Parse the string of options in the format "TYPE:KEY1:VALUE1;TYPE:KEY2:VALUE2", + * for example "int:path_num:11;string:path_type:debug;int:details_level:3;bool:is_flat_routing:0". + * It provides a simple interface to check value presence and access them. +*/ + +class TelegramOptions { + private: + enum { + INDEX_TYPE = 0, + INDEX_NAME, + INDEX_VALUE, + TOTAL_INDEXES_NUM + }; + + struct Option { + std::string type; + std::string value; + }; + + public: + TelegramOptions(const std::string& data, const std::vector& expectedKeys) { + // parse data string + std::vector options = splitString(data, ';'); + for (const std::string& optionStr : options) { + std::vector fragments = splitString(optionStr, ':'); + if (fragments.size() == TOTAL_INDEXES_NUM) { + std::string name = fragments[INDEX_NAME]; + Option option{fragments[INDEX_TYPE], fragments[INDEX_VALUE]}; + if (isDataTypeSupported(option.type)) { + m_options[name] = option; + } else { + m_errors.emplace_back("bad type for option [" + optionStr + "]"); + } + } else { + m_errors.emplace_back("bad option [" + optionStr + "]"); + } + } + + // check keys presense + checkKeysPresence(expectedKeys); + } + + ~TelegramOptions() {} + + bool hasErrors() const { return !m_errors.empty(); } + + std::map> getMapOfSets(const std::string& name) { + std::map> result; + std::string dataStr = getString(name); + if (!dataStr.empty()) { + std::vector pathes = splitString(dataStr, '|'); + for (const std::string& path : pathes) { + std::vector pathStruct = splitString(path, '#'); + if (pathStruct.size() == 2) { + std::string pathIndexStr = pathStruct[0]; + std::string pathElementIndexesStr = pathStruct[1]; + std::vector pathElementIndexes = splitString(pathElementIndexesStr, ','); + std::set elements; + for (const std::string& pathElementIndex : pathElementIndexes) { + if (std::optional optValue = tryConvertToInt(pathElementIndex.c_str())) { + elements.insert(optValue.value()); + } + } + if (std::optional optPathIndex = tryConvertToInt(pathIndexStr.c_str())) { + result[optPathIndex.value()] = elements; + } + } else { + m_errors.emplace_back("wrong path data structure = " + path); + } + } + } + return result; + } + + std::string getString(const std::string& name) { + std::string result; + if (auto it = m_options.find(name); it != m_options.end()) { + result = it->second.value; + } + return result; + } + + int getInt(const std::string& name, int failValue) { + if (std::optional opt = tryConvertToInt(m_options[name].value)) { + return opt.value(); + } else { + m_errors.emplace_back("cannot get int value for option " + name); + return failValue; + } + } + + bool getBool(const std::string& name, bool failValue) { + if (std::optional opt = tryConvertToInt(m_options[name].value)) { + return opt.value(); + } else { + m_errors.emplace_back("cannot get bool value for option " + name); + return failValue; + } + } + + std::string errorsStr() const { + std::string result; + for (const std::string& error : m_errors) { + result += error + ";"; + } + return result; + } + + private: + std::unordered_map m_options; + std::vector m_errors; + + std::vector splitString(const std::string& input, char delimiter) { + std::vector tokens; + std::istringstream tokenStream(input); + std::string token; + + while (std::getline(tokenStream, token, delimiter)) { + tokens.push_back(token); + } + + return tokens; + } + + bool isDataTypeSupported(const std::string& type) { + static std::set supportedTypes{"int", "string", "bool"}; + return supportedTypes.count(type) != 0; + } + + bool checkKeysPresence(const std::vector& keys) { + bool result = true; + for (const std::string& key : keys) { + if (m_options.find(key) == m_options.end()) { + m_errors.emplace_back("cannot find required option " + key); + result = false; + } + } + return result; + } +}; + +} // namespace server + +#endif // TELEGRAMOPTIONS_H diff --git a/vpr/src/server/telegramparser.cpp b/vpr/src/server/telegramparser.cpp new file mode 100644 index 00000000000..57e9c6e0d13 --- /dev/null +++ b/vpr/src/server/telegramparser.cpp @@ -0,0 +1,71 @@ +#include "telegramparser.h" +#include "convertutils.h" +#include "commconstants.h" + +namespace comm { + +std::optional TelegramParser::tryExtractJsonValueStr(const std::string& jsonString, const std::string& key) { + std::optional result; + + // Find the position of the key + size_t keyPos = jsonString.find("\"" + key + "\":"); + + if (keyPos == std::string::npos) { + // Key not found + return result; + } + + // Find the position of the value after the key + size_t valuePosStart = jsonString.find("\"", keyPos + key.length() + std::string("\":\"").size()); + + if (valuePosStart == std::string::npos) { + // Value not found + return result; + } + + // Find the position of the closing quote for the value + size_t valueEnd = jsonString.find("\"", valuePosStart + std::string("\"").size()); + + if (valueEnd == std::string::npos) { + // Closing quote not found + return result; + } + + // Extract the value substring + result = jsonString.substr(valuePosStart + 1, (valueEnd - valuePosStart) - 1); + return result; +} + +std::optional TelegramParser::tryExtractFieldJobId(const std::string& message) { + std::optional result; + if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_JOB_ID)) { + result = tryConvertToInt(strOpt.value()); + } + return result; +} + +std::optional TelegramParser::tryExtractFieldCmd(const std::string& message) { + std::optional result; + if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_CMD)) { + result = tryConvertToInt(strOpt.value()); + } + return result; +} + +std::optional TelegramParser::tryExtractFieldOptions(const std::string& message) { + return tryExtractJsonValueStr(message, comm::KEY_OPTIONS); +} + +std::optional TelegramParser::tryExtractFieldData(const std::string& message) { + return tryExtractJsonValueStr(message, comm::KEY_DATA); +} + +std::optional TelegramParser::tryExtractFieldStatus(const std::string& message) { + std::optional result; + if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_STATUS)) { + result = tryConvertToInt(strOpt.value()); + } + return result; +} + +} // namespace comm diff --git a/vpr/src/server/telegramparser.h b/vpr/src/server/telegramparser.h new file mode 100644 index 00000000000..0f1d0114abb --- /dev/null +++ b/vpr/src/server/telegramparser.h @@ -0,0 +1,29 @@ +#ifndef TELEGRAMPARSER_H +#define TELEGRAMPARSER_H + +#include +#include + +namespace comm { + +/** + * @brief Dummy JSON parser using regular expressions. + * + * This module provides helper methods to extract values for a keys as "JOB_ID", "CMD", or "OPTIONS" + * from a JSON schema structured as follows: {JOB_ID:num, CMD:enum, OPTIONS:string}. + */ +class TelegramParser { + public: + static std::optional tryExtractFieldJobId(const std::string& message); + static std::optional tryExtractFieldCmd(const std::string& message); + static std::optional tryExtractFieldOptions(const std::string& message); + static std::optional tryExtractFieldData(const std::string& message); + static std::optional tryExtractFieldStatus(const std::string& message); + + private: + static std::optional tryExtractJsonValueStr(const std::string& jsonString, const std::string& key); +}; + +} // namespace comm + +#endif // TELEGRAMPARSER_H diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 6157c9b980d..db422bea509 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "vtr_assert.h" #include "vtr_log.h" @@ -17,7 +18,6 @@ #include "vpr_utils.h" #include "cluster_placement.h" #include "place_macro.h" -#include "string.h" #include "pack_types.h" #include "device_grid.h" #include "timing_fail_error.h" @@ -181,7 +181,7 @@ void sync_grid_to_blocks() { } if (device_ctx.grid.get_width_offset({blk_x, blk_y, blk_layer}) != 0 || device_ctx.grid.get_height_offset({blk_x, blk_y, blk_layer}) != 0) { - VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Large block not aligned in placment for cluster_ctx.blocks %lu at (%d, %d, %d, %d).", + VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Large block not aligned in placement for cluster_ctx.blocks %lu at (%d, %d, %d, %d).", size_t(blk_id), blk_x, blk_y, blk_z, blk_layer); } @@ -675,7 +675,7 @@ void get_pin_range_for_block(const ClusterBlockId blk_id, *pin_high = sub_tile.sub_tile_to_tile_pin_indices[rel_pin_high]; } -t_physical_tile_type_ptr find_tile_type_by_name(std::string name, const std::vector& types) { +t_physical_tile_type_ptr find_tile_type_by_name(const std::string& name, const std::vector& types) { for (auto const& type : types) { if (type.name == name) { return &type; @@ -814,7 +814,7 @@ t_physical_tile_type_ptr find_most_common_tile_type(const DeviceGrid& grid) { return max_type; } -InstPort parse_inst_port(std::string str) { +InstPort parse_inst_port(const std::string& str) { InstPort inst_port(str); auto& device_ctx = g_vpr_ctx.device(); @@ -1172,7 +1172,7 @@ t_pb_graph_pin* get_pb_graph_node_pin_from_block_pin(ClusterBlockId iblock, int return nullptr; } -const t_port* find_pb_graph_port(const t_pb_graph_node* pb_gnode, std::string port_name) { +const t_port* find_pb_graph_port(const t_pb_graph_node* pb_gnode, const std::string& port_name) { const t_pb_graph_pin* gpin = find_pb_graph_pin(pb_gnode, port_name, 0); if (gpin != nullptr) { @@ -1181,7 +1181,7 @@ const t_port* find_pb_graph_port(const t_pb_graph_node* pb_gnode, std::string po return nullptr; } -const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, std::string port_name, int index) { +const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, const std::string& port_name, int index) { for (int iport = 0; iport < pb_gnode->num_input_ports; iport++) { if (pb_gnode->num_input_pins[iport] < index) continue; @@ -2240,7 +2240,7 @@ void pretty_print_float(const char* prefix, double value, int num_digits, int sc } } -void print_timing_stats(std::string name, +void print_timing_stats(const std::string& name, const t_timing_analysis_profile_info& current, const t_timing_analysis_profile_info& past) { VTR_LOG("%s timing analysis took %g seconds (%g STA, %g slack) (%zu full updates: %zu setup, %zu hold, %zu combined).\n", @@ -2508,3 +2508,21 @@ void add_pb_child_to_list(std::list& pb_list, const t_pb* parent_pb } } } + +float get_min_cross_layer_delay() { + const auto& rr_graph = g_vpr_ctx.device().rr_graph; + float min_delay = std::numeric_limits::max(); + + for (const auto& driver_node : rr_graph.nodes()) { + for (size_t edge_id = 0; edge_id < rr_graph.num_edges(driver_node); edge_id++) { + const auto& sink_node = rr_graph.edge_sink_node(driver_node, edge_id); + if (rr_graph.node_layer(driver_node) != rr_graph.node_layer(sink_node)) { + int i_switch = rr_graph.edge_switch(driver_node, edge_id); + float edge_delay = rr_graph.rr_switch_inf(RRSwitchId(i_switch)).Tdel; + min_delay = std::min(min_delay, edge_delay); + } + } + } + + return min_delay; +} diff --git a/vpr/src/util/vpr_utils.h b/vpr/src/util/vpr_utils.h index 1ba3dcb35b7..a4c20feb61b 100644 --- a/vpr/src/util/vpr_utils.h +++ b/vpr/src/util/vpr_utils.h @@ -153,10 +153,10 @@ std::vector find_clb_pin_sink_atom_pins(ClusterBlockId clb, int logic std::tuple find_pb_route_clb_input_net_pin(ClusterBlockId clb, int sink_pb_route_id); //Returns the port matching name within pb_gnode -const t_port* find_pb_graph_port(const t_pb_graph_node* pb_gnode, std::string port_name); +const t_port* find_pb_graph_port(const t_pb_graph_node* pb_gnode, const std::string& port_name); //Returns the graph pin matching name at pin index -const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, std::string port_name, int index); +const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, const std::string& port_name, int index); AtomPinId find_atom_pin(ClusterBlockId blk_id, const t_pb_graph_pin* pb_gpin); @@ -168,7 +168,7 @@ t_physical_tile_type_ptr find_most_common_tile_type(const DeviceGrid& grid); //Parses a block_name.port[x:y] (e.g. LAB.data_in[3:10]) pin range specification, if no pin range is specified //looks-up the block port and fills in the full range -InstPort parse_inst_port(std::string str); +InstPort parse_inst_port(const std::string& str); //Returns the block type which is most likely the logic block t_logical_block_type_ptr infer_logic_block_type(const DeviceGrid& grid); @@ -250,7 +250,7 @@ int max_pins_per_grid_tile(); void pretty_print_uint(const char* prefix, size_t value, int num_digits, int scientific_precision); void pretty_print_float(const char* prefix, double value, int num_digits, int scientific_precision); -void print_timing_stats(std::string name, +void print_timing_stats(const std::string& name, const t_timing_analysis_profile_info& current, const t_timing_analysis_profile_info& past = t_timing_analysis_profile_info()); @@ -311,4 +311,14 @@ t_arch_switch_inf create_internal_arch_sw(float delay); void add_pb_child_to_list(std::list& pb_list, const t_pb* parent_pb); +/** + * @brief Iterate over all inter-layer switch types and return the minimum delay of it. + * useful four router lookahead to to have some estimate of the cost of crossing a layer + * @param arch_switch_inf + * @param segment_inf + * @param wire_to_ipin_arch_sw_id + * @return + */ +float get_min_cross_layer_delay(); + #endif diff --git a/vpr/test/test_server_convertutils.cpp b/vpr/test/test_server_convertutils.cpp new file mode 100644 index 00000000000..d7851832cc0 --- /dev/null +++ b/vpr/test/test_server_convertutils.cpp @@ -0,0 +1,15 @@ +#include "convertutils.h" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +TEST_CASE("test_server_convert_utils_to_int", "[vpr]") { + REQUIRE(std::optional{-2} == tryConvertToInt("-2")); + REQUIRE(std::optional{0} == tryConvertToInt("0")); + REQUIRE(std::optional{2} == tryConvertToInt("2")); + REQUIRE(std::nullopt == tryConvertToInt("2.")); + REQUIRE(std::nullopt == tryConvertToInt("2.0")); + REQUIRE(std::nullopt == tryConvertToInt("two")); + REQUIRE(std::nullopt == tryConvertToInt("2k")); + REQUIRE(std::nullopt == tryConvertToInt("k2")); +} diff --git a/vpr/test/test_server_taskresolver.cpp b/vpr/test/test_server_taskresolver.cpp new file mode 100644 index 00000000000..0561e5ca7cd --- /dev/null +++ b/vpr/test/test_server_taskresolver.cpp @@ -0,0 +1,81 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +#include "taskresolver.h" + +namespace { + +TEST_CASE("test_server_taskresolver_cmdSpamFilter", "[vpr]") { + server::TaskResolver resolver; + resolver.addTask(server::Task{1, 1}); + resolver.addTask(server::Task{2, 1}); + resolver.addTask(server::Task{3, 1}); + resolver.addTask(server::Task{4, 1}); + resolver.addTask(server::Task{5, 1}); + + std::vector finished; + resolver.takeFinished(finished); + + REQUIRE(finished.size() == 4); + + for (const auto& task : finished) { + REQUIRE(task.isFinished()); + REQUIRE(task.hasError()); + REQUIRE(task.jobId() != 1); + } + REQUIRE(resolver.tasksNum() == 1); + server::Task task = resolver.tasks()[0]; + REQUIRE(task.jobId() == 1); + REQUIRE(task.cmd() == 1); +} + +TEST_CASE("test_server_taskresolver_cmdOverrideFilter", "[vpr]") { + server::TaskResolver resolver; + resolver.addTask(server::Task{1, 2, "1"}); + resolver.addTask(server::Task{2, 2, "11"}); + resolver.addTask(server::Task{3, 2, "222"}); + + std::vector finished; + resolver.takeFinished(finished); + + REQUIRE(finished.size() == 2); + + for (const auto& task : finished) { + REQUIRE(task.isFinished()); + REQUIRE(task.hasError()); + REQUIRE(task.jobId() != 3); + } + REQUIRE(resolver.tasksNum() == 1); + server::Task task = resolver.tasks()[0]; + REQUIRE(task.jobId() == 3); + REQUIRE(task.cmd() == 2); + REQUIRE(task.options() == "222"); +} + +TEST_CASE("test_server_taskresolver_cmdSpamAndOverrideOptions", "[vpr]") { + server::TaskResolver resolver; + resolver.addTask(server::Task{1, 2, "1"}); + resolver.addTask(server::Task{2, 2, "11"}); + resolver.addTask(server::Task{3, 2, "222"}); + resolver.addTask(server::Task{4, 2, "222"}); + resolver.addTask(server::Task{5, 1}); + resolver.addTask(server::Task{6, 1}); + resolver.addTask(server::Task{7, 1}); + + std::vector finished; + resolver.takeFinished(finished); + + REQUIRE(resolver.tasksNum() == 2); + server::Task task0 = resolver.tasks()[0]; + server::Task task1 = resolver.tasks()[1]; + + REQUIRE(task0.jobId() == 3); + REQUIRE(task0.cmd() == 2); + REQUIRE(task0.options() == "222"); + + REQUIRE(task1.jobId() == 5); + REQUIRE(task1.cmd() == 1); + REQUIRE(task1.options() == ""); +} + +} // namespace \ No newline at end of file diff --git a/vpr/test/test_server_telegrambuffer.cpp b/vpr/test/test_server_telegrambuffer.cpp new file mode 100644 index 00000000000..e08ecc45fe8 --- /dev/null +++ b/vpr/test/test_server_telegrambuffer.cpp @@ -0,0 +1,89 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +#include "telegrambuffer.h" + +namespace { + +TEST_CASE("test_server_bytearray", "[vpr]") { + comm::ByteArray array1{"111"}; + comm::ByteArray array2{"222"}; + comm::ByteArray array{array1}; + array.append(array2); + + REQUIRE(array.at(0) == '1'); + REQUIRE(array.at(1) == '1'); + REQUIRE(array.at(2) == '1'); + REQUIRE(array.at(3) == '2'); + REQUIRE(array.at(4) == '2'); + REQUIRE(array.at(5) == '2'); + + REQUIRE(array.to_string() == "111222"); + + REQUIRE(array.size() == 6); + + array.append('3'); + + REQUIRE(array.size() == 7); + REQUIRE(array.to_string() == "1112223"); + + REQUIRE(array.at(6) == '3'); + + array.clear(); + + REQUIRE(array.size() == 0); + REQUIRE(array.to_string() == ""); +} + +TEST_CASE("test_server_telegrambuffer_oneOpened", "[vpr]") { + comm::TelegramBuffer buff{1024}; + buff.append(comm::ByteArray{"111"}); + buff.append(comm::ByteArray{"222"}); + + auto frames = buff.takeFrames(); + REQUIRE(frames.size() == 0); + + REQUIRE(buff.data().to_string() == "111222"); +} + +TEST_CASE("test_server_telegrambuffer_oneFinishedOneOpened", "[vpr]") { + comm::TelegramBuffer buff{1024}; + buff.append(comm::ByteArray{"111\x17"}); + buff.append(comm::ByteArray{"222"}); + + auto frames = buff.takeFrames(); + REQUIRE(frames.size() == 1); + + REQUIRE(frames[0].to_string() == "111"); + + REQUIRE(buff.data().to_string() == "222"); +} + +TEST_CASE("test_server_telegrambuffer_twoFinished", "[vpr]") { + comm::TelegramBuffer buff{1024}; + buff.append(comm::ByteArray{"111\x17"}); + buff.append(comm::ByteArray{"222\x17"}); + + auto frames = buff.takeFrames(); + REQUIRE(frames.size() == 2); + + REQUIRE(frames[0].to_string() == "111"); + REQUIRE(frames[1].to_string() == "222"); + + REQUIRE(buff.data().to_string() == ""); +} + +TEST_CASE("test_server_telegrambuffer_twoCleared", "[vpr]") { + comm::TelegramBuffer buff{1024}; + buff.append(comm::ByteArray{"111\x17"}); + buff.append(comm::ByteArray{"222\x17"}); + + buff.clear(); + + auto frames = buff.takeFrames(); + REQUIRE(frames.size() == 0); + + REQUIRE(buff.data().to_string() == ""); +} + +} // namespace \ No newline at end of file diff --git a/vpr/test/test_server_telegramoptions.cpp b/vpr/test/test_server_telegramoptions.cpp new file mode 100644 index 00000000000..36ff80157f1 --- /dev/null +++ b/vpr/test/test_server_telegramoptions.cpp @@ -0,0 +1,19 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +#include "telegramoptions.h" + +namespace { + +TEST_CASE("test_server_telegramoptions", "[vpr]") { + server::TelegramOptions options{"int:path_num:11;string:path_type:debug;int:details_level:3;bool:is_flat_routing:0", {"path_num", "path_type", "details_level", "is_flat_routing"}}; + + REQUIRE(options.errorsStr() == ""); + + REQUIRE(options.getString("path_type") == "debug"); + REQUIRE(options.getInt("path_num", -1) == 11); + REQUIRE(options.getInt("details_level", -1) == 3); + REQUIRE(options.getBool("is_flat_routing", true) == false); +} + +} // namespace diff --git a/vpr/test/test_server_telegramparser.cpp b/vpr/test/test_server_telegramparser.cpp new file mode 100644 index 00000000000..5f7ecc6062f --- /dev/null +++ b/vpr/test/test_server_telegramparser.cpp @@ -0,0 +1,14 @@ +#include "telegramparser.h" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +TEST_CASE("test_server_telegram_parser_base", "[vpr]") { + const std::string tdata{R"({"JOB_ID":"7","CMD":"2","OPTIONS":"type1:name1:value1;type2:name2:v a l u e 2;t3:n3:v3","DATA":"some_data...","STATUS":"1"})"}; + + REQUIRE(std::optional{7} == comm::TelegramParser::tryExtractFieldJobId(tdata)); + REQUIRE(std::optional{2} == comm::TelegramParser::tryExtractFieldCmd(tdata)); + REQUIRE(std::optional{"type1:name1:value1;type2:name2:v a l u e 2;t3:n3:v3"} == comm::TelegramParser::tryExtractFieldOptions(tdata)); + REQUIRE(std::optional{"some_data..."} == comm::TelegramParser::tryExtractFieldData(tdata)); + REQUIRE(std::optional{1} == comm::TelegramParser::tryExtractFieldStatus(tdata)); +} diff --git a/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml b/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml new file mode 100644 index 00000000000..5193264205e --- /dev/null +++ b/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml b/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml new file mode 100644 index 00000000000..6b027fe6a21 --- /dev/null +++ b/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml @@ -0,0 +1,635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/benchmarks/microbenchmarks/and_clk.blif b/vtr_flow/benchmarks/microbenchmarks/and_clk.blif new file mode 100644 index 00000000000..3f31b82b8d7 --- /dev/null +++ b/vtr_flow/benchmarks/microbenchmarks/and_clk.blif @@ -0,0 +1,12 @@ + +.model discrete_dffn +.inputs clk_ni d_i +.outputs d_o + +.names clk_ni int_clk_reg_exp_0 +0 1 + +.subckt dff D=d_i Q=d_o C=int_clk_reg_exp_0 + +.end + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt new file mode 100755 index 00000000000..fbf3358dd11 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt @@ -0,0 +1,29 @@ +############################################ +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/blif/4 + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=diffeq.blif +circuit_list_add=ex5p.blif +circuit_list_add=s298.blif + +# Add architectures to list to sweep +arch_list_add=k4_N4_tileable_90nm.xml + +# Parse info and how to parse +parse_file=vpr_standard.txt + +# How to parse QoR info +qor_parse_file=qor_standard.txt + +# Pass requirements +pass_requirements_file=pass_requirements.txt + +script_params=-starting_stage vpr -track_memory_usage + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt new file mode 100644 index 00000000000..8576e7f7dc7 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt @@ -0,0 +1,4 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem yosys_synth_time max_yosys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time placed_wirelength_est place_mem place_time place_quench_time placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time +k4_N4_tileable_90nm.xml diffeq.blif common 9.82 vpr 61.45 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 417 64 -1 -1 success v8.0.0-6725-gff83963de-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.3.0 on Linux-4.4.0-19041-Microsoft x86_64 2022-11-01T15:38:22 LAPTOP-CVNHOGSN /home/tangxifan/vtr-verilog-to-routing/vtr_flow/tasks 62920 64 39 1935 1974 1 1104 520 23 23 529 clb auto 18.8 MiB 0.14 10227 61.2 MiB 0.57 0.01 6.71028 -1587.65 -6.71028 6.71028 0.25 0.0007736 0.0005813 0.0896507 0.0699632 34 17207 50 983127 929624 921133. 1741.27 7.25 0.536827 0.433199 14874 22 8645 29434 2727679 902790 6.8984 6.8984 -1779.84 -6.8984 0 0 1.17586e+06 2222.80 0.07 0.39 0.058014 0.0505961 +k4_N4_tileable_90nm.xml ex5p.blif common 15.22 vpr 52.07 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 346 8 -1 -1 success v8.0.0-6725-gff83963de-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.3.0 on Linux-4.4.0-19041-Microsoft x86_64 2022-11-01T15:38:22 LAPTOP-CVNHOGSN /home/tangxifan/vtr-verilog-to-routing/vtr_flow/tasks 53324 8 63 1072 1135 0 909 417 21 21 441 clb auto 14.2 MiB 0.10 11660 52.1 MiB 0.46 0.00 7.1886 -311.061 -7.1886 nan 0.20 0.000496 0.0003719 0.0573232 0.0458367 56 22886 46 804782 771343 1.13430e+06 2572.11 12.88 0.310125 0.253318 18533 21 8880 28500 4332796 1534540 7.97924 nan -339.269 -7.97924 0 0 1.45200e+06 3292.52 0.08 0.50 0.0368923 0.0322975 +k4_N4_tileable_90nm.xml s298.blif common 21.05 vpr 75.85 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 571 4 -1 -1 success v8.0.0-6725-gff83963de-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.3.0 on Linux-4.4.0-19041-Microsoft x86_64 2022-11-01T15:38:22 LAPTOP-CVNHOGSN /home/tangxifan/vtr-verilog-to-routing/vtr_flow/tasks 77672 4 6 1942 1948 1 1193 581 26 26 676 clb auto 21.3 MiB 0.14 14254 75.9 MiB 0.69 0.01 13.2884 -103 -13.2884 13.2884 0.33 0.0008181 0.0006197 0.0966404 0.0764946 42 26119 42 1.28409e+06 1.27294e+06 1.41510e+06 2093.35 17.58 0.615507 0.493511 22147 19 9442 45483 4771423 1297512 13.6006 13.6006 -107.558 -13.6006 0 0 1.86822e+06 2763.64 0.19 0.67 0.0640435 0.0560669 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/config.txt new file mode 100644 index 00000000000..a3ceed7c14d --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/config.txt @@ -0,0 +1,27 @@ +############################################## +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/microbenchmarks + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=and_clk.blif + +# Add architectures to list to sweep +arch_list_add=k4_frac_N4_fracff_localClkGen_40nm.xml + +# Parse info and how to parse +parse_file=vpr_standard.txt + +# How to parse QoR info +qor_parse_file=qor_standard.txt + +# Pass requirements +pass_requirements_file=pass_requirements.txt + +# Script parameters +script_params_common =-starting_stage vpr --route_chan_width 300 --max_router_iterations 400 --router_lookahead map --initial_pres_fac 1.0 --router_profiler_astar_fac 1.5 --seed 3 --read_vpr_constraints ../../../../floor_plan.xml:../../../../route_constraint.xml --write_vpr_constraints vpr_constraints.xml --clock_modeling ideal diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/golden_results.txt new file mode 100644 index 00000000000..1de222ff286 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/golden_results.txt @@ -0,0 +1,2 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time placed_wirelength_est place_mem place_time place_quench_time placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_num_rr_graph_nodes crit_path_num_rr_graph_edges crit_path_collapsed_nodes crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops crit_path_total_internal_heap_pushes crit_path_total_internal_heap_pops crit_path_total_external_heap_pushes crit_path_total_external_heap_pops crit_path_total_external_SOURCE_pushes crit_path_total_external_SOURCE_pops crit_path_total_internal_SOURCE_pushes crit_path_total_internal_SOURCE_pops crit_path_total_external_SINK_pushes crit_path_total_external_SINK_pops crit_path_total_internal_SINK_pushes crit_path_total_internal_SINK_pops crit_path_total_external_IPIN_pushes crit_path_total_external_IPIN_pops crit_path_total_internal_IPIN_pushes crit_path_total_internal_IPIN_pops crit_path_total_external_OPIN_pushes crit_path_total_external_OPIN_pops crit_path_total_internal_OPIN_pushes crit_path_total_internal_OPIN_pops crit_path_total_external_CHANX_pushes crit_path_total_external_CHANX_pops crit_path_total_internal_CHANX_pushes crit_path_total_internal_CHANX_pops crit_path_total_external_CHANY_pushes crit_path_total_external_CHANY_pops crit_path_total_internal_CHANY_pushes crit_path_total_internal_CHANY_pops crit_path_rt_node_SOURCE_pushes crit_path_rt_node_SINK_pushes crit_path_rt_node_IPIN_pushes crit_path_rt_node_OPIN_pushes crit_path_rt_node_CHANX_pushes crit_path_rt_node_CHANY_pushes crit_path_adding_all_rt crit_path_adding_high_fanout_rt crit_path_total_number_of_adding_all_rt_from_calling_high_fanout_rt critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_create_rr_graph_time crit_path_create_intra_cluster_rr_graph_time crit_path_tile_lookahead_computation_time crit_path_router_lookahead_computation_time crit_path_total_timing_analysis_time crit_path_total_sta_time +k4_frac_N4_fracff_localClkGen_40nm.xml and_clk.blif common 0.07 vpr 46.89 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 2 2 -1 -1 success bd5999406-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.15.0-67-generic x86_64 2023-03-17T17:48:18 workstation /home/tao/works/dev/route/vtr-verilog-to-routing/vtr_flow/tasks 48016 2 1 4 5 1 4 5 4 4 16 clb auto 7.9 MiB 0.00 11 46.9 MiB 0.00 0.00 1.13498 -1.13498 -1.13498 1.13498 0.00 3.031e-06 1.541e-06 3.5814e-05 2.5748e-05 -1 8 1 215576 107788 120301. 7518.81 0.00 0.00010342 7.9026e-05 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/floor_plan.xml b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/floor_plan.xml new file mode 100644 index 00000000000..861adf40966 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/floor_plan.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/route_constraint.xml b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/route_constraint.xml new file mode 100644 index 00000000000..ca3d794706b --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/route_constraint.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt index 0e973fc00d0..14b58d67652 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt @@ -1,3 +1,4 @@ +regression_tests/vtr_reg_strong/koios regression_tests/vtr_reg_strong/strong_absorb_buffers regression_tests/vtr_reg_strong/strong_analysis_only regression_tests/vtr_reg_strong/strong_analytic_placer @@ -81,5 +82,7 @@ regression_tests/vtr_reg_strong/koios regression_tests/vtr_reg_strong/koios_no_complex_dsp regression_tests/vtr_reg_strong/strong_timing_fail regression_tests/vtr_reg_strong/strong_timing_no_fail +regression_tests/vtr_reg_strong/strong_vpr_constraint +regression_tests/vtr_reg_strong/strong_tileable_rr_graph regression_tests/vtr_reg_strong/strong_noc regression_tests/vtr_reg_strong/strong_flat_router