/* * See vpr/SRC/route/build_switchblocks.c for a detailed description of how the new * switch block format works and what files are involved. * * * A large chunk of this file is dedicated to helping parse the initial switchblock * specificaiton in the XML arch file, providing error checking, etc. * * Another large chunk of this file is dedicated to parsing the actual formulas * specified by the switch block permutation functions into their numeric counterparts. */ #include #include #include #include #include #include #include #include "vtr_assert.h" #include "vtr_util.h" #include "pugixml.hpp" #include "pugixml_util.hpp" #include "arch_error.h" #include "read_xml_util.h" #include "arch_util.h" #include "arch_types.h" #include "physical_types.h" #include "parse_switchblocks.h" using pugiutil::ReqOpt; /**** Function Declarations ****/ /*---- Functions for Parsing Switchblocks from Architecture ----*/ //Load an XML wireconn specification into a t_wireconn_inf t_wireconn_inf parse_wireconn(pugi::xml_node node, const pugiutil::loc_data& loc_data); //Process the desired order of a wireconn static void parse_switchpoint_order(const char* order, SwitchPointOrder& switchpoint_order); //Process a wireconn defined in the inline style (using attributes) void parse_wireconn_inline(pugi::xml_node node, const pugiutil::loc_data& loc_data, t_wireconn_inf& wc); //Process a wireconn defined in the multinode style (more advanced specification) void parse_wireconn_multinode(pugi::xml_node node, const pugiutil::loc_data& loc_data, t_wireconn_inf& wc); //Process a or sub-node of a multinode wireconn t_wire_switchpoints parse_wireconn_from_to_node(pugi::xml_node node, const pugiutil::loc_data& loc_data); /* parses the wire types specified in the comma-separated 'ch' char array into the vector wire_points_vec. * Spaces are trimmed off */ static void parse_comma_separated_wire_types(const char* ch, std::vector& wire_switchpoints); /* parses the wirepoints specified in ch into the vector wire_points_vec */ static void parse_comma_separated_wire_points(const char* ch, std::vector& wire_switchpoints); /* Parses the number of connections type */ static void parse_num_conns(std::string num_conns, t_wireconn_inf& wireconn); /* checks for correctness of a unidir switchblock. */ static void check_unidir_switchblock(const t_switchblock_inf* sb); /* checks for correctness of a bidir switchblock. */ static void check_bidir_switchblock(const t_permutation_map* permutation_map); /* checks for correctness of a wireconn segment specification. */ static void check_wireconn(const t_arch* arch, const t_wireconn_inf& wireconn); /**** Function Definitions ****/ /*---- Functions for Parsing Switchblocks from Architecture ----*/ /* Reads-in the wire connections specified for the switchblock in the xml arch file */ void read_sb_wireconns(const t_arch_switch_inf* /*switches*/, int /*num_switches*/, pugi::xml_node Node, t_switchblock_inf* sb, const pugiutil::loc_data& loc_data) { /* Make sure that Node is a switchblock */ check_node(Node, "switchblock", loc_data); int num_wireconns; pugi::xml_node SubElem; /* count the number of specified wire connections for this SB */ num_wireconns = count_children(Node, "wireconn", loc_data, ReqOpt::OPTIONAL); sb->wireconns.reserve(num_wireconns); if (num_wireconns > 0) { SubElem = get_first_child(Node, "wireconn", loc_data); } for (int i = 0; i < num_wireconns; i++) { t_wireconn_inf wc = parse_wireconn(SubElem, loc_data); sb->wireconns.push_back(wc); SubElem = SubElem.next_sibling(SubElem.name()); } return; } t_wireconn_inf parse_wireconn(pugi::xml_node node, const pugiutil::loc_data& loc_data) { t_wireconn_inf wc; size_t num_children = count_children(node, "from", loc_data, ReqOpt::OPTIONAL); num_children += count_children(node, "to", loc_data, ReqOpt::OPTIONAL); if (num_children == 0) { parse_wireconn_inline(node, loc_data, wc); } else { VTR_ASSERT(num_children > 0); parse_wireconn_multinode(node, loc_data, wc); } return wc; } void parse_wireconn_inline(pugi::xml_node node, const pugiutil::loc_data& loc_data, t_wireconn_inf& wc) { //Parse an inline wireconn definition, using attributes expect_only_attributes(node, {"num_conns", "from_type", "to_type", "from_switchpoint", "to_switchpoint", "from_order", "to_order"}, loc_data); /* get the connection style */ const char* char_prop = get_attribute(node, "num_conns", loc_data).value(); parse_num_conns(char_prop, wc); /* get from type */ char_prop = get_attribute(node, "from_type", loc_data).value(); parse_comma_separated_wire_types(char_prop, wc.from_switchpoint_set); /* get to type */ char_prop = get_attribute(node, "to_type", loc_data).value(); parse_comma_separated_wire_types(char_prop, wc.to_switchpoint_set); /* get the source wire point */ char_prop = get_attribute(node, "from_switchpoint", loc_data).value(); parse_comma_separated_wire_points(char_prop, wc.from_switchpoint_set); /* get the destination wire point */ char_prop = get_attribute(node, "to_switchpoint", loc_data).value(); parse_comma_separated_wire_points(char_prop, wc.to_switchpoint_set); char_prop = get_attribute(node, "from_order", loc_data, ReqOpt::OPTIONAL).value(); parse_switchpoint_order(char_prop, wc.from_switchpoint_order); char_prop = get_attribute(node, "to_order", loc_data, ReqOpt::OPTIONAL).value(); parse_switchpoint_order(char_prop, wc.to_switchpoint_order); } void parse_wireconn_multinode(pugi::xml_node node, const pugiutil::loc_data& loc_data, t_wireconn_inf& wc) { expect_only_children(node, {"from", "to"}, loc_data); /* get the connection style */ const char* char_prop = get_attribute(node, "num_conns", loc_data).value(); parse_num_conns(char_prop, wc); char_prop = get_attribute(node, "from_order", loc_data, ReqOpt::OPTIONAL).value(); parse_switchpoint_order(char_prop, wc.from_switchpoint_order); char_prop = get_attribute(node, "to_order", loc_data, ReqOpt::OPTIONAL).value(); parse_switchpoint_order(char_prop, wc.to_switchpoint_order); size_t num_from_children = count_children(node, "from", loc_data); size_t num_to_children = count_children(node, "to", loc_data); VTR_ASSERT(num_from_children > 0); VTR_ASSERT(num_to_children > 0); for (pugi::xml_node child : node.children()) { if (child.name() == std::string("from")) { t_wire_switchpoints from_switchpoints = parse_wireconn_from_to_node(child, loc_data); wc.from_switchpoint_set.push_back(from_switchpoints); } else if (child.name() == std::string("to")) { t_wire_switchpoints to_switchpoints = parse_wireconn_from_to_node(child, loc_data); wc.to_switchpoint_set.push_back(to_switchpoints); } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Unrecognized child node '%s' of parent node '%s'", node.name(), child.name()); } } } t_wire_switchpoints parse_wireconn_from_to_node(pugi::xml_node node, const pugiutil::loc_data& loc_data) { expect_only_attributes(node, {"type", "switchpoint"}, loc_data); size_t attribute_count = count_attributes(node, loc_data); if (attribute_count != 2) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Expected only 2 attributes on node '%s'", node.name()); } t_wire_switchpoints wire_switchpoints; wire_switchpoints.segment_name = get_attribute(node, "type", loc_data).value(); auto points_str = get_attribute(node, "switchpoint", loc_data).value(); for (const auto& point_str : vtr::split(points_str, ",")) { int switchpoint = vtr::atoi(point_str); wire_switchpoints.switchpoints.push_back(switchpoint); } if (wire_switchpoints.switchpoints.empty()) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Empty switchpoint specification", node.name()); } return wire_switchpoints; } static void parse_switchpoint_order(const char* order, SwitchPointOrder& switchpoint_order) { if (order == std::string("")) { switchpoint_order = SwitchPointOrder::SHUFFLED; //Default } else if (order == std::string("fixed")) { switchpoint_order = SwitchPointOrder::FIXED; } else if (order == std::string("shuffled")) { switchpoint_order = SwitchPointOrder::SHUFFLED; } else { archfpga_throw(__FILE__, __LINE__, "Unrecognized switchpoint order '%s'", order); } } /* parses the wire types specified in the comma-separated 'ch' char array into the vector wire_points_vec. * Spaces are trimmed off */ static void parse_comma_separated_wire_types(const char* ch, std::vector& wire_switchpoints) { auto types = vtr::split(ch, ","); if (types.empty()) { archfpga_throw(__FILE__, __LINE__, "parse_comma_separated_wire_types: found empty wireconn wire type entry\n"); } for (const auto& type : types) { t_wire_switchpoints wsp; wsp.segment_name = type; wire_switchpoints.push_back(wsp); } } /* parses the wirepoints specified in the comma-separated 'ch' char array into the vector wire_points_vec */ static void parse_comma_separated_wire_points(const char* ch, std::vector& wire_switchpoints) { auto points = vtr::split(ch, ","); if (points.empty()) { archfpga_throw(__FILE__, __LINE__, "parse_comma_separated_wire_points: found empty wireconn wire point entry\n"); } for (const auto& point_str : points) { int point = vtr::atoi(point_str); for (auto& wire_switchpoint : wire_switchpoints) { wire_switchpoint.switchpoints.push_back(point); } } } static void parse_num_conns(std::string num_conns, t_wireconn_inf& wireconn) { //num_conns is now interpretted as a formula and processed in build_switchblocks wireconn.num_conns_formula = num_conns; } /* Loads permutation funcs specified under Node into t_switchblock_inf. Node should be * */ void read_sb_switchfuncs(pugi::xml_node Node, t_switchblock_inf* sb, const pugiutil::loc_data& loc_data) { /* Make sure the passed-in is correct */ check_node(Node, "switchfuncs", loc_data); pugi::xml_node SubElem; /* get the number of specified permutation functions */ int num_funcs = count_children(Node, "func", loc_data, ReqOpt::OPTIONAL); const char* func_type; const char* func_formula; std::vector* func_ptr; /* used to index into permutation map of switchblock */ SB_Side_Connection conn; /* now we iterate through all the specified permutation functions, and * load them into the switchblock structure as appropriate */ if (num_funcs > 0) { SubElem = get_first_child(Node, "func", loc_data); } for (int ifunc = 0; ifunc < num_funcs; ifunc++) { /* get function type */ func_type = get_attribute(SubElem, "type", loc_data).as_string(nullptr); /* get function formula */ func_formula = get_attribute(SubElem, "formula", loc_data).as_string(nullptr); /* go through all the possible cases of func_type */ if (0 == strcmp(func_type, "lt")) { conn.set_sides(LEFT, TOP); } else if (0 == strcmp(func_type, "lr")) { conn.set_sides(LEFT, RIGHT); } else if (0 == strcmp(func_type, "lb")) { conn.set_sides(LEFT, BOTTOM); } else if (0 == strcmp(func_type, "tl")) { conn.set_sides(TOP, LEFT); } else if (0 == strcmp(func_type, "tb")) { conn.set_sides(TOP, BOTTOM); } else if (0 == strcmp(func_type, "tr")) { conn.set_sides(TOP, RIGHT); } else if (0 == strcmp(func_type, "rt")) { conn.set_sides(RIGHT, TOP); } else if (0 == strcmp(func_type, "rl")) { conn.set_sides(RIGHT, LEFT); } else if (0 == strcmp(func_type, "rb")) { conn.set_sides(RIGHT, BOTTOM); } else if (0 == strcmp(func_type, "bl")) { conn.set_sides(BOTTOM, LEFT); } else if (0 == strcmp(func_type, "bt")) { conn.set_sides(BOTTOM, TOP); } else if (0 == strcmp(func_type, "br")) { conn.set_sides(BOTTOM, RIGHT); } else { /* unknown permutation function */ archfpga_throw(__FILE__, __LINE__, "Unknown permutation function specified: %s\n", func_type); } func_ptr = &(sb->permutation_map[conn]); /* Here we load the specified switch function(s) */ func_ptr->push_back(std::string(func_formula)); func_ptr = nullptr; /* get the next switchblock function */ SubElem = SubElem.next_sibling(SubElem.name()); } return; } /* checks for correctness of switch block read-in from the XML architecture file */ void check_switchblock(const t_switchblock_inf* sb, const t_arch* arch) { /* get directionality */ enum e_directionality directionality = sb->directionality; /* Check for errors in the switchblock descriptions */ if (UNI_DIRECTIONAL == directionality) { check_unidir_switchblock(sb); } else { VTR_ASSERT(BI_DIRECTIONAL == directionality); check_bidir_switchblock(&(sb->permutation_map)); } /* check that specified wires exist */ for (const auto& wireconn : sb->wireconns) { check_wireconn(arch, wireconn); } //TODO: /* check that the wire segment directionality matches the specified switch block directionality */ /* check for duplicate names */ /* check that specified switches exist */ /* check that type of switchblock matches type of switch specified */ } /* checks for correctness of a unidirectional switchblock. hard exit if error found (to be changed to throw later) */ static void check_unidir_switchblock(const t_switchblock_inf* sb) { /* Check that the destination wire points are always the starting points (i.e. of wire point 0) */ for (const t_wireconn_inf& wireconn : sb->wireconns) { for (const t_wire_switchpoints& wire_to_points : wireconn.to_switchpoint_set) { if (wire_to_points.switchpoints.size() > 1 || wire_to_points.switchpoints[0] != 0) { archfpga_throw(__FILE__, __LINE__, "Unidirectional switch blocks are currently only allowed to drive the start points of wire segments\n"); } } } } /* checks for correctness of a bidirectional switchblock */ static void check_bidir_switchblock(const t_permutation_map* permutation_map) { /**** check that if side1->side2 is specified, then side2->side1 is not, as it is implicit ****/ /* variable used to index into the permutation map */ SB_Side_Connection conn; /* iterate over all combinations of from_side -> to side */ for (e_side from_side : {TOP, RIGHT, BOTTOM, LEFT}) { for (e_side to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* can't connect a switchblock side to itself */ if (from_side == to_side) { continue; } /* index into permutation map with this variable */ conn.set_sides(from_side, to_side); /* check if a connection between these sides exists */ t_permutation_map::const_iterator it = (*permutation_map).find(conn); if (it != (*permutation_map).end()) { /* the two sides are connected */ /* check if the opposite connection has been specified */ conn.set_sides(to_side, from_side); it = (*permutation_map).find(conn); if (it != (*permutation_map).end()) { archfpga_throw(__FILE__, __LINE__, "If a bidirectional switch block specifies a connection from side1->side2, no connection should be specified from side2->side1 as it is implicit.\n"); } } } } return; } static void check_wireconn(const t_arch* arch, const t_wireconn_inf& wireconn) { for (const t_wire_switchpoints& wire_switchpoints : wireconn.from_switchpoint_set) { auto seg_name = wire_switchpoints.segment_name; //Make sure the segment exists const t_segment_inf* seg_info = find_segment(arch, seg_name); if (!seg_info) { archfpga_throw(__FILE__, __LINE__, "Failed to find segment '%s' for from type specification\n", seg_name.c_str()); } //Check that the specified switch points are valid for (int switchpoint : wire_switchpoints.switchpoints) { if (switchpoint < 0) { archfpga_throw(__FILE__, __LINE__, "Invalid from_switchpoint '%d' (must be >= 0)\n", switchpoint, seg_name.c_str()); } if (switchpoint >= seg_info->length) { archfpga_throw(__FILE__, __LINE__, "Invalid from_switchpoints '%d' (must be < %d)\n", switchpoint, seg_info->length); } //TODO: check that points correspond to valid sb locations } } for (const t_wire_switchpoints& wire_switchpoints : wireconn.to_switchpoint_set) { auto seg_name = wire_switchpoints.segment_name; //Make sure the segment exists const t_segment_inf* seg_info = find_segment(arch, seg_name); if (!seg_info) { archfpga_throw(__FILE__, __LINE__, "Failed to find segment '%s' for to type specification\n", seg_name.c_str()); } //Check that the specified switch points are valid for (int switchpoint : wire_switchpoints.switchpoints) { if (switchpoint < 0) { archfpga_throw(__FILE__, __LINE__, "Invalid to_switchpoint '%d' (must be >= 0)\n", switchpoint, seg_name.c_str()); } if (switchpoint >= seg_info->length) { archfpga_throw(__FILE__, __LINE__, "Invalid to_switchpoints '%d' (must be < %d)\n", switchpoint, seg_info->length); } //TODO: check that points correspond to valid sb locations } } } /*---- Functions for Parsing the Symbolic Switchblock Formulas ----*/ /* returns integer result according to the specified switchblock formula and data. formula may be piece-wise */ int get_sb_formula_raw_result(const char* formula, const t_formula_data& mydata) { /* the result of the formula will be an integer */ int result = -1; /* check formula */ if (nullptr == formula) { archfpga_throw(__FILE__, __LINE__, "in get_sb_formula_result: SB formula pointer NULL\n"); } else if ('\0' == formula[0]) { archfpga_throw(__FILE__, __LINE__, "in get_sb_formula_result: SB formula empty\n"); } /* parse based on whether formula is piece-wise or not */ if (is_piecewise_formula(formula)) { //EXPERIMENTAL result = parse_piecewise_formula(formula, mydata); } else { result = parse_formula(formula, mydata); } return result; }